private static Mesh CreateBottom(Polygons path, double bottomHeight, double scaling) { var mesh = path.CreateVertexStorage(scaling).TriangulateFaces(zHeight: bottomHeight); mesh.ReverseFaces(); return(mesh); }
public static Mesh Stitch(Polygons bottomLoop, double bottomHeight, Polygons topLoop, double topHeight, double scaling = 1000) { // only a bottom if (bottomLoop?.Count > 0 && (topLoop == null || topLoop.Count == 0)) { // if there is no top than we need to create a top return(CreateTop(bottomLoop, bottomHeight, scaling)); } // only a top if ((bottomLoop == null || bottomLoop.Count == 0) && topLoop.Count > 0) { // if there is no bottom than we need to create bottom return(CreateBottom(topLoop, topHeight, scaling)); } // simple bottom and top if (bottomLoop.Count == 1 && topLoop.Count == 1 && bottomLoop[0].Count == topLoop[0].Count) { var mesh = CreateSimpleWall(bottomLoop[0], bottomHeight * 1000, topLoop[0], topHeight * 1000); mesh.Transform(Matrix4X4.CreateScale(1 / scaling)); return(mesh); } var all = new Polygons(); all.AddRange(bottomLoop); all.AddRange(topLoop); all = all.GetCorrectedWinding(); var bevelLoop = all.CreateVertexStorage().TriangulateFaces(); for (var i = 0; i < bevelLoop.Vertices.Count; i++) { bevelLoop.Vertices[i] = bevelLoop.Vertices[i] + new Vector3Float(0, 0, 16); } return(bevelLoop); }
public static VertexStorage Offset(this IVertexSource a, double distance, JoinType joinType = JoinType.jtMiter, double scale = 1000) { var aPolys = a.CreatePolygons(scale); aPolys = aPolys.GetCorrectedWinding(); var offseter = new ClipperOffset(); offseter.AddPaths(aPolys, joinType, EndType.etClosedPolygon); var solution = new Polygons(); offseter.Execute(ref solution, distance * scale); Clipper.CleanPolygons(solution); VertexStorage output = solution.CreateVertexStorage(); output.Add(0, 0, ShapePath.FlagsAndCommand.Stop); return(output); }
public static Mesh Revolve(IVertexSource source, int angleSteps = 30, double angleStart = 0, double angleEnd = MathHelper.Tau) { angleSteps = Math.Max(angleSteps, 3); angleStart = MathHelper.Range0ToTau(angleStart); angleEnd = MathHelper.Range0ToTau(angleEnd); // convert to clipper polygons and scale so we can ensure good shapes Polygons polygons = source.CreatePolygons(); // ensure good winding and consistent shapes // clip against x=0 left and right // mirror left material across the origin // union mirrored left with right material // convert the data back to PathStorage VertexStorage cleanedPath = polygons.CreateVertexStorage(); Mesh mesh = new Mesh(); var hasStartAndEndFaces = angleStart > 0.000001; hasStartAndEndFaces |= angleEnd < MathHelper.Tau - 0.000001; // check if we need to make closing faces if (hasStartAndEndFaces) { // make a face for the start CachedTesselator teselatedSource = new CachedTesselator(); Mesh extrudedVertexSource = TriangulateFaces(source, teselatedSource); extrudedVertexSource.Transform(Matrix4X4.CreateRotationX(MathHelper.Tau / 4)); extrudedVertexSource.Transform(Matrix4X4.CreateRotationZ(angleStart)); mesh.CopyFaces(extrudedVertexSource); } // make the outside shell double angleDelta = (angleEnd - angleStart) / angleSteps; double currentAngle = angleStart; if (!hasStartAndEndFaces) { angleSteps--; } for (int i = 0; i < angleSteps; i++) { AddRevolveStrip(cleanedPath, mesh, currentAngle, currentAngle + angleDelta); currentAngle += angleDelta; } if (!hasStartAndEndFaces) { if (((angleEnd - angleStart) < .0000001 || (angleEnd - MathHelper.Tau - angleStart) < .0000001) && (angleEnd - currentAngle) > .0000001) { // make sure we close the shape exactly AddRevolveStrip(cleanedPath, mesh, currentAngle, angleStart); } } else // add the end face { // make a face for the end CachedTesselator teselatedSource = new CachedTesselator(); Mesh extrudedVertexSource = TriangulateFaces(source, teselatedSource); extrudedVertexSource.Transform(Matrix4X4.CreateRotationX(MathHelper.Tau / 4)); extrudedVertexSource.Transform(Matrix4X4.CreateRotationZ(currentAngle)); extrudedVertexSource.ReverseFaceEdges(); mesh.CopyFaces(extrudedVertexSource); } // return the completed mesh return(mesh); }
public static Mesh Extrude(this IVertexSource vertexSource, double zHeight) { Polygons polygons = vertexSource.CreatePolygons(); // ensure good winding and consistent shapes polygons = polygons.GetCorrectedWinding(); // convert the data back to PathStorage vertexSource = polygons.CreateVertexStorage(); CachedTesselator teselatedSource = new CachedTesselator(); Mesh mesh = vertexSource.TriangulateFaces(teselatedSource); int numIndicies = teselatedSource.IndicesCache.Count; mesh.Translate(new Vector3(0, 0, zHeight)); // then the outside edge for (int i = 0; i < numIndicies; i += 3) { Vector2 v0 = teselatedSource.VerticesCache[teselatedSource.IndicesCache[i + 0].Index].Position; Vector2 v1 = teselatedSource.VerticesCache[teselatedSource.IndicesCache[i + 1].Index].Position; Vector2 v2 = teselatedSource.VerticesCache[teselatedSource.IndicesCache[i + 2].Index].Position; if (v0 == v1 || v1 == v2 || v2 == v0) { continue; } var bottomVertex0 = new Vector3(v0, 0); var bottomVertex1 = new Vector3(v1, 0); var bottomVertex2 = new Vector3(v2, 0); var topVertex0 = new Vector3(v0, zHeight); var topVertex1 = new Vector3(v1, zHeight); var topVertex2 = new Vector3(v2, zHeight); if (teselatedSource.IndicesCache[i + 0].IsEdge) { mesh.CreateFace(new Vector3[] { bottomVertex0, bottomVertex1, topVertex1, topVertex0 }); } if (teselatedSource.IndicesCache[i + 1].IsEdge) { mesh.CreateFace(new Vector3[] { bottomVertex1, bottomVertex2, topVertex2, topVertex1 }); } if (teselatedSource.IndicesCache[i + 2].IsEdge) { mesh.CreateFace(new Vector3[] { bottomVertex2, bottomVertex0, topVertex0, topVertex2 }); } } // then the bottom for (int i = 0; i < numIndicies; i += 3) { Vector2 v0 = teselatedSource.VerticesCache[teselatedSource.IndicesCache[i + 0].Index].Position; Vector2 v1 = teselatedSource.VerticesCache[teselatedSource.IndicesCache[i + 1].Index].Position; Vector2 v2 = teselatedSource.VerticesCache[teselatedSource.IndicesCache[i + 2].Index].Position; if (v0 == v1 || v1 == v2 || v2 == v0) { continue; } mesh.CreateFace(new Vector3[] { new Vector3(v2, 0), new Vector3(v1, 0), new Vector3(v0, 0) }); } mesh.CleanAndMerge(); return(mesh); }
private void DoSmoothing(long maxDist, int interations) { bool closedPath = true; var path = this.Children.OfType <IPathObject>().FirstOrDefault(); if (path == null) { // clear our existing data VertexSource = new VertexStorage(); return; } var sourceVertices = path.VertexSource; var inputPolygons = sourceVertices.CreatePolygons(); Polygons outputPolygons = new Polygons(); foreach (Polygon inputPolygon in inputPolygons) { int numVerts = inputPolygon.Count; long maxDistSquared = maxDist * maxDist; var smoothedPositions = new Polygon(numVerts); foreach (IntPoint inputPosition in inputPolygon) { smoothedPositions.Add(inputPosition); } for (int iteration = 0; iteration < interations; iteration++) { var positionsThisPass = new Polygon(numVerts); foreach (IntPoint inputPosition in smoothedPositions) { positionsThisPass.Add(inputPosition); } int startIndex = closedPath ? 0 : 1; int endIndex = closedPath ? numVerts : numVerts - 1; for (int i = startIndex; i < endIndex; i++) { // wrap back to the previous index IntPoint prev = positionsThisPass[(i + numVerts - 1) % numVerts]; IntPoint cur = positionsThisPass[i]; IntPoint next = positionsThisPass[(i + 1) % numVerts]; IntPoint newPos = (prev + cur + next) / 3; IntPoint delta = newPos - inputPolygon[i]; if (delta.LengthSquared() > maxDistSquared) { delta = delta.GetLength(maxDist); newPos = inputPolygon[i] + delta; } smoothedPositions[i] = newPos; } } outputPolygons.Add(smoothedPositions); outputPolygons = ClipperLib.Clipper.CleanPolygons(outputPolygons, Math.Max(maxDist / 10, 1.415)); } VertexSource = outputPolygons.CreateVertexStorage(); }
private static Mesh CreateTop(Polygons path, double topHeight, double scaling) { return(path.CreateVertexStorage(scaling).TriangulateFaces(zHeight: topHeight)); }