diff --git a/PlantWorld.cs b/PlantWorld.cs index 522e720..659d0e7 100644 --- a/PlantWorld.cs +++ b/PlantWorld.cs @@ -60,7 +60,7 @@ public partial class PlantWorld : Node2D if (eventMouseButton.ButtonIndex == MouseButton.Right && eventMouseButton.Pressed) { var pos = eventMouseButton.Position * GetViewportTransform(); - CreatePlant(pos, 128); + CreatePlant(pos, 12); } } } diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..b370ceb --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + diff --git a/main.tscn b/main.tscn index 97b8f8d..18b9708 100644 --- a/main.tscn +++ b/main.tscn @@ -1,10 +1,13 @@ -[gd_scene load_steps=3 format=3 uid="uid://d2rgfedikyr2w"] +[gd_scene load_steps=4 format=3 uid="uid://d2rgfedikyr2w"] -[ext_resource type="PackedScene" uid="uid://ixy2rotcnjk2" path="res://plant.tscn" id="1_mv10f"] +[ext_resource type="Script" path="res://Main.cs" id="1_jrcl7"] [ext_resource type="PackedScene" path="res://camera_2d.tscn" id="2_pvpm2"] +[ext_resource type="PackedScene" uid="uid://cvjqldltcr7nl" path="res://plantWorld.tscn" id="2_t7d21"] [node name="Main" type="Node2D"] - -[node name="Plant" parent="." instance=ExtResource("1_mv10f")] +script = ExtResource("1_jrcl7") [node name="Camera2D" parent="." instance=ExtResource("2_pvpm2")] + +[node name="PlantWorld" parent="." node_paths=PackedStringArray("Camera") instance=ExtResource("2_t7d21")] +Camera = NodePath("../Camera2D") diff --git a/src/BaseLifeComponent.cs b/src/BaseLifeComponent.cs index fc13cc4..a812c1e 100644 --- a/src/BaseLifeComponent.cs +++ b/src/BaseLifeComponent.cs @@ -1,4 +1,4 @@ -namespace DrawingDemo; +namespace LifeComponent; using Godot; @@ -6,7 +6,7 @@ public delegate void DiedEventHandler(); public class BaseLifeComponent { - public float Energy = 1000; + public float Energy = 100; public float Age = 0; public int MaxAge = 150; public float Health = 100; @@ -15,10 +15,10 @@ public class BaseLifeComponent public event DiedEventHandler OnDied; - public void Tick(float timeDelta) + public void Tick(float timeDelta, float size) { Age += timeDelta; - ConsumeEnergy(0.1f); //Baseconsumption + ConsumeEnergy(0.1f * size); //Baseconsumption if (Age > MaxAge || Health <= 0) { diff --git a/src/Genetics.cs b/src/Genetics.cs index 3e81314..2aef5a1 100644 --- a/src/Genetics.cs +++ b/src/Genetics.cs @@ -1,9 +1,10 @@ +using System; using Godot; using Color = Godot.Color; -namespace DrawingDemo; +namespace LifeComponent; -public static class ColorHelper +public static class GeneHelper { public static Color ColorFrom6Bit(byte colorcode) { @@ -19,10 +20,22 @@ public static class ColorHelper return new Color(value1 / colorperBit, value2 / colorperBit, value3 / colorperBit, value4 / colorperBit); } + + + // public static int MapCodonToInt(byte codon, int min, int max) + // { + // int result = 0; + // if (codon > max) + // result = codon / max; + // if (codon < min) + // result = codon; + // + // return result; + // } } public class Genetics { - public GenStorage DNA = new GenStorage(); + public GenStorage DNA; public Color MainColor { @@ -30,10 +43,17 @@ public class Genetics { var codon = DNA.GetCodon(GenMapper.MainColor); - return ColorHelper.ColorFrom6Bit(codon); + return GeneHelper.ColorFrom6Bit(codon); } } + public Genetics() + { + DNA = new GenStorage(12); + DNA.Randomize(); + + } + } public enum GenMapper @@ -52,9 +72,10 @@ public struct GenStorage { public byte[] Codons; - public GenStorage() + public GenStorage(int codonCount) { - Codons = new byte[4] {42,0,0,0}; + Codons = new byte[codonCount];// {42,0,0,0}; + } @@ -89,4 +110,38 @@ public struct GenStorage return Codons[codon]; } + public byte[] GetBasePairs(int codonNr) + { + var codon = GetCodon(codonNr); + byte value1 = (byte)((codon & 0b11000000) >> 6); + byte value2 = (byte)((codon & 0b00110000) >> 4); + byte value3 = (byte)((codon & 0b00001100) >> 2); + byte value4 = (byte)(codon & 0b00000011); + return new[] { value1, value2, value3, value4 }; + } + public float CodonAsFloat(int codonNr, float min = 0, float max = 1, int pairs = 4) + { + float result = 0; + var codon = GetCodon(codonNr); + if (pairs == 4) + { + result = Mathf.Lerp(min, max, (float) 255 / codon); + } + else + { + var values = GetBasePairs(codonNr); + // ... + } + + return result; + } +} + + + +public class PhenotypeAttribute : Attribute +{ + // Codon Marker index and base-pair lenght + // z.b. für angle between 0 - 6.2 = 1 Codon = 8 Byte -> lerp(0, 6.2, codon) + } \ No newline at end of file diff --git a/src/Plant.cs b/src/Plant.cs index b1349d9..919b583 100644 --- a/src/Plant.cs +++ b/src/Plant.cs @@ -4,112 +4,302 @@ using System.Collections.Generic; using System.Linq; public partial class Plant : MeshInstance2D { + private PlantLife _plantLife; public Dictionary Segments; - public Vector2 Origin; private int _segmentIdCounter = 0; public PlantSegmentNode RootNode; - public Rect2 ClientRectangle = new Rect2(Vector2.Zero, new Vector2(-80, 80)); - + public Rect2 ClientRectangle = new Rect2(Vector2.Zero, new Vector2(-10, 10)); + public bool CanGrow = true; public override void _Ready() { - Start(); + //Start(); - - } - public void Start() + public void Init(int startNodes) { + _plantLife = new PlantLife(this); Segments = new Dictionary(); - PlantSegmentNode root = new PlantSegmentNode(null, maxChildren:1); - root.Direction = Vector2.Up; - root.NodeType = SegmentNodeTypes.Trunk; + _plantLife.Init(); + + PlantSegmentNode root = new PlantSegmentNode(null, maxChildren:_plantLife._plantParams.RootChildren); + root.Direction = _plantLife._plantParams.GrowDirection; + root.NodeType = PlantNodeTypes.Trunk; + RootNode = root; Segments.Add(_segmentIdCounter, root); _segmentIdCounter++; - GrowDemo(4096); + GrowDemo(startNodes); this.Mesh = CreateMesh(); + CalulateClientRectangle(); + } - public Mesh CreateMesh() + public void CreateRootNode() { - ArrayMesh arrMesh = new ArrayMesh(); - var arrays = new Godot.Collections.Array(); - arrays.Resize((int)Mesh.ArrayType.Max); - - //Vector2[] vertices = CalculateVertices(); - Vector2[] vertices = CalculateLineVertices(); - Color[] colors = Helper.MakeColorArray(vertices.Length, 3); - - arrays[(int)Mesh.ArrayType.Vertex] = vertices; - arrays[(int)Mesh.ArrayType.Color] = colors; - //arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays); - arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Lines, arrays); - return arrMesh; } - - // Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(double delta) { + if (_plantLife != null) + _plantLife.Tick((float)delta); + } + + #region Segments + + public void GrowDemo(int nodes) +{ + for (int i = 0; i < nodes -1; i++) + { + GrowNewNode( PlantParams.Default()); } - public void GrowDemo(int nodes) +} + + public void AddNewSegment(PlantSegmentNode seg, PlantSegmentNode parent, int index) { - for (int i = 0; i < nodes -1; i++) + if (parent.AddChild(seg, index)) + { + Segments.Add(_segmentIdCounter, seg); + _segmentIdCounter++; + + this.UpdateAllGeometry(); + } + else { - GrowNode(); + GD.PrintErr("Error bei Add"); } } + public void GrowNewNode(PlantParams pp) + { + int maxChildren = pp.MaxChildren; + PlantNodeTypes type = PlantNodeTypes.Trunk; + + type = pp.GetRatioNodeType(); + PlantNodeParams p = pp.NodeParams; + + int maxDescentants = 4096; + + var maxADiv = p.MaxToParentAngleRange / 2; + var a1 = p.MinToParentAngleRange; + var a2 = p.MaxToParentAngleRange; + + var randAngleZeroDiv = (float)GD.RandRange(a1, a2); + + float randF = (0.5f - GD.Randf()); + int randI = GD.RandRange(0, 3); - public void GrowNewNode(PlantNodeParams p) + var parent = GetFreeSlotSegment(false); + if (parent == null) { - //var randAngle = (float)GD.RandRange(-p.AllowedParentDiv/2, p.AllowedParentDiv/2); - var randAngleZeroDiv = (float)GD.RandRange(20, 45); - - float randF = (0.5f - GD.Randf()); - int randI = GD.RandRange(0, 10); - - int maxChildren = p.MaxChildren; - int maxDescentants = 4096; - var parent = GetFreeSlotSegment(false); - PlantSegmentNode seg; - Vector2 dir = parent.Direction; - - - var freeindex = parent.FreeNodeIndex(); - if (freeindex == -1) - throw new System.Exception("Now free index, trotz free slot"); - switch (freeindex) - { - case 0: // Center - dir = dir.Rotated(randF); - maxChildren = 2; - maxDescentants = 4096; - break; - case 1: // left - dir = dir.Rotated(-randAngleZeroDiv); - break; - case 2: - dir = dir.Rotated(+randAngleZeroDiv); - break; - // 3D... + GD.PrintErr("Outof Nodes"); + CanGrow = false; + return; + } + + PlantSegmentNode seg; + Vector2 dir = parent.Direction; + Vector2 size = parent.Size; + + // get sibling dirs, if + // var sibRots = parent.GetChildrenToParentRotations(); + // if (sibRots.Length == 0) // first child -> dir: center + // { + // dir = dir.Rotated(randF/8); + // } + // else + // { + // calc free spot: + // min distAngle = ~ maxparentdiv / max_children + // float optimalSpace = p.MaxToParentAngleRange / p.MaxChildren; + // if (p.MinAngleBetweenSibs > optimalSpace) + // { + // // to tight -> dont grow. + // } + // + // var r = FindSpot(p, sibRots); + //} + + var freeindex = parent.FreeNodeIndex(); + if (freeindex == -1) + throw new System.Exception("Now free index, trotz free slot"); + switch (freeindex) + { + case 0: // Center + dir = dir.Rotated(randF/8); + maxChildren = 2; + size *= 0.95f; + maxDescentants = 4096; + break; + case 1: // left + dir = dir.Rotated(-randAngleZeroDiv); + size *= 0.85f; + maxDescentants = 128; + maxChildren = maxChildren; + break; + case 2: + dir = dir.Rotated(+randAngleZeroDiv); + size *= 0.85f; + maxDescentants = 128; + maxChildren = maxChildren; + break; + // 3D... + + } + if (RootNode.Direction.Dot(dir) < 0) // ..<0 = 180 grad maxRootDeviationAngle) + maxChildren = 0; // wenn überhang -> branach ende + + if (maxChildren == 0) // wird nix mehr -> kann ein blatt werden + { + //type = PlantNodeTypes.Leafes; + } + + + seg = new PlantSegmentNode(parent, maxChildren, maxDescentants); + seg.Direction = dir; + seg.Size = size; + + seg.NodeType = type; + + // if (CheckCollision(seg)) + // { + // // Add stump dummy - so slot is ignored + // seg.NodeType = PlantNodeTypes.Stump; + // seg.MaxChildren = seg.ChildrenCount; + // seg.Size = Vector2.One; + // } + + AddNewSegment(seg, parent, freeindex); + return; + + if (parent.AddChild(seg, freeindex)) + { + Segments.Add(_segmentIdCounter, seg); + _segmentIdCounter++; + } + else + { + GD.PrintErr("Error bei Add"); + } + } + + public float FindSpot(PlantNodeParams p, float[] usedAngles) + { + // calc free spot: + // min distAngle = ~ maxparentdiv / max_children + + if (usedAngles.Length == 0) + return p.PreferedRotationToParent; + + float optimalSpace = p.MaxToParentAngleRange / p.MaxChildren; + if (p.MinAngleBetweenSibs > optimalSpace) + { + // to tight -> dont grow. + GD.PrintErr("FindSpot: to tight! = params shit"); + return 0; + } + + List spots = new List(); + + for (int i = 0; i < usedAngles.Length; i++) + { + if (i + 1 >= usedAngles.Length) + break; - } - seg = new PlantSegmentNode(parent, p.MaxChildren); - if (parent.AddChild(seg, freeindex)) + if (usedAngles[i] - usedAngles[i + 1] >= p.MinAngleBetweenSibs) { - Segments.Add(_segmentIdCounter, seg); - _segmentIdCounter++; - } - else - { - GD.PrintErr("Error bei Add"); + spots.Add(usedAngles[i]); } } - public void GrowNode() + + + if (spots.Count > 0) + { + return spots[GD.RandRange(0, spots.Count - 1)]; + } + else + { + return 0; + } + } + + public void GrowNewNode2(PlantNodeParams p) + { + int maxChildren = p.MaxChildren; + int maxDescentants = 4096; + + //var randAngle = (float)GD.RandRange(-p.AllowedParentDiv/2, p.AllowedParentDiv/2); + var randAngleZeroDiv = (float)GD.RandRange(12, 23); + randAngleZeroDiv = Mathf.DegToRad(randAngleZeroDiv); + + float randF = (0.5f - GD.Randf()); + int randI = GD.RandRange(0, 3); + + + var parent = GetFreeSlotSegment(false); + PlantSegmentNode seg; + Vector2 dir = parent.Direction; + Vector2 size = parent.Size; + + // get sibling dirs, if + var sibRots = parent.GetChildrenToParentRotations(); + + + var freeindex = parent.FreeNodeIndex(); + if (freeindex == -1) + throw new System.Exception("Now free index, trotz free slot"); + switch (freeindex) + { + case 0: // Center + dir = dir.Rotated(randF/8); + maxChildren = 2; + size *= 0.95f; + maxDescentants = 4096; + break; + case 1: // left + dir = dir.Rotated(-randAngleZeroDiv); + size *= 0.85f; + maxDescentants = 128; + maxChildren = maxChildren - randI; + break; + case 2: + dir = dir.Rotated(+randAngleZeroDiv); + size *= 0.85f; + maxDescentants = 128; + maxChildren = maxChildren - randI; + break; + // 3D... + + } + if (RootNode.Direction.Dot(dir) < 0) // ..<0 = 180 grad maxRootDeviationAngle) + maxChildren = 0; // wenn überhang -> branach ende + + seg = new PlantSegmentNode(parent, maxChildren, maxDescentants); + seg.Direction = dir; + seg.Size = size; + + // if (CheckCollision(seg)) + // { + // // Add stump dummy - so slot is ignored + // seg.NodeType = PlantNodeTypes.Stump; + // seg.MaxChildren = seg.ChildrenCount; + // seg.Size = Vector2.One; + // } + + if (parent.AddChild(seg, freeindex)) + { + Segments.Add(_segmentIdCounter, seg); + _segmentIdCounter++; + } + else + { + GD.PrintErr("Error bei Add"); + } + } + public void GrowNode_old() { // Config float minAngleDiv =Mathf.DegToRad(23); @@ -173,7 +363,7 @@ public partial class Plant : MeshInstance2D // { // //GD.Print("Collision ", seg); // // Add stump dummy - so slot is ignored - // seg.NodeType = SegmentNodeTypes.Stump; + // seg.NodeType = PlantNodeTypes.Stump; // seg.MaxChildren = seg.ChildrenCount; // seg.Size = Vector2.Zero;//seg.Size * 0.4f; // } @@ -189,6 +379,86 @@ public partial class Plant : MeshInstance2D } } + + + public bool CheckCollision(PlantSegmentNode node) + { + + foreach (var kv in Segments) + { + var seg = kv.Value; + + if (seg == node || seg.NodeType == PlantNodeTypes.Stump || seg == node.Parent) + continue; + + + Vector2 from1 = node.PositionRelativeParent; + Vector2 to1 = from1 + node.Line; + + Vector2 from2 = seg.PositionRelativeParent; + Vector2 to2 = seg.PositionRelativeParent + seg.Line; + var intersection = Geometry2D.SegmentIntersectsSegment(from1, to1, from2, to2); + //Geometry2D.inter + if (intersection.VariantType != Variant.Type.Nil) + { + return true; + } + + } + return false; + + } + private PlantSegmentNode GetFreeSlotSegment(bool descending = false) + { + + Dictionary elements = Segments; + //List list = elements.OrderBy(x => x.Key).ToList(); + + // if (descending) + // elements = elements.OrderByDescending(x => x.Key).ToDictionary(x => x.Key, x => x.Value); + // else + // { + // elements = elements.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); + // + // } + + foreach (var item in elements) + { + if (item.Value.HasFreeChildSlot) + return item.Value; + } + return null; + } + + #endregion + + #region Geometry + + + public void UpdateAllGeometry() + { + this.Mesh = CreateMesh(); + CalulateClientRectangle(); + } + + public Mesh CreateMesh() + { + ArrayMesh arrMesh = new ArrayMesh(); + var arrays = new Godot.Collections.Array(); + arrays.Resize((int)Mesh.ArrayType.Max); + + //Vector2[] vertices = CalculateVertices(); + Vector2[] vertices = CalculateLineVertices(); + Color[] colors = Helper.MakeColorArray(vertices.Length, 3); + + arrays[(int)Mesh.ArrayType.Vertex] = vertices; + arrays[(int)Mesh.ArrayType.Color] = colors; + + //arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays); + arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Lines, arrays); + return arrMesh; + } + public Vector2[] CalculateLineVertices() { int vertexCount = 2 * Segments.Count; @@ -246,55 +516,29 @@ public partial class Plant : MeshInstance2D } return vertices; } - - public bool CheckCollision(PlantSegmentNode node) + + public void CalulateClientRectangle() { - return false; - - } - private PlantSegmentNode GetFreeSlotSegment(bool descending = false) - { - - Dictionary elements = Segments; - //List list = elements.OrderBy(x => x.Key).ToList(); - - if (descending) - elements = elements.OrderByDescending(x => x.Key).ToDictionary(x => x.Key, x => x.Value); - else - { - elements = elements.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); + float margin = 2; + float maxX = Segments.Max(x => (x.Value.VecToRoot + x.Value.Line).X); + float maxY = Segments.Max(x => (x.Value.VecToRoot + x.Value.Line).Y); + float minX = Segments.Min(x => x.Value.VecToRoot.X); + float minY = Segments.Min(x => x.Value.VecToRoot.Y); + + Vector2 min = new Vector2(minX - margin, minY - margin); + Vector2 max = new Vector2(maxX + margin, maxY + margin); + Vector2 pos = min; + Vector2 size = max - min; + pos += Position; + ClientRectangle = new Rect2(pos, size); + // je nachdemn ob + oder - .... + //ClientRectangle = new Rect2(min, max); - } - - foreach (var item in elements) - { - if (item.Value.HasFreeChildSlot) - return item.Value; - } - return null; } -} + #endregion -public struct PlantNodeParams -{ - // Angle to grow - calculated from 0°(front) localspace - public float AllowedParentDiv = Mathf.DegToRad(80); - public float AllowedRootDiv = Mathf.DegToRad(180); - //public float MinAngleParentDiv = 0; - //public float MaxAngleParentDiv = 1f; - //public float MaxRootDeviationAngle = 3.14f; - public int MaxChildren = 3; - public Vector2 Size = Vector2.One; - public Vector2 Direction = Vector2.Up; - public string ConfigName = "default"; - public SegmentNodeTypes NodeType = SegmentNodeTypes.Trunk; - - public PlantNodeParams() - { - - } } diff --git a/src/PlantLife.cs b/src/PlantLife.cs new file mode 100644 index 0000000..765c853 --- /dev/null +++ b/src/PlantLife.cs @@ -0,0 +1,87 @@ + +using System; +using System.Collections.Generic; +using Godot; +using LifeComponent; + +public class PlantLife +{ + private Genetics _genes; + private BaseLifeComponent _baseLife; + private Plant _plant_RefUp; + public PlantParams _plantParams; + + public float _size = 1f; + //public PlantNodeParams RootParams; + public PlantLife(Plant plantRef) + { + + _plant_RefUp = plantRef; + _genes = new Genetics(); + _baseLife = new BaseLifeComponent(); + + } + + public void Init() + { + _plantParams = new PlantParams(); + _plantParams.NodeParams.maxToRootAngleRange = Mathf.DegToRad(270); + + GenesToPhenes(); + //_plantParams.GrowDirection = Vector2.Right; + _plantParams.NodeParams.MaxToParentAngleRange = Mathf.DegToRad(90); + _baseLife.Born(); + } + + public void GenesToPhenes() + { + // var c3 = _genes.DNA.Codons[3]; + var bytes = _genes.DNA.GetBasePairs(3); + + var a = bytes[0]; + _plantParams.AverageChildren = a; + var b = bytes[1]; + + //var c = bytes[2]; + //_plantParams.MaxChildren = c + 1; + + var d = bytes[3]; + + if (b == 0) + _plantParams.GrowDirection = Vector2.Up; + if (b == 1) + _plantParams.GrowDirection = Vector2.Left; + if (b == 2) + _plantParams.GrowDirection = Vector2.Right; + if (b == 3) + _plantParams.GrowDirection = Vector2.Down; + + //var v = Mathf.LerpAngle(0.5f, 5.8f, (float)d/4); // 0 - 6.2 + var v = Mathf.Lerp(0.5f, 5.8f, (float)d/4); + _plantParams.NodeParams.MaxToParentAngleRange = v; + + } + + public void Tick(float deltaTime) + { + _baseLife.Tick(deltaTime, _size); + + _baseLife.AddEnergy(1 * _size * 0.7f); + Grow(); + } + + public void Grow() + { + if (_baseLife.Energy > 50) + { + if (_plant_RefUp.CanGrow) + _plant_RefUp.GrowNewNode(_plantParams); + + //GD.Print("Life grew! :) "); + _baseLife.Energy -= 30; + _size = _plant_RefUp.Segments.Count; + + //_plant_RefUp.AddNewSegment() + } + } +} \ No newline at end of file diff --git a/src/PlantParmeters.cs b/src/PlantParmeters.cs new file mode 100644 index 0000000..8a98ecd --- /dev/null +++ b/src/PlantParmeters.cs @@ -0,0 +1,91 @@ +using System; +using Godot; +using LifeComponent; + + +public struct PlantParams +{ + public PlantNodeParams NodeParams; + + public float maxToRootAngleRange = Mathf.DegToRad(180); + public Vector2 GrowDirection = Vector2.Up; + public int RootChildren = 1; + public int AverageChildren = 2; + [Phenotype] + public int MaxChildren = 3; // Hardlimit - should overrule Nodes + public float SizeDecayOrderFactor = 0.99f; + + public float LeaveRatio = 0.1f; + public float TrunkRatio = 0.5f; + + public PlantParams() + { + //RootParams = rootParams; + NodeParams = PlantNodeParams.Default(); + NodeParams.MaxChildren = MaxChildren; + } + + public static PlantParams Default() + { + return new PlantParams(); + } + public PlantNodeTypes GetRatioNodeType() + { + var r1 = (float)GD.RandRange(0, 10) / 10; + + //if (r1 < LeaveRatio) + // return PlantNodeTypes.Leafes; + if (r1 > LeaveRatio && r1 < TrunkRatio) + return PlantNodeTypes.Trunk; + + return PlantNodeTypes.Branch; + } + + + +} + +public struct PlantNodeParams +{ + // Angle to grow - calculated from 0°(front) localspace. +/- angle/2 + [Phenotype] + public float MinToParentAngleRange = Mathf.DegToRad(12); + [Phenotype] + public float MaxToParentAngleRange = Mathf.DegToRad(80); + + public float maxToRootAngleRange = Mathf.DegToRad(180); + public float MinAngleBetweenSibs = Mathf.DegToRad(12); + //public float MinAngleParentDiv = 0; + //public float MaxAngleParentDiv = 1f; + //public float MaxRootDeviationAngle = 3.14f; + public int MaxChildren = 3; + public Vector2 Size = Vector2.One; + public float PreferedRotationToParent = 0f; + + public string ConfigName = "default"; + public PlantNodeTypes NodeType = PlantNodeTypes.Trunk; + + public PlantNodeParams() + { + + } + + + public static PlantNodeParams Default() + { + return new PlantNodeParams(); + } + + public static PlantNodeParams Trunk() + { + var p = new PlantNodeParams(); + p.MaxChildren = 2; + p.NodeType = PlantNodeTypes.Trunk; + p.maxToRootAngleRange = 0.1f; + + + return p; + } + +} + diff --git a/src/SegmentNodes.cs b/src/SegmentNodes.cs index 119acd7..6dc6640 100644 --- a/src/SegmentNodes.cs +++ b/src/SegmentNodes.cs @@ -4,11 +4,11 @@ using System.Linq; using Godot; -public enum SegmentNodeTypes +public enum PlantNodeTypes { Trunk, Branch, - Leave, + Leafes, Fruit, Stump, // basically blocks growth } @@ -21,33 +21,41 @@ public class PlantSegmentNode : LifeFormSegmentNode public Vector2 VecToRoot; public Vector2 Direction; - private SegmentNodeTypes _nodeType = SegmentNodeTypes.Branch; + private PlantNodeTypes _nodeType = PlantNodeTypes.Branch; - public SegmentNodeTypes NodeType + public PlantNodeTypes NodeType { get { return _nodeType; } set { - if (value == SegmentNodeTypes.Stump) + _nodeType = value; + if (value == PlantNodeTypes.Stump) { Size = Vector2.Zero; MaxChildren = ChildrenCount; Direction = ((PlantSegmentNode)Parent).Direction.Normalized(); - _nodeType = value; + base.RedimChildren(); + } - + else if (value == PlantNodeTypes.Branch) + { + //MaxChildren = base.MaxChildren + 2; + Size = Size * 0.95f; + } + else if (value == PlantNodeTypes.Leafes) + { + MaxChildren = ChildrenCount; + base.RedimChildren(); + } + } } - public float Rotation - { - get { return Mathf.RadToDeg(Direction.Angle()); } - } public PlantSegmentNode(PlantSegmentNode parent, int maxChildren = 2, int maxRecursiveChildren = 4096) : base(parent, maxChildren, maxRecursiveChildren) { if (parent != null) // is child { Direction = parent.Direction.Normalized(); - Size = parent.Size / parent.MaxChildren; + Size = parent.Size; // parent.MaxChildren; PositionRelativeParent = parent.PositionRelativeParent + parent.Line; VecToRoot = parent.VecToRoot + parent.Line; } @@ -62,6 +70,20 @@ public class PlantSegmentNode : LifeFormSegmentNode } } + public float[] GetChildrenToParentRotations() + { + float[] r = new float[ChildrenCount]; + + for (int i = 0; i < ChildrenCount - 1; i++) + { + var child = Get_Child(i);// Children[i]; + if (child != null) + r[i] = ((PlantSegmentNode)Children[i]).Direction.Angle(); + } + + return r; + + } public void UpdateSelfToParent() { if (IsRoot) @@ -178,14 +200,14 @@ public abstract class LifeFormSegmentNode } else { - value = 0; + _maxChildren = 0; } } } public LifeFormSegmentNode(LifeFormSegmentNode parent, int maxChildren = 3, int maxRecursiveChildren = 4096) { - _maxChildren = maxChildren; + MaxChildren = maxChildren; if (parent == null) { @@ -204,13 +226,13 @@ public abstract class LifeFormSegmentNode if (_maxRecursiveChildren <= 0) { _maxRecursiveChildren = 0; - _maxChildren = 0; + MaxChildren = 0; } else if (_maxRecursiveChildren < _maxChildren) - _maxChildren = maxRecursiveChildren; + MaxChildren = maxRecursiveChildren; } - Children = new LifeFormSegmentNode[_maxChildren]; + Children = new LifeFormSegmentNode[MaxChildren]; // Iterate ID Id = LifeFormSegmentNode.NodeCounter; @@ -263,15 +285,34 @@ public abstract class LifeFormSegmentNode } public int ChildrenCount { - //get { return Children.Count(x => x != null); } + get { return _childCount; } } public bool HasFreeChildSlot { - get { return ChildrenCount < _maxChildren; } + get { return ChildrenCount < MaxChildren; } } + public void RedimChildren() + { + + if (Children.Length > MaxChildren) + { + if (ChildrenCount <= MaxChildren) + Children = new LifeFormSegmentNode[MaxChildren]; + else + { + // hmmm.... sollte eigentlich nicht sein. wenn ich nicht lösche, mal schauen. + GD.PrintErr("Hui RedimChildren: nodes löschen?"); + } + + //LifeFormSegmentNode[] cc = new LifeFormSegmentNode[MaxChildren]; + + //var copy = Children.Clone(); + + } + } public int FreeNodeIndex() { // nur first ist immer rechtslastig -> rand? @@ -282,18 +323,17 @@ public abstract class LifeFormSegmentNode if (Children[0] == null) // center zuerst return 0; - if (_maxChildren > 1) // 1 rechts oder 2 links + if (MaxChildren > 1) // 1 rechts oder 2 links { - List nodes = new List(); - for (int j = 0; j < _maxChildren; j++) + List freeidx = new List(); + for (int j = 0; j < MaxChildren; j++) { if (Children[j] == null) - nodes.Add(j); + freeidx.Add(j); } - - int li = 0; - li = GD.RandRange(0, nodes.Count-1); - return nodes[li]; + + int li = GD.RandRange(0, freeidx.Count-1); + return freeidx[li]; } return -1; }