public Glulam Generate() { GlulamData data = new GlulamData(Centreline, Width, Height, Frames.ToArray(), CrossSectionSamples, 100); Glulam g = Glulam.CreateGlulam(Centreline, Frames.ToArray(), data); return(g); }
/// <summary> /// Join a glulam onto another one. Returns null if join is not possible. /// </summary> /// <param name="glulam"></param> /// <returns></returns> public Glulam Join(Glulam glulam) { Rhino.Geometry.Intersect.CurveIntersections ci; ci = Rhino.Geometry.Intersect.Intersection.CurveCurve(Centreline, glulam.Centreline, Tolerance, OverlapTolerance); if (ci.Count != 1) { return(null); } if (ci[0].IsOverlap) { return(null); } if (Math.Abs(Centreline.TangentAt(ci[0].ParameterA) * glulam.Centreline.TangentAt(ci[0].ParameterB)) < AngleTolerance) { return(null); } Curve[] NewCentreline = Curve.JoinCurves(new Curve[] { Centreline, glulam.Centreline }); if (NewCentreline.Length != 1) { return(null); } GlulamOrientation NewOrientation = Orientation.Duplicate(); NewOrientation.Join(glulam.Orientation); Glulam new_glulam = CreateGlulam(NewCentreline[0], NewOrientation, Data.Duplicate()); new_glulam.Data.Samples = Data.Samples + glulam.Data.Samples; return(new_glulam); }
static public Glulam CreateGlulamFromBeamGeometry2(Curve curve, Mesh beam, out double true_width, out double true_height, out double true_length, double extra = 0.0) { Mesh mm = curve.MapToCurveSpace(beam); BoundingBox bb = mm.GetBoundingBox(true); double x = bb.Center.X; double y = bb.Center.Y; double tmin, tmax; curve.LengthParameter(bb.Min.Z, out tmin); curve.LengthParameter(bb.Max.Z, out tmax); Plane twist = Plane.WorldXY; Polyline ch; mm = mm.FitToAxes(Plane.WorldXY, out ch, ref twist); bb = mm.GetBoundingBox(true); double dx = bb.Max.X - bb.Min.X; double dy = bb.Max.Y - bb.Min.Y; Plane cp; curve.PerpendicularFrameAt(tmin, out cp); double angle = Vector3d.VectorAngle(Vector3d.XAxis, twist.XAxis); int sign = Math.Sign(twist.YAxis * Vector3d.XAxis); Curve[] segments = curve.Split(new double[] { tmin, tmax }); if (segments.Length == 3) { curve = segments[1]; } else { curve = segments[0]; } Beam b = new Beam(curve, null, new Plane[] { cp }); //curve = b.CreateOffsetCurve(-x, -y); curve = b.CreateOffsetCurve(x, y); curve = curve.Extend(CurveEnd.Both, extra, CurveExtensionStyle.Smooth); cp.Transform(Rhino.Geometry.Transform.Rotation(angle * sign, cp.ZAxis, cp.Origin)); GlulamData data = GlulamData.FromCurveLimits(curve, dx + extra * 2, dy + extra * 2, new Plane[] { cp }); true_length = curve.GetLength(); true_width = dx + extra * 2; true_height = dy + extra * 2; return(Glulam.CreateGlulam(curve, new Plane[] { cp }, data)); }
public override GlulamAssembly Duplicate() { Glulam[] new_branches = new Glulam[Branches.Length]; for (int i = 0; i < Branches.Length; ++i) { new_branches[i] = Branches[i].Duplicate(); } return(new BifurcatingAssembly2(new_branches)); }
static public Brep GetGlulamBisector(Glulam g1, Glulam g2, double extension = 50.0, bool normalized = false) { Glulam[] g = new Glulam[2] { g1, g2 }; int shorter = g[0].Centreline.GetLength() > g[1].Centreline.GetLength() ? 1 : 0; int longer = 1 - shorter; double length = g[shorter].Centreline.GetLength(); double[] t = g[shorter].Centreline.DivideByCount(10, true); double t2; List <Point3d>[] edge_pts = new List <Point3d> [2]; for (int i = 0; i < 2; ++i) { edge_pts[i] = new List <Point3d>(); } double tl; for (int i = 0; i < t.Length; ++i) { tl = g[shorter].Centreline.GetLength(new Interval(g[shorter].Centreline.Domain.Min, t[i])); g[longer].Centreline.LengthParameter(tl, out t2); //g[longer].Centreline.ClosestPoint(g[shorter].Centreline.PointAt(t[i]), out t2); Plane p = Interpolation.InterpolatePlanes2(g[shorter].GetPlane(t[i]), g[longer].GetPlane(t2), 0.5); edge_pts[0].Add(p.Origin + p.YAxis * extension); edge_pts[1].Add(p.Origin - p.YAxis * extension); } Curve[] crvs = new Curve[4]; for (int i = 0; i < 2; ++i) { crvs[i] = Curve.CreateControlPointCurve(edge_pts[i], 2); crvs[i] = crvs[i].Extend(CurveEnd.Both, extension, CurveExtensionStyle.Smooth); //crvs[i] = crvs[i].Rebuild(10, 3, true); } crvs[2] = (new Line(crvs[0].PointAtStart, crvs[1].PointAtStart)).ToNurbsCurve(); crvs[3] = (new Line(crvs[0].PointAtEnd, crvs[1].PointAtEnd)).ToNurbsCurve(); Brep brep = Brep.CreateEdgeSurface(crvs); //Brep[] brep = Brep.CreateFromLoft(crvs, crvs[0].PointAtStart, crvs[0].PointAtStart, // LoftType.Straight, false); return(brep); }
// PLACEHOLDER public BifurcatingAssembly(Glulam trunk, Glulam[] branches) : base(new Curve[1 + branches.Length]) { Centrelines[0] = trunk.Centreline; for (int i = 0; i < branches.Length; ++i) { Centrelines[1 + i] = branches[i].Centreline; } Trunk = trunk; Branches = branches; // TODO: check for continuity between trunk and branches }
public Glulam Trim(Interval domain, double overlap) { double l1 = Centreline.GetLength(new Interval(Centreline.Domain.Min, domain.Min)); double l2 = Centreline.GetLength(new Interval(Centreline.Domain.Min, domain.Max)); double t1, t2; if (!Centreline.LengthParameter(l1 - overlap, out t1)) { t1 = domain.Min; } if (!Centreline.LengthParameter(l2 + overlap, out t2)) { t2 = domain.Max; } domain = new Interval( Math.Max(t1, Centreline.Domain.Min), Math.Min(t2, Centreline.Domain.Max)); double length = Centreline.GetLength(domain); if (domain.IsDecreasing || length < overlap || length < Glulam.OverlapTolerance) { return(null); } double percentage = length / Centreline.GetLength(); GlulamData data = Data.Duplicate(); data.Samples = Math.Max(6, (int)(data.Samples * percentage)); Curve trimmed_curve = Centreline.Trim(domain); GlulamOrientation trimmed_orientation = Orientation.Trim(domain); trimmed_orientation.Remap(Centreline, trimmed_curve); Glulam glulam = CreateGlulam(trimmed_curve, trimmed_orientation, data); return(glulam); }
/// <summary> /// Create glulam with frames that are aligned with a Surface. The input curve does not /// necessarily have to lie on the Surface. /// </summary> /// <param name="curve">Input centreline of the Glulam.</param> /// <param name="srf">Surface to align the Glulam orientation to.</param> /// <param name="num_samples">Number of orientation frames to use for alignment.</param> /// <returns>New Glulam oriented to the Surface.</returns> static public Glulam CreateGlulamNormalToSurface_old(Curve curve, Brep brep, int num_samples = 20, GlulamData data = null) { double[] t = curve.DivideByCount(num_samples, true); List <Plane> planes = new List <Plane>(); double u, v; Vector3d xaxis, yaxis, zaxis; ComponentIndex ci; Point3d pt; for (int i = 0; i < t.Length; ++i) { brep.ClosestPoint(curve.PointAt(t[i]), out pt, out ci, out u, out v, 0, out yaxis); zaxis = curve.TangentAt(t[i]); xaxis = Vector3d.CrossProduct(zaxis, yaxis); planes.Add(new Plane(curve.PointAt(t[i]), xaxis, yaxis)); } return(Glulam.CreateGlulam(curve, planes.ToArray(), data)); }
/// <summary> /// Split glulam into two at parameter t. /// </summary> /// <param name="t">Curve parameter to split glulam at.</param> /// <returns>List of new glulams.</returns> public List <Glulam> Split(double t) { if (!Centreline.Domain.IncludesParameter(t)) { return(null); } double percentage = (t - Centreline.Domain.Min) / (Centreline.Domain.Max - Centreline.Domain.Min); Plane split_plane = GetPlane(t); Curve[] split_curves = Centreline.Split(t); if (split_curves == null || split_curves.Length != 2) { return(null); } GlulamData Data1 = Data.Duplicate(); Data1.Samples = (int)(Data.Samples * percentage); GlulamOrientation[] SplitOrientations = Orientation.Split(new double[] { t }); Glulam Blank1 = CreateGlulam(split_curves[0], SplitOrientations[0], Data1); GlulamData Data2 = Data.Duplicate(); Data2.Samples = (int)(Data.Samples * (1 - percentage)); Glulam Blank2 = CreateGlulam(split_curves[1], SplitOrientations[1], Data2); List <Glulam> blanks = new List <Glulam>() { Blank1, Blank2 }; return(blanks); }
/// <summary> /// Gets fibre direction throughout this Glulam, given another Glulam that contains it. /// </summary> /// <param name="blank">Glulam blank to compare against.</param> /// <param name="angles">List of angles. The fibre direction deviates by this much from the centreline.</param> /// <param name="divX">Number of sampling divisions in X.</param> /// <param name="divY">Number of sampling divisions in Y.</param> /// <param name="divZ">Number of sampling divisions along the length of the Glulam.</param> /// <returns></returns> public List <Ray3d> FibreDeviation(Glulam blank, out List <double> angles, int divX = 8, int divY = 8, int divZ = 50) { double stepX = Data.LamWidth * Data.NumWidth / (divX + 1); double stepY = Data.LamHeight * Data.NumHeight / (divY + 1); List <Ray3d> rays = new List <Ray3d>(); angles = new List <double>(); double[] tt = this.Centreline.DivideByCount(divZ, true); double t; for (int z = 0; z < tt.Length; ++z) { for (int y = -divY / 2; y <= divY / 2; ++y) { for (int x = -divX / 2; x <= divX / 2; ++x) { Plane BePlane = this.GetPlane(tt[z]); Point3d pt = BePlane.Origin + BePlane.YAxis * stepY * y + BePlane.XAxis * stepX * x; blank.Centreline.ClosestPoint(pt, out t); Vector3d tanBl = blank.Centreline.TangentAt(t); Vector3d tanBe = this.Centreline.TangentAt(tt[z]); double angle = Math.Acos(Math.Abs(tanBl * tanBe)); rays.Add(new Ray3d(pt, tanBl)); angles.Add(angle); } } } return(rays); }
List <Curve> LamellaOutlines(Glulam g) { double[] t = g.Centreline.DivideByCount(g.Data.Samples, true); List <Plane> planes = t.Select(x => g.GetPlane(x)).ToList(); Point3d[][] pts = new Point3d[4][]; pts[0] = new Point3d[g.Data.NumWidth + 1]; pts[1] = new Point3d[g.Data.NumHeight + 1]; pts[2] = new Point3d[g.Data.NumWidth + 1]; pts[3] = new Point3d[g.Data.NumHeight + 1]; double hWidth = g.Width / 2; double hHeight = g.Height / 2; // Create points for lamella corners for (int i = 0; i <= g.Data.NumWidth; ++i) { pts[0][i] = new Point3d(-hWidth + g.Data.LamWidth * i, -hHeight, 0); pts[2][i] = new Point3d(-hWidth + g.Data.LamWidth * i, hHeight, 0); } for (int i = 0; i <= g.Data.NumHeight; ++i) { pts[1][i] = new Point3d(-hWidth, -hHeight + g.Data.LamHeight * i, 0); pts[3][i] = new Point3d(hWidth, -hHeight + g.Data.LamHeight * i, 0); } List <Point3d>[][] crv_pts = new List <Point3d> [4][]; crv_pts[0] = new List <Point3d> [g.Data.NumWidth + 1]; crv_pts[1] = new List <Point3d> [g.Data.NumHeight + 1]; crv_pts[2] = new List <Point3d> [g.Data.NumWidth + 1]; crv_pts[3] = new List <Point3d> [g.Data.NumHeight + 1]; Transform xform; Point3d pt; // Create curve points foreach (Plane p in planes) { xform = Rhino.Geometry.Transform.PlaneToPlane(Plane.WorldXY, p); for (int i = 0; i <= g.Data.NumWidth; ++i) { pt = new Point3d(pts[0][i]); pt.Transform(xform); if (crv_pts[0][i] == null) { crv_pts[0][i] = new List <Point3d>(); } crv_pts[0][i].Add(pt); pt = new Point3d(pts[2][i]); pt.Transform(xform); if (crv_pts[2][i] == null) { crv_pts[2][i] = new List <Point3d>(); } crv_pts[2][i].Add(pt); } for (int i = 0; i <= g.Data.NumHeight; ++i) { pt = new Point3d(pts[1][i]); pt.Transform(xform); if (crv_pts[1][i] == null) { crv_pts[1][i] = new List <Point3d>(); } crv_pts[1][i].Add(pt); pt = new Point3d(pts[3][i]); pt.Transform(xform); if (crv_pts[3][i] == null) { crv_pts[3][i] = new List <Point3d>(); } crv_pts[3][i].Add(pt); } } // Create lamella side curves List <Curve> crvs = new List <Curve>(); for (int i = 0; i <= g.Data.NumWidth; ++i) { crvs.Add(Curve.CreateInterpolatedCurve(crv_pts[0][i], 3)); crvs.Add(Curve.CreateInterpolatedCurve(crv_pts[2][i], 3)); } for (int i = 0; i <= g.Data.NumHeight; ++i) { crvs.Add(Curve.CreateInterpolatedCurve(crv_pts[1][i], 3)); crvs.Add(Curve.CreateInterpolatedCurve(crv_pts[3][i], 3)); } // Create lamella end curves Point3d p0, p1; xform = Rhino.Geometry.Transform.PlaneToPlane(Plane.WorldXY, planes.First()); for (int i = 0; i <= g.Data.NumWidth; ++i) { p0 = new Point3d(pts[0][i]); p0.Transform(xform); p1 = new Point3d(pts[2][i]); p1.Transform(xform); crvs.Add(new Line(p0, p1).ToNurbsCurve()); } for (int i = 0; i <= g.Data.NumHeight; ++i) { p0 = new Point3d(pts[1][i]); p0.Transform(xform); p1 = new Point3d(pts[3][i]); p1.Transform(xform); crvs.Add(new Line(p0, p1).ToNurbsCurve()); } xform = Rhino.Geometry.Transform.PlaneToPlane(Plane.WorldXY, planes.Last()); for (int i = 0; i <= g.Data.NumWidth; ++i) { p0 = new Point3d(pts[0][i]); p0.Transform(xform); p1 = new Point3d(pts[2][i]); p1.Transform(xform); crvs.Add(new Line(p0, p1).ToNurbsCurve()); } for (int i = 0; i <= g.Data.NumHeight; ++i) { p0 = new Point3d(pts[1][i]); p0.Transform(xform); p1 = new Point3d(pts[3][i]); p1.Transform(xform); crvs.Add(new Line(p0, p1).ToNurbsCurve()); } return(crvs); }
public List <Ray3d> FibreDeviation(Glulam blank, int divX = 8, int divY = 8, int divZ = 50) { List <double> angles; return(FibreDeviation(blank, out angles, divX, divY, divZ)); }
public Glulam[] Split(IList <double> t, double overlap = 0.0) { if (t.Count < 1) { return new Glulam[] { this.Duplicate() } } ; if (t.Count < 2) { if (Centreline.Domain.IncludesParameter(t[0])) { return(Split(t[0]).ToArray()); } else { return new Glulam[] { this.Duplicate() } }; } Glulam temp = this; List <double> parameters = new List <double>(); foreach (double p in t) { if (Centreline.Domain.IncludesParameter(p)) { parameters.Add(p); } } parameters.Sort(); //Curve[] centrelines = Centreline.Split(t); //GlulamOrientation[] orientations = Orientation.Split(t); Glulam[] glulams = new Glulam[parameters.Count]; int num_splits = 0; for (int i = 1; i < parameters.Count - 1; ++i) { List <Glulam> splits = temp.Split(parameters[i], overlap); if (splits == null || splits.Count < 2) { continue; } if (splits[0] != null) { glulams[i - 1] = splits[0]; num_splits++; } temp = splits[1]; } if (temp != null) { glulams[glulams.Length - 1] = temp; } return(glulams); }
/// <summary> /// Create a Glulam from arbitrary geometry and a guide curve. The curve describes the fibre direction of the Glulam. This will /// create a Glulam which completely envelops the input geometry and whose centreline is offset from the input guide curve, to /// preserve the desired fibre orientation. /// </summary> /// <param name="curve">Guide curve to direct the Glulam.</param> /// <param name="beam">Beam geometry as Mesh.</param> /// <param name="extra">Extra material tolerance to leave on Glulam (the width and height of the Glulam will be /// increased by this much).</param> /// <returns>A new Glulam which envelops the input beam geometry, plus an extra tolerance as defined above.</returns> static public Glulam CreateGlulamFromBeamGeometry(Curve curve, Mesh beam, out double true_width, out double true_height, out double true_length, double extra = 0.0) { double t, l; Plane cp = Plane.Unset; Plane cpp; Polyline ch; Mesh m = new Mesh(); List <Plane> frames = new List <Plane>(); double[] tt = curve.DivideByCount(20, true); if (curve.IsLinear()) { m = beam.DuplicateMesh(); curve.PerpendicularFrameAt(curve.Domain.Min, out cp); m.Transform(Rhino.Geometry.Transform.PlaneToPlane(cp, Plane.WorldXY)); m.Faces.Clear(); Plane twist = Plane.WorldXY; m = m.FitToAxes(Plane.WorldXY, out ch, ref twist); double angle = Vector3d.VectorAngle(Vector3d.XAxis, twist.XAxis); int sign = Math.Sign(twist.YAxis * Vector3d.XAxis); cp.Transform(Rhino.Geometry.Transform.Rotation(angle * sign, cp.ZAxis, cp.Origin)); frames.Add(cp); } else if (curve.TryGetPlane(out cpp, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance)) { for (int i = 0; i < tt.Length; ++i) { Vector3d xaxis = Vector3d.CrossProduct(cpp.ZAxis, curve.TangentAt(tt[i])); cp = new Plane(curve.PointAt(tt[i]), xaxis, cpp.ZAxis); frames.Add(cp); } for (int i = 0; i < beam.Vertices.Count; ++i) { Point3d p = new Point3d(beam.Vertices[i]); curve.ClosestPoint(p, out t); l = curve.GetLength(new Interval(curve.Domain.Min, t)); Vector3d xaxis = Vector3d.CrossProduct(cpp.ZAxis, curve.TangentAt(t)); cp = new Plane(curve.PointAt(t), xaxis, cpp.ZAxis); p.Transform(Rhino.Geometry.Transform.PlaneToPlane(cp, Plane.WorldXY)); p.Z = l; m.Vertices.Add(p); } } else { for (int i = 0; i < beam.Vertices.Count; ++i) { Point3d p = new Point3d(beam.Vertices[i]); curve.ClosestPoint(p, out t); l = curve.GetLength(new Interval(curve.Domain.Min, t)); curve.PerpendicularFrameAt(t, out cp); p.Transform(Rhino.Geometry.Transform.PlaneToPlane(cp, Plane.WorldXY)); p.Z = l; m.Vertices.Add(p); } Plane twist = Plane.WorldXY; m = m.FitToAxes(Plane.WorldXY, out ch, ref twist); double angle = Vector3d.VectorAngle(Vector3d.XAxis, twist.XAxis); int sign = Math.Sign(twist.YAxis * Vector3d.XAxis); for (int i = 0; i < tt.Length; ++i) { curve.PerpendicularFrameAt(tt[i], out cp); cp.Transform(Rhino.Geometry.Transform.Rotation(angle * sign, cp.ZAxis, cp.Origin)); frames.Add(cp); } } m.Faces.AddFaces(beam.Faces); m.FaceNormals.ComputeFaceNormals(); BoundingBox bb = m.GetBoundingBox(true); double offsetX = bb.Center.X; double offsetY = bb.Center.Y; Brep bb2 = bb.ToBrep(); bb2.Transform(Rhino.Geometry.Transform.PlaneToPlane(Plane.WorldXY, cp)); true_width = bb.Max.X - bb.Min.X + extra; true_height = bb.Max.Y - bb.Min.Y + extra; // Now we create the glulam... //tasTools.Lam.Glulam glulam = tasTools.Lam.Glulam.CreateGlulam(curve, frames.ToArray()); Beam temp_beam = new Beam(curve, null, frames.ToArray()); int samples = (int)(curve.GetLength() / GlulamData.DefaultSampleDistance); Curve new_curve = temp_beam.CreateOffsetCurve(offsetX, offsetY, samples, true); new_curve = new_curve.Extend(CurveEnd.Both, 5.0 + extra, CurveExtensionStyle.Smooth); GlulamData data = GlulamData.FromCurveLimits(new_curve, frames.ToArray()); if (new_curve.IsPlanar()) { data.LamHeight = Math.Ceiling(true_height); data.Lamellae = new Stick[(int)(Math.Ceiling(true_width / data.LamWidth)), 1]; } else if (new_curve.IsLinear()) { data.LamHeight = Math.Ceiling(true_height); data.LamWidth = Math.Ceiling(true_width); data.Lamellae = new Stick[1, 1]; } else { data.Lamellae = new Stick[(int)(Math.Ceiling(true_width / data.LamWidth)), (int)(Math.Ceiling(true_height / data.LamHeight))]; } Glulam glulam = tas.Lam.Glulam.CreateGlulam(new_curve, frames.ToArray(), data); /* * tt = glulam.Centreline.DivideByCount(100, true); * double maxK = 0.0; * * int index = 0; * Vector3d kvec = Vector3d.Unset; * Vector3d temp; * * for (int i = 0; i < tt.Length; ++i) * { * temp = glulam.Centreline.CurvatureAt(tt[i]); * if (temp.Length > maxK) * { * index = i; * kvec = temp; * maxK = temp.Length; * } * } * Plane frame = glulam.GetPlane(tt[index]); * * double min_lam_width = 1.0; * double min_lam_height = 1.0; * double max_lam_width = (double)((int)Math.Ceiling(true_width / 10.0) * 10.0); * double max_lam_height = (double)((int)Math.Ceiling(true_height / 10.0) * 10.0); * * double lam_width = Math.Ceiling(Math.Min(1 / (Math.Abs(kvec * frame.XAxis) * 200), max_lam_width)); * double lam_height = Math.Ceiling(Math.Min(1 / (Math.Abs(kvec * frame.YAxis) * 200), max_lam_height)); * * if (lam_width == 0.0) lam_width = max_lam_width; * if (lam_height == 0.0) lam_height = max_lam_height; * * glulam.Data.LamHeight = lam_height; * glulam.Data.LamWidth = lam_width; * glulam.Data.NumHeight = (int)(Math.Ceiling(true_height / lam_height)); * glulam.Data.NumWidth = (int)(Math.Ceiling(true_width / lam_width)); * * //double new_lam_height, new_lam_width; * * if (glulam.Data.NumHeight * glulam.Data.LamHeight - true_height > 20.0) * glulam.Data.LamHeight = Math.Ceiling((true_height + 10.0) / glulam.Data.NumHeight); * if (glulam.Data.NumWidth * glulam.Data.LamWidth - true_width > 20.0) * glulam.Data.LamWidth = Math.Ceiling((true_width + 10.0) / glulam.Data.NumWidth); */ true_length = new_curve.GetLength(); return(glulam); }
/// <summary> /// Create glulam with frames that are aligned with a Brep. The input curve does not /// necessarily have to lie on the Brep. /// </summary> /// <param name="curve">Input centreline of the glulam.</param> /// <param name="brep">Brep to align the glulam orientation to.</param> /// <param name="num_samples">Number of orientation frames to use for alignment.</param> /// <returns>New Glulam oriented to the brep.</returns> static public Glulam CreateGlulamNormalToSurface(Curve curve, Brep brep, int num_samples = 20, GlulamData data = null) { Plane[] frames = tas.Core.Util.Misc.FramesNormalToSurface(curve, brep, num_samples); return(Glulam.CreateGlulam(curve, frames, data)); }
public BasicAssembly(Glulam blank, Plane p) : base(blank.Centreline, p) { Blank = blank; Centrelines = new Curve[] { blank.Centreline }; }
public BasicAssembly(Glulam blank) : base(blank.Centreline, blank.Centreline.GetAlignedPlane(100)) { Blank = blank; Centrelines = new Curve[] { blank.Centreline }; }
// PLACEHOLDER public BranchingAssembly(Glulam blank) : base(blank.Centreline) { MainTrunk = blank; }