private void TraverseRelationshipsAndMarkColumns(LatticeNode ln, int DistanceFromKey) { for (int i = ln.DistanceFromKey + 1; i <= maxDistanceFromKey; i++) //enumerate the levels above this node { List <int> loopOrder = new List <int>(); if (LayoutMethod == VisualizeAttributeLattice.LatticeLayoutMethod.ShortSingleLevelRelationshipsFirst) { for (int j = i; j <= maxDistanceFromKey; j++) //enumerate the distance from the next node to the top... helps to start with a bunch of short relationships { loopOrder.Add(j); } } else { int lastJ = -1; for (int j = i + 1; lastJ != i; j = (j >= maxDistanceFromKey ? i : j + 1)) //enumerate the distance from the next node to the top... helps to start with a bunch of short relationships { lastJ = j; loopOrder.Add(j); } } foreach (int j in loopOrder) //enumerate the distance from the next node to the top... helps to start with a bunch of short relationships { foreach (LatticeRelationship lr in ln.Relationships) { LatticeNode r = lr.node; if (r.DistanceFromKey == i && r.DistanceFromKey + r.MinRelationshipDistance == j) { if (ln.MinRelationshipColumnDistance < Math.Abs(ln.MinColumnPosition - r.MinColumnPosition)) { ln.MinRelationshipColumnDistance = Math.Abs(ln.MinColumnPosition - r.MinColumnPosition); } if (r.DistanceFromKey >= DistanceFromKey) { if (r.MaxColumnPosition == arColumnsUsed[DistanceFromKey - 1] && ln.MaxColumnPosition > r.MaxColumnPosition) { if (r.MinColumnPosition == 0) { r.MinColumnPosition = ln.MinColumnPosition; } arColumnsUsed[DistanceFromKey - 1] = ln.MaxColumnPosition; r.MaxColumnPosition = arColumnsUsed[DistanceFromKey - 1]; TraverseRelationshipsAndMarkColumns(r, DistanceFromKey + 1); } else if (r.MinColumnPosition == 0) { r.MinColumnPosition = Math.Max(arColumnsUsed[DistanceFromKey - 1] + 1, ln.MinColumnPosition); r.MaxColumnPosition = Math.Max(arColumnsUsed[DistanceFromKey - 1] + 1, ln.MaxColumnPosition); arColumnsUsed[DistanceFromKey - 1] = r.MaxColumnPosition; TraverseRelationshipsAndMarkColumns(r, DistanceFromKey + 1); } } } } } } }
private void TraverseRelationshipsAndDraw(LatticeNode ln, Graphics g) { if (ln.IsKey) { ln.x = canvas.Width / 2 - NODE_WIDTH / 2; } else if (ln.MinColumnPosition == ln.MaxColumnPosition) { ln.x = ln.MinColumnPosition * NODE_SPACING + (ln.MinColumnPosition - 1) * NODE_WIDTH; } else { ln.x = (((float)ln.MaxColumnPosition - ln.MinColumnPosition) / 2 + ln.MinColumnPosition) * NODE_SPACING + ((((float)ln.MaxColumnPosition - ln.MinColumnPosition) / 2 + ln.MinColumnPosition) - 1) * NODE_WIDTH; } ln.y = canvas.Height - (maxNodeHeight + NODE_SPACING) * (ln.DistanceFromKey + 1); ln.Rendered = true; foreach (LatticeRelationship lr in ln.Relationships) { LatticeNode r = lr.node; if (!r.Rendered) { TraverseRelationshipsAndDraw(r, g); } Pen pen = new Pen((lr.Visible ? Color.Green : Color.Gray), 1.55F); pen.DashStyle = (lr.RelationshipType == RelationshipType.Rigid ? DashStyle.Solid : DashStyle.Dash); if (lr.RelationshipType != RelationshipType.Rigid) { pen.DashPattern = new float[] { 1F, 1.55F }; //pen.DashOffset = (int)((new Random()).NextDouble()*10); } if (r.DistanceFromKey - ln.DistanceFromKey > 1 && ln.x == r.x) { //multilevel relationships which are completely vertical could never be seen pen.Width = 5; pen.Color = Color.Red; pen.DashStyle = DashStyle.Dash; pen.DashPattern = new float[] { 0.4F, 1.5F }; } //purposefully don't put an end cap on it... we could note the cardinality, but according to http://msdn2.microsoft.com/en-us/library/ms176124.aspx that has no impact //GraphicsPath hPath = new GraphicsPath(); //hPath.AddLine(new Point(-2, -2), new Point(2, -2)); //CustomLineCap cap = new CustomLineCap(null, hPath); //pen.CustomEndCap = cap; //g.DrawLine(new Pen(Color.White, 7), ln.x + NODE_WIDTH / 2, ln.y, r.x + NODE_WIDTH / 2, r.y + maxNodeHeight); //if we get a bunch of redundant relationships with crossing lines, we might want to turn this on g.DrawLine(pen, ln.x + NODE_WIDTH / 2, ln.y, r.x + NODE_WIDTH / 2, r.y + maxNodeHeight); } }
private int TraverseRelationshipsAndMarkMaxDepth(LatticeNode ln, int DistanceFromKey) { int depth = DistanceFromKey; foreach (LatticeRelationship lr in ln.Relationships) { LatticeNode r = lr.node; if (r.DistanceFromKey >= DistanceFromKey) { depth = Math.Max(depth, TraverseRelationshipsAndMarkMaxDepth(r, DistanceFromKey + 1)); } } ln.MaxRelationshipDepth = depth; return(depth); }
private void TraverseRelationshipsAndMarkMinDistance(LatticeNode ln, int DistanceFromKey) { foreach (LatticeRelationship lr in ln.Relationships) { LatticeNode r = lr.node; if (r.DistanceFromKey == DistanceFromKey) { TraverseRelationshipsAndMarkMinDistance(r, DistanceFromKey + 1); } if (ln.MinRelationshipDistance < r.DistanceFromKey - ln.DistanceFromKey) { ln.MinRelationshipDistance = r.DistanceFromKey - ln.DistanceFromKey; } } }
private void TraverseRelationshipsAndMarkDistance(LatticeNode ln, int DistanceFromKey) { foreach (LatticeRelationship lr in ln.Relationships) { LatticeNode r = lr.node; if (r.DistanceFromKey < DistanceFromKey) { r.DistanceFromKey = DistanceFromKey; } if (DistanceFromKey + 1 < Nodes.Count) //prevent circular references from causing an infinite loop { TraverseRelationshipsAndMarkDistance(r, DistanceFromKey + 1); } } }
private int TraverseRelationshipsAndMarkMaxDepth(LatticeNode ln, int DistanceFromKey) { int depth = DistanceFromKey; foreach (LatticeRelationship lr in ln.Relationships) { LatticeNode r = lr.node; if (r.DistanceFromKey >= DistanceFromKey) { depth = Math.Max(depth, TraverseRelationshipsAndMarkMaxDepth(r, DistanceFromKey + 1)); } } ln.MaxRelationshipDepth = depth; return depth; }
private void TraverseRelationshipsAndMarkDistance(LatticeNode ln, int DistanceFromKey) { foreach (LatticeRelationship lr in ln.Relationships) { LatticeNode r = lr.node; if (r.DistanceFromKey < DistanceFromKey) r.DistanceFromKey = DistanceFromKey; if (DistanceFromKey + 1 < Nodes.Count) //prevent circular references from causing an infinite loop { TraverseRelationshipsAndMarkDistance(r, DistanceFromKey + 1); } } }
public LatticeRelationship(LatticeNode ln, RelationshipType rt, bool visible) { this.node = ln; this.RelationshipType = rt; this.Visible = visible; }
/// <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); }
public Bitmap Render() { canvas = new Bitmap(NODE_WIDTH, 1000); Graphics g = Graphics.FromImage(canvas); g.SmoothingMode = SmoothingMode.AntiAlias; StringFormat centered = new StringFormat(); centered.Alignment = StringAlignment.Center; //find out the max node height so that all nodes can be sized the same //also find the key attribute LatticeNode key = null; foreach (LatticeNode ln in this.Nodes.Values) { SizeF sz = g.MeasureString(ln.Text, NODE_FONT, NODE_WIDTH, centered); ln.TextHeight = sz.Height; if (maxNodeHeight < ln.TextHeight + 10) { maxNodeHeight = ln.TextHeight + 10; } if (ln.IsKey) { key = ln; } } g.Dispose(); g = null; canvas.Dispose(); canvas = null; //mark distance from key TraverseRelationshipsAndMarkDistance(key, 1); TraverseRelationshipsAndMarkMinDistance(key, 1); //throw error if any nodes aren't related to the key foreach (LatticeNode ln in this.Nodes.Values) { if (ln.DistanceFromKey == 0 && !ln.IsKey) { throw new Exception("Node " + ln.Text + " is not related to the key directly or indirectly!"); } } //figure out the number of nodes wide and tall for (int i = 1; i < Nodes.Count; i++) { int tempCount = 0; foreach (LatticeNode ln in this.Nodes.Values) { if (ln.DistanceFromKey == i) { tempCount++; } } if (maxNodesAcross < tempCount) { maxNodesAcross = tempCount; } if (tempCount == 0) { break; } else { maxDistanceFromKey = i; } } if (LayoutMethod == VisualizeAttributeLattice.LatticeLayoutMethod.DeepestPathsFirst) { arColumnsUsed = new int[maxDistanceFromKey]; foreach (LatticeNode ln in this.Nodes.Values) { if (!ln.IsKey) { maxNodesAcross = Math.Max(maxNodesAcross, ++arColumnsUsed[ln.DistanceFromKey - 1]); } } if (maxNodesAcross % 2 == 0) { maxNodesAcross++; //make sure it's an odd number so the center column of nodes will be in center } TraverseRelationshipsAndMarkMaxDepth(key, 1); arLayoutMatrix = new bool[maxDistanceFromKey, maxNodesAcross]; for (int i = maxDistanceFromKey + 1; i >= 0; i--) { TraverseDeepestRelationshipsAndMarkColumns(key, 1, i); } } else { //holds the number of columns which have been used so far arColumnsUsed = new int[maxDistanceFromKey]; TraverseRelationshipsAndMarkColumns(key, 1); for (int i = 0; i < maxDistanceFromKey; i++) { if (maxNodesAcross < arColumnsUsed[i]) { maxNodesAcross = arColumnsUsed[i]; } } } canvas = new Bitmap((int)(NODE_WIDTH * maxNodesAcross + NODE_SPACING * (maxNodesAcross + 1)), (int)(maxNodeHeight * (maxDistanceFromKey + 1) + NODE_SPACING * (maxDistanceFromKey + 2) + 50)); g = Graphics.FromImage(canvas); g.SmoothingMode = SmoothingMode.AntiAlias; g.FillRectangle(new SolidBrush(Color.White), 0, 0, canvas.Width, canvas.Height); //draw title Font titleFont = new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold); SizeF titleSize = g.MeasureString(this.Title, titleFont, canvas.Width, centered); g.DrawString(this.Title, titleFont, new SolidBrush(Color.Black), new RectangleF(0, Math.Max(50 / 2 - titleSize.Height / 2, 0), canvas.Width, Math.Max(50, titleSize.Height)), centered); //draw the relationships TraverseRelationshipsAndDraw(key, g); //draw the nodes foreach (LatticeNode ln in this.Nodes.Values) { RectangleF fillRect = new RectangleF(ln.x, ln.y, NODE_WIDTH, maxNodeHeight); g.FillRectangle(new SolidBrush((ln.Visible ? Color.LightBlue : Color.LightGray)), fillRect); //Color.FromArgb(153, 204, 204) //this color is in the default palette for a GIF RectangleF textRect = new RectangleF(ln.x, ln.y, NODE_WIDTH, maxNodeHeight); textRect.Y += (maxNodeHeight - ln.TextHeight) / 2; g.DrawString(ln.Text, new Font(FontFamily.GenericSansSerif, 10, (ln.Enabled ? FontStyle.Regular : FontStyle.Italic)), new SolidBrush((ln.Enabled ? Color.Black : Color.Gray)), textRect, centered); } g.Dispose(); return(canvas); }
/// <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) { // 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); }
/// <summary> /// Do unit selection. /// </summary> /// <param name="searchConfig">Search configuration.</param> /// <returns>The best path.</returns> public List<PathNodeInfo> UnitSelecting(SearchConfig searchConfig) { ILatticeProvider latticeProvider = searchConfig.LatticeProvider; ITargetCostCalculator targetCostCalculator = searchConfig.TargetCostCalculator; IJoinCalculator joinCostCalculator = searchConfig.JoinCostCalculator; int targetCount = latticeProvider.GetColumnCount(); if (targetCount == 0) { string message = Helper.NeutralFormat("The ILatticeProvider contains 0 targets!"); throw new InvalidDataException(message); } List<PathNodeInfo> bestPath = new List<PathNodeInfo>(targetCount); TargetNode[] targets = new TargetNode[targetCount]; LatticeNode[][] lattice = new LatticeNode[targetCount][]; for (int i = 0; i < targetCount; i++) { int candidateCount = latticeProvider.GetRowCount(i); if (candidateCount == 0) { string message = Helper.NeutralFormat("The ILatticeProvider contains 0 candidates for target {0}!", i); throw new InvalidDataException(message); } targets[i] = new TargetNode(latticeProvider.GetTarget(i), candidateCount); lattice[i] = new LatticeNode[candidateCount]; for (int j = 0; j < candidateCount; j++) { PathNodeInfo pathNodeInfo = new PathNodeInfo(latticeProvider.GetCandidate(i, j), float.PositiveInfinity, float.PositiveInfinity); lattice[i][j] = new LatticeNode(pathNodeInfo, float.PositiveInfinity, -1); } } for (int i = 0; i < targets[0].CandidateCount; i++) { LatticeNode nodeCur = lattice[0][i]; float targetCost = targetCostCalculator.GetTargetCost(targets[0].Target, nodeCur.PathNodeInfo.Candidate); nodeCur.BestScore = targetCost; nodeCur.PathNodeInfo.TargetCost = targetCost; } for (int col = 1; col < targetCount; col++) { TargetNode targetCur = targets[col]; TargetNode targetLeft = targets[col - 1]; for (int i = 0; i < targetCur.CandidateCount; i++) { LatticeNode nodeCur = lattice[col][i]; LatticeNode nodeLeft = lattice[col - 1][0]; float joinCost = joinCostCalculator.GetJoinCost(targetLeft.Target, targetCur.Target, nodeLeft.PathNodeInfo.Candidate, nodeCur.PathNodeInfo.Candidate); float bestPreScore = nodeLeft.BestScore + joinCost; int bestPreNodeIndex = 0; float bestJoinCost = joinCost; for (int j = 1; j < targets[col - 1].CandidateCount; j++) { nodeLeft = lattice[col - 1][j]; joinCost = joinCostCalculator.GetJoinCost(targetLeft.Target, targetCur.Target, nodeLeft.PathNodeInfo.Candidate, nodeCur.PathNodeInfo.Candidate); float preCost = nodeLeft.BestScore + joinCost; if (preCost < bestPreScore) { bestPreScore = preCost; bestPreNodeIndex = j; bestJoinCost = joinCost; } } float targetCost = targetCostCalculator.GetTargetCost(targetCur.Target, nodeCur.PathNodeInfo.Candidate); nodeCur.BestScore = bestPreScore + targetCost; nodeCur.BestPreNodeIndex = bestPreNodeIndex; nodeCur.PathNodeInfo.TargetCost = targetCost; nodeCur.PathNodeInfo.JoinCost = bestJoinCost; } } int columnIndex = targetCount - 1; LatticeNode bestNode = lattice[columnIndex][0]; float bestScore = bestNode.BestScore; for (int i = 1; i < targets[columnIndex].CandidateCount; i++) { LatticeNode curNode = lattice[columnIndex][i]; if (curNode.BestScore < bestScore) { bestNode = curNode; bestScore = bestNode.BestScore; } } while (true) { bestPath.Add(bestNode.PathNodeInfo); columnIndex--; if (columnIndex < 0) { break; } bestNode = lattice[columnIndex][bestNode.BestPreNodeIndex]; } bestPath.Reverse(); return bestPath; }
private void TraverseDeepestRelationshipsAndMarkColumns(LatticeNode ln, int DistanceFromKey, int MaxRelationshipDepth) { foreach (LatticeRelationship lr in ln.Relationships) { if (lr.node.DistanceFromKey != DistanceFromKey) { continue; //if there are redundant attribute relationships with different depths, then skip marking this if we're at the wrong depth } LatticeNode r = lr.node; if (r.MaxRelationshipDepth == MaxRelationshipDepth && r.MinColumnPosition == 0) { int pos = (int)Math.Round(maxNodesAcross / 2.0, MidpointRounding.AwayFromZero); bool bColumnSet = false; //start by going the only the direction (left or right) that the child node is going if (DistanceFromKey > 1) { for (int j = 0; j < maxNodesAcross / 2.0; j++) { if (ln.MaxColumnPosition > pos && !arLayoutMatrix[DistanceFromKey - 1, pos + j - 1]) { bColumnSet = true; arLayoutMatrix[DistanceFromKey - 1, pos + j - 1] = true; r.MinColumnPosition = pos + j; r.MaxColumnPosition = pos + j; break; } else if (ln.MinColumnPosition < pos && !arLayoutMatrix[DistanceFromKey - 1, pos - j - 1]) { bColumnSet = true; arLayoutMatrix[DistanceFromKey - 1, pos - j - 1] = true; r.MinColumnPosition = pos - j; r.MaxColumnPosition = pos - j; break; } } } //if there's no room going the way that the child node went, then go the other way if (!bColumnSet) { for (int j = 0; j < maxNodesAcross / 2.0; j++) { if (!arLayoutMatrix[DistanceFromKey - 1, pos + j - 1]) { bColumnSet = true; arLayoutMatrix[DistanceFromKey - 1, pos + j - 1] = true; r.MinColumnPosition = pos + j; r.MaxColumnPosition = pos + j; break; } else if (!arLayoutMatrix[DistanceFromKey - 1, pos - j - 1]) { bColumnSet = true; arLayoutMatrix[DistanceFromKey - 1, pos - j - 1] = true; r.MinColumnPosition = pos - j; r.MaxColumnPosition = pos - j; break; } } } } TraverseDeepestRelationshipsAndMarkColumns(r, DistanceFromKey + 1, MaxRelationshipDepth); } }
private void TraverseRelationshipsAndMarkColumns(LatticeNode ln, int DistanceFromKey) { for (int i = ln.DistanceFromKey + 1; i <= maxDistanceFromKey; i++) //enumerate the levels above this node { List<int> loopOrder = new List<int>(); if (LayoutMethod == VisualizeAttributeLattice.LatticeLayoutMethod.ShortSingleLevelRelationshipsFirst) { for (int j = i; j <= maxDistanceFromKey; j++) //enumerate the distance from the next node to the top... helps to start with a bunch of short relationships { loopOrder.Add(j); } } else { int lastJ = -1; for (int j = i + 1; lastJ != i; j = (j >= maxDistanceFromKey ? i : j + 1)) //enumerate the distance from the next node to the top... helps to start with a bunch of short relationships { lastJ = j; loopOrder.Add(j); } } foreach (int j in loopOrder) //enumerate the distance from the next node to the top... helps to start with a bunch of short relationships { foreach (LatticeRelationship lr in ln.Relationships) { LatticeNode r = lr.node; if (r.DistanceFromKey == i && r.DistanceFromKey + r.MinRelationshipDistance == j) { if (ln.MinRelationshipColumnDistance < Math.Abs(ln.MinColumnPosition - r.MinColumnPosition)) { ln.MinRelationshipColumnDistance = Math.Abs(ln.MinColumnPosition - r.MinColumnPosition); } if (r.DistanceFromKey >= DistanceFromKey) { if (r.MaxColumnPosition == arColumnsUsed[DistanceFromKey - 1] && ln.MaxColumnPosition > r.MaxColumnPosition) { if (r.MinColumnPosition == 0) r.MinColumnPosition = ln.MinColumnPosition; arColumnsUsed[DistanceFromKey - 1] = ln.MaxColumnPosition; r.MaxColumnPosition = arColumnsUsed[DistanceFromKey - 1]; TraverseRelationshipsAndMarkColumns(r, DistanceFromKey + 1); } else if (r.MinColumnPosition == 0) { r.MinColumnPosition = Math.Max(arColumnsUsed[DistanceFromKey - 1] + 1, ln.MinColumnPosition); r.MaxColumnPosition = Math.Max(arColumnsUsed[DistanceFromKey - 1] + 1, ln.MaxColumnPosition); arColumnsUsed[DistanceFromKey - 1] = r.MaxColumnPosition; TraverseRelationshipsAndMarkColumns(r, DistanceFromKey + 1); } } } } } } }
/// <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); }