Browse Source

naja

master
subversion23 2 years ago
parent
commit
eafb8a2355
  1. 2
      PlantWorld.cs
  2. 1
      icon.svg
  3. 11
      main.tscn
  4. 8
      src/BaseLifeComponent.cs
  5. 67
      src/Genetics.cs
  6. 400
      src/Plant.cs
  7. 87
      src/PlantLife.cs
  8. 91
      src/PlantParmeters.cs
  9. 88
      src/SegmentNodes.cs

2
PlantWorld.cs

@ -60,7 +60,7 @@ public partial class PlantWorld : Node2D @@ -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);
}
}
}

1
icon.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="124" height="124" rx="14" fill="#363d52" stroke="#212532" stroke-width="4"/><g transform="scale(.101) translate(122 122)"><g fill="#fff"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 813 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H447l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c3 34 55 34 58 0v-86c-3-34-55-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></g></svg>

After

Width:  |  Height:  |  Size: 950 B

11
main.tscn

@ -1,10 +1,13 @@ @@ -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")

8
src/BaseLifeComponent.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
namespace DrawingDemo;
namespace LifeComponent;
using Godot;
@ -6,7 +6,7 @@ public delegate void DiedEventHandler(); @@ -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 @@ -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)
{

67
src/Genetics.cs

@ -1,9 +1,10 @@ @@ -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 @@ -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,8 +43,15 @@ public class Genetics @@ -30,8 +43,15 @@ 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();
}
}
@ -52,9 +72,10 @@ public struct GenStorage @@ -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 @@ -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)
}

400
src/Plant.cs

@ -4,79 +4,248 @@ using System.Collections.Generic; @@ -4,79 +4,248 @@ using System.Collections.Generic;
using System.Linq;
public partial class Plant : MeshInstance2D
{
private PlantLife _plantLife;
public Dictionary<int, PlantSegmentNode> 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<int, PlantSegmentNode>();
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++)
{
GrowNode();
}
GrowNewNode( PlantParams.Default());
}
}
public void AddNewSegment(PlantSegmentNode seg, PlantSegmentNode parent, int index)
{
if (parent.AddChild(seg, index))
{
Segments.Add(_segmentIdCounter, seg);
_segmentIdCounter++;
public void GrowNewNode(PlantNodeParams p)
this.UpdateAllGeometry();
}
else
{
//var randAngle = (float)GD.RandRange(-p.AllowedParentDiv/2, p.AllowedParentDiv/2);
var randAngleZeroDiv = (float)GD.RandRange(20, 45);
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, 10);
int randI = GD.RandRange(0, 3);
var parent = GetFreeSlotSegment(false);
if (parent == null)
{
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<float> spots = new List<float>();
for (int i = 0; i < usedAngles.Length; i++)
{
if (i + 1 >= usedAngles.Length)
break;
if (usedAngles[i] - usedAngles[i + 1] >= p.MinAngleBetweenSibs)
{
spots.Add(usedAngles[i]);
}
}
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();
@ -85,20 +254,41 @@ public partial class Plant : MeshInstance2D @@ -85,20 +254,41 @@ public partial class Plant : MeshInstance2D
switch (freeindex)
{
case 0: // Center
dir = dir.Rotated(randF);
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...
}
seg = new PlantSegmentNode(parent, p.MaxChildren);
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);
@ -109,7 +299,7 @@ public partial class Plant : MeshInstance2D @@ -109,7 +299,7 @@ public partial class Plant : MeshInstance2D
GD.PrintErr("Error bei Add");
}
}
public void GrowNode()
public void GrowNode_old()
{
// Config
float minAngleDiv =Mathf.DegToRad(23);
@ -173,7 +363,7 @@ public partial class Plant : MeshInstance2D @@ -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 @@ -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<int, PlantSegmentNode> elements = Segments;
//List<PlantSegmentNode> list = elements.OrderBy(x => x.Key).ToList<PlantSegmentNode>();
// 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;
@ -247,54 +517,28 @@ public partial class Plant : MeshInstance2D @@ -247,54 +517,28 @@ public partial class Plant : MeshInstance2D
return vertices;
}
public bool CheckCollision(PlantSegmentNode node)
{
return false;
}
private PlantSegmentNode GetFreeSlotSegment(bool descending = false)
public void CalulateClientRectangle()
{
Dictionary<int, PlantSegmentNode> elements = Segments;
//List<PlantSegmentNode> list = elements.OrderBy(x => x.Key).ToList<PlantSegmentNode>();
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;
}
}
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;
#endregion
public PlantNodeParams()
{
}
}

87
src/PlantLife.cs

@ -0,0 +1,87 @@ @@ -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()
}
}
}

91
src/PlantParmeters.cs

@ -0,0 +1,91 @@ @@ -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;
}
}

88
src/SegmentNodes.cs

@ -4,11 +4,11 @@ using System.Linq; @@ -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,25 +21,33 @@ public class PlantSegmentNode : LifeFormSegmentNode @@ -21,25 +21,33 @@ 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;
}
public float Rotation
else if (value == PlantNodeTypes.Leafes)
{
get { return Mathf.RadToDeg(Direction.Angle()); }
MaxChildren = ChildrenCount;
base.RedimChildren();
}
}
}
public PlantSegmentNode(PlantSegmentNode parent, int maxChildren = 2, int maxRecursiveChildren = 4096) : base(parent, maxChildren, maxRecursiveChildren)
@ -47,7 +55,7 @@ public class PlantSegmentNode : LifeFormSegmentNode @@ -47,7 +55,7 @@ public class PlantSegmentNode : LifeFormSegmentNode
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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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<int> nodes = new List<int>();
for (int j = 0; j < _maxChildren; j++)
List<int> freeidx = new List<int>();
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;
}

Loading…
Cancel
Save