protected IPlantCell MoveUpOrDown(IFluidCarrier <TFluid> carrier, IPlantPart part) { var cells = part.Cells; var currentTop = carrier.Current.Geometry.TopCenter.Y; var destTop = carrier.Destination.Geometry.BottomCenter.Y; IPlantCell next; if (IsWithinHeight(carrier.Current.Geometry, carrier.Destination.Geometry)) { next = MoveTowardsDestination(carrier, cells); } else if (currentTop >= destTop) { next = FindUpOrDown(false, carrier.Current, part); } else { next = FindUpOrDown(true, carrier.Current, part); } return(next); }
private IPlantPart FindTopInternode(out int leafCount) { int count = 0; IPlantPart upperInternode = null; Stack <IPlantPart> parts = new Stack <IPlantPart>(new[] { plant.ShootSystem.Stem.Internode }); while (parts.Count > 0) { var part = parts.Pop(); foreach (var conn in part.Connections) { parts.Push(conn); } if (part.PartType == PlantPartType.Petiole) { count++; } if (part.PartType == PlantPartType.Internode && !((Internode)part).HasUpperNode()) { upperInternode = part; } } leafCount = count > 0 ? count : 1; return(upperInternode); }
private IPlantCell FindInConnection(bool up, IPlantCell cell, IPlantPart part) { foreach (var conn in part.Connections) { FindUpOrDown(up, cell, conn); } return(null); }
private void HandlePlantPart(IPlantPart plantPart, bool isShoot) { if (isShoot) { HandleShootPart(plantPart); } else { HandleRootPart(plantPart); } }
public void Develop(IPlantPart plantPart, SimulationStateSnapshot snapshot) { switch (plantPart) { case Internode internode: internodeDevelopment.Develop(internode, snapshot); break; case Root root: rootDevelopment.Develop(root, snapshot); break; } }
public IPlantCell[] Divide(IPlantCell cell, IPlantPart plantPart) { ICellGeometry geo = cell.Geometry; Vector3 halfPoint = GetCellHalfWay(geo); ICellGeometry topCellGeometry = new CellGeometry(geo.TopCenter, halfPoint, geo.Face); ICellGeometry bottomCellGeometry = new CellGeometry(halfPoint, geo.BottomCenter, geo.Face); IPlantCell[] cells = new IPlantCell[2]; cells[0] = CreatePlantCell(cell.CellType, topCellGeometry, cell.Vacuole, cell.CellWall); cells[1] = CreatePlantCell(cell.CellType, bottomCellGeometry, cell.Vacuole, cell.CellWall); return(cells); }
public void Solve(IPlantPart part) { var cells = part.Cells.ToArray(); do { DoubleIterateCells(cells, (a, b) => { if (a.Equals(b) || !collisionDetection.Colliding(a, b, false)) { return; } SolveCell(a, b); }); } while (DoesAnyCollide(cells)); }
private void IteratePlantParts(IPlantPart start, bool isShoot) { var postponedParts = new Stack <IPlantPart>(new[] { start }); while (postponedParts.Count > 0) { IPlantPart part = postponedParts.Pop(); PushConnections(part.Connections, postponedParts); sucroseTransporter.Transport(part); HandlePlantPart(part, isShoot); cellBodySystem.Solve(part); } }
private int GetThickness(IPlantPart part) { switch (part.PartType) { case PlantPartType.Stem when part.BranchCount > 0: return(7); case PlantPartType.Petiole: return(5); case PlantPartType.Root: return(7); default: return(10); } }
private PlantNodeModelState CreateStateFromPlantPart(IPlantPart part) { var asTop = part.PartType == PlantPartType.Root; var descriptor = descriptorService.Describe(part, asTop); var top3 = descriptor.Top; var bot3 = descriptor.Bottom; var top = new Vector2(top3.X, top3.Y); var bot = new Vector2(bot3.X, bot3.Y); return(new PlantNodeModelState { Thickness = ComputeThickness(descriptor), Description = $"{part.PartType} ({part.BranchCount})", Connections = new PlantNodeModelState[0], Coordinates = new[] { bot, top } }); }
public override void Transport(IPlantPart part) { if (!part.Cells.Any()) { return; } var inTransit = CarrierCollection.GetInTransit(); foreach (var transit in inTransit) { if (MoveCarrier(transit, part)) { Logger.LogDebug("{Transit} reached goal", transit.Destination.Geometry.TopCenter); // transit.Current.StarchStorage.Amount += transit.Fluid.Amount; CarrierCollection.Delete(transit.Destination); } } }
protected IPlantCell FindUpOrDown(bool up, IPlantCell cell, IPlantPart part) { foreach (var c in part.Cells) { if (c.CellType == cell.CellType && CollisionDetection.Neighbors(cell, c, true)) { if (up && cell.Geometry.TopCenter.Y <= c.Geometry.BottomCenter.Y) { return(c); } if (!up && cell.Geometry.BottomCenter.Y >= c.Geometry.TopCenter.Y) { return(c); } } } // If we end up here, that means that there are no cells above or below the current cell // which could mean that it must go to next plant part connection return(FindInConnection(up, cell, part)); }
protected virtual Vector3 DetermineGrowthDirection(IPlantCell cell, IPlantPart part) { var geo = cell.Geometry; var direction = new Vector3(0, 1, 0); var outwards = Vector3.Zero; if (part.BranchCount > 0) { if (geo.BottomCenter.X > 0) { outwards += new Vector3(2, 0, 0); } else if (geo.BottomCenter.X < 0) { outwards += new Vector3(-2, 0, 0); } } return(direction + outwards); }
protected bool MoveCarrier(IFluidCarrier <TFluid> carrier, IPlantPart part) { // Get all the cells in the current plant part var cells = part.Cells; // The current carrier placement var current = carrier.Current; // Get the neighbors for the current carrier placement var neighbors = GetNeighboringCells(current, cells); IPlantCell next; // If the carrier is at the destination if (carrier.Current.Geometry.TopCenter.Equals(carrier.Destination.Geometry.TopCenter)) { return(true); } // If the carrier is in a transport cell, it must move up or down if (carrier.IsInTransportCell) { next = MoveUpOrDown(carrier, part); } // If the carrier is not in a transport cell then it must move towards a transport cell // or the destination based on the height of the current placement else { next = GetClosestCellTowards(carrier, neighbors); } if (next != null) { carrier.Current = next; } return(false); }
private IEnumerable <PlantNodeModelState> IterateSystem(IPlantPart startPart) { IList <PlantNodeModelState> states = new List <PlantNodeModelState>(); Stack <IPlantPart> stack = new Stack <IPlantPart>(new[] { startPart }); while (stack.Count > 0) { var part = stack.Pop(); if (!SkipTypes.Contains(part.PartType)) { states.Add(CreateStateFromPlantPart(part)); } foreach (var connection in part.Connections) { stack.Push(connection); } } return(states); }
public void GrowRootCell(IPlantCell cell, IPlantPart part, SimulationStateSnapshot state) { MoveBottomPointDownwards(cell, part, state); }
private void HandleRootPart(IPlantPart part) { plantPartDeveloper.Develop(part, currentState); }
public bool ShouldDivide(IPlantCell cell, IPlantPart plantPart, SimulationStateSnapshot state) { return(false); }
public abstract void Transport(IPlantPart part);
/// <summary> /// Describe the physical properties of a plant part /// </summary> /// <param name="part">The plant part to describe</param> /// <param name="centerTop">Whether to compute the center as the sum of top- or bottom max</param> /// <returns>A PlantPartDescriptor object describing the provided plant part</returns> /// <remarks>Involves looping over all cells in the plant part</remarks> public IPlantPartDescriptor Describe(IPlantPart part, bool centerTop) { // Sum coordinates for computing averages float sumX = 0; float sumY = 0; float sumZ = 0; // Max and min values of all the x, y and z coordinates float xMax = float.MinValue; float yMax = float.MinValue; float zMax = float.MinValue; float xMin = float.MaxValue; float yMin = float.MaxValue; float zMin = float.MaxValue; // Cell counter var cellCount = 0; // Compute the above values foreach (var cell in part.Cells) { var top = cell.Geometry.TopCenter; var bottom = cell.Geometry.BottomCenter; xMax = Math.Max(xMax, top.X); yMax = Math.Max(yMax, top.Y); zMax = Math.Max(zMax, top.Z); xMin = Math.Min(xMin, bottom.X); yMin = Math.Min(yMin, bottom.Y); zMin = Math.Min(zMin, bottom.Z); if (centerTop) { sumX += top.X; sumY += top.Y; sumZ += top.Z; } else { sumX += bottom.X; sumY += bottom.Y; sumZ += bottom.Z; } cellCount++; } // The average is computed by the total X and Z divided by the cellCount var center = new Vector2(sumX / cellCount, sumZ / cellCount); // The highest and lowest point, so that Y is yMax and yMin with the center as X and Z. var highest = new Vector3(center.X, yMax, center.Y); var lowest = new Vector3(center.X, yMin, center.Y); // The plant height is the euclidean distance between the highest and lowest point var height = Vector3.Distance(highest, lowest); var widestX = new Vector2(xMax, center.Y); var minX = new Vector2(xMin, center.Y); var widestZ = new Vector2(center.X, zMax); var minZ = new Vector2(center.X, zMin); // Thickness is computed by the euclidean distance between the center and widest coordinate var thicknessX = Vector2.Distance(center, widestX); var thicknessZ = Vector2.Distance(center, widestZ); return(new PlantPartDescriptor { Top = highest, Bottom = lowest, Height = height, MaxX = xMax, MaxY = yMax, MaxZ = zMax, MinX = xMin, MinY = yMin, MinZ = zMin, WidthX = thicknessX, WidthZ = thicknessZ }); }
public void GrowShootCell(IPlantCell cell, IPlantPart part, SimulationStateSnapshot state) { MoveTopPointUpwards(cell, part, state); }
protected virtual Vector3 DetermineGrowth(IPlantCell cell, IPlantPart part, SimulationStateSnapshot state) { var growth = DetermineGrowthDirection(cell, part) * DetermineGrowthFactor(cell, state); return(growth); }
private void MoveBottomPointDownwards(IPlantCell cell, IPlantPart part, SimulationStateSnapshot state) { var geo = cell.Geometry; geo.BottomCenter -= DetermineGrowth(cell, part, state); }
private void MoveTopPointUpwards(IPlantCell cell, IPlantPart part, SimulationStateSnapshot state) { var geo = cell.Geometry; geo.TopCenter += DetermineGrowth(cell, part, state); }