private static void AddPoint([CanBeNull] Matrix3x2?transform, Vector2 point, List <ContourVertex> vertices) { ContourVertex vertex; if (transform != null) { var pt = Matrix3x2.Transform(transform.Value, point); vertex = new ContourVertex { Position = new Vec3 { X = pt.X, Y = pt.Y, Z = StandardZ } }; } else { vertex = new ContourVertex { Position = new Vec3 { X = point.X, Y = point.Y, Z = StandardZ } }; } vertices.Add(vertex); }
public List <VertexPositionColor> GetTesselationVertices(Color color) { var fakeSector = new CubeSector(CubeSector.CubeSectorFace.FRONT, 0, 0, 8); List <List <ContourVertex> > contours = new List <List <ContourVertex> >(); foreach (var inner in inners) { var innerContour = inner.Skip(1).Select(x => Vector2DToContourVertex(x, true)).ToList(); // skip 1 since our loops end in a duplicate if (innerContour.Count == 3) { // TODO: the tesselator just doesn't like triangles!? seriously?? var avg01 = new ContourVertex() { Position = new Vec3() { X = innerContour[0].Position.X / 2 + innerContour[1].Position.X / 2, Y = innerContour[0].Position.Y / 2 + innerContour[1].Position.Y / 2, Z = 0 }, Data = innerContour[0].Data }; innerContour.Insert(1, avg01); } contours.Add(innerContour); } foreach (var outer in outers) { var outerContours = new List <List <ContourVertex> >() { outer.Skip(1).Select(y => Vector2DToContourVertex(y)).ToList() }; // skip 1 since our loops end in a duplicate contours.AddRange(outerContours); } var vertices = OSMPolygonBufferGenerator.Tesselate(contours, color); return(vertices); }
void tessPoly(PointF[] source, Color polyColor, float alpha) { // Now we need to check for polyfill, and triangulate the polygon if needed. //if (enableFilledPolys) { var tess = new Tess(); ContourVertex[] contour = new ContourVertex[source.Length]; for (int pt = 0; pt < contour.Length; pt++) { contour[pt].Position = new Vec3 { X = source[pt].X, Y = source[pt].Y, Z = 0 }; } tess.AddContour(contour, ContourOrientation.Clockwise); // keep our orientation to allow holes to be handled. // Triangulate. tess.Tessellate(WindingRule.NonZero, ElementType.Polygons, 3); // We don't have any hole polygons here. // Iterate triangles and create output geometry for (int i = 0; i < tess.ElementCount; i++) { PointF[] tempPoly = new PointF[3]; // 3 points. tempPoly[0] = new PointF((float)tess.Vertices[tess.Elements[i * 3]].Position.X, (float)tess.Vertices[tess.Elements[i * 3]].Position.Y); tempPoly[1] = new PointF((float)tess.Vertices[tess.Elements[(i * 3) + 1]].Position.X, (float)tess.Vertices[tess.Elements[(i * 3) + 1]].Position.Y); tempPoly[2] = new PointF((float)tess.Vertices[tess.Elements[(i * 3) + 2]].Position.X, (float)tess.Vertices[tess.Elements[(i * 3) + 2]].Position.Y); tessPolyList.Add(new ovp_Poly(clockwiseOrder(tempPoly).ToArray(), polyColor, alpha)); } } }
protected virtual int[] GetTriangleIndices() { if (tess == null) { tess = new Tess(); } var contour = new ContourVertex[Vertices.Count]; for (var i = 0; i < contour.Length; i++) { var vertex = Vertices[i]; contour[i].Position = new Vec3 { X = vertex.X, Y = vertex.Y }; } tess.AddContour(contour, ContourOrientation.Clockwise); tess.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3); var indices = new int[tess.Elements.Length]; for (var i = 0; i < indices.Length; i++) { var position = tess.Vertices[tess.Elements[i]].Position; indices[i] = Vertices.IndexOf(new Vector2(position.X, position.Y)); } return(indices); }
/// <summary> /// Gets a triangulation, where the Indices are related to the Points /// </summary> /// <param name="Indices">Indices</param> /// <param name="Points">List ofPoints</param> public void TriAngulation(List <IndexType> Indices, List <xyf> Points) { Tess Tess = new Tess(); Tess.UsePooling = true; for (int i = 0; i < Count; i++) { ContourVertex[] CV = new ContourVertex[this[i].Count]; for (int j = 0; j < this[i].Count; j++) { CV[j] = new ContourVertex(); CV[j].Position.X = (float)this[i][j].X; CV[j].Position.Y = (float)this[i][j].Y; } Tess.AddContour(CV); } Tess.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3); Indices.Clear(); for (int i = 0; i < Tess.Elements.Length; i++) { Indices.Add((IndexType)(Tess.Elements[i])); } for (int i = 0; i < Tess.Vertices.Length; i++) { Points.Add(new xyf(Tess.Vertices[i].Position.X, Tess.Vertices[i].Position.Y)); } }
internal List <List <ContourVertex> > ToContours() { List <List <ContourVertex> > contours = new List <List <ContourVertex> >(); List <GraphNode> starts = nodes.Where(x => x.prevConnections.Count == 0 && x.nextConnections.Count == 1).ToList(); HashSet <GraphNode> visited = new HashSet <GraphNode>(); foreach (var start in starts) { List <ContourVertex> contour = new List <ContourVertex>(); GraphNode next = start; GraphNode prev = null; while (next != null) { ContourVertex vertex = new ContourVertex(); vertex.Position = new Vec3 { X = (float)next.pos.X, Y = (float)next.pos.Y, Z = 0 }; contour.Add(vertex); visited.Add(next); GraphNode nextnext = GetNext(prev, next); prev = next; next = nextnext; } if (contour.Count > 0) { contours.Add(contour); } } // now find the loops foreach (var node in nodes) { List <ContourVertex> contour = new List <ContourVertex>(); GraphNode next = node; GraphNode prev = null; while (!visited.Contains(next)) { ContourVertex vertex = new ContourVertex(); vertex.Data = next.isHole; // TODO: get rid of this vertex.Position = new Vec3 { X = (float)next.pos.X, Y = (float)next.pos.Y, Z = 0 }; contour.Add(vertex); visited.Add(next); GraphNode nextnext = GetNext(prev, next); if (nextnext == null) { break; } prev = next; next = nextnext; } if (contour.Count > 0) { contour.Add(contour[0]); // close it? contours.Add(contour); } } return(contours); }
public static void GenerateFillMesh(ShapeData shape, MeshBuilder meshBuilder) { Profiler.BeginSample("GenerateFillMesh"); if (shape.GetVertexInfoList().Count < 3) { return; } Matrix4x4 m = Matrix4x4.TRS(Vector3.zero, Quaternion.LookRotation(shape.FillNormal), Vector3.one).inverse; // tesselation ContourVertex[] contour = new ContourVertex[shape.GetVertexInfoList().Count - 2 - (shape.IsStrokeClosed ? 1 : 0)]; // ignore first and last point for (int i = 0; i < contour.Length; i++) { Vector3 p = m.MultiplyPoint(shape.GetVertexInfoList()[i + 1].position); var pos = new Vec3(); pos.X = p.x; pos.Y = p.y; pos.Z = 0; var v = new ContourVertex(); v.Position = pos; v.Data = shape.GetVertexInfoList()[i + 1].position; contour[i] = v; } if (tesselator == null) { tesselator = new Tess(); //tesselator.UsePooling = true; } tesselator.AddContour(contour, ContourOrientation.CounterClockwise); tesselator.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3, delegate(Vec3 position, object[] data, float[] weights) { return((Vector3)data[0] * weights[0] + (Vector3)data[1] * weights[1] + (Vector3)data[2] * weights[2] + (Vector3)data[3] * weights[3]); }); meshBuilder.currentSubmesh = 0; for (int i = 0; i < tesselator.ElementCount; i++) { meshBuilder.AddTriangle( meshBuilder.currentVertCount + tesselator.Elements[i * 3 + 0], meshBuilder.currentVertCount + tesselator.Elements[i * 3 + 1], meshBuilder.currentVertCount + tesselator.Elements[i * 3 + 2]); } for (int i = 0; i < tesselator.Vertices.Length; i++) { meshBuilder.AddVert(); meshBuilder.SetCurrentPosition((Vector3)tesselator.Vertices[i].Data); meshBuilder.SetCurrentColor(shape.FillColor); meshBuilder.SetCurrentUV0((Vector2)(Vector3)tesselator.Vertices[i].Data); } Profiler.EndSample(); }
internal void Tessellate(TessellatorPool tessellators) { if (Indices == null) { if (actualPositions.Length == 4) { Indices = SpriteIndices; return; } var pos = new ContourVertex[actualPositions.Length]; var tessellator = tessellators.GetTessellatorAvailable(); try { for (int i = 0; i < actualPositions.Length; i++) { pos[i] = actualPositions[i].GetContourVertex(); } tessellator.AddContour(pos, ContourOrientation.Clockwise); tessellator.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3); Indices = new short[tessellator.Elements.Length]; Span <short> indexConversionTable = stackalloc short[actualPositions.Length]; indexConversionTable.Fill(-1); if (tessellator.VertexCount != actualPositions.Length) //Detecting addition or deletion of points { throw new ArithmeticException("The shapes may be intersecting."); } else { for (var i = 0; i < tessellator.Elements.Length; i++) { int index = tessellator.Elements[i]; short conv = indexConversionTable[index]; if (conv == -1) { var position = tessellator.Vertices[index].Position.GetVector(); short q = (short)Array.FindIndex(actualPositions, a => a.Position == position); if (q == -1) { throw new KeyNotFoundException("No matching value was found."); } indexConversionTable[index] = Indices[i] = q; } else { Indices[i] = conv; } } } } finally { tessellators.ReturnTessellator(tessellator); } } }
/// <summary> /// Gets the contour vertex. /// </summary> /// <param name="vertexPositionColorTexture">The vertex position color texture.</param> /// <returns></returns> public static ContourVertex GetContourVertex(this VertexPositionColorTexture vertexPositionColorTexture) { var g = new ContourVertex(); g.Position = new Vec3() { X = vertexPositionColorTexture.Position.X, Y = vertexPositionColorTexture.Position.Y, Z = vertexPositionColorTexture.Position.Z }; return(g); }
/// <summary> /// Adds a closed contour to be tessellated. /// </summary> /// <param name="vertices"> Vertices of the contour. </param> /// <param name="forceOrientation"> /// Orientation of the contour. /// <see cref="ContourOrientation.Original"/> keeps the orientation of the input vertices. /// <see cref="ContourOrientation.Clockwise"/> and <see cref="ContourOrientation.CounterClockwise"/> /// force the vertices to have a specified orientation. /// </param> public void AddContour(IList <Vector3> vertices, ContourOrientation forceOrientation = ContourOrientation.Original) { var contour = new ContourVertex[vertices.Count]; for (int i = 0; i < vertices.Count; i++) { Vector3 vertex = vertices[i]; contour[i].Position = new Vec3(vertex.x, vertex.y, vertex.z); } tess.AddContour(contour, forceOrientation); }
private void RefreshAsset(string name) { var asset = _data.GetAsset(name); _sw.Reset(); foreach (var poly in asset.Polygons) { var v = new ContourVertex[poly.Count]; for (int i = 0; i < poly.Count; i++) { v[i].Position = new Vec3 { X = poly[i].X, Y = poly[i].Y }; v[i].Data = poly[i].Color; } _sw.Start(); _tess.AddContour(v, poly.Orientation); _sw.Stop(); } _sw.Start(); _tess.Tessellate(_windingRule, ElementType.Polygons, _polySize, VertexCombine); _sw.Stop(); var output = new PolygonSet(); for (int i = 0; i < _tess.ElementCount; i++) { var poly = new Polygon(); for (int j = 0; j < _polySize; j++) { int index = _tess.Elements[i * _polySize + j]; if (index == -1) { continue; } var v = new PolygonPoint { X = _tess.Vertices[index].Position.X, Y = _tess.Vertices[index].Position.Y, Color = (Color)_tess.Vertices[index].Data }; poly.Add(v); } output.Add(poly); } statusMain.Text = string.Format("{0:F3} ms - {1} polygons (of {2} vertices) {3}", _sw.Elapsed.TotalMilliseconds, _tess.ElementCount, _polySize, _polySize == 3 ? "... triangles" : ""); _canvas.Input = asset.Polygons; _canvas.Output = output; _canvas.Invalidate(); }
List <PointF[]> checkPoly(PointF[] poly) { List <PointF[]> output = new List <PointF[]>(); PointF[] source = poly.ToArray(); if ((poly[0].X != poly[poly.Length - 1].X) && (poly[0].Y != poly[poly.Length - 1].Y)) { PointF[] tempPoly = new PointF[poly.Length + 1]; for (int pt = 0; pt < poly.Length; pt++) { tempPoly[pt] = new PointF(poly[pt].X, poly[pt].Y); } tempPoly[tempPoly.Length - 1] = new PointF(tempPoly[0].X, tempPoly[0].Y); source = tempPoly.ToArray(); } // Now we need to check for polyfill, and triangulate the polygon if needed. if (enableFilledPolys) { var tess = new Tess(); ContourVertex[] contour = new ContourVertex[source.Length]; for (int pt = 0; pt < contour.Length; pt++) { contour[pt].Position = new Vec3 { X = source[pt].X, Y = source[pt].Y, Z = 0 }; } tess.AddContour(contour, ContourOrientation.Clockwise); // keep our orientation to allow holes to be handled. // Triangulate. tess.Tessellate(WindingRule.Positive, ElementType.Polygons, 3); // We don't have any hole polygons here. // Iterate triangles and create output geometry for (int i = 0; i < tess.ElementCount; i++) { PointF[] tempPoly = new PointF[3]; // 3 points. tempPoly[0] = new PointF((float)tess.Vertices[tess.Elements[i * 3]].Position.X, (float)tess.Vertices[tess.Elements[i * 3]].Position.Y); tempPoly[1] = new PointF((float)tess.Vertices[tess.Elements[(i * 3) + 1]].Position.X, (float)tess.Vertices[tess.Elements[(i * 3) + 1]].Position.Y); tempPoly[2] = new PointF((float)tess.Vertices[tess.Elements[(i * 3) + 2]].Position.X, (float)tess.Vertices[tess.Elements[(i * 3) + 2]].Position.Y); output.Add(clockwiseOrder(tempPoly).ToArray()); } } else { output.Add(source.ToArray()); } return(output); }
public static void GenerateShadowMesh(Mesh mesh, Vector3[] shapePath) { Color meshInteriorColor = new Color(0, 0, 0, 1); List <Vector3> vertices = new List <Vector3>(); List <int> triangles = new List <int>(); List <Vector4> tangents = new List <Vector4>(); // Create interior geometry int pointCount = shapePath.Length; var inputs = new ContourVertex[pointCount]; for (int i = 0; i < pointCount; ++i) { inputs[i] = new ContourVertex() { Position = new Vec3() { X = shapePath[i].x, Y = shapePath[i].y }, Data = meshInteriorColor } } ; Tess tessI = new Tess(); tessI.AddContour(inputs, ContourOrientation.Original); tessI.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3, InterpCustomVertexData); var indicesI = tessI.Elements.Select(i => i).ToArray(); var verticesI = tessI.Vertices.Select(v => new Vector3(v.Position.X, v.Position.Y, 0)).ToArray(); vertices.AddRange(verticesI); triangles.AddRange(indicesI); InitializeTangents(vertices.Count, tangents); List <Edge> edges = new List <Edge>(); PopulateEdgeArray(vertices, triangles, edges); SortEdges(edges); CreateShadowTriangles(vertices, triangles, tangents, edges); Vector3[] finalVertices = vertices.ToArray(); int[] finalTriangles = triangles.ToArray(); Vector4[] finalTangents = tangents.ToArray(); mesh.Clear(); mesh.vertices = finalVertices; mesh.triangles = finalTriangles; mesh.tangents = finalTangents; } }
/// <summary> /// trs vertices to eliminate Z and eleminate duplicates /// </summary> ContourVertex[] make2DSegment(CGVolume vol, int index) { Matrix4x4 m = getMat(vol, index, false); int idx = vol.GetSegmentIndex(index); ContourVertex[] res = new ContourVertex[vol.CrossSize]; for (int i = 0; i < vol.CrossSize; i++) { res[i] = m.MultiplyPoint(vol.Vertex[idx + i]).ContourVertex(); } return(res); }
public static void ToTess(PolygonSet pset, Tess tess) { foreach (var poly in pset) { var v = new ContourVertex[poly.Count]; for (int i = 0; i < poly.Count; i++) { v[i].Position = new Vec3(poly[i].X, poly[i].Y, poly[i].Z); v[i].Data = poly[i].Color; } tess.AddContour(v, poly.Orientation); } }
private static void BuildGeometryFromClipPaths(VectorUtils.Geometry geom, List <List <IntPoint> > paths, List <Vector2> outVerts, List <UInt16> outInds, ref UInt16 maxIndex) { var vertices = new List <Vector2>(100); var indices = new List <UInt16>(vertices.Capacity * 3); var vertexIndex = new Dictionary <IntPoint, UInt16>(); foreach (var path in paths) { if (path.Count == 3) { // Triangle case, no need to tessellate foreach (var pt in path) { StoreClipVertex(vertexIndex, vertices, indices, pt, ref maxIndex); } } else if (path.Count > 3) { // Generic polygon case, we need to tessellate first var tess = new Tess(); var contour = new ContourVertex[path.Count]; for (int i = 0; i < path.Count; ++i) { contour[i] = new ContourVertex() { Position = new Vec3() { X = path[i].X, Y = path[i].Y, Z = 0.0f } } } ; tess.AddContour(contour, ContourOrientation.Original); var windingRule = WindingRule.NonZero; tess.Tessellate(windingRule, ElementType.Polygons, 3); foreach (var e in tess.Elements) { var v = tess.Vertices[e]; var pt = new IntPoint(v.Position.X, v.Position.Y); StoreClipVertex(vertexIndex, vertices, indices, pt, ref maxIndex); } } } var invMatrix = geom.WorldTransform.Inverse(); outVerts.AddRange(vertices.Select(v => invMatrix * v)); outInds.AddRange(indices); }
internal static ContourVertex[] ToContourVertexArray(this Polygon poly) { var contour = new List <ContourVertex>(); foreach (var vert in poly.Vertices) { var cv = new ContourVertex(); cv.Position = new Vec3 { X = vert.X, Y = vert.Y, Z = vert.Z }; contour.Add(cv); } return(contour.ToArray()); }
public static UnityEngine.Mesh BuildMesh(List <Vector2[]> contours) { Tess tesselation = new Tess(); foreach (Vector2[] contour in contours) { if (contour == null) { continue; } int pathLength = contour.Length; ContourVertex[] path = new ContourVertex[pathLength]; for (int j = 0; j < pathLength; j++) { Vector2 position = contour[j]; path[j].Position = new Vec3 { X = position.x, Y = position.y, Z = 0f }; } tesselation.AddContour(path); } tesselation.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3); UnityEngine.Mesh mesh = new UnityEngine.Mesh(); int meshVertexCount = tesselation.Vertices.Length; Vector3[] vertices = new Vector3[meshVertexCount]; for (int i = 0; i < meshVertexCount; i++) { vertices[i] = new Vector3(tesselation.Vertices[i].Position.X, tesselation.Vertices[i].Position.Y, 0f); } int numTriangles = tesselation.ElementCount; int[] triangles = new int[numTriangles * 3]; for (int i = 0; i < numTriangles; i++) { triangles[i * 3] = tesselation.Elements[i * 3]; triangles[i * 3 + 1] = tesselation.Elements[i * 3 + 1]; triangles[i * 3 + 2] = tesselation.Elements[i * 3 + 2]; } mesh.vertices = vertices; mesh.triangles = triangles; return(mesh); }
private void AddContour(Tess tess, Polygon p, bool reversed = false) { var numPoints = p.Vertices.Count; var contour = new ContourVertex[numPoints]; for (var i = 0; i < numPoints; i++) { var v = p.Vertices[i]; contour[i].Position = new Vec3 { X = v.X, Y = v.Y, Z = v.Z }; } tess.AddContour(contour, reversed ? ContourOrientation.Clockwise : ContourOrientation.CounterClockwise); }
internal static ContourVertex[] ToContourVertexArray(this List <Vector3> pts) { var contour = new ContourVertex[pts.Count]; for (var i = 0; i < contour.Length; i++) { var v = pts[i]; contour[i] = new ContourVertex(); contour[i].Position = new Vec3 { X = v.X, Y = v.Y, Z = v.Z }; } return(contour); }
internal ContourVertex[] ToContourVertexArray() { var contour = new ContourVertex[this.Vertices.Length]; for (var i = 0; i < this.Vertices.Length; i++) { var v = this.Vertices[i]; contour[i] = new ContourVertex(); contour[i].Position = new Vec3 { X = v.Position.X, Y = v.Position.Y, Z = v.Position.Z }; } return(contour); }
/// <summary> /// Convert Loop to an array of ContourVertex. /// </summary> /// <param name="loop"></param> /// <param name="face"></param> internal static ContourVertex[] ToContourVertexArray(this Loop loop, Face face) { var contour = new List <ContourVertex>(); foreach (var edge in loop.Edges) { var cv = new ContourVertex(); cv.Position = new Vec3 { X = edge.Vertex.Point.X, Y = edge.Vertex.Point.Y, Z = edge.Vertex.Point.Z }; contour.Add(cv); } return(contour.ToArray()); }
internal static void AddContour(Tess tess, Polygon p) { var numPoints = p.Vertices.Length; var contour = new ContourVertex[numPoints]; for (var i = 0; i < numPoints; i++) { var v = p.Vertices[i]; contour[i].Position = new Vec3 { X = v.X, Y = v.Y, Z = v.Z }; } tess.AddContour(contour); }
public List <PointF[]> Triangulate(PolyTree solution) { List <PointF[]> list = new List <PointF[]>(); Tess tess = new Tess(); tess.NoEmptyPolygons = true; Func <IntPoint, ContourVertex> selector = delegate(IntPoint p) { ContourVertex result = default(ContourVertex); result.Position = new Vec3 { X = (float)p.X, Y = (float)p.Y, Z = 0f }; return(result); }; for (PolyNode polyNode = solution.GetFirst(); polyNode != null; polyNode = polyNode.GetNext()) { if (!polyNode.IsOpen) { ContourVertex[] vertices = polyNode.Contour.Select(selector).ToArray(); tess.AddContour(vertices); } } tess.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3); int elementCount = tess.ElementCount; for (int i = 0; i < elementCount; i++) { Vec3 position = tess.Vertices[tess.Elements[i * 3 + 0]].Position; Vec3 position2 = tess.Vertices[tess.Elements[i * 3 + 1]].Position; Vec3 position3 = tess.Vertices[tess.Elements[i * 3 + 2]].Position; List <PointF> list2 = new List <PointF> { new PointF(position.X, position.Y), new PointF(position2.X, position2.Y), new PointF(position3.X, position3.Y) }; if (Math.Cross(list2[0], list2[1], list2[2]) > 0f) { list2.Reverse(); } list.Add(list2.ToArray()); } return(list); }
private static ContourVertex[] ToContourVertices(this List <Csg.Vertex> vertices) { var result = new ContourVertex[vertices.Count]; for (var i = 0; i < vertices.Count; i++) { result[i] = new ContourVertex() { Position = new Vec3() { X = vertices[i].Pos.X, Y = vertices[i].Pos.Y, Z = vertices[i].Pos.Z }, Data = vertices[i].Tex }; } return(result); }
public void AddVertex(Vec3 v, ref ContourVertex contourVertex) { if (_vertices.Contains(v)) { contourVertex = _vertices[v]; } else { _vertices.Add(v, ref contourVertex); _minX = Math.Min(_minX, v.X); _minY = Math.Min(_minY, v.Y); _minZ = Math.Min(_minZ, v.Z); _maxX = Math.Max(_maxX, v.X); _maxY = Math.Max(_maxY, v.Y); _maxZ = Math.Max(_maxZ, v.Z); } }
/// <summary> /// Convert Loop to an array of ContourVertex. /// </summary> /// <param name="loop"></param> /// <param name="transform">An optional transform to apply to the contour.</param> internal static ContourVertex[] ToContourVertexArray(this Loop loop, Transform transform = null) { var contour = new ContourVertex[loop.Edges.Count]; for (var i = 0; i < loop.Edges.Count; i++) { var edge = loop.Edges[i]; var p = transform == null ? edge.Vertex.Point : transform.OfPoint(edge.Vertex.Point); var cv = new ContourVertex { Position = new Vec3 { X = p.X, Y = p.Y, Z = p.Z } }; contour[i] = cv; } return(contour); }
public static void GetFalloffShape(Vector3[] shapePath, ref List <Vector2> extrusionDir) { int pointCount = shapePath.Length; var inputs = new ContourVertex[pointCount]; for (int i = 0; i < pointCount; ++i) { inputs[i] = new ContourVertex() { Position = new Vec3() { X = shapePath[i].x, Y = shapePath[i].y }, Data = null } } ; GetFalloffExtrusion(inputs, pointCount, ref extrusionDir); }
private static void AddPoints([CanBeNull] Matrix3x2?transform, [NotNull] Vector2[] points, List <ContourVertex> vertices) { if (points.Length < 2) { return; } // Don't add the first point. var verts = new ContourVertex[points.Length - 1]; if (transform != null) { for (var i = 1; i < points.Length; ++i) { var pt = Matrix3x2.Transform(transform.Value, points[i]); verts[i - 1] = new ContourVertex { Position = new Vec3 { X = pt.X, Y = pt.Y, Z = StandardZ } }; } } else { for (var i = 1; i < points.Length; ++i) { var pt = points[i]; verts[i - 1] = new ContourVertex { Position = new Vec3 { X = pt.X, Y = pt.Y, Z = StandardZ } }; } } vertices.AddRange(verts); }
public static List <Vector2> GetFeatheredShape(Vector3[] shapePath, float feathering) { int pointCount = shapePath.Length; var inputs = new ContourVertex[pointCount]; for (int i = 0; i < pointCount; ++i) { inputs[i] = new ContourVertex() { Position = new Vec3() { X = shapePath[i].x, Y = shapePath[i].y }, Data = null } } ; var feathered = UpdateFeatheredShapeLightMesh(inputs, pointCount, feathering); return(feathered); }
/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the * origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives * a place to insert the new vertex in the global vertex list. We insert * the new vertex *before* vNext so that algorithms which walk the vertex * list will not see the newly created vertices. */ private static void MakeVertex(ContourVertex newVertex, HalfEdge eOrig, ContourVertex vNext) { HalfEdge e; ContourVertex vPrev; ContourVertex vNew = newVertex; /* insert in circular doubly-linked list before vNext */ vPrev = vNext.prevVertex; vNew.prevVertex = vPrev; vPrev.nextVertex = vNew; vNew.nextVertex = vNext; vNext.prevVertex = vNew; vNew.edgeThisIsOriginOf = eOrig; vNew.clientIndex = 0; /* leave coords, s, t undefined */ /* fix other edges on this vertex loop */ e = eOrig; do { e.originVertex = vNew; e = e.nextEdgeCCWAroundOrigin; } while (e != eOrig); }
// __gl_meshMakeEdge creates one edge, two vertices, and a loop (face). // The loop consists of the two new half-edges. public HalfEdge MakeEdge() { var newVertex1 = new ContourVertex(); var newVertex2 = new ContourVertex(); var newFace = new Face(); HalfEdge e; e = MakeEdge(halfEdgeHead); MakeVertex(newVertex1, e, vertexHead); MakeVertex(newVertex2, e.otherHalfOfThisEdge, vertexHead); MakeFace(newFace, e, faceHead); return e; }
static void ConnectLeftDegenerate(Tesselator tess, ActiveRegion regUp, ContourVertex vEvent) /* * The currentSweepVertex vertex lies exactly on an already-processed edge or vertex. * Adding the new vertex involves splicing it into the already-processed * part of the mesh. */ { HalfEdge e, eTopLeft, eTopRight, eLast; ActiveRegion reg; e = regUp.upperHalfEdge; if (e.originVertex.VertEq(vEvent)) { /* e.Org is an unprocessed vertex - just combine them, and wait * for e.Org to be pulled from the queue */ SpliceMergeVertices(tess, e, vEvent.edgeThisIsOriginOf); return; } if (!e.directionVertex.VertEq(vEvent)) { /* General case -- splice vEvent into edge e which passes through it */ Mesh.meshSplitEdge(e.otherHalfOfThisEdge); if (regUp.fixUpperEdge) { /* This edge was fixable -- delete unused portion of original edge */ Mesh.DeleteHalfEdge(e.nextEdgeCCWAroundOrigin); regUp.fixUpperEdge = false; } Mesh.meshSplice(vEvent.edgeThisIsOriginOf, e); SweepEvent(tess, vEvent); /* recurse */ return; } /* vEvent coincides with e.Dst, which has already been processed. * Splice in the additional right-going edges. */ regUp = TopRightRegion(regUp); reg = RegionBelow(regUp); eTopRight = reg.upperHalfEdge.otherHalfOfThisEdge; eTopLeft = eLast = eTopRight.nextEdgeCCWAroundOrigin; if (reg.fixUpperEdge) { /* Here e.Dst has only a single fixable edge going right. * We can delete it since now we have some real right-going edges. */ if (eTopLeft == eTopRight) { throw new Exception(); /* there are some left edges too */ } DeleteRegion(reg); Mesh.DeleteHalfEdge(eTopRight); eTopRight = eTopLeft.Oprev; } Mesh.meshSplice(vEvent.edgeThisIsOriginOf, eTopRight); if (!eTopLeft.EdgeGoesLeft()) { /* e.Dst had no left-going edges -- indicate this to AddRightEdges() */ eTopLeft = null; } AddRightEdges(tess, regUp, eTopRight.nextEdgeCCWAroundOrigin, eLast, eTopLeft, true); }
/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that * eNew == eOrg.Lnext, and eNew.Dst is a newly created vertex. * eOrg and eNew will have the same left face. */ private static HalfEdge meshAddEdgeVertex(HalfEdge eOrg) { HalfEdge eNewSym; HalfEdge eNew = MakeEdge(eOrg); eNewSym = eNew.otherHalfOfThisEdge; /* Connect the new edge appropriately */ Splice(eNew, eOrg.nextEdgeCCWAroundLeftFace); /* Set the vertex and face information */ eNew.originVertex = eOrg.directionVertex; { var newVertex = new ContourVertex(); MakeVertex(newVertex, eNewSym, eNew.originVertex); } eNew.leftFace = eNewSym.leftFace = eOrg.leftFace; return eNew; }
/* * We've computed a new intersection point, now we need a "data" pointer * from the user so that we can refer to this new vertex in the * rendering callbacks. */ private static void GetIntersectData(Tesselator tess, ContourVertex isect, ContourVertex orgUp, ContourVertex dstUp, ContourVertex orgLo, ContourVertex dstLo) { var data4 = new int[4]; var weights4 = new double[4]; data4[0] = orgUp.clientIndex; data4[1] = dstUp.clientIndex; data4[2] = orgLo.clientIndex; data4[3] = dstLo.clientIndex; isect.coords[0] = isect.coords[1] = isect.coords[2] = 0; VertexWeights(isect, orgUp, dstUp, out weights4[0], out weights4[1]); VertexWeights(isect, orgLo, dstLo, out weights4[2], out weights4[3]); CallCombine(tess, isect, data4, weights4, true); }
static void CallCombine(Tesselator tess, ContourVertex intersectionVertex, int[] vertexIndexArray, double[] vertexWeights, bool needed) { /* Copy coord data in case the callback changes it. */ double c0 = intersectionVertex.C_0; double c1 = intersectionVertex.C_1; double c2 = intersectionVertex.C_2; intersectionVertex.clientIndex = 0; tess.CallCombine(c0, c1, c2, vertexIndexArray, vertexWeights, out intersectionVertex.clientIndex); if (intersectionVertex.clientIndex == 0) { if (!needed) { intersectionVertex.clientIndex = vertexIndexArray[0]; } else { /* The only fatal error is when two edges are found to intersect, * but the user has not provided the callback necessary to handle * generated intersection points. */ throw new Exception("You need to provided a callback to handle generated intersection points."); } } }
static double TransSign(ContourVertex u, ContourVertex v, ContourVertex w) { /* Returns a number whose sign matches TransEval(u,v,w) but which * is cheaper to evaluate. Returns > 0, == 0 , or < 0 * as v is above, on, or below the edge uw. */ double gapL, gapR; if (!u.TransLeq(v) || !v.TransLeq(w)) { throw new Exception(); } gapL = v.y - u.y; gapR = w.y - v.y; if (gapL + gapR > 0) { return (v.x - w.x) * gapL + (v.x - u.x) * gapR; } /* vertical line */ return 0; }
/* * Purpose: connect a "left" vertex (one where both edges go right) * to the processed portion of the mesh. Let R be the active region * containing vEvent, and let U and L be the upper and lower edge * chains of R. There are two possibilities: * * - the normal case: split R into two regions, by connecting vEvent to * the rightmost vertex of U or L lying to the left of the sweep line * * - the degenerate case: if vEvent is close enough to U or L, we * merge vEvent into that edge chain. The sub-cases are: * - merging with the rightmost vertex of U or L * - merging with the active edge of U or L * - merging with an already-processed portion of U or L */ private static void ConnectLeftVertex(Tesselator tess, ContourVertex vEvent) { ActiveRegion regUp, regLo, reg; HalfEdge eUp, eLo, eNew; var tmp = new ActiveRegion(); /* assert( vEvent.anEdge.Onext.Onext == vEvent.anEdge ); */ /* Get a pointer to the active region containing vEvent */ tmp.upperHalfEdge = vEvent.edgeThisIsOriginOf.otherHalfOfThisEdge; /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */ regUp = Dictionary.dictSearch(tess.edgeDictionary, tmp).Key; regLo = RegionBelow(regUp); eUp = regUp.upperHalfEdge; eLo = regLo.upperHalfEdge; /* Try merging with U or L first */ if (ContourVertex.EdgeSign(eUp.directionVertex, vEvent, eUp.originVertex) == 0) { ConnectLeftDegenerate(tess, regUp, vEvent); return; } /* Connect vEvent to rightmost processed vertex of either chain. * e.Dst is the vertex that we will connect to vEvent. */ reg = eLo.directionVertex.VertLeq(eUp.directionVertex) ? regUp : regLo; if (regUp.inside || reg.fixUpperEdge) { if (reg == regUp) { eNew = Mesh.meshConnect(vEvent.edgeThisIsOriginOf.otherHalfOfThisEdge, eUp.nextEdgeCCWAroundLeftFace); } else { HalfEdge tempHalfEdge = Mesh.meshConnect(eLo.Dnext, vEvent.edgeThisIsOriginOf); eNew = tempHalfEdge.otherHalfOfThisEdge; } if (reg.fixUpperEdge) { FixUpperEdge(reg, eNew); } else { ComputeWinding(tess, AddRegionBelow(tess, regUp, eNew)); } SweepEvent(tess, vEvent); } else { /* The new vertex is in a region which does not belong to the polygon. * We don''t need to connect this vertex to the rest of the mesh. */ AddRightEdges(tess, regUp, vEvent.edgeThisIsOriginOf, vEvent.edgeThisIsOriginOf, null, true); } }
static void Swap(ref ContourVertex a, ref ContourVertex b) { ContourVertex t = a; a = b; b = t; }
static double TransEval(ContourVertex u, ContourVertex v, ContourVertex w) { /* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w), * evaluates the t-coord of the edge uw at the s-coord of the vertex v. * Returns v.s - (uw)(v.t), ie. the signed distance from uw to v. * If uw is vertical (and thus passes thru v), the result is zero. * * The calculation is extremely accurate and stable, even when v * is very close to u or w. In particular if we set v.s = 0 and * let r be the negated result (this evaluates (uw)(v.t)), then * r is guaranteed to satisfy MIN(u.s,w.s) <= r <= MAX(u.s,w.s). */ double gapL, gapR; if (!u.TransLeq(v) || !v.TransLeq(w)) { throw new Exception(); } gapL = v.y - u.y; gapR = w.y - v.y; if (gapL + gapR > 0) { if (gapL < gapR) { return (v.x - u.x) + (u.x - w.x) * (gapL / (gapL + gapR)); } else { return (v.x - w.x) + (w.x - u.x) * (gapR / (gapL + gapR)); } } /* vertical line */ return 0; }
static void GetIntersectData(Tesselator tess, ContourVertex isect, ContourVertex orgUp, ContourVertex dstUp, ContourVertex orgLo, ContourVertex dstLo) /* * We've computed a new intersection point, now we need a "data" pointer * from the user so that we can refer to this new vertex in the * rendering callbacks. */ { int[] data4 = new int[4]; double[] weights4 = new double[4]; data4[0] = orgUp.clientIndex; data4[1] = dstUp.clientIndex; data4[2] = orgLo.clientIndex; data4[3] = dstLo.clientIndex; isect.C_0 = isect.C_1 = isect.C_2 = 0; VertexWeights(isect, orgUp, dstUp, out weights4[0], out weights4[1]); VertexWeights(isect, orgLo, dstLo, out weights4[2], out weights4[3]); CallCombine(tess, isect, data4, weights4, true); }
static void VertexWeights(ContourVertex isect, ContourVertex org, ContourVertex dst, out double weights0, out double weights1) /* * Find some weights which describe how the intersection vertex is * a linear combination of "org" and "dest". Each of the two edges * which generated "isect" is allocated 50% of the weight; each edge * splits the weight between its org and dst according to the * relative distance to "isect". */ { double t1 = VertL1dist(org, isect); double t2 = VertL1dist(dst, isect); weights0 = 0.5 * t2 / (t1 + t2); weights1 = 0.5 * t1 / (t1 + t2); isect.C_0 += weights0 * org.C_0 + weights1 * dst.C_0; isect.C_1 += weights0 * org.C_1 + weights1 * dst.C_1; isect.C_2 += weights0 * org.C_2 + weights1 * dst.C_2; }
static double VertL1dist(ContourVertex u, ContourVertex v) { return Math.Abs(u.x - v.x) + Math.Abs(u.y - v.y); }
/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the * mesh connectivity and topology. It changes the mesh so that * eOrg.Onext <- OLD( eDst.Onext ) * eDst.Onext <- OLD( eOrg.Onext ) * where OLD(...) means the value before the meshSplice operation. * * This can have two effects on the vertex structure: * - if eOrg.Org != eDst.Org, the two vertices are merged together * - if eOrg.Org == eDst.Org, the origin is split into two vertices * In both cases, eDst.Org is changed and eOrg.Org is untouched. * * Similarly (and independently) for the face structure, * - if eOrg.Lface == eDst.Lface, one loop is split into two * - if eOrg.Lface != eDst.Lface, two distinct loops are joined into one * In both cases, eDst.Lface is changed and eOrg.Lface is unaffected. * * Some special cases: * If eDst == eOrg, the operation has no effect. * If eDst == eOrg.Lnext, the new face will have a single edge. * If eDst == eOrg.Lprev, the old face will have a single edge. * If eDst == eOrg.Onext, the new vertex will have a single edge. * If eDst == eOrg.Oprev, the old vertex will have a single edge. */ public static void meshSplice(HalfEdge eOrg, HalfEdge eDst) { bool joiningLoops = false; bool joiningVertices = false; if (eOrg == eDst) return; if (eDst.originVertex != eOrg.originVertex) { /* We are merging two disjoint vertices -- destroy eDst.Org */ joiningVertices = true; KillVertex(eDst.originVertex, eOrg.originVertex); } if (eDst.leftFace != eOrg.leftFace) { /* We are connecting two disjoint loops -- destroy eDst.Lface */ joiningLoops = true; KillFace(eDst.leftFace, eOrg.leftFace); } /* Change the edge structure */ Splice(eDst, eOrg); if (!joiningVertices) { var newVertex = new ContourVertex(); /* We split one vertex into two -- the new vertex is eDst.Org. * Make sure the old vertex points to a valid half-edge. */ MakeVertex(newVertex, eDst, eOrg.originVertex); eOrg.originVertex.edgeThisIsOriginOf = eOrg; } if (!joiningLoops) { var newFace = new Face(); /* We split one loop into two -- the new loop is eDst.Lface. * Make sure the old face points to a valid half-edge. */ MakeFace(newFace, eDst, eOrg.leftFace); eOrg.leftFace.halfEdgeThisIsLeftFaceOf = eOrg; } }
static void EdgeIntersect(ContourVertex o1, ContourVertex d1, ContourVertex o2, ContourVertex d2, ref ContourVertex v) /* Given edges (o1,d1) and (o2,d2), compute their point of intersection. * The computed point is guaranteed to lie in the intersection of the * bounding rectangles defined by each edge. */ { double z1, z2; /* This is certainly not the most efficient way to find the intersection * of two line segments, but it is very numerically stable. * * Strategy: find the two middle vertices in the VertLeq ordering, * and interpolate the intersection s-value from these. Then repeat * using the TransLeq ordering to find the intersection t-value. */ if (!o1.VertLeq(d1)) { Swap(ref o1, ref d1); } if (!o2.VertLeq(d2)) { Swap(ref o2, ref d2); } if (!o1.VertLeq(o2)) { Swap(ref o1, ref o2); Swap(ref d1, ref d2); } if (!o2.VertLeq(d1)) { /* Technically, no intersection -- do our best */ v.x = (o2.x + d1.x) / 2; } else if (d1.VertLeq(d2)) { /* Interpolate between o2 and d1 */ z1 = ContourVertex.EdgeEval(o1, o2, d1); z2 = ContourVertex.EdgeEval(o2, d1, d2); if (z1 + z2 < 0) { z1 = -z1; z2 = -z2; } v.x = Interpolate(z1, o2.x, z2, d1.x); } else { /* Interpolate between o2 and d2 */ z1 = ContourVertex.EdgeSign(o1, o2, d1); z2 = -ContourVertex.EdgeSign(o1, d2, d1); if (z1 + z2 < 0) { z1 = -z1; z2 = -z2; } v.x = Interpolate(z1, o2.x, z2, d2.x); } /* Now repeat the process for t */ if (!o1.TransLeq(d1)) { Swap(ref o1, ref d1); } if (!o2.TransLeq(d2)) { Swap(ref o2, ref d2); } if (!o1.TransLeq(o2)) { Swap(ref o1, ref o2); Swap(ref d1, ref d2); } if (!o2.TransLeq(d1)) { /* Technically, no intersection -- do our best */ v.y = (o2.y + d1.y) / 2; } else if (d1.TransLeq(d2)) { /* Interpolate between o2 and d1 */ z1 = TransEval(o1, o2, d1); z2 = TransEval(o2, d1, d2); if (z1 + z2 < 0) { z1 = -z1; z2 = -z2; } v.y = Interpolate(z1, o2.y, z2, d1.y); } else { /* Interpolate between o2 and d2 */ z1 = TransSign(o1, o2, d1); z2 = -TransSign(o1, d2, d1); if (z1 + z2 < 0) { z1 = -z1; z2 = -z2; } v.y = Interpolate(z1, o2.y, z2, d2.y); } }
// __gl_meshMakeEdge creates one edge, two vertices, and a loop (face). // The loop consists of the two new half-edges. public HalfEdge MakeEdge() { ContourVertex newVertex1 = new ContourVertex(); ContourVertex newVertex2 = new ContourVertex(); Face newFace = new Face(); HalfEdge e; e = MakeEdge(this.halfEdgeHead); MakeVertex(newVertex1, e, this.vertexHead); MakeVertex(newVertex2, e.otherHalfOfThisEdge, this.vertexHead); MakeFace(newFace, e, this.faceHead); return e; }
static bool CheckForIntersect(Tesselator tess, ActiveRegion regUp) /* * Check the upper and lower edges of the given region to see if * they intersect. If so, create the intersection and add it * to the data structures. * * Returns true if adding the new intersection resulted in a recursive * call to AddRightEdges(); in this case all "dirty" regions have been * checked for intersections, and possibly regUp has been deleted. */ { ActiveRegion regLo = RegionBelow(regUp); HalfEdge eUp = regUp.upperHalfEdge; HalfEdge eLo = regLo.upperHalfEdge; ContourVertex orgUp = eUp.originVertex; ContourVertex orgLo = eLo.originVertex; ContourVertex dstUp = eUp.directionVertex; ContourVertex dstLo = eLo.directionVertex; double tMinUp, tMaxLo; ContourVertex isect = new ContourVertex(); ContourVertex orgMin; HalfEdge e; if (dstLo.VertEq(dstUp)) { throw new Exception(); } if (ContourVertex.EdgeSign(dstUp, tess.currentSweepVertex, orgUp) > 0) { throw new Exception(); } if (ContourVertex.EdgeSign(dstLo, tess.currentSweepVertex, orgLo) < 0) { throw new Exception(); } if (orgUp == tess.currentSweepVertex || orgLo == tess.currentSweepVertex) { throw new Exception(); } if (regUp.fixUpperEdge || regLo.fixUpperEdge) { throw new Exception(); } if (orgUp == orgLo) { return false; /* right endpoints are the same */ } tMinUp = Math.Min(orgUp.y, dstUp.y); tMaxLo = Math.Max(orgLo.y, dstLo.y); if (tMinUp > tMaxLo) { return false; /* t ranges do not overlap */ } if (orgUp.VertLeq(orgLo)) { if (ContourVertex.EdgeSign(dstLo, orgUp, orgLo) > 0) { return false; } } else { if (ContourVertex.EdgeSign(dstUp, orgLo, orgUp) < 0) { return false; } } EdgeIntersect(dstUp, orgUp, dstLo, orgLo, ref isect); // The following properties are guaranteed: if (!(Math.Min(orgUp.y, dstUp.y) <= isect.y)) { throw new System.Exception(); } if (!(isect.y <= Math.Max(orgLo.y, dstLo.y))) { throw new System.Exception(); } if (!(Math.Min(dstLo.x, dstUp.x) <= isect.x)) { throw new System.Exception(); } if (!(isect.x <= Math.Max(orgLo.x, orgUp.x))) { throw new System.Exception(); } if (isect.VertLeq(tess.currentSweepVertex)) { /* The intersection point lies slightly to the left of the sweep line, * so move it until it''s slightly to the right of the sweep line. * (If we had perfect numerical precision, this would never happen * in the first place). The easiest and safest thing to do is * replace the intersection by tess.currentSweepVertex. */ isect.x = tess.currentSweepVertex.x; isect.y = tess.currentSweepVertex.y; } /* Similarly, if the computed intersection lies to the right of the * rightmost origin (which should rarely happen), it can cause * unbelievable inefficiency on sufficiently degenerate inputs. * (If you have the test program, try running test54.d with the * "X zoom" option turned on). */ orgMin = orgUp.VertLeq(orgLo) ? orgUp : orgLo; if (orgMin.VertLeq(isect)) { isect.x = orgMin.x; isect.y = orgMin.y; } if (isect.VertEq(orgUp) || isect.VertEq(orgLo)) { /* Easy case -- intersection at one of the right endpoints */ CheckForRightSplice(tess, regUp); return false; } if ((!dstUp.VertEq(tess.currentSweepVertex) && ContourVertex.EdgeSign(dstUp, tess.currentSweepVertex, isect) >= 0) || (!dstLo.VertEq(tess.currentSweepVertex) && ContourVertex.EdgeSign(dstLo, tess.currentSweepVertex, isect) <= 0)) { /* Very unusual -- the new upper or lower edge would pass on the * wrong side of the sweep currentSweepVertex, or through it. This can happen * due to very small numerical errors in the intersection calculation. */ if (dstLo == tess.currentSweepVertex) { /* Splice dstLo into eUp, and process the new region(s) */ Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); Mesh.meshSplice(eLo.otherHalfOfThisEdge, eUp); regUp = TopLeftRegion(regUp); eUp = RegionBelow(regUp).upperHalfEdge; FinishLeftRegions(tess, RegionBelow(regUp), regLo); AddRightEdges(tess, regUp, eUp.Oprev, eUp, eUp, true); return true; } if (dstUp == tess.currentSweepVertex) { /* Splice dstUp into eLo, and process the new region(s) */ Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); Mesh.meshSplice(eUp.nextEdgeCCWAroundLeftFace, eLo.Oprev); regLo = regUp; regUp = TopRightRegion(regUp); e = RegionBelow(regUp).upperHalfEdge.Rprev; regLo.upperHalfEdge = eLo.Oprev; eLo = FinishLeftRegions(tess, regLo, null); AddRightEdges(tess, regUp, eLo.nextEdgeCCWAroundOrigin, eUp.Rprev, e, true); return true; } /* Special case: called from ConnectRightVertex. If either * edge passes on the wrong side of tess.currentSweepVertex, split it * (and wait for ConnectRightVertex to splice it appropriately). */ if (ContourVertex.EdgeSign(dstUp, tess.currentSweepVertex, isect) >= 0) { regUp.RegionAbove().dirty = regUp.dirty = true; Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); eUp.originVertex.x = tess.currentSweepVertex.x; eUp.originVertex.y = tess.currentSweepVertex.y; } if (ContourVertex.EdgeSign(dstLo, tess.currentSweepVertex, isect) <= 0) { regUp.dirty = regLo.dirty = true; Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); eLo.originVertex.x = tess.currentSweepVertex.x; eLo.originVertex.y = tess.currentSweepVertex.y; } /* leave the rest for ConnectRightVertex */ return false; } /* General case -- split both edges, splice into new vertex. * When we do the splice operation, the order of the arguments is * arbitrary as far as correctness goes. However, when the operation * creates a new face, the work done is proportional to the size of * the new face. We expect the faces in the processed part of * the mesh (ie. eUp.Lface) to be smaller than the faces in the * unprocessed original contours (which will be eLo.Oprev.Lface). */ Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); Mesh.meshSplice(eLo.Oprev, eUp); eUp.originVertex.x = isect.x; eUp.originVertex.y = isect.y; tess.vertexPriorityQue.Add(out eUp.originVertex.priorityQueueHandle, eUp.originVertex); /* __gl_pqSortInsert */ GetIntersectData(tess, eUp.originVertex, orgUp, dstUp, orgLo, dstLo); regUp.RegionAbove().dirty = regUp.dirty = regLo.dirty = true; return false; }
/* * Find some weights which describe how the intersection vertex is * a linear combination of "org" and "dest". Each of the two edges * which generated "isect" is allocated 50% of the weight; each edge * splits the weight between its org and dst according to the * relative distance to "isect". */ private static void VertexWeights(ContourVertex isect, ContourVertex org, ContourVertex dst, out double weights0, out double weights1) { double t1 = VertL1dist(org, isect); double t2 = VertL1dist(dst, isect); weights0 = 0.5*t2/(t1 + t2); weights1 = 0.5*t1/(t1 + t2); isect.coords[0] += weights0*org.coords[0] + weights1*dst.coords[0]; isect.coords[1] += weights0*org.coords[1] + weights1*dst.coords[1]; isect.coords[2] += weights0*org.coords[2] + weights1*dst.coords[2]; }
static void ConnectLeftVertex(Tesselator tess, ContourVertex vEvent) /* * Purpose: connect a "left" vertex (one where both edges go right) * to the processed portion of the mesh. Let R be the active region * containing vEvent, and let U and L be the upper and lower edge * chains of R. There are two possibilities: * * - the normal case: split R into two regions, by connecting vEvent to * the rightmost vertex of U or L lying to the left of the sweep line * * - the degenerate case: if vEvent is close enough to U or L, we * merge vEvent into that edge chain. The sub-cases are: * - merging with the rightmost vertex of U or L * - merging with the active edge of U or L * - merging with an already-processed portion of U or L */ { ActiveRegion regUp, regLo, reg; HalfEdge eUp, eLo, eNew; ActiveRegion tmp = new ActiveRegion(); /* assert( vEvent.anEdge.Onext.Onext == vEvent.anEdge ); */ /* Get a pointer to the active region containing vEvent */ tmp.upperHalfEdge = vEvent.edgeThisIsOriginOf.otherHalfOfThisEdge; /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */ regUp = Dictionary.dictSearch(tess.edgeDictionary, tmp).Key; regLo = RegionBelow(regUp); eUp = regUp.upperHalfEdge; eLo = regLo.upperHalfEdge; /* Try merging with U or L first */ if (ContourVertex.EdgeSign(eUp.directionVertex, vEvent, eUp.originVertex) == 0) { ConnectLeftDegenerate(tess, regUp, vEvent); return; } /* Connect vEvent to rightmost processed vertex of either chain. * e.Dst is the vertex that we will connect to vEvent. */ reg = eLo.directionVertex.VertLeq(eUp.directionVertex) ? regUp : regLo; if (regUp.inside || reg.fixUpperEdge) { if (reg == regUp) { eNew = Mesh.meshConnect(vEvent.edgeThisIsOriginOf.otherHalfOfThisEdge, eUp.nextEdgeCCWAroundLeftFace); } else { HalfEdge tempHalfEdge = Mesh.meshConnect(eLo.Dnext, vEvent.edgeThisIsOriginOf); eNew = tempHalfEdge.otherHalfOfThisEdge; } if (reg.fixUpperEdge) { FixUpperEdge(reg, eNew); } else { ComputeWinding(tess, AddRegionBelow(tess, regUp, eNew)); } SweepEvent(tess, vEvent); } else { /* The new vertex is in a region which does not belong to the polygon. * We don''t need to connect this vertex to the rest of the mesh. */ AddRightEdges(tess, regUp, vEvent.edgeThisIsOriginOf, vEvent.edgeThisIsOriginOf, null, true); } }
/* KillVertex( vDel ) destroys a vertex and removes it from the global * vertex list. It updates the vertex loop to point to a given new vertex. */ private static void KillVertex(ContourVertex vDel, ContourVertex newOrg) { HalfEdge e, eStart = vDel.edgeThisIsOriginOf; ContourVertex vPrev, vNext; /* change the origin of all affected edges */ e = eStart; do { e.originVertex = newOrg; e = e.nextEdgeCCWAroundOrigin; } while (e != eStart); /* delete from circular doubly-linked list */ vPrev = vDel.prevVertex; vNext = vDel.nextVertex; vNext.prevVertex = vPrev; vPrev.nextVertex = vNext; }
static void SweepEvent(Tesselator tess, ContourVertex vEvent) /* * Does everything necessary when the sweep line crosses a vertex. * Updates the mesh and the edge dictionary. */ { ActiveRegion regUp, reg; HalfEdge e, eTopLeft, eBottomLeft; tess.currentSweepVertex = vEvent; /* for access in EdgeLeq() */ /* Check if this vertex is the right endpoint of an edge that is * already in the dictionary. In this case we don't need to waste * time searching for the location to insert new edges. */ e = vEvent.edgeThisIsOriginOf; while (e.regionThisIsUpperEdgeOf == null) { e = e.nextEdgeCCWAroundOrigin; if (e == vEvent.edgeThisIsOriginOf) { /* All edges go right -- not incident to any processed edges */ ConnectLeftVertex(tess, vEvent); return; } } /* Processing consists of two phases: first we "finish" all the * active regions where both the upper and lower edges terminate * at vEvent (ie. vEvent is closing off these regions). * We mark these faces "inside" or "outside" the polygon according * to their winding number, and delete the edges from the dictionary. * This takes care of all the left-going edges from vEvent. */ regUp = TopLeftRegion(e.regionThisIsUpperEdgeOf); reg = RegionBelow(regUp); eTopLeft = reg.upperHalfEdge; eBottomLeft = FinishLeftRegions(tess, reg, null); /* Next we process all the right-going edges from vEvent. This * involves adding the edges to the dictionary, and creating the * associated "active regions" which record information about the * regions between adjacent dictionary edges. */ if (eBottomLeft.nextEdgeCCWAroundOrigin == eTopLeft) { /* No right-going edges -- add a temporary "fixable" edge */ ConnectRightVertex(tess, regUp, eBottomLeft); } else { AddRightEdges(tess, regUp, eBottomLeft.nextEdgeCCWAroundOrigin, eTopLeft, eTopLeft, true); } }