public static bool ApplyLockRoom(AdvicePackage advice, ZoneManifest zm, ProgramManifest pm) { var bounds = advice.Bounds; foreach (ZonePackage zone in zm.Zones) { foreach (RoomPackage room in zone.Rooms) { foreach (PlacementPackage item in room.PlacedItems) { PointContainment pc = bounds.Contains(item.Dims.Center); if (pc == PointContainment.Inside) { if (advice.CapturedProgram == null) { advice.CapturedProgram = new List <PlacementPackage>(); } advice.CapturedProgram.Add(item); if (pm.ProgramPackages[item.Program.Priority].Quota > 0) { pm.ProgramPackages[item.Program.Priority].Quota--; } } } } } zm.FloorPlan.ExemptionProfiles.Add(advice.Bounds); return(true); }
public bool lineWithinRegion(Brep region, LineCurve line) { //Check if line is completely within a planar region int agreement = 0; Point3d midPt = line.PointAt((line.Domain.Max - line.Domain.Min) / 2); foreach (BrepLoop loop in region.Loops) { if (loop.LoopType == BrepLoopType.Outer) { Curve iLoop = loop.To3dCurve(); PointContainment ptcontain = iLoop.Contains(midPt); if (ptcontain != PointContainment.Inside) { agreement += 1; } } if (loop.LoopType == BrepLoopType.Inner) { Curve iLoop = loop.To3dCurve(); PointContainment ptcontain = iLoop.Contains(midPt); if (ptcontain != PointContainment.Outside) { agreement += 1; } } if (loop.LoopType == BrepLoopType.Slit) { break; } if (loop.LoopType == BrepLoopType.Unknown) { break; } } if (agreement == 0) { bool inside = true; return(inside); } else { bool inside = false; return(inside); } }
/// <summary> /// Basic collision detection routine for single item placement. /// </summary> /// <param name="room">Room currently being populated.</param> /// <param name="candidate">PlacementPackaged being checked for fidelity.</param> /// <param name="pm"></param> /// <returns></returns> public static bool PlacementIsValid(RoomPackage room, PlacementPackage candidate, ProgramManifest pm) { //Verify placed item does not clash with previously placed items. foreach (PlacementPackage placedItem in room.PlacedItems) { if (Confirm.CurvesIntersect(placedItem.Bounds, candidate.Bounds, true)) { if (Confirm.CurvesIntersect(placedItem.Bounds, candidate.Bounds, false)) { return(false); } CurveIntersections ccx = Intersection.CurveCurve(placedItem.Bounds, candidate.Bounds, 0.1, 0.1); if (ccx.Count(x => x.IsOverlap) > 1) { return(false); } } PointContainment insideCheck = placedItem.Bounds.Contains(candidate.Bounds.GetBoundingBox(Plane.WorldXY).Center); if (insideCheck == PointContainment.Inside || insideCheck == PointContainment.Coincident) { return(false); } } //Verify item is completely within boundary of room. foreach (Curve edgeCurve in room.AllEdgeCurves) { if (Confirm.CurvesIntersect(candidate.Bounds, edgeCurve, false)) { return(false); } /* * if (!Confirm.PointInRegion(room.Region, new CurveBounds(candidate.Bounds).Center)) * { * return false; * } */ } //Verify program area is not larger than total room area. if (room.Region.GetArea() < pm.ProgramPackages[candidate.ProgramIndex].OccupationArea) { return(false); } return(true); }
public static Curve BooleanOpenCurve(Curve ClosedC, Curve C, out int result) { PointContainment cont0 = ClosedC.Contains(C.PointAtEnd, Plane.WorldXY, 0.01); PointContainment cont1 = ClosedC.Contains(C.PointAtStart, Plane.WorldXY, 0.01); bool isCurveInsideStart = (cont0 == PointContainment.Inside || cont0 == PointContainment.Coincident); bool isCurveInsideEnd = (cont1 == PointContainment.Inside || cont1 == PointContainment.Coincident); Rhino.Geometry.Intersect.CurveIntersections ci = Rhino.Geometry.Intersect.Intersection.CurveCurve(C, ClosedC, 0.01, 0.01); Interval interval = C.Domain; if (isCurveInsideStart && isCurveInsideEnd) { result = 2; return(C); } else if (isCurveInsideStart && !isCurveInsideEnd) { result = 1; if (ci.Count == 0) { result = 0; return(null); } return(C.Trim(ci[0].ParameterA, interval.T0)); } else if (!isCurveInsideStart && isCurveInsideEnd) { if (ci.Count == 0) { result = 0; return(null); } result = 1; return(C.Trim(interval.T1, ci[0].ParameterA)); } else { if (ci.Count == 2) { result = 1; return(C.Trim(ci[0].ParameterA, ci[1].ParameterA)); } } result = 0; return(null); }
public static void PlaneSlice(this Brep brep, Plane sliceplane, ref List <Curve> curves) { //brep.Faces.StandardizeFaceSurfaces(); //BrepSolidOrientation bso = brep.SolidOrientation; Curve[] xCrvs; Point3d[] xPts; bool[] inside; Rhino.Geometry.Intersect.Intersection.BrepPlane(brep, sliceplane, 0.001, out xCrvs, out xPts); if (xCrvs == null) { return; } xCrvs = Rhino.Geometry.Curve.JoinCurves(xCrvs); curves = new List <Curve>(); inside = new bool[xCrvs.Length]; for (int i = 0; i < xCrvs.Length; ++i) { //int sign = 1; Point3d tpt = xCrvs[i].PointAtNormalizedLength(0.5); Vector3d n = brep.ClosestNormal(tpt); n = n.ProjectToPlane(sliceplane); tpt.Transform(Transform.Translation(n * 0.001)); PointContainment pcon = xCrvs[i].Contains(tpt, sliceplane, 0.0001); if (pcon == PointContainment.Inside) { inside[i] = true; //sign = -1; } else { inside[i] = false; //sign = 1; } curves.Add(xCrvs[i]); Curve[] offsets = xCrvs[i].Offset(tpt, sliceplane.ZAxis, 3.0, 0.0001, CurveOffsetCornerStyle.Sharp); //Curve[] offsets = xCrvs[i].Offset(sliceplane, 3.0 * sign, 0.001, CurveOffsetCornerStyle.Sharp); curves.AddRange(offsets); } return; }
private bool BoundaryContainsCurve(Curve shape, Curve boundary) { bool IsContained = false; List <Point3d> vertices = shape.ToNurbsCurve().Points.Select(p => p.Location).ToList(); for (int i = 0; i < vertices.Count; i++) { Point3d vertex = vertices[i]; PointContainment pContainment = boundary.Contains(vertex, Plane.WorldXY, 0.00001); if (pContainment.Equals(PointContainment.Inside)) { IsContained = true; } else { IsContained = false; break; } } return(IsContained); }
public List <Curve> RemovePoly(List <Curve> bsp_tree, Curve int_crv) { List <Curve> del_crv = new List <Curve>(); for (int i = 0; i < bsp_tree.Count; i++) { try { Point3d cen = AreaMassProperties.Compute(bsp_tree[i]).Centroid; PointContainment t = bsp_tree[i].Contains(cen, Plane.WorldXY, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance); if (t.ToString() == ("Inside")) { del_crv.Add(bsp_tree[i]); } } catch (Exception) { } for (int j = 0; j < del_crv.Count; j++) { bsp_tree.Remove(del_crv[j]); } } return(bsp_tree); }
private IList <Rectangle3d> CheckFit(BoundingBox bx, Rectangle3d rect, Curve boundary, double offset, bool withRotation, List <Point3d> outPts, double searchSpacing, bool axisAligned, List <Vector3d> vs) { bool IsRotated = false; bool searching = true; double angle = 1.5708; List <double> spacing = FindSpacing(rect, offset); IList <Rectangle3d> tRects = new List <Rectangle3d>(); double dX = searchSpacing; double dY = searchSpacing; double xMin = bx.Min.X; double yMin = bx.Min.Y; Point3d p = new Point3d(xMin, yMin, 0.0); Vector3d translationVector = new Vector3d(rect.Plane.XAxis.X, rect.Plane.YAxis.Y, 1); translationVector.Unitize(); for (double y = yMin; y < bx.Max.Y; y += dY) { for (double x = xMin; x < bx.Max.X; x += dX) { Vector3d v = new Vector3d(x, y, 0.0); Vector3d pv = new Vector3d(p); if (axisAligned) { pv = new Vector3d(translationVector.X * dX, translationVector.Y * dY, 1); } Vector3d toVector = Vector3d.Add(v, pv); Point3d pNew = new Point3d(toVector); p = pNew; PointContainment pc = boundary.Contains(p, Plane.WorldXY, 0.1); if (pc == PointContainment.Inside) { // Translate rect to new position Rectangle3d tRect = TranslateRect(p, rect); // Check containment of rect bool IsContained = BoundaryContainsCurve(tRect.ToNurbsCurve(), boundary); if (!IsContained && withRotation && !searching) { tRect.Transform(Transform.Rotation(angle, p)); IsContained = BoundaryContainsCurve(tRect.ToNurbsCurve(), boundary); IsRotated = true; } if (IsContained) { if (searching) { searching = false; dX = spacing[0]; dY = spacing[1]; xMin = (1 - bx.Min.X % p.X) * p.X; } outPts.Add(p); vs.Add(pv); tRects.Add(tRect); } if (IsRotated && withRotation) { // Update spacing double t = dX; dX = dY; dY = t; IsRotated = false; } } } } return(tRects); }
/// <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) { ///Gather GHA inputs Curve boundary = null; DA.GetData <Curve>(0, ref boundary); string osmFilePath = string.Empty; DA.GetData <string>("OSM Data Location", ref osmFilePath); //string userSRStext = "WGS84"; //DA.GetData<string>(2, ref userSRStext); List <string> filterWords = new List <string>(); DA.GetDataList <string>(2, filterWords); List <string> filterKeyValue = new List <string>(); DA.GetDataList <string>(3, filterKeyValue); Transform xformToMetric = new Transform(scaleToMetric); Transform xformFromMetric = new Transform(scaleFromMetric); ///Declare trees Rectangle3d recs = new Rectangle3d(); GH_Structure <GH_String> fieldNames = new GH_Structure <GH_String>(); GH_Structure <GH_String> fieldValues = new GH_Structure <GH_String>(); GH_Structure <IGH_GeometricGoo> geometryGoo = new GH_Structure <IGH_GeometricGoo>(); GH_Structure <IGH_GeometricGoo> buildingGoo = new GH_Structure <IGH_GeometricGoo>(); Point3d max = new Point3d(); Point3d min = new Point3d(); if (boundary != null) { Point3d maxM = boundary.GetBoundingBox(true).Corner(true, false, true); max = Heron.Convert.XYZToWGS(maxM); Point3d minM = boundary.GetBoundingBox(true).Corner(false, true, true); min = Heron.Convert.XYZToWGS(minM); } /// get extents (why is this not part of OsmSharp?) System.Xml.Linq.XDocument xdoc = System.Xml.Linq.XDocument.Load(osmFilePath); if (xdoc.Root.Element("bounds") != null) { double minlat = System.Convert.ToDouble(xdoc.Root.Element("bounds").Attribute("minlat").Value); double minlon = System.Convert.ToDouble(xdoc.Root.Element("bounds").Attribute("minlon").Value); double maxlat = System.Convert.ToDouble(xdoc.Root.Element("bounds").Attribute("maxlat").Value); double maxlon = System.Convert.ToDouble(xdoc.Root.Element("bounds").Attribute("maxlon").Value); Point3d boundsMin = Heron.Convert.WGSToXYZ(new Point3d(minlon, minlat, 0)); Point3d boundsMax = Heron.Convert.WGSToXYZ(new Point3d(maxlon, maxlat, 0)); recs = new Rectangle3d(Plane.WorldXY, boundsMin, boundsMax); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "Cannot determine the extents of the OSM file. A 'bounds' element may not be present in the file. " + "Try turning off clipping in this component's menu."); } using (var fileStreamSource = File.OpenRead(osmFilePath)) { /// create a source. OsmSharp.Streams.XmlOsmStreamSource source = new OsmSharp.Streams.XmlOsmStreamSource(fileStreamSource); /// filter by bounding box OsmSharp.Streams.OsmStreamSource sourceClipped = source; if (clipped) { sourceClipped = source.FilterBox((float)max.X, (float)max.Y, (float)min.X, (float)min.Y, true); } /// create a dictionary of elements OsmSharp.Db.Impl.MemorySnapshotDb sourceMem = new OsmSharp.Db.Impl.MemorySnapshotDb(sourceClipped); /// filter the source var filtered = from osmGeos in sourceClipped where osmGeos.Tags != null select osmGeos; if (filterWords.Any()) { filtered = from osmGeos in filtered where osmGeos.Tags.ContainsAnyKey(filterWords) select osmGeos; } if (filterKeyValue.Any()) { List <Tag> tags = new List <Tag>(); foreach (string term in filterKeyValue) { string[] kv = term.Split(','); Tag tag = new Tag(kv[0], kv[1]); tags.Add(tag); } filtered = from osmGeos in filtered where osmGeos.Tags.Intersect(tags).Any() select osmGeos; } source.Dispose(); /// loop over all objects and count them. int nodes = 0, ways = 0, relations = 0; Dictionary <PolylineCurve, GH_Path> bldgOutlines = new Dictionary <PolylineCurve, GH_Path>(); List <BuildingPart> buildingParts = new List <BuildingPart>(); foreach (OsmSharp.OsmGeo osmGeo in filtered) { //NODES if (osmGeo.Type == OsmGeoType.Node) { OsmSharp.Node n = (OsmSharp.Node)osmGeo; GH_Path nodesPath = new GH_Path(0, nodes); //populate Fields and Values for each node fieldNames.AppendRange(GetKeys(osmGeo), nodesPath); fieldValues.AppendRange(GetValues(osmGeo), nodesPath); //get geometry for node Point3d nPoint = Heron.Convert.WGSToXYZ(new Point3d((double)n.Longitude, (double)n.Latitude, 0)); geometryGoo.Append(new GH_Point(nPoint), nodesPath); //increment nodes nodes++; } //////////////////////////////////////////////////////////// //WAYS if (osmGeo.Type == OsmGeoType.Way) { OsmSharp.Way w = (OsmSharp.Way)osmGeo; GH_Path waysPath = new GH_Path(1, ways); //populate Fields and Values for each way fieldNames.AppendRange(GetKeys(osmGeo), waysPath); fieldValues.AppendRange(GetValues(osmGeo), waysPath); //get polyline geometry for way List <Point3d> wayNodes = new List <Point3d>(); foreach (long j in w.Nodes) { OsmSharp.Node n = (OsmSharp.Node)sourceMem.Get(OsmGeoType.Node, j); wayNodes.Add(Heron.Convert.WGSToXYZ(new Point3d((double)n.Longitude, (double)n.Latitude, 0))); } PolylineCurve pL = new PolylineCurve(wayNodes); if (pL.IsClosed) { //create base surface Brep[] breps = Brep.CreatePlanarBreps(pL, DocumentTolerance()); geometryGoo.Append(new GH_Brep(breps[0]), waysPath); } else { geometryGoo.Append(new GH_Curve(pL), waysPath); } //building massing if ((w.Tags.ContainsKey("building") || w.Tags.ContainsKey("building:part")))// && !w.Tags.ContainsKey("construction")) { if (pL.IsClosed) { ///Populate dictionary for sorting building parts later if (w.Tags.ContainsKey("building")) { bldgOutlines.Add(pL, waysPath); } CurveOrientation orient = pL.ClosedCurveOrientation(Plane.WorldXY); if (orient != CurveOrientation.CounterClockwise) { pL.Reverse(); } ///Move polylines to min height double minHeightWay = GetMinBldgHeight(osmGeo); Vector3d minVec = new Vector3d(0, 0, minHeightWay); //minVec.Transform(xformFromMetric); if (minHeightWay > 0.0) { var minHeightTranslate = Transform.Translation(minVec); pL.Transform(minHeightTranslate); } Vector3d hVec = new Vector3d(0, 0, GetBldgHeight(osmGeo) - minHeightWay); //hVec.Transform(xformFromMetric); Extrusion ex = Extrusion.Create(pL, hVec.Z, true); IGH_GeometricGoo bldgGoo = GH_Convert.ToGeometricGoo(ex); ///Save building parts for sorting later and remove part from geometry goo tree if (w.Tags.ContainsKey("building:part")) { BuildingPart bldgPart = new BuildingPart(pL, bldgGoo, fieldNames[waysPath], fieldValues[waysPath], osmGeo); buildingParts.Add(bldgPart); fieldNames.RemovePath(waysPath); fieldValues.RemovePath(waysPath); geometryGoo.RemovePath(waysPath); ways = ways - 1; } else { buildingGoo.Append(bldgGoo, waysPath); } } } //increment ways ways++; } /////////////////////////////////////////////////////////// //RELATIONS if (osmGeo.Type == OsmGeoType.Relation) { OsmSharp.Relation r = (OsmSharp.Relation)osmGeo; GH_Path relationPath = new GH_Path(2, relations); //populate Fields and Values for each relation fieldNames.AppendRange(GetKeys(osmGeo), relationPath); fieldValues.AppendRange(GetValues(osmGeo), relationPath); List <Curve> pLines = new List <Curve>(); // start members loop for (int mem = 0; mem < r.Members.Length; mem++) { GH_Path memberPath = new GH_Path(2, relations, mem); OsmSharp.RelationMember rMem = r.Members[mem]; OsmSharp.OsmGeo rMemGeo = sourceMem.Get(rMem.Type, rMem.Id); if (rMemGeo != null) { //get geometry for node if (rMemGeo.Type == OsmGeoType.Node) { long memNodeId = rMem.Id; OsmSharp.Node memN = (OsmSharp.Node)sourceMem.Get(rMem.Type, rMem.Id); Point3d memPoint = Heron.Convert.WGSToXYZ(new Point3d((double)memN.Longitude, (double)memN.Latitude, 0)); geometryGoo.Append(new GH_Point(memPoint), memberPath); } //get geometry for way if (rMem.Type == OsmGeoType.Way) { long memWayId = rMem.Id; OsmSharp.Way memWay = (OsmSharp.Way)rMemGeo; //get polyline geometry for way List <Point3d> memNodes = new List <Point3d>(); foreach (long memNodeId in memWay.Nodes) { OsmSharp.Node memNode = (OsmSharp.Node)sourceMem.Get(OsmGeoType.Node, memNodeId); memNodes.Add(Heron.Convert.WGSToXYZ(new Point3d((double)memNode.Longitude, (double)memNode.Latitude, 0))); } PolylineCurve memPolyline = new PolylineCurve(memNodes); geometryGoo.Append(new GH_Curve(memPolyline.ToNurbsCurve()), memberPath); CurveOrientation orient = memPolyline.ClosedCurveOrientation(Plane.WorldXY); if (orient != CurveOrientation.CounterClockwise) { memPolyline.Reverse(); } pLines.Add(memPolyline.ToNurbsCurve()); } //get nested relations if (rMem.Type == OsmGeoType.Relation) { ///not sure if this is needed } } } //end members loop bool allClosed = true; foreach (Curve pc in pLines) { if (!pc.IsClosed) { allClosed = false; } } if (pLines.Count > 0 && allClosed) { ///Move polylines to min height double minHeight = GetMinBldgHeight(osmGeo); if (minHeight > 0.0) { Vector3d minVec = new Vector3d(0, 0, minHeight); //minVec.Transform(xformFromMetric); var minHeightTranslate = Transform.Translation(minVec); for (int i = 0; i < pLines.Count; i++) { pLines[i].Transform(minHeightTranslate); } } ///Create base surface Brep[] breps = Brep.CreatePlanarBreps(pLines, DocumentTolerance()); geometryGoo.RemovePath(relationPath); foreach (Brep b in breps) { geometryGoo.Append(new GH_Brep(b), relationPath); ///Building massing if (r.Tags.ContainsKey("building") || r.Tags.ContainsKey("building:part")) { Vector3d hVec = new Vector3d(0, 0, GetBldgHeight(osmGeo) - minHeight); //hVec.Transform(xformFromMetric); ///Create extrusion from base surface buildingGoo.Append(new GH_Brep(Brep.CreateFromOffsetFace(b.Faces[0], hVec.Z, DocumentTolerance(), false, true)), relationPath); } } } ///Increment relations relations++; } ///End relation loop } ///End filtered loop ///Add building parts to sub-branches under main building for (int partIndex = 0; partIndex < buildingParts.Count; partIndex++) { BuildingPart bldgPart = buildingParts[partIndex]; Point3d partPoint = bldgPart.PartFootprint.PointAtStart; partPoint.Z = 0; bool replaceBuidingMass = false; GH_Path mainBuildingMassPath = new GH_Path(); PolylineCurve massOutline = new PolylineCurve(); bool isRoof = bldgPart.PartOsmGeo.Tags.TryGetValue("roof:shape", out string isRoofString); if (isRoof) { bldgPart.PartGoo = BldgPartToRoof(bldgPart); } foreach (KeyValuePair <PolylineCurve, GH_Path> pair in bldgOutlines) { PointContainment pc = pair.Key.Contains(partPoint, Plane.WorldXY, DocumentTolerance()); if (pc != PointContainment.Outside) { ///Create new sub-branch int numSubBranches = 0; GH_Path partPath = pair.Value.AppendElement(numSubBranches); while (buildingGoo.PathExists(partPath)) { numSubBranches++; partPath = pair.Value.AppendElement(numSubBranches); } ///Add data to sub-branch fieldNames.AppendRange(bldgPart.PartFieldNames, partPath); fieldValues.AppendRange(bldgPart.PartFieldValues, partPath); buildingGoo.Append(bldgPart.PartGoo, partPath); ///Remove the main building mass replaceBuidingMass = true; mainBuildingMassPath = pair.Value; massOutline = pair.Key; } } ///Remove the main building mass if (replaceBuidingMass) { buildingGoo.RemovePath(mainBuildingMassPath); buildingGoo.Append(new GH_Curve(massOutline), mainBuildingMassPath); } else { GH_Path extrasPath = new GH_Path(3, partIndex); buildingGoo.Append(bldgPart.PartGoo, extrasPath); fieldNames.AppendRange(bldgPart.PartFieldNames, extrasPath); fieldValues.AppendRange(bldgPart.PartFieldValues, extrasPath); } } } ///end osm source loop if (recs.IsValid) { DA.SetData(0, recs); } DA.SetDataTree(1, fieldNames); DA.SetDataTree(2, fieldValues); DA.SetDataTree(3, geometryGoo); DA.SetDataTree(4, buildingGoo); } ///end SolveInstance
public static List <Polyline> RemoveLoop(Polyline looped, bool isCCW) { Curve loopedCrv = looped.ToNurbsCurve(); //Search selfX params. List <double> selfXParams = new List <double>(); var selfIntersect = Intersection.CurveSelf(loopedCrv, 0); foreach (IntersectionEvent e in selfIntersect) { selfXParams.Add(e.ParameterA); selfXParams.Add(e.ParameterB); } //Set trimming intervals. List <double> trimmingParams = new List <double>(); for (int i = 0; i < looped.Count; i++) { trimmingParams.Add(i); } trimmingParams.AddRange(selfXParams); trimmingParams.Sort(); List <Interval> trimmingInterval = new List <Interval>(); int paramCount = trimmingParams.Count; for (int i = 0; i < paramCount - 1; i++) { trimmingInterval.Add( new Interval(trimmingParams[i], trimmingParams[(paramCount + i + 1) % paramCount])); } //Get segments. List <LineCurve> trimmedSegs = new List <LineCurve>(); foreach (Interval i in trimmingInterval) { trimmedSegs.Add( new LineCurve(looped.PointAt(i.T0), looped.PointAt(i.T1))); } //Divide segments into in/out group. List <LineCurve> innerSegs = new List <LineCurve>(); List <LineCurve> outerSegs = new List <LineCurve>(); foreach (LineCurve i in trimmedSegs) { Vector3d toInnerCCW = i.TangentAtStart; toInnerCCW.Rotate(Math.PI / 2, Vector3d.ZAxis); Point3d inOutTestPoint = (i.PointAtStart + i.PointAtEnd) / 2 + toInnerCCW; PointContainment inOutResult = loopedCrv.Contains(inOutTestPoint); if (inOutResult == PointContainment.Inside) { innerSegs.Add(i); } else { outerSegs.Add(i); } } //Decided output by curve orientation. List <LineCurve> outputSegs; if (isCCW) { outputSegs = innerSegs; } else { outputSegs = outerSegs; } if (outputSegs.Count > 0) { Curve[] outputCrv = Curve.JoinCurves(outputSegs); return(outputCrv.Select(n => CurveTools.ToPolyline(n)).ToList()); } return(new List <Polyline>()); }