From 8df5aa04da2efff5b69fb126267547c2b993768c Mon Sep 17 00:00:00 2001 From: adQuid <28126232+adQuid@users.noreply.github.com> Date: Thu, 5 Jan 2023 21:50:20 -0500 Subject: [PATCH] Improved membranes (#74) * allow concave membranes --- src/microbe_stage/Membrane.cs | 77 +++++++++++++++++----------- src/microbe_stage/Microbe.Contact.cs | 4 +- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/microbe_stage/Membrane.cs b/src/microbe_stage/Membrane.cs index f3a012f922c..27cd6c7c2a1 100644 --- a/src/microbe_stage/Membrane.cs +++ b/src/microbe_stage/Membrane.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Godot; using Array = Godot.Collections.Array; +using System.Linq; /// /// Membrane for microbes @@ -284,17 +285,16 @@ public Vector3 GetVectorTowardsNearestPointOfMembrane(float x, float y) /// /// Return the position of the closest organelle to the target point if it is less then a certain threshold away. /// - public Vector2 FindClosestOrganelles(Vector2 target) + public Vector2 FindClosestOrganelleInRange(Vector2 origin, float range) { - // The distance we want the membrane to be from the organelles squared. - float closestSoFar = 4; + float closestSoFar = range; Vector2 closest = new Vector2(INVALID_FOUND_ORGANELLE, INVALID_FOUND_ORGANELLE); foreach (var pos in OrganellePositions) { - float lenToObject = (target - pos).LengthSquared(); + float lenToObject = (origin - pos).LengthSquared(); - if (lenToObject < 4 && lenToObject < closestSoFar) + if (lenToObject < closestSoFar) { closestSoFar = lenToObject; closest = pos; @@ -304,6 +304,15 @@ public Vector2 FindClosestOrganelles(Vector2 target) return closest; } + public Vector2 FindCenterOfOrganellesInRange(Vector2 origin, float range) + { + List points = new List() { origin }; + + points.AddRange(OrganellePositions.Where(x => (origin - x).LengthSquared() < range)); + + return new Vector2(points.Sum(x => x.x) / points.Count(), points.Sum(x => x.y) / points.Count()); + } + public bool MatchesCacheParameters(ICacheableData cacheData) { if (cacheData is IComputedMembraneData data) @@ -320,16 +329,16 @@ public long ComputeCacheHash() /// /// Decides where the point needs to move based on the position of the closest organelle. /// - private static Vector2 GetMovement(Vector2 target, Vector2 closestOrganelle) + private Vector2 GetMovement(Vector2 target, Vector2 closestOrganelle) { - float power = Mathf.Pow(2.7f, -(target - closestOrganelle).Length() / 10) / 50; + float power = Mathf.Pow(2.7f, -(target - closestOrganelle).Length() / 10) / 250; return (closestOrganelle - target) * power; } - private static Vector2 GetMovementForCellWall(Vector2 target, Vector2 closestOrganelle) + private Vector2 GetMovementForCellWall(Vector2 target, Vector2 closestOrganelle) { - float power = Mathf.Pow(10.0f, -(target - closestOrganelle).Length()) / 50; + float power = Mathf.Pow(3.1f, -(target - closestOrganelle).Length() / 3) / 250; return (closestOrganelle - target) * power; } @@ -459,6 +468,8 @@ private void InitializeMesh() // Half the side length of the original square that is compressed to make the membrane. int cellDimensions = 10; + var nodeLength = cellDimensions / membraneResolution; + foreach (var pos in OrganellePositions) { if (Mathf.Abs(pos.x) + 1 > cellDimensions) @@ -475,37 +486,41 @@ private void InitializeMesh() previousWorkBuffer.Capacity = vertices2D.Capacity; nextWorkBuffer.Capacity = previousWorkBuffer.Capacity; + // left wall of square for (int i = membraneResolution; i > 0; i--) { previousWorkBuffer.Add(new Vector2(-cellDimensions, - cellDimensions - 2 * cellDimensions / membraneResolution * i)); + cellDimensions - 2 * nodeLength * i)); } + // bottom wall of square for (int i = membraneResolution; i > 0; i--) { previousWorkBuffer.Add(new Vector2( - cellDimensions - 2 * cellDimensions / membraneResolution * i, + cellDimensions - 2 * nodeLength * i, cellDimensions)); } + // right wall of square for (int i = membraneResolution; i > 0; i--) { previousWorkBuffer.Add(new Vector2(cellDimensions, - -cellDimensions + 2 * cellDimensions / membraneResolution * i)); + -cellDimensions + 2 * nodeLength * i)); } + // bottom wall of square for (int i = membraneResolution; i > 0; i--) { previousWorkBuffer.Add(new Vector2( - -cellDimensions + 2 * cellDimensions / membraneResolution * i, + -cellDimensions + 2 * nodeLength * i, -cellDimensions)); } // This needs to actually run a bunch of times as the points moving towards the organelles is iterative. // We use rotating work buffers to save time on skipping useless copies - for (int i = 0; i < 40 * cellDimensions; i++) + for (int i = 0; i < 60 * cellDimensions; i++) { - DrawCorrectMembrane(cellDimensions, previousWorkBuffer, nextWorkBuffer); + DrawMembrane(cellDimensions, previousWorkBuffer, nextWorkBuffer, Type.CellWall ? GetMovementForCellWall : GetMovement); (previousWorkBuffer, nextWorkBuffer) = (nextWorkBuffer, previousWorkBuffer); } @@ -635,18 +650,6 @@ private void BuildMesh() return writeIndex; } - private void DrawCorrectMembrane(float cellDimensions, List sourceBuffer, List targetBuffer) - { - if (Type.CellWall) - { - DrawMembrane(cellDimensions, sourceBuffer, targetBuffer, GetMovementForCellWall); - } - else - { - DrawMembrane(cellDimensions, sourceBuffer, targetBuffer, GetMovement); - } - } - private void DrawMembrane(float cellDimensions, List sourceBuffer, List targetBuffer, Func movementFunc) { @@ -658,13 +661,27 @@ private void DrawCorrectMembrane(float cellDimensions, List sourceBuffe targetBuffer.RemoveAt(targetBuffer.Count - 1); // Loops through all the points in the membrane and relocates them as necessary. - for (int i = 0, end = sourceBuffer.Count; i < end; ++i) + for (int i = 0, end = sourceBuffer.Count; i < end; i++) { - var closestOrganelle = FindClosestOrganelles(sourceBuffer[i]); + var closestOrganelle = FindClosestOrganelleInRange(sourceBuffer[i], 3f); if (closestOrganelle == new Vector2(INVALID_FOUND_ORGANELLE, INVALID_FOUND_ORGANELLE)) { - targetBuffer[i] = (sourceBuffer[(end + i - 1) % end] + sourceBuffer[(i + 1) % end]) / 2; + var distantOrganelle = FindCenterOfOrganellesInRange(sourceBuffer[i], 5f); + + var midpoint = (sourceBuffer[(end + i - 1) % end] + sourceBuffer[(i + 1) % end]) / 2; + + if (distantOrganelle == sourceBuffer[i]) + { + targetBuffer[i] = midpoint; + } + else + { + var movementDirection = movementFunc(sourceBuffer[i], distantOrganelle + distantOrganelle + midpoint / 3); + + targetBuffer[i] = new Vector2(sourceBuffer[i].x - movementDirection.x, + sourceBuffer[i].y - movementDirection.y); + } } else { diff --git a/src/microbe_stage/Microbe.Contact.cs b/src/microbe_stage/Microbe.Contact.cs index 6fa986afcb9..1f51165360b 100644 --- a/src/microbe_stage/Microbe.Contact.cs +++ b/src/microbe_stage/Microbe.Contact.cs @@ -419,7 +419,9 @@ public void SendOrganellePositionsToMembrane() foreach (var entry in organelles.Organelles) { var cartesian = Hex.AxialToCartesian(entry.Position); - organellePositions.Add(new Vector2(cartesian.x, cartesian.z)); + organellePositions + .AddRange(entry.Definition.Hexes + .Select(x => new Vector2(Hex.AxialToCartesian(x).x, Hex.AxialToCartesian(x).z) + new Vector2(cartesian.x, cartesian.z))); } Membrane.OrganellePositions = organellePositions;