public void Validate(UnitCell attacker, HexCell target, int damage, Action onCompletionCallback) { if (this.attacker != attacker) { Debug.LogError("Invalid attacker!"); return; } if (this.target != target) { Debug.LogError("Invalid target!"); isActioned = true; attacker.AttackComplete(); return; } if (this.damage != damage) { Debug.LogError($"Damage mismatch. Expected {this.damage} got {damage}"); this.damage = damage; } this.damage = damage; this.onCompletionCallback = onCompletionCallback; isValidated = true; CompleteAttackIfSynced(); }
public ValidatedAttack(Int16 id, UnitCell attacker, HexCell target, int damage) { ID = id; this.attacker = attacker; this.target = target; this.damage = damage; }
/** * Spawn cell into world from object pool */ public void spawn(Vector3Int pos, UnitCell parent) { // If cell exists in this position, kill it if (getCell(pos) != null) { kill(pos); } // Expand object pool if required if (cellPool.Count == 0) { expandPool(1, cellPrefab); } // Get inactive cell GameObject GameObject cellObj = cellPool.Pop(); // Set GameObject position cellObj.transform.position = getWorldPosition(pos); cellObj.transform.rotation = Quaternion.identity; cellObj.SetActive(true); UnitCell cell = cellObj.GetComponent <UnitCell>(); cell.pos = pos; cell.state = SPAWNING; cell.generation = parent ? parent.generation + 1 : 0; setCell(pos, cellObj); }
public static ViewBox CreateViewBox(int cellsLong, int cellsHigh, int cellsWide, float boxSize, float rotX, float rotY, float rotZ, string filepath, Vector3 initPos) { Vector3 middlePoint = initPos; //making the object that will be copied GameObject boxObject = Instantiate(ViewBoxPreFab.gameObject, middlePoint, Quaternion.Euler(rotX, rotY, rotZ)); ViewBox viewBox = boxObject.GetComponent <ViewBox>(); //using square scales for proper display float boxLength = boxSize; float boxHeight = boxSize; float boxWidth = boxSize; viewBox.cells = new int[] { cellsLong, cellsHigh, cellsWide }; viewBox.boxSize = new float[] { boxLength, boxHeight, boxWidth }; Vector3 boxNum = new Vector3(boxLength, boxHeight, boxWidth); viewBox.origin = new float[] { -boxLength / 2f, -boxHeight / 2f, -boxWidth / 2f }; viewBox.rotations = new float[] { rotX, rotY, rotZ }; boxObject.transform.localScale = boxNum; //could also detect if there are other view boxes and try to line them up // this is the unit cell that is copied; it gets disabled later UnitCell primeUnitCell = UnitCell.NewUnitCell(filepath, boxObject.GetComponent <ViewBox>()); for (int i = 0; i < cellsLong; i++) { for (int j = 0; j < cellsHigh; j++) { for (int k = 0; k < cellsWide; k++) { Vector3 xposVec = primeUnitCell.vecA * (float)i; Vector3 yposVec = primeUnitCell.vecB * (float)j; Vector3 zposVec = primeUnitCell.vecC * (float)k; float xpos = xposVec.x + yposVec.x + zposVec.x; float ypos = xposVec.y + yposVec.y + zposVec.y; float zpos = xposVec.z + yposVec.z + zposVec.z; Vector3 newPos = new Vector3(xpos, ypos, zpos); newPos += new Vector3(viewBox.origin[0], viewBox.origin[1], viewBox.origin[2]); GameObject iterUnitCell = Instantiate(primeUnitCell.gameObject, boxObject.transform); iterUnitCell.transform.localPosition = newPos; } } } //disables this instance but keeps all of the other ones that are corectly positioned wrt eachother primeUnitCell.gameObject.SetActive(false); return(viewBox); }
public static int GetDamage(UnitCell attacker, HexCell target) { var attackerStats = attacker.GetStats(); var damageMultiplier = Mathf.Max(1f - target.GetDamageMitigation(), 0f); return(Mathf.RoundToInt(attackerStats.Attack * damageMultiplier)); }
/** * Return cell state at given position */ public int getState(Vector3Int pos) { UnitCell cell = getCell(pos); if (!cell) { return(DEAD); } return(cell.state); }
/** * Returns True if given cell should die */ protected virtual bool shouldDie(UnitCell cell) { int adjacentCells = adjacentCellCount(cell.pos); return( (cellLife != IMMORTAL && cell.age >= cellLife) || adjacentCells >= neighboursCausingDeathMax || adjacentCells <= neighboursCausingDeathMin ); }
/** * Attempt to spawn child cells */ protected virtual void spawnChildren(UnitCell cell) { foreach (Vector3Int apos in adjacentPositions(cell.pos)) { if (shouldSpawn(apos)) { spawn(apos, cell); } } }
/** * Update state of spawning cells and dying cells */ public virtual void advanceState(UnitCell cell) { if (cell.state == SPAWNING) { cell.state = ALIVE; } if (cell.state == DYING) { kill(cell.pos); } }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Cell behaviour * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Update cell spawning and cell death */ public virtual void update(UnitCell cell) { cell.age += 1; if (cell.canMultiply) { spawnChildren(cell); } if (shouldDie(cell)) { cell.state = DYING; } }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { // 1. Retrieve/validate input var curves = new List <Curve>(); if (!DA.GetDataList(0, curves)) { return; } // 2. Convert curve input to line input var lines = new List <Line>(); foreach (Curve curve in curves) { // Make sure the curve is linear, if not, return error and abort. if (!curve.IsLinear()) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "All struts must be linear."); return; } // Convert curve to line lines.Add(new Line(curve.PointAtStart, curve.PointAtEnd)); } // 3. Instantiate UnitCell object. UnitCell cell = new UnitCell(lines); // 4. CheckValidity instance method to check the unit cell. Use the return value to output useful error message. switch (cell.CheckValidity()) { case -1: AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Invalid cell - opposing faces must be identical."); return; case 0: AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Invalid cell - each face needs at least one node lying on it."); return; case 1: AddRuntimeMessage(GH_RuntimeMessageLevel.Blank, "Your cell is valid!"); break; } // 5. Set output DA.SetData(0, new UnitCellGoo(cell)); }
/** * Update cell spawning and cell death */ public override void update(UnitCell cell) { if (cells.Count > 1024) { return; } cell.age += 1; if (cell.age > 1 && cell.canMultiply) { spawnChildren(cell); cell.canMultiply = false; } if (shouldDie(cell)) { cell.state = DYING; } }
protected override void OnStart() { modify = Entity.Instance.UnitList.Clone(); petCell.DataSource = new PetCell(petItemPrefab, modify); petCell.ActionDelegate = this; petCell.PressDelegate = this; var view = new UnitCell(petInfoItemPrefab); unitCell.DataSource = view; unitCell.ActionDelegate = this; unitCell.PressDelegate = this; view.GetCurrentUnit = () => { return(modify.items[0]); }; SetupUnit(); Observer.Instance.Subscribe(UnitList.UpdateEvent, OnSubscribe); base.OnStart(); }
// functions within UnitCell public static UnitCell NewUnitCell(string filepath, ViewBox parentViewBox) { //this is the order these functions should be run in, otherwise there won't be the correct things in place GameObject unitCellObject = Instantiate(UnitCellPreFab.gameObject, parentViewBox.transform); UnitCell unitCell = unitCellObject.GetComponent <UnitCell>(); string[] atomsCIF = unitCell.ReadCif(filepath); unitCell.parentViewBox = parentViewBox; unitCell.unitCellScale = unitCell.SetScaleSize(); unitCell.SetCorners(); unitCell.atomList = unitCell.MakeAtoms(atomsCIF); unitCell.SetAtomColors(); unitCell.DrawAllBoundaries(); return((unitCell));; }
public void OnAttackReceived(Int16 id, UnitCell attacker, HexCell target, int damage, bool isDestroyed) { var attackIndex = attackActions.FindIndex(a => a.ID == id); if (attackIndex < 0) { Debug.LogError($"Received invalid attack action with id {id}"); return; } var attack = attackActions[attackIndex]; attackActions.RemoveAt(attackIndex); attack.Validate(attacker, target, damage, () => { if (isDestroyed) { game.Cells.RemoveCell(target); } }); }
/** * Kill cell at given position and return cell to object pool */ public void kill(Vector3Int pos) { GameObject cellObj = getCellObject(pos); if (!cellObj) { return; } // Deactivate object and add to stack cellObj.SetActive(false); cellPool.Push(cellObj); // Reset cell attributes UnitCell cell = cellObj.GetComponent <UnitCell>(); cell.state = DEAD; cell.age = 0; cell.generation = 0; // Remove reference to GameObject setCell(pos, null); }
// Will be called by Unitcell while it is making all of the atoms originally public static Atom NewAtom(Element atomElement, Vector3 atomPosition, Vector3 latticePosition, string atomLabel, UnitCell parentUnitCell) { GameObject atomObject = Instantiate(Atom.AtomPreFab.gameObject, parentUnitCell.transform); Atom atom = atomObject.GetComponent <Atom>(); atomObject.transform.localPosition = atomPosition; atom.atomElement = atomElement; atom.atomPosition = atomPosition; atom.latticePosition = latticePosition; //atomPosition is set relative to the unit cell and the unit cell will be scaled based off a, b, c atom.atomLabel = atomLabel; atom.parentUnitCell = parentUnitCell; float atomSize = (float)atomElement.GetAtomicRadius() * parentUnitCell.unitCellScale; atom.transform.localScale = new Vector3(atomSize, atomSize, atomSize); return(atom); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object can be used to retrieve data from input parameters and /// to store data in output parameters.</param> protected override void SolveInstance(IGH_DataAccess DA) { // 1. Retrieve and validate data var cell = new UnitCell(); double radius = 0; double height = 0; int nU = 0; int nV = 0; int nW = 0; bool morphed = false; if (!DA.GetData(0, ref cell)) { return; } if (!DA.GetData(1, ref radius)) { return; } if (!DA.GetData(2, ref height)) { return; } if (!DA.GetData(3, ref nU)) { return; } if (!DA.GetData(4, ref nV)) { return; } if (!DA.GetData(5, ref nW)) { return; } if (!DA.GetData(6, ref morphed)) { return; } if (!cell.isValid) { return; } if (radius == 0) { return; } if (height == 0) { return; } if (nU == 0) { return; } if (nV == 0) { return; } if (nW == 0) { return; } // 2. Initialize the lattice var lattice = new Lattice(); // Will contain the morphed uv spaces (as surface-surface, surface-axis or surface-point) var spaceTree = new DataTree <GeometryBase>(); // 3. Define cylinder Plane basePlane = Plane.WorldXY; Surface cylinder = (new Cylinder(new Circle(basePlane, radius), height)).ToNurbsSurface(); cylinder = cylinder.Transpose(); LineCurve axis = new LineCurve(basePlane.Origin, basePlane.Origin + height * basePlane.ZAxis); // 4. Package the number of cells in each direction into an array float[] N = new float[3] { nU, nV, nW }; // 5. Normalize the UV-domain Interval unitDomain = new Interval(0, 1); cylinder.SetDomain(0, unitDomain); // surface u-direction cylinder.SetDomain(1, unitDomain); // surface v-direction axis.Domain = unitDomain; // 6. Prepare cell (this is a UnitCell object) cell = cell.Duplicate(); cell.FormatTopology(); // 7. Map nodes to design space // Loop through the uvw cell grid for (int u = 0; u <= N[0]; u++) { for (int v = 0; v <= N[1]; v++) { for (int w = 0; w <= N[2]; w++) { // Construct cell path in tree GH_Path treePath = new GH_Path(u, v, w); // Fetch the list of nodes to append to, or initialise it var nodeList = lattice.Nodes.EnsurePath(treePath); // This loop maps each node index in the cell onto the UV-surface maps for (int i = 0; i < cell.Nodes.Count; i++) { double usub = cell.Nodes[i].X; // u-position within unit cell (local) double vsub = cell.Nodes[i].Y; // v-position within unit cell (local) double wsub = cell.Nodes[i].Z; // w-position within unit cell (local) double[] uvw = { u + usub, v + vsub, w + wsub }; // uvw-position (global) // Check if the node belongs to another cell (i.e. it's relative path points outside the current cell) bool isOutsideCell = (cell.NodePaths[i][0] > 0 || cell.NodePaths[i][1] > 0 || cell.NodePaths[i][2] > 0); // Check if current uvw-position is beyond the upper boundary bool isOutsideSpace = (uvw[0] > N[0] || uvw[1] > N[1] || uvw[2] > N[2]); if (isOutsideCell || isOutsideSpace) { nodeList.Add(null); } else { Point3d pt1, pt2; Vector3d[] derivatives; // Construct z-position vector Vector3d vectorZ = height * basePlane.ZAxis * uvw[0] / N[0]; // Compute pt1 (on axis) pt1 = basePlane.Origin + vectorZ; // Compute pt2 (on surface) cylinder.Evaluate(uvw[0] / N[0], uvw[1] / N[1], 2, out pt2, out derivatives); // Create vector joining these two points Vector3d wVect = pt2 - pt1; // Instantiate new node var newNode = new LatticeNode(pt1 + wVect * uvw[2] / N[2]); // Add new node to tree nodeList.Add(newNode); } } } // Define the uv space map tree (used for morphing) if (morphed && u < N[0] && v < N[1]) { GH_Path spacePath = new GH_Path(u, v); // Set trimming interval var uInterval = new Interval((u) / N[0], (u + 1) / N[0]); var vInterval = new Interval((v) / N[1], (v + 1) / N[1]); // Create sub-surface and sub axis Surface ss1 = cylinder.Trim(uInterval, vInterval); Curve ss2 = axis.Trim(uInterval); // Unitize domains ss1.SetDomain(0, unitDomain); ss1.SetDomain(1, unitDomain); ss2.Domain = unitDomain; // Save to the space tree spaceTree.Add(ss1, spacePath); spaceTree.Add(ss2, spacePath); } } } // 8. Map struts to the node tree if (morphed) { lattice.MorphMapping(cell, spaceTree, N); } else { lattice.ConformMapping(cell, N); } // 9. Set output DA.SetDataList(0, lattice.Struts); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object can be used to retrieve data from input parameters and /// to store data in output parameters.</param> protected override void SolveInstance(IGH_DataAccess DA) { // 1. Declare placeholder variables and assign initial invalid data. // This way, if the input parameters fail to supply valid data, we know when to abort var cell = new UnitCell(); double xCellSize = 0; double yCellSize = 0; double zCellSize = 0; int nX = 0; int nY = 0; int nZ = 0; // 2. Retrieve input data. if (!DA.GetData(0, ref cell)) { return; } if (!DA.GetData(1, ref xCellSize)) { return; } if (!DA.GetData(2, ref yCellSize)) { return; } if (!DA.GetData(3, ref zCellSize)) { return; } if (!DA.GetData(4, ref nX)) { return; } if (!DA.GetData(5, ref nY)) { return; } if (!DA.GetData(6, ref nZ)) { return; } // 3. If data is invalid, we need to abort. if (!cell.isValid) { return; } if (xCellSize == 0) { return; } if (yCellSize == 0) { return; } if (zCellSize == 0) { return; } if (nX == 0) { return; } if (nY == 0) { return; } if (nZ == 0) { return; } // 4. Initialise the lattice object var lattice = new Lattice(); // 5. Prepare cell (this is a UnitCell object) cell = cell.Duplicate(); cell.FormatTopology(); // 6. Define BasePlane and directional iteration vectors Plane basePlane = Plane.WorldXY; Vector3d vectorX = xCellSize * basePlane.XAxis; Vector3d vectorY = yCellSize * basePlane.YAxis; Vector3d vectorZ = zCellSize * basePlane.ZAxis; float[] N = new float[3] { nX, nY, nZ }; // 7. Map nodes to design space // Loop through the uvw cell grid for (int u = 0; u <= N[0]; u++) { for (int v = 0; v <= N[1]; v++) { for (int w = 0; w <= N[2]; w++) { // Construct cell path in tree GH_Path treePath = new GH_Path(u, v, w); // Fetch the list of nodes to append to, or initialise it var nodeList = lattice.Nodes.EnsurePath(treePath); // This loop maps each node in the cell for (int i = 0; i < cell.Nodes.Count; i++) { double usub = cell.Nodes[i].X; // u-position within unit cell (local) double vsub = cell.Nodes[i].Y; // v-position within unit cell (local) double wsub = cell.Nodes[i].Z; // w-position within unit cell (local) double[] uvw = { u + usub, v + vsub, w + wsub }; // uvw-position (global) // Check if the node belongs to another cell (i.e. it's relative path points outside the current cell) bool isOutsideCell = (cell.NodePaths[i][0] > 0 || cell.NodePaths[i][1] > 0 || cell.NodePaths[i][2] > 0); // Check if current uvw-position is beyond the upper boundary bool isOutsideSpace = (uvw[0] > N[0] || uvw[1] > N[1] || uvw[2] > N[2]); if (isOutsideCell || isOutsideSpace) { nodeList.Add(null); } else { // Compute position vector Vector3d V = uvw[0] * vectorX + uvw[1] * vectorY + uvw[2] * vectorZ; // Instantiate new node var newNode = new LatticeNode(basePlane.Origin + V); // Add new node to tree nodeList.Add(newNode); } } } } } // 8. Map struts to the node tree lattice.ConformMapping(cell, N); // 9. Set output DA.SetDataList(0, lattice.Struts); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { // 0. Generate input menu list Component = this; GrasshopperDocument = this.OnPingDocument(); // Only generate it if the input has no source if (Component.Params.Input[0].SourceCount == 0) { InputTools.TopoSelect(ref Component, ref GrasshopperDocument, 0, 11); } // 1. Retrieve input int cellType = 0; if (!DA.GetData(0, ref cellType)) { return; } // 2. Instantiate line list var lines = new List <Line>(); // 3. Set cell size double d = 5; // 4. Switch statement for the different cell types switch (cellType) { // "GRID" case 0: lines = GridLines(d); break; // "X" case 1: lines = XLines(d); break; // "STAR" case 2: lines = StarLines(d); break; // "CROSS" case 3: lines = CrossLines(d); break; // "TESSERACT" case 4: lines = TesseractLines(d); break; // "VINTILES" case 5: lines = VintileLines(d); break; // "OCTET" case 6: lines = OctetLines(d); break; // "DIAMOND" case 7: lines = DiamondLines(d); break; // "HONEYCOMB" case 8: lines = Honeycomb(d); break; // "AUXETIC HONEYCOMB" case 9: lines = AuxeticHoneycomb(d); break; } // 5. Instantiate UnitCell object and check validity. var cell = new UnitCell(lines); if (!cell.isValid) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Invalid cell - this is embarassing."); } // 6. Construct normalized cell lines (for optional output) var lines2 = new List <Line>(); foreach (IndexPair nodePair in cell.NodePairs) { lines2.Add(new Line(cell.Nodes[nodePair.I], cell.Nodes[nodePair.J])); } // 7. Set output (as LatticeCellGoo) DA.SetData(0, new UnitCellGoo(cell)); DA.SetDataList(1, lines2); }
protected override bool shouldDie(UnitCell cell) { return(cell.age > 2); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { // 1. Retrieve and validate inputs var cell = new UnitCell(); Surface surface = null; Point3d pt = Point3d.Unset; int nU = 0; int nV = 0; int nW = 0; bool morphed = false; if (!DA.GetData(0, ref cell)) { return; } if (!DA.GetData(1, ref surface)) { return; } if (!DA.GetData(2, ref pt)) { return; } if (!DA.GetData(3, ref nU)) { return; } if (!DA.GetData(4, ref nV)) { return; } if (!DA.GetData(5, ref nW)) { return; } if (!DA.GetData(6, ref morphed)) { return; } if (!cell.isValid) { return; } if (!surface.IsValid) { return; } if (!pt.IsValid) { return; } if (nU == 0) { return; } if (nV == 0) { return; } if (nW == 0) { return; } // 2. Initialize the node tree, derivative tree and morphed space tree var lattice = new Lattice(); var spaceTree = new DataTree <GeometryBase>(); // will contain the morphed uv spaces (as surface-surface, surface-axis or surface-point) // 3. Package the number of cells in each direction into an array float[] N = new float[3] { nU, nV, nW }; // 4. Normalize the UV-domain Interval unitDomain = new Interval(0, 1); surface.SetDomain(0, unitDomain); // surface u-direction surface.SetDomain(1, unitDomain); // surface v-direction // 5. Prepare cell (this is a UnitCell object) cell = cell.Duplicate(); cell.FormatTopology(); // 6. Map nodes to design space // Loop through the uvw cell grid for (int u = 0; u <= N[0]; u++) { for (int v = 0; v <= N[1]; v++) { for (int w = 0; w <= N[2]; w++) { // Construct cell path in tree GH_Path treePath = new GH_Path(u, v, w); // Fetch the list of nodes to append to, or initialise it var nodeList = lattice.Nodes.EnsurePath(treePath); // This loop maps each node in the cell onto the UV-surface map for (int i = 0; i < cell.Nodes.Count; i++) { double usub = cell.Nodes[i].X; // u-position within unit cell (local) double vsub = cell.Nodes[i].Y; // v-position within unit cell (local) double wsub = cell.Nodes[i].Z; // w-position within unit cell (local) double[] uvw = { u + usub, v + vsub, w + wsub }; // uvw-position (global) // Check if the node belongs to another cell (i.e. it's relative path points outside the current cell) bool isOutsideCell = (cell.NodePaths[i][0] > 0 || cell.NodePaths[i][1] > 0 || cell.NodePaths[i][2] > 0); // Check if current uvw-position is beyond the upper boundary bool isOutsideSpace = (uvw[0] > N[0] || uvw[1] > N[1] || uvw[2] > N[2]); if (isOutsideCell || isOutsideSpace) { nodeList.Add(null); } else { Point3d pt1; // Initialize for surface 2 Point3d pt2; Vector3d[] derivatives; // Set pt1 (on point) pt1 = pt; // Compute pt2 (on surface) surface.Evaluate(uvw[0] / N[0], uvw[1] / N[1], 2, out pt2, out derivatives); // Create vector joining the two points (this is our w-range) Vector3d wVect = pt2 - pt1; // Create the node, accounting for the position along the w-direction var newNode = new LatticeNode(pt1 + wVect * uvw[2] / N[2]); // Add node to tree nodeList.Add(newNode); } } } // Define the uv space tree (used for morphing) if (morphed && u < N[0] && v < N[1]) { GH_Path spacePath = new GH_Path(u, v); // Set trimming interval var uInterval = new Interval((u) / N[0], (u + 1) / N[0]); var vInterval = new Interval((v) / N[1], (v + 1) / N[1]); // Create sub-surface and point (never changes) Surface ss1 = surface.Trim(uInterval, vInterval); Point ss2 = new Point(pt); // Unitize domain ss1.SetDomain(0, unitDomain); ss1.SetDomain(1, unitDomain); // Save to the space tree spaceTree.Add(ss1, spacePath); spaceTree.Add(ss2, spacePath); } } } // 7. Map struts to the node tree if (morphed) { lattice.MorphMapping(cell, spaceTree, N); } else { lattice.ConformMapping(cell, N); } // 8. Set output DA.SetDataList(0, lattice.Struts); }
protected override void spawnChildren(UnitCell cell) { spawn(cell.pos + Vector3Int.left, cell); cell.canMultiply = false; }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { // 1. Retrieve and validate data var cell = new UnitCell(); GeometryBase designSpace = null; Plane orientationPlane = Plane.Unset; double xCellSize = 0; double yCellSize = 0; double zCellSize = 0; double minLength = 0; // the trim tolerance (i.e. minimum strut length) double maxLength = 0; bool strictlyIn = false; if (!DA.GetData(0, ref cell)) { return; } if (!DA.GetData(1, ref designSpace)) { return; } if (!DA.GetData(2, ref orientationPlane)) { return; } if (!DA.GetData(3, ref xCellSize)) { return; } if (!DA.GetData(4, ref yCellSize)) { return; } if (!DA.GetData(5, ref zCellSize)) { return; } if (!DA.GetData(6, ref minLength)) { return; } if (!DA.GetData(7, ref maxLength)) { return; } if (!DA.GetData(8, ref strictlyIn)) { return; } if (!cell.isValid) { return; } if (!designSpace.IsValid) { return; } if (!orientationPlane.IsValid) { return; } if (xCellSize == 0) { return; } if (yCellSize == 0) { return; } if (zCellSize == 0) { return; } if (minLength >= xCellSize || minLength >= yCellSize || minLength >= zCellSize) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Tolerance parameter cannot be larger than the unit cell dimensions."); return; } // 2. Validate the design space int spaceType = FrameTools.ValidateSpace(ref designSpace); if (spaceType == 0) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Design space must be a closed Brep, Mesh or Surface"); return; } double tol = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance; // 3. Compute oriented bounding box and its corner points Box bBox = new Box(); designSpace.GetBoundingBox(orientationPlane, out bBox); Point3d[] bBoxCorners = bBox.GetCorners(); // Set basePlane based on the bounding box Plane basePlane = new Plane(bBoxCorners[0], bBoxCorners[1], bBoxCorners[3]); // 4. Determine number of iterations required to fill the box, and package into array double xLength = bBoxCorners[0].DistanceTo(bBoxCorners[1]); double yLength = bBoxCorners[0].DistanceTo(bBoxCorners[3]); double zLength = bBoxCorners[0].DistanceTo(bBoxCorners[4]); int nX = (int)Math.Ceiling(xLength / xCellSize); // Roundup to next integer if non-integer int nY = (int)Math.Ceiling(yLength / yCellSize); int nZ = (int)Math.Ceiling(zLength / zCellSize); float[] N = new float[3] { nX, nY, nZ }; // 5. Initialize nodeTree var lattice = new Lattice(); // 6. Prepare cell (this is a UnitCell object) cell = cell.Duplicate(); cell.FormatTopology(); // 7. Define iteration vectors in each direction (accounting for Cell Size) Vector3d vectorU = xCellSize * basePlane.XAxis; Vector3d vectorV = yCellSize * basePlane.YAxis; Vector3d vectorW = zCellSize * basePlane.ZAxis; // 8. Map nodes to design space // Loop through the uvw cell grid for (int u = 0; u <= N[0]; u++) { for (int v = 0; v <= N[1]; v++) { for (int w = 0; w <= N[2]; w++) { // Construct cell path in tree GH_Path treePath = new GH_Path(u, v, w); // Fetch the list of nodes to append to, or initialise it var nodeList = lattice.Nodes.EnsurePath(treePath); // This loop maps each node in the cell for (int i = 0; i < cell.Nodes.Count; i++) { double usub = cell.Nodes[i].X; // u-position within unit cell (local) double vsub = cell.Nodes[i].Y; // v-position within unit cell (local) double wsub = cell.Nodes[i].Z; // w-position within unit cell (local) double[] uvw = { u + usub, v + vsub, w + wsub }; // uvw-position (global) // Check if the node belongs to another cell (i.e. it's relative path points outside the current cell) bool isOutsideCell = (cell.NodePaths[i][0] > 0 || cell.NodePaths[i][1] > 0 || cell.NodePaths[i][2] > 0); // Check if current uvw-position is beyond the upper boundary bool isOutsideSpace = (uvw[0] > N[0] || uvw[1] > N[1] || uvw[2] > N[2]); if (isOutsideCell || isOutsideSpace) { nodeList.Add(null); } else { // Compute position vector Vector3d V = uvw[0] * vectorU + uvw[1] * vectorV + uvw[2] * vectorW; var newNode = new LatticeNode(basePlane.Origin + V); // Check if point is inside - use unstrict tolerance, meaning it can be outside the surface by the specified tolerance bool isInside = FrameTools.IsPointInside(designSpace, newNode.Point3d, spaceType, tol, strictlyIn); // Set the node state (it's location wrt the design space) if (isInside) { newNode.State = LatticeNodeState.Inside; } else { newNode.State = LatticeNodeState.Outside; } // Add node to tree nodeList.Add(newNode); } } } } } // 9. Map struts to the node tree lattice.UniformMapping(cell, designSpace, spaceType, N, minLength, maxLength); // 10. Set output DA.SetDataList(0, lattice.Struts); }