diff --git a/Demo/Android/Xamarin.OpenGL/Xamarin.OpenGL.csproj b/Demo/Android/Xamarin.OpenGL/Xamarin.OpenGL.csproj index 9d0361cf..49fb4c7f 100644 --- a/Demo/Android/Xamarin.OpenGL/Xamarin.OpenGL.csproj +++ b/Demo/Android/Xamarin.OpenGL/Xamarin.OpenGL.csproj @@ -94,10 +94,6 @@ {280d17d5-4435-4ead-839a-33c5a8990e7e} Typography.OpenFont - - {fb5f78f5-c921-405d-8f21-42f7c15c2ad9} - PixelFarm.MiniAgg.One - diff --git a/Demo/Shared/DrawingGL.Common.projitems b/Demo/Shared/DrawingGL.Common.projitems index b7342c69..80ac7e74 100644 --- a/Demo/Shared/DrawingGL.Common.projitems +++ b/Demo/Shared/DrawingGL.Common.projitems @@ -33,7 +33,6 @@ - diff --git a/Demo/Shared/DrawingGL.Text/TextPrinter.cs b/Demo/Shared/DrawingGL.Text/TextPrinter.cs index c2cf1f64..4d6cf526 100644 --- a/Demo/Shared/DrawingGL.Text/TextPrinter.cs +++ b/Demo/Shared/DrawingGL.Text/TextPrinter.cs @@ -3,6 +3,7 @@ using Typography.OpenFont; using Typography.TextLayout; using Typography.Contours; +using Tesselate; namespace DrawingGL.Text { @@ -23,7 +24,7 @@ class TextPrinter : TextPrinterBase // for tess // SimpleCurveFlattener _curveFlattener; - TessTool _tessTool; + Tesselate.TessTool _tessTool; Typeface _currentTypeface; @@ -31,11 +32,11 @@ class TextPrinter : TextPrinterBase struct ProcessedGlyph { public readonly float[] tessData; - public readonly ushort tessNElements; - public ProcessedGlyph(float[] tessData, ushort tessNElements) + public readonly ushort vertextCount; + public ProcessedGlyph(float[] tessData, ushort vertextCount) { this.tessData = tessData; - this.tessNElements = tessNElements; + this.vertextCount = vertextCount; } } GlyphMeshCollection _glyphMeshCollection = new GlyphMeshCollection(); @@ -48,7 +49,7 @@ public TextPrinter() // _curveFlattener = new SimpleCurveFlattener(); - _tessTool = new TessTool(); + _tessTool = new Tesselate.TessTool(); } @@ -181,17 +182,17 @@ public void GenerateGlyphRuns(TextRun outputTextRun, char[] charBuffer, int star //do tess int[] endContours; float[] flattenPoints = _curveFlattener.Flatten(writablePath._points, out endContours); - int nTessElems; - tessData = _tessTool.TessPolygon(flattenPoints, endContours, out nTessElems); - //------- - processGlyph = new ProcessedGlyph(tessData, (ushort)nTessElems); + + tessData = _tessTool.TessAsTriVertexArray(flattenPoints, endContours, out int vertexCount); + processGlyph = new ProcessedGlyph(tessData, (ushort)vertexCount); + _glyphMeshCollection.RegisterCachedGlyph(glyphPlan.glyphIndex, processGlyph); } outputTextRun.AddGlyph( new GlyphRun(glyphPlan, processGlyph.tessData, - processGlyph.tessNElements)); + processGlyph.vertextCount)); } } public override void DrawString(char[] textBuffer, int startAt, int len, float x, float y) diff --git a/Demo/Shared/DrawingGL/Path.cs b/Demo/Shared/DrawingGL/Path.cs index ec7491f9..4b4dacc3 100644 --- a/Demo/Shared/DrawingGL/Path.cs +++ b/Demo/Shared/DrawingGL/Path.cs @@ -111,13 +111,13 @@ public struct GlyphRun //glyph run contains... //1. Typography.TextLayout.UnscaledGlyphPlan _glyphPlan; //10 bytes - public float[] tessData; //4 - public ushort nTessElements;//2 - internal GlyphRun(Typography.TextLayout.UnscaledGlyphPlan glyphPlan, float[] tessData, ushort nTessElements) + public float[] _tessData; //4 + public ushort _vertextCount; + internal GlyphRun(Typography.TextLayout.UnscaledGlyphPlan glyphPlan, float[] tessData, ushort vertextCount) { _glyphPlan = glyphPlan; - this.tessData = tessData; - this.nTessElements = nTessElements; + _tessData = tessData; + _vertextCount = vertextCount; } public Typography.TextLayout.UnscaledGlyphPlan GlyphPlan => _glyphPlan; diff --git a/Demo/Shared/DrawingGL/SimpleCanvas.cs b/Demo/Shared/DrawingGL/SimpleCanvas.cs index 0a317dda..09ea65a4 100644 --- a/Demo/Shared/DrawingGL/SimpleCanvas.cs +++ b/Demo/Shared/DrawingGL/SimpleCanvas.cs @@ -21,7 +21,7 @@ public class SimpleCanvas MyMat4 _flipVerticalView; MyMat4 _orthoAndFlip; - TessTool _tessTool; + Tesselate.TessTool _tessTool; SimpleCurveFlattener _curveFlattener; //--------------------------------- CanvasToShaderSharedResource _shaderRes; @@ -56,7 +56,7 @@ public SimpleCanvas(int view_width, int view_height) //------------ //tools Tesselate.Tesselator tt = new Tesselate.Tesselator(); - _tessTool = new TessTool(tt); + _tessTool = new Tesselate.TessTool(tt); _curveFlattener = new SimpleCurveFlattener(); ClearColor = Color.White; //-------- @@ -133,8 +133,8 @@ public void FillTextRun(TextRun textRun, float x, float y) _fillShader.FillTriangles( - run.tessData, - run.nTessElements, + run._tessData, + run._vertextCount , this.FillColor ); } diff --git a/Demo/Shared/DrawingGL/TessTool.cs b/Demo/Shared/DrawingGL/TessTool.cs index 019f87da..f2b36e1f 100644 --- a/Demo/Shared/DrawingGL/TessTool.cs +++ b/Demo/Shared/DrawingGL/TessTool.cs @@ -11,39 +11,33 @@ using System; using System.Collections.Generic; -using Tesselate; + -namespace DrawingGL +namespace Tesselate { - struct TessVertex2d + public struct TessVertex2d { - public double m_X; - public double m_Y; + public double x; + public double y; public TessVertex2d(double x, double y) { - m_X = x; - m_Y = y; + this.x = x; + this.y = y; } -#if DEBUG - public override string ToString() - { - return this.m_X + "," + this.m_Y; - } -#endif - } + + /// /// listen and handle the event from tesslator /// - class TessListener + class TessListener : Tesselator.ITessListener { internal List _tempVertexList = new List(); internal List _resultIndexList = new List(); int _inputVertexCount; - Tesselator.TriangleListType _triangleListType; - + //Tesselator.TriangleListType _triangleListType; public TessListener() { //empty not use @@ -51,13 +45,17 @@ public TessListener() _tempVertexList.Add(new TessVertex2d(0, 0)); } - void OnBegin(Tesselator.TriangleListType type) + void Tesselator.ITessListener.BeginRead() { } + void Tesselator.ITessListener.Begin(Tesselator.TriangleListType type) { +#if DEBUG + if (type != Tesselator.TriangleListType.Triangles) { } - _triangleListType = type; +#endif + //_triangleListType = type; //what type of triangle list //Console.WriteLine("begin: " + type.ToString()); @@ -81,21 +79,21 @@ void OnBegin(Tesselator.TriangleListType type) //} } - void OnEnd() + void Tesselator.ITessListener.End() { //Assert.IsTrue(GetNextOutputAsString() == "E"); //Console.WriteLine("end"); } - void OnVertex(int index) + void Tesselator.ITessListener.Vertext(int index) { //Assert.IsTrue(GetNextOutputAsString() == "V"); //Assert.AreEqual(GetNextOutputAsInt(), index); if (index < 0) { //use data from temp store*** - //that will be append to the end of result - _resultIndexList.Add((ushort)(_inputVertexCount + (-index))); + //that will be appended to the end of result + _resultIndexList.Add((ushort)(_inputVertexCount + (-index)));//** minus,=> make it positive sign. //resultVertexList.Add(this.tempVertextList[-index]); //Console.WriteLine("temp_v_cb:" + index + ":(" + tempVertextList[-index] + ")"); @@ -108,18 +106,20 @@ void OnVertex(int index) } } - void OnEdgeFlag(bool IsEdge) + + public bool NeedEdgeFlag { get; set; } + void Tesselator.ITessListener.EdgeFlag(bool boundaryEdge_isEdge) { //Console.WriteLine("edge: " + IsEdge); //Assert.IsTrue(GetNextOutputAsString() == "F"); //Assert.AreEqual(GetNextOutputAsBool(), IsEdge); } - void OnCombine(double v0, - double v1, - double v2, - ref Tesselator.CombineParameters combinePars, - out int outData) + void Tesselator.ITessListener.Combine(double v0, + double v1, + double v2, + ref Tesselator.CombineParameters combinePars, + out int outData) { //double error = .001; //Assert.IsTrue(GetNextOutputAsString() == "C"); @@ -147,6 +147,15 @@ void OnCombine(double v0, //---------------------------------------- } + + public bool NeedMash { get; set; } + void Tesselator.ITessListener.Mesh(Mesh mesh) + { + + } + + + /// /// connect to actual Tesselator /// @@ -154,14 +163,18 @@ void OnCombine(double v0, /// public void Connect(Tesselator tesselator, bool setEdgeFlag) { - tesselator.callBegin = OnBegin; - tesselator.callEnd = OnEnd; - tesselator.callVertex = OnVertex; - tesselator.callCombine = OnCombine; - if (setEdgeFlag) - { - tesselator.callEdgeFlag = OnEdgeFlag; - } + + NeedEdgeFlag = setEdgeFlag; + tesselator.SetListener(this); + + //tesselator.callBegin = OnBegin; + //tesselator.callEnd = OnEnd; + //tesselator.callVertex = OnVertex; + //tesselator.callCombine = OnCombine; + //if (setEdgeFlag) + //{ + // tesselator.callEdgeFlag = OnEdgeFlag; + //} } /// /// clear previous results and load a new input vertex list @@ -171,18 +184,17 @@ public void ResetAndLoadInputVertexList(int inputVertexCount) { _inputVertexCount = inputVertexCount; //1. reset - _triangleListType = Tesselator.TriangleListType.LineLoop;//? + //_triangleListType = Tesselator.TriangleListType.LineLoop;//? _tempVertexList.Clear(); _resultIndexList.Clear(); } } - class TessTool + public class TessTool { readonly Tesselator _tess; readonly TessListener _tessListener; - public TessTool() : this(new Tesselator() { WindingRule = Tesselator.WindingRuleType.NonZero }) { } public TessTool(Tesselator tess) { @@ -195,18 +207,19 @@ public Tesselator.WindingRuleType WindingRuleType get => _tess.WindingRule; set => _tess.WindingRule = value; } - public List TessIndexList => _tessListener._resultIndexList; - public List TempVertexList => _tessListener._tempVertexList; + internal List TessIndexList => _tessListener._resultIndexList; + internal List TempVertexList => _tessListener._tempVertexList; public bool TessPolygon(float[] vertex2dCoords, int[] contourEndPoints) { + //internal tess the polygon + int ncoords = vertex2dCoords.Length / 2; _tessListener.ResetAndLoadInputVertexList(ncoords); - if (ncoords == 0) { return false; } - //this support sub contour in the same array of vertex2dCoords + if (ncoords == 0) return false; //----------------------- + //this support sub contour in the same array of vertex2dCoords _tess.BeginPolygon(); - - if (contourEndPoints == null || contourEndPoints.Length == 1) + if (contourEndPoints == null) { //only 1 contour int beginAt = 0; @@ -216,13 +229,15 @@ public bool TessPolygon(float[] vertex2dCoords, int[] contourEndPoints) { _tess.AddVertex( vertex2dCoords[i << 1], //*2 - vertex2dCoords[(i << 1) + 1], 0, i); //*2+1 + vertex2dCoords[(i << 1) + 1], i); //*2+1 } beginAt = thisContourEndAt + 1; _tess.EndContour(); + } else { + //may have more than 1 contour int nContourCount = contourEndPoints.Length; int beginAt = 0; for (int m = 0; m < nContourCount; ++m) @@ -232,25 +247,20 @@ public bool TessPolygon(float[] vertex2dCoords, int[] contourEndPoints) for (int i = beginAt; i < thisContourEndAt; ++i) { _tess.AddVertex( - vertex2dCoords[i << 1], - vertex2dCoords[(i << 1) + 1], - 0, + vertex2dCoords[i << 1], //*2 + vertex2dCoords[(i << 1) + 1], //*2+1 i); - } beginAt = thisContourEndAt + 1; _tess.EndContour(); } } - // - // _tess.EndPolygon(); + //----------------------- return true; } } - - - static class TessToolExtensions + public static class TessToolExtensions { /// /// tess and read result as triangle list vertex array (for GLES draw-array) @@ -260,7 +270,10 @@ static class TessToolExtensions /// /// /// - public static float[] TessAsTriVertexArray(this TessTool tessTool, float[] vertex2dCoords, int[] contourEndPoints, out int vertexCount) + public static float[] TessAsTriVertexArray(this TessTool tessTool, + float[] vertex2dCoords, + int[] contourEndPoints, + out int vertexCount) { if (!tessTool.TessPolygon(vertex2dCoords, contourEndPoints)) { @@ -286,8 +299,8 @@ public static float[] TessAsTriVertexArray(this TessTool tessTool, float[] verte { //extra coord (newly created) TessVertex2d extraVertex = tempVertexList[index - orgVertexCount]; - vtx[n] = (float)extraVertex.m_X; - vtx[n + 1] = (float)extraVertex.m_Y; + vtx[n] = (float)extraVertex.x; + vtx[n + 1] = (float)extraVertex.y; } else { @@ -343,14 +356,995 @@ public static ushort[] TessAsTriIndexArray(this TessTool tessTool, for (int i = vertex2dCoords.Length; i < endAt; ++i) { TessVertex2d v = tempVertexList[p]; - outputCoords[q] = (float)v.m_X; - outputCoords[q + 1] = (float)v.m_Y; + outputCoords[q] = (float)v.x; + outputCoords[q + 1] = (float)v.y; p++; q += 2; } return indexList.ToArray(); } + } + + public class Tesselator + { + // The begin/end calls must be properly nested. We keep track of + // the current state to enforce the ordering. + enum ProcessingState + { + Dormant, InPolygon, InContour + } + // We cache vertex data for single-contour polygons so that we can + // try a quick-and-dirty decomposition first. + const int MAX_CACHE_SIZE = 100; + internal const double MAX_COORD = 1.0e150; + + + public struct CombineParameters + { + public int d0, d1, d2, d3; + public double w0, w1, w2, w3; + } + public enum TriangleListType + { + LineLoop, + Triangles, + TriangleStrip, + TriangleFan + } + + public enum WindingRuleType + { + //see: https://www.glprogramming.com/red/chapter11.html + //http://what-when-how.com/opengl-programming-guide/polygon-tessellation-tessellators-and-quadrics-opengl-programming-part-2/ + + Odd, + NonZero, + Positive, + Negative, + ABS_GEQ_Two, + } + + public interface ITessListener + { + void BeginRead(); + + /*** state needed for rendering callbacks (see render.c) ***/ + void Combine(double c1, double c2, double c3, ref CombineParameters combinePars, out int outData); + void Begin(TriangleListType type); + void Vertext(int data); + void End(); + + // + void EdgeFlag(bool boundaryEdge); + bool NeedEdgeFlag { get; } + // + void Mesh(Mesh mesh); + bool NeedMash { get; } + } + + + + WindingRuleType _windingRule; // rule for determining polygon interior + ProcessingState _processingState; /* what begin/end calls have we seen? */ + HalfEdge _lastHalfEdge; /* lastEdge.Org is the most recent vertex */ + + // + internal Mesh _mesh; /* stores the input contours, and eventually the tessellation itself */ + internal Dictionary _edgeDictionary; /* edge dictionary for sweep line */ + internal MaxFirstList _vertexPriorityQue = new MaxFirstList(); + internal ContourVertex currentSweepVertex; /* current sweep event being processed */ + + + + //---------------- + ITessListener _tessListener; + bool _doEdgeCallback; + bool _doMeshCallback; + + /*** state needed for rendering callbacks (see render.c) ***/ + bool _boundaryOnly; /* Extract contours, not triangles */ + Face _lonelyTriList; + /* list of triangles which could not be rendered as strips or fans */ + + //public delegate void CallBeginDelegate(TriangleListType type); + //public CallBeginDelegate callBegin; + //public delegate void CallEdgeFlagDelegate(bool boundaryEdge); + //public CallEdgeFlagDelegate callEdgeFlag; + //public delegate void CallVertexDelegate(int data); + //public CallVertexDelegate callVertex; + //public delegate void CallEndDelegate(); + //public CallEndDelegate callEnd; + //public delegate void CallMeshDelegate(Mesh mesh); + //public CallMeshDelegate callMesh; + + ////---------------- + //public delegate void CallCombineDelegate( + // double c1, double c2, double c3, ref CombineParameters combinePars, out int outData); + //public CallCombineDelegate callCombine; + //---------------- + + + + // + /*** state needed to cache single-contour polygons for renderCache() */ + + bool _emptyCache; /* empty cache on next vertex() call */ + int _cacheCount; /* number of cached vertices */ + TessVertex2d[] _simpleVertexCache = new TessVertex2d[MAX_CACHE_SIZE]; /* the vertex data */ + int[] _indexCached = new int[MAX_CACHE_SIZE]; + // + public Tesselator() + { + /* Only initialize fields which can be changed by the api. Other fields + * are initialized where they are used. + */ + _processingState = ProcessingState.Dormant; + _windingRule = Tesselator.WindingRuleType.NonZero;//default + _boundaryOnly = false; + } + + ~Tesselator() + { + //TODO: review here... + RequireState(ProcessingState.Dormant); + } + + public void SetListener(ITessListener listener) + { + _tessListener = listener; + _doEdgeCallback = listener.NeedEdgeFlag; + _doMeshCallback = listener.NeedMash; + } + + bool EdgeCallBackSet => _doEdgeCallback; + + public WindingRuleType WindingRule + { + get => _windingRule; + set => _windingRule = value; + } + + public bool BoundaryOnly + { + get => _boundaryOnly; + set => _boundaryOnly = value; + } + + public bool IsWindingInside(int numCrossings) + { + switch (_windingRule) + { + case Tesselator.WindingRuleType.Odd: + return (numCrossings & 1) != 0; + case Tesselator.WindingRuleType.NonZero: + return (numCrossings != 0); + case Tesselator.WindingRuleType.Positive: + return (numCrossings > 0); + case Tesselator.WindingRuleType.Negative: + return (numCrossings < 0); + case Tesselator.WindingRuleType.ABS_GEQ_Two: + return (numCrossings >= 2) || (numCrossings <= -2); + } + throw new Exception(); + } + + void CallBegin(TriangleListType triangleType) + { + _tessListener.Begin(triangleType); + //callBegin?.Invoke(triangleType); + } + void CallVertex(int vertexData) + { + _tessListener.Vertext(vertexData); + //callVertex?.Invoke(vertexData); + } + void CallEdgeFlag(bool edgeState) + { + _tessListener.EdgeFlag(edgeState); + //callEdgeFlag?.Invoke(edgeState); + } + void CallEnd() + { + _tessListener.End(); + } + + internal void CallCombine(double v0, + double v1, double v2, + ref CombineParameters combinePars, + out int outData) + { + outData = 0; + _tessListener.Combine(v0, v1, v2, ref combinePars, out outData); + } + + void GotoState(ProcessingState newProcessingState) + { + while (_processingState != newProcessingState) + { + /* We change the current state one level at a time, to get to + * the desired state. + */ + if (_processingState < newProcessingState) + { + switch (_processingState) + { + case ProcessingState.Dormant: + throw new Exception("MISSING_BEGIN_POLYGON"); + case ProcessingState.InPolygon: + throw new Exception("MISSING_BEGIN_CONTOUR"); + default: + break; + } + } + else + { + switch (_processingState) + { + case ProcessingState.InContour: + throw new Exception("MISSING_END_CONTOUR"); + case ProcessingState.InPolygon: + throw new Exception("MISSING_END_POLYGON"); + default: + break; + } + } + } + } + + void RequireState(ProcessingState state) + { + if (_processingState != state) + { + GotoState(state); + } + } + + public virtual void BeginPolygon() + { + RequireState(ProcessingState.Dormant); + _processingState = ProcessingState.InPolygon; + _cacheCount = 0; + _emptyCache = false; + _mesh = null; + } + + public void BeginContour() + { + RequireState(ProcessingState.InPolygon); + _processingState = ProcessingState.InContour; + _lastHalfEdge = null; + if (_cacheCount > 0) + { + // Just set a flag so we don't get confused by empty contours + _emptyCache = true; + } + } + + bool InnerAddVertex(double x, double y, int data) + { + HalfEdge e; + e = _lastHalfEdge; + if (e == null) + { + /* Make a self-loop (one vertex, one edge). */ + e = _mesh.MakeEdge(); + Mesh.meshSplice(e, e._otherHalfOfThisEdge); + } + else + { + /* Create a new vertex and edge which immediately follow e + * in the ordering around the left face. + */ + if (Mesh.meshSplitEdge(e) == null) + { + return false; + } + e = e._nextEdgeCCWAroundLeftFace; + } + + /* The new vertex is now e.Org. */ + e._originVertex._clientIndex = data; + e._originVertex._C_0 = x; + e._originVertex._C_1 = y; + /* The winding of an edge says how the winding number changes as we + * cross from the edge''s right face to its left face. We add the + * vertices in such an order that a CCW contour will add +1 to + * the winding number of the region inside the contour. + */ + e._winding = 1; + e._otherHalfOfThisEdge._winding = -1; + _lastHalfEdge = e; + return true; + } + + void EmptyCache() + { + TessVertex2d[] vCaches = _simpleVertexCache; + int[] index_caches = _indexCached; + _mesh = new Mesh(); + int count = _cacheCount; + for (int i = 0; i < count; i++) + { + TessVertex2d v = vCaches[i]; + this.InnerAddVertex(v.x, v.y, index_caches[i]); + } + _cacheCount = 0; + _emptyCache = false; + } + + void CacheVertex(double x, double y, double z, int data) + { + TessVertex2d v = new TessVertex2d(); + v.x = x; + v.y = y; + _simpleVertexCache[_cacheCount] = v; + _indexCached[_cacheCount] = data; + ++_cacheCount; + } + void CacheVertex(double x, double y, int data) + { + TessVertex2d v = new TessVertex2d(); + v.x = x; + v.y = y; + _simpleVertexCache[_cacheCount] = v; + _indexCached[_cacheCount] = data; + ++_cacheCount; + } + + public void AddVertex(double x, double y, int data) + { + RequireState(ProcessingState.InContour); + + if (_emptyCache) + { + EmptyCache(); + _lastHalfEdge = null; + } + + //.... + if (x < -MAX_COORD || x > MAX_COORD || + y < -MAX_COORD || y > MAX_COORD) + { + throw new Exception("Your coordinate exceeded -" + MAX_COORD.ToString() + "."); + } + //.... + // + if (_mesh == null) + { + if (_cacheCount < MAX_CACHE_SIZE) + { + CacheVertex(x, y, data); + return; + } + EmptyCache(); + } + + InnerAddVertex(x, y, data); + } + public void AddVertex(double x, double y, double z, int data) + { + RequireState(ProcessingState.InContour); + + if (_emptyCache) + { + EmptyCache(); + _lastHalfEdge = null; + } + + //.... + if (x < -MAX_COORD || x > MAX_COORD || + y < -MAX_COORD || y > MAX_COORD || + z < -MAX_COORD || z > MAX_COORD) + { + throw new Exception("Your coordinate exceeded -" + MAX_COORD.ToString() + "."); + } + //.... + // + if (_mesh == null) + { + if (_cacheCount < MAX_CACHE_SIZE) + { + CacheVertex(x, y, data); + return; + } + EmptyCache(); + } + + InnerAddVertex(x, y, data); + } + + public void EndContour() + { + RequireState(ProcessingState.InContour); + _processingState = ProcessingState.InPolygon; + } + void CheckOrientation() + { + double area = 0; + Face curFace, faceHead = _mesh._faceHead; + ContourVertex vHead = _mesh._vertexHead; + HalfEdge curHalfEdge; + /* When we compute the normal automatically, we choose the orientation + * so that the sum of the signed areas of all contours is non-negative. + */ + for (curFace = faceHead._nextFace; curFace != faceHead; curFace = curFace._nextFace) + { + curHalfEdge = curFace._halfEdgeThisIsLeftFaceOf; + if (curHalfEdge._winding <= 0) + { + continue; + } + + do + { + area += (curHalfEdge._originVertex.x - curHalfEdge.DirectionVertex.x) + * (curHalfEdge._originVertex.y + curHalfEdge.DirectionVertex.y); + curHalfEdge = curHalfEdge._nextEdgeCCWAroundLeftFace; + } while (curHalfEdge != curFace._halfEdgeThisIsLeftFaceOf); + } + + if (area < 0) + { + /* Reverse the orientation by flipping all the t-coordinates */ + for (ContourVertex curVertex = vHead._nextVertex; curVertex != vHead; curVertex = curVertex._nextVertex) + { + curVertex.y = -curVertex.y; + } + } + } + + void ProjectPolygon() + { + ContourVertex v, vHead = _mesh._vertexHead; + // Project the vertices onto the sweep plane + for (v = vHead._nextVertex; v != vHead; v = v._nextVertex) + { + v.x = v._C_0; + v.y = -v._C_1; + } + + CheckOrientation(); + } + + public void EndPolygon() + { + RequireState(ProcessingState.InPolygon); + _processingState = ProcessingState.Dormant; + if (_mesh == null) + { + if (!this.EdgeCallBackSet && !_doMeshCallback) + { + /* Try some special code to make the easy cases go quickly + * (eg. convex polygons). This code does NOT handle multiple contours, + * intersections, edge flags, and of course it does not generate + * an explicit mesh either. + */ + if (RenderCache()) + { + return; + } + } + + EmptyCache(); /* could've used a label*/ + } + + /* Determine the polygon normal and project vertices onto the plane + * of the polygon. + */ + ProjectPolygon(); + /* __gl_computeInterior( this ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by this.windingRule. + * Each interior region is guaranteed to be monotone. + */ + ActiveRegion.ComputeInterior(this); + bool rc = true; + /* If the user wants only the boundary contours, we throw away all edges + * except those which separate the interior from the exterior. + * Otherwise we tessellate all the regions marked "inside". + */ + if (_boundaryOnly) + { + rc = _mesh.SetWindingNumber(1, true); + } + else + { + rc = _mesh.TessellateInterior(); + } + + _mesh.CheckMesh(); + + //if (this.callBegin != null || this.callEnd != null + // || this.callVertex != null || this.callEdgeFlag != null) + //{ + if (_boundaryOnly) + { + RenderBoundary(_mesh); /* output boundary contours */ + } + else + { + RenderMesh(_mesh); /* output strips and fans */ + } + //} + + if (_doMeshCallback) + { + /* Throw away the exterior faces, so that all faces are interior. + * This way the user doesn't have to check the "inside" flag, + * and we don't need to even reveal its existence. It also leaves + * the freedom for an implementation to not generate the exterior + * faces in the first place. + */ + _mesh.DiscardExterior(); + _tessListener.Mesh(_mesh);/* user wants the mesh itself */ + //callMesh(mesh); /* user wants the mesh itself */ + _mesh = null; + return; + } + _mesh = null; + } + + class FaceCount + { + public FaceCount(int _size, HalfEdge _eStart, RenderDelegate _render) + { + size = _size; + eStart = _eStart; + render = _render; + } + + public int size; /* number of triangles used */ + public HalfEdge eStart; /* edge where this primitive starts */ + public delegate void RenderDelegate(Tesselator tess, HalfEdge edge, int data); + event RenderDelegate render; + // routine to render this primitive + + public void CallRender(Tesselator tess, HalfEdge edge, int data) + { + render(tess, edge, data); + } + } + + /************************ Strips and Fans decomposition ******************/ + + /* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle + * fans, strips, and separate triangles. A substantial effort is made + * to use as few rendering primitives as possible (ie. to make the fans + * and strips as large as possible). + * + * The rendering output is provided as callbacks (see the api). + */ + void RenderMesh(Mesh mesh) + { + Face f; + /* Make a list of separate triangles so we can render them all at once */ + _lonelyTriList = null; + for (f = mesh._faceHead._nextFace; f != mesh._faceHead; f = f._nextFace) + { + f._marked = false; + } + for (f = mesh._faceHead._nextFace; f != mesh._faceHead; f = f._nextFace) + { + /* We examine all faces in an arbitrary order. Whenever we find + * an unprocessed face F, we output a group of faces including F + * whose size is maximum. + */ + if (f._isInterior && !f._marked) + { + RenderMaximumFaceGroup(f); + if (!f._marked) + { + throw new System.Exception(); + } + } + } + if (_lonelyTriList != null) + { + RenderLonelyTriangles(_lonelyTriList); + _lonelyTriList = null; + } + } + + + void RenderMaximumFaceGroup(Face fOrig) + { + /* We want to find the largest triangle fan or strip of unmarked faces + * which includes the given face fOrig. There are 3 possible fans + * passing through fOrig (one centered at each vertex), and 3 possible + * strips (one for each CCW permutation of the vertices). Our strategy + * is to try all of these, and take the primitive which uses the most + * triangles (a greedy approach). + */ + HalfEdge e = fOrig._halfEdgeThisIsLeftFaceOf; + FaceCount max = new FaceCount(1, e, new FaceCount.RenderDelegate(RenderTriangle)); + FaceCount newFace; + max.size = 1; + max.eStart = e; + if (!this.EdgeCallBackSet) + { + newFace = MaximumFan(e); if (newFace.size > max.size) { max = newFace; } + newFace = MaximumFan(e._nextEdgeCCWAroundLeftFace); if (newFace.size > max.size) { max = newFace; } + newFace = MaximumFan(e.Lprev); if (newFace.size > max.size) { max = newFace; } + + newFace = MaximumStrip(e); if (newFace.size > max.size) { max = newFace; } + newFace = MaximumStrip(e._nextEdgeCCWAroundLeftFace); if (newFace.size > max.size) { max = newFace; } + newFace = MaximumStrip(e.Lprev); if (newFace.size > max.size) { max = newFace; } + } + + max.CallRender(this, max.eStart, max.size); + } + + FaceCount MaximumFan(HalfEdge eOrig) + { + /* eOrig.Lface is the face we want to render. We want to find the size + * of a maximal fan around eOrig.Org. To do this we just walk around + * the origin vertex as far as possible in both directions. + */ + FaceCount newFace = new FaceCount(0, null, new FaceCount.RenderDelegate(RenderFan)); + Face trail = null; + HalfEdge e; + for (e = eOrig; !e._leftFace.Marked(); e = e._nextEdgeCCWAroundOrigin) + { + Face.AddToTrail(ref e._leftFace, ref trail); + ++newFace.size; + } + for (e = eOrig; !e.rightFace.Marked(); e = e.Oprev) + { + Face f = e.rightFace; + Face.AddToTrail(ref f, ref trail); + e.rightFace = f; + ++newFace.size; + } + newFace.eStart = e; + Face.FreeTrail(ref trail); + return newFace; + } + + + static bool IsEven(int n) + { + return (((n) & 1) == 0); + } + + FaceCount MaximumStrip(HalfEdge eOrig) + { + /* Here we are looking for a maximal strip that contains the vertices + * eOrig.Org, eOrig.Dst, eOrig.Lnext.Dst (in that order or the + * reverse, such that all triangles are oriented CCW). + * + * Again we walk forward and backward as far as possible. However for + * strips there is a twist: to get CCW orientations, there must be + * an *even* number of triangles in the strip on one side of eOrig. + * We walk the strip starting on a side with an even number of triangles; + * if both side have an odd number, we are forced to shorten one side. + */ + FaceCount newFace = new FaceCount(0, null, RenderStrip); + int headSize = 0, tailSize = 0; + Face trail = null; + HalfEdge e, eTail, eHead; + for (e = eOrig; !e._leftFace.Marked(); ++tailSize, e = e._nextEdgeCCWAroundOrigin) + { + Face.AddToTrail(ref e._leftFace, ref trail); + ++tailSize; + e = e.Dprev; + if (e._leftFace.Marked()) break; + Face.AddToTrail(ref e._leftFace, ref trail); + } + eTail = e; + for (e = eOrig; !e.rightFace.Marked(); ++headSize, e = e.Dnext) + { + Face f = e.rightFace; + Face.AddToTrail(ref f, ref trail); + e.rightFace = f; + ++headSize; + e = e.Oprev; + if (e.rightFace.Marked()) break; + f = e.rightFace; + Face.AddToTrail(ref f, ref trail); + e.rightFace = f; + } + eHead = e; + newFace.size = tailSize + headSize; + if (IsEven(tailSize)) + { + newFace.eStart = eTail._otherHalfOfThisEdge; + } + else if (IsEven(headSize)) + { + newFace.eStart = eHead; + } + else + { + /* Both sides have odd length, we must shorten one of them. In fact, + * we must start from eHead to guarantee inclusion of eOrig.Lface. + */ + --newFace.size; + newFace.eStart = eHead._nextEdgeCCWAroundOrigin; + } + + Face.FreeTrail(ref trail); + return newFace; + } + + + void RenderTriangle(Tesselator tess, HalfEdge e, int size) + { + /* Just add the triangle to a triangle list, so we can render all + * the separate triangles at once. + */ + if (size != 1) + { + throw new Exception(); + } + Face.AddToTrail(ref e._leftFace, ref _lonelyTriList); + } + + + void RenderLonelyTriangles(Face f) + { + /* Now we render all the separate triangles which could not be + * grouped into a triangle fan or strip. + */ + HalfEdge e; + bool newState = false; + bool edgeState = false; /* force edge state output for first vertex */ + bool sentFirstEdge = false; + this.CallBegin(Tesselator.TriangleListType.Triangles); + for (; f != null; f = f._trail) + { + /* Loop once for each edge (there will always be 3 edges) */ + + e = f._halfEdgeThisIsLeftFaceOf; + do + { + if (this.EdgeCallBackSet) + { + /* Set the "edge state" to TRUE just before we output the + * first vertex of each edge on the polygon boundary. + */ + newState = !e.rightFace._isInterior; + if (edgeState != newState || !sentFirstEdge) + { + sentFirstEdge = true; + edgeState = newState; + this.CallEdgeFlag(edgeState); + } + } + + this.CallVertex(e._originVertex._clientIndex); + e = e._nextEdgeCCWAroundLeftFace; + } while (e != f._halfEdgeThisIsLeftFaceOf); + } + + this.CallEnd(); + } + + + static void RenderFan(Tesselator tess, HalfEdge e, int size) + { + /* Render as many CCW triangles as possible in a fan starting from + * edge "e". The fan *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + tess.CallBegin(Tesselator.TriangleListType.TriangleFan); + tess.CallVertex(e._originVertex._clientIndex); + tess.CallVertex(e.DirectionVertex._clientIndex); + while (!e._leftFace.Marked()) + { + e._leftFace._marked = true; + --size; + e = e._nextEdgeCCWAroundOrigin; + tess.CallVertex(e.DirectionVertex._clientIndex); + } + + if (size != 0) + { + throw new Exception(); + } + tess.CallEnd(); + } + + + static void RenderStrip(Tesselator tess, HalfEdge halfEdge, int size) + { + /* Render as many CCW triangles as possible in a strip starting from + * edge "e". The strip *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + tess.CallBegin(Tesselator.TriangleListType.TriangleStrip); + tess.CallVertex(halfEdge._originVertex._clientIndex); + tess.CallVertex(halfEdge.DirectionVertex._clientIndex); + while (!halfEdge._leftFace.Marked()) + { + halfEdge._leftFace._marked = true; + --size; + halfEdge = halfEdge.Dprev; + tess.CallVertex(halfEdge._originVertex._clientIndex); + if (halfEdge._leftFace.Marked()) break; + halfEdge._leftFace._marked = true; + --size; + halfEdge = halfEdge._nextEdgeCCWAroundOrigin; + tess.CallVertex(halfEdge.DirectionVertex._clientIndex); + } + + if (size != 0) + { + throw new Exception(); + } + tess.CallEnd(); + } + + + /************************ Boundary contour decomposition ******************/ + + /* Takes a mesh, and outputs one + * contour for each face marked "inside". The rendering output is + * provided as callbacks. + */ + void RenderBoundary(Mesh mesh) + { + for (Face curFace = mesh._faceHead._nextFace; curFace != mesh._faceHead; curFace = curFace._nextFace) + { + if (curFace._isInterior) + { + this.CallBegin(Tesselator.TriangleListType.LineLoop); + HalfEdge curHalfEdge = curFace._halfEdgeThisIsLeftFaceOf; + do + { + this.CallVertex(curHalfEdge._originVertex._clientIndex); + curHalfEdge = curHalfEdge._nextEdgeCCWAroundLeftFace; + } while (curHalfEdge != curFace._halfEdgeThisIsLeftFaceOf); + this.CallEnd(); + } + } + } + + + /************************ Quick-and-dirty decomposition ******************/ + + const int SIGN_INCONSISTENT = 2; + int ComputeNormal(ref double nx, ref double ny, ref double nz) + /* + * Check that each triangle in the fan from v0 has a + * consistent orientation with respect to norm3[]. If triangles are + * consistently oriented CCW, return 1; if CW, return -1; if all triangles + * are degenerate return 0; otherwise (no consistent orientation) return + * SIGN_INCONSISTENT. + */ + { + var vCache = _simpleVertexCache; + TessVertex2d v0 = vCache[0]; + int vcIndex; + double dot, xc, yc, xp, yp; + double n0; + double n1; + double n2; + int sign = 0; + /* Find the polygon normal. It is important to get a reasonable + * normal even when the polygon is self-intersecting (eg. a bowtie). + * Otherwise, the computed normal could be very tiny, but perpendicular + * to the true plane of the polygon due to numerical noise. Then all + * the triangles would appear to be degenerate and we would incorrectly + * decompose the polygon as a fan (or simply not render it at all). + * + * We use a sum-of-triangles normal algorithm rather than the more + * efficient sum-of-trapezoids method (used in CheckOrientation() + * in normal.c). This lets us explicitly reverse the signed area + * of some triangles to get a reasonable normal in the self-intersecting + * case. + */ + vcIndex = 1; + var v = vCache[vcIndex]; + xc = v.x - v0.x; + yc = v.y - v0.y; + int c_count = _cacheCount; + while (++vcIndex < c_count) + { + xp = xc; yp = yc; + v = vCache[vcIndex]; + xc = v.x - v0.x; + yc = v.y - v0.y; + /* Compute (vp - v0) cross (vc - v0) */ + n0 = 0; + n1 = 0; + n2 = xp * yc - yp * xc; + dot = n0 * nx + n1 * ny + n2 * nz; + if (dot != 0) + { + /* Check the new orientation for consistency with previous triangles */ + if (dot > 0) + { + if (sign < 0) + { + return SIGN_INCONSISTENT; + } + sign = 1; + } + else + { + if (sign > 0) + { + return SIGN_INCONSISTENT; + } + sign = -1; + } + } + } + + return sign; + } + + /* Takes a single contour and tries to render it + * as a triangle fan. This handles convex polygons, as well as some + * non-convex polygons if we get lucky. + * + * Returns TRUE if the polygon was successfully rendered. The rendering + * output is provided as callbacks (see the api). + */ + bool RenderCache() + { + int sign; + if (_cacheCount < 3) + { + /* Degenerate contour -- no output */ + return true; + } + double normal_x = 0; + double normal_y = 0; + double normal_z = 1; + sign = this.ComputeNormal(ref normal_x, ref normal_y, ref normal_z); + if (sign == SIGN_INCONSISTENT) + { + // Fan triangles did not have a consistent orientation + return false; + } + if (sign == 0) + { + // All triangles were degenerate + return true; + } + + /* Make sure we do the right thing for each winding rule */ + switch (_windingRule) + { + case Tesselator.WindingRuleType.Odd: + case Tesselator.WindingRuleType.NonZero: + break; + case Tesselator.WindingRuleType.Positive: + if (sign < 0) return true; + break; + case Tesselator.WindingRuleType.Negative: + if (sign > 0) return true; + break; + case Tesselator.WindingRuleType.ABS_GEQ_Two: + return true; + } + + this.CallBegin(this.BoundaryOnly ? Tesselator.TriangleListType.LineLoop + : (_cacheCount > 3) ? Tesselator.TriangleListType.TriangleFan + : Tesselator.TriangleListType.Triangles); + this.CallVertex(_indexCached[0]); + if (sign > 0) + { + int c_count = _cacheCount; + for (int vcIndex = 1; vcIndex < c_count; ++vcIndex) + { + this.CallVertex(_indexCached[vcIndex]); + } + } + else + { + for (int vcIndex = _cacheCount - 1; vcIndex > 0; --vcIndex) + { + this.CallVertex(_indexCached[vcIndex]); + } + } + this.CallEnd(); + return true; + } } + } \ No newline at end of file diff --git a/Demo/Shared/Tesselate/ActiveRegion.cs b/Demo/Shared/Tesselate/ActiveRegion.cs index f7602bb8..8d7eac01 100644 --- a/Demo/Shared/Tesselate/ActiveRegion.cs +++ b/Demo/Shared/Tesselate/ActiveRegion.cs @@ -37,6 +37,8 @@ // C# Port port by: Lars Brubaker // larsbrubaker@gmail.com // Copyright (C) 2007 +// 2017, SGI (same license as above), WinterDev + ** */ @@ -46,21 +48,23 @@ * sweep line crosses each vertex, we update the affected regions. */ + + using System; namespace Tesselate { public class ActiveRegion { - public HalfEdge upperHalfEdge; /* upper edge, directed right to left */ - Dictionary.Node upperHalfEdgeDictNode; /* dictionary node corresponding to eUp */ - int windingNumber; /* used to determine which regions are + HalfEdge _upperHalfEdge; /* upper edge, directed right to left */ + Dictionary.Node _upperHalfEdgeDictNode; /* dictionary node corresponding to eUp */ + int _windingNumber; /* used to determine which regions are * inside the polygon */ - bool inside; /* is this region inside the polygon? */ - bool sentinel; /* marks fake edges at t = +/-infinity */ - bool dirty; /* marks regions where the upper or lower + bool _inside; /* is this region inside the polygon? */ + bool _sentinel; /* marks fake edges at t = +/-infinity */ + bool _dirty; /* marks regions where the upper or lower * edge has changed, but we haven't checked * whether they intersect yet */ - bool fixUpperEdge; /* marks temporary edges introduced when + bool _fixUpperEdge; /* marks temporary edges introduced when * we process a "right vertex" (one without * any edges leaving to the right) */ public ActiveRegion() @@ -93,14 +97,14 @@ public static int ComputeInterior(Tesselator tess) RemoveDegenerateEdges(tess); InitPriorityQue(tess); InitEdgeDict(tess); - while (!tess.vertexPriorityQue.IsEmpty) + while (!tess._vertexPriorityQue.IsEmpty) { - vertex = tess.vertexPriorityQue.DeleteMin(); + vertex = tess._vertexPriorityQue.DeleteMin(); for (; ; ) { - if (!tess.vertexPriorityQue.IsEmpty) + if (!tess._vertexPriorityQue.IsEmpty) { - vertexNext = tess.vertexPriorityQue.FindMin(); /* __gl_pqSortMinimum */ + vertexNext = tess._vertexPriorityQue.FindMin(); /* __gl_pqSortMinimum */ } else { @@ -121,8 +125,8 @@ public static int ComputeInterior(Tesselator tess) * gap between them. This kind of error is especially obvious * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY). */ - vertexNext = tess.vertexPriorityQue.DeleteMin(); /* __gl_pqSortExtractMin*/ - SpliceMergeVertices(tess, vertex.edgeThisIsOriginOf, vertexNext.edgeThisIsOriginOf); + vertexNext = tess._vertexPriorityQue.DeleteMin(); /* __gl_pqSortExtractMin*/ + SpliceMergeVertices(tess, vertex._edgeThisIsOriginOf, vertexNext._edgeThisIsOriginOf); } SweepEvent(tess, vertex); } @@ -130,16 +134,16 @@ public static int ComputeInterior(Tesselator tess) /* Set tess.currentSweepVertex for debugging purposes */ /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ - tess.currentSweepVertex = tess.edgeDictionary.GetMinNode().Key.upperHalfEdge.originVertex; + tess.currentSweepVertex = tess._edgeDictionary.GetMinNode().Key._upperHalfEdge._originVertex; DoneEdgeDict(tess); DonePriorityQ(tess); - if (!RemoveDegenerateFaces(tess.mesh)) + if (!RemoveDegenerateFaces(tess._mesh)) { return 0; } - tess.mesh.CheckMesh(); + tess._mesh.CheckMesh(); return 1; } @@ -184,8 +188,8 @@ public static int ComputeInterior(Tesselator tess) static void AddWinding(HalfEdge eDst, HalfEdge eSrc) { - eDst.winding += eSrc.winding; - eDst.otherHalfOfThisEdge.winding += eSrc.otherHalfOfThisEdge.winding; + eDst._winding += eSrc._winding; + eDst._otherHalfOfThisEdge._winding += eSrc._otherHalfOfThisEdge._winding; } public static bool EdgeLeq(Tesselator tess, ActiveRegion reg1, ActiveRegion reg2) @@ -204,49 +208,49 @@ public static bool EdgeLeq(Tesselator tess, ActiveRegion reg1, ActiveRegion reg2 ContourVertex currentSweepVertex = tess.currentSweepVertex; HalfEdge e1, e2; double t1, t2; - e1 = reg1.upperHalfEdge; - e2 = reg2.upperHalfEdge; - if (e1.directionVertex == currentSweepVertex) + e1 = reg1._upperHalfEdge; + e2 = reg2._upperHalfEdge; + if (e1.DirectionVertex == currentSweepVertex) { - if (e2.directionVertex == currentSweepVertex) + if (e2.DirectionVertex == currentSweepVertex) { /* Two edges right of the sweep line which meet at the sweep currentSweepVertex. * Sort them by slope. */ - if (e1.originVertex.VertLeq(e2.originVertex)) + if (e1._originVertex.VertLeq(e2._originVertex)) { - return ContourVertex.EdgeSign(e2.directionVertex, e1.originVertex, e2.originVertex) <= 0; + return ContourVertex.EdgeSign(e2.DirectionVertex, e1._originVertex, e2._originVertex) <= 0; } - return ContourVertex.EdgeSign(e1.directionVertex, e2.originVertex, e1.originVertex) >= 0; + return ContourVertex.EdgeSign(e1.DirectionVertex, e2._originVertex, e1._originVertex) >= 0; } - return ContourVertex.EdgeSign(e2.directionVertex, currentSweepVertex, e2.originVertex) <= 0; + return ContourVertex.EdgeSign(e2.DirectionVertex, currentSweepVertex, e2._originVertex) <= 0; } - if (e2.directionVertex == currentSweepVertex) + if (e2.DirectionVertex == currentSweepVertex) { - return ContourVertex.EdgeSign(e1.directionVertex, currentSweepVertex, e1.originVertex) >= 0; + return ContourVertex.EdgeSign(e1.DirectionVertex, currentSweepVertex, e1._originVertex) >= 0; } /* General case - compute signed distance *from* e1, e2 to currentSweepVertex */ - t1 = ContourVertex.EdgeEval(e1.directionVertex, currentSweepVertex, e1.originVertex); - t2 = ContourVertex.EdgeEval(e2.directionVertex, currentSweepVertex, e2.originVertex); + t1 = ContourVertex.EdgeEval(e1.DirectionVertex, currentSweepVertex, e1._originVertex); + t2 = ContourVertex.EdgeEval(e2.DirectionVertex, currentSweepVertex, e2._originVertex); return (t1 >= t2); } static void DeleteRegion(ActiveRegion reg) { - if (reg.fixUpperEdge) + if (reg._fixUpperEdge) { /* It was created with zero winding number, so it better be * deleted with zero winding number (ie. it better not get merged * with a real edge). */ - if (reg.upperHalfEdge.winding != 0) + if (reg._upperHalfEdge._winding != 0) { throw new System.Exception(); } } - reg.upperHalfEdge.regionThisIsUpperEdgeOf = null; - reg.upperHalfEdgeDictNode.Delete(); + reg._upperHalfEdge._regionThisIsUpperEdgeOf = null; + reg._upperHalfEdgeDictNode.Delete(); reg = null; } @@ -256,42 +260,42 @@ static bool FixUpperEdge(ActiveRegion reg, HalfEdge newEdge) * Replace an upper edge which needs fixing (see ConnectRightVertex). */ { - if (!reg.fixUpperEdge) + if (!reg._fixUpperEdge) { throw new Exception(); } - Mesh.DeleteHalfEdge(reg.upperHalfEdge); - reg.fixUpperEdge = false; - reg.upperHalfEdge = newEdge; - newEdge.regionThisIsUpperEdgeOf = reg; + Mesh.DeleteHalfEdge(reg._upperHalfEdge); + reg._fixUpperEdge = false; + reg._upperHalfEdge = newEdge; + newEdge._regionThisIsUpperEdgeOf = reg; return true; } ActiveRegion RegionAbove() { - return this.upperHalfEdgeDictNode.next.Key; + return _upperHalfEdgeDictNode.next.Key; } static ActiveRegion RegionBelow(ActiveRegion r) { - return r.upperHalfEdgeDictNode.prev.Key; + return r._upperHalfEdgeDictNode.prev.Key; } static ActiveRegion TopLeftRegion(ActiveRegion reg) { - ContourVertex org = reg.upperHalfEdge.originVertex; + ContourVertex org = reg._upperHalfEdge._originVertex; HalfEdge e; /* Find the region above the uppermost edge with the same origin */ do { reg = reg.RegionAbove(); - } while (reg.upperHalfEdge.originVertex == org); + } while (reg._upperHalfEdge._originVertex == org); /* If the edge above was a temporary edge introduced by ConnectRightVertex, * now is the time to fix it. */ - if (reg.fixUpperEdge) + if (reg._fixUpperEdge) { - e = Mesh.meshConnect(RegionBelow(reg).upperHalfEdge.otherHalfOfThisEdge, reg.upperHalfEdge.nextEdgeCCWAroundLeftFace); + e = Mesh.meshConnect(RegionBelow(reg)._upperHalfEdge._otherHalfOfThisEdge, reg._upperHalfEdge._nextEdgeCCWAroundLeftFace); if (e == null) { return null; @@ -307,12 +311,12 @@ static ActiveRegion TopLeftRegion(ActiveRegion reg) static ActiveRegion TopRightRegion(ActiveRegion reg) { - ContourVertex dst = reg.upperHalfEdge.directionVertex; + ContourVertex dst = reg._upperHalfEdge.DirectionVertex; /* Find the region above the uppermost edge with the same destination */ do { reg = reg.RegionAbove(); - } while (reg.upperHalfEdge.directionVertex == dst); + } while (reg._upperHalfEdge.DirectionVertex == dst); return reg; } @@ -327,20 +331,20 @@ static ActiveRegion AddRegionBelow(Tesselator tess, */ { ActiveRegion regNew = new ActiveRegion(); - regNew.upperHalfEdge = eNewUp; + regNew._upperHalfEdge = eNewUp; /* __gl_dictListInsertBefore */ - regNew.upperHalfEdgeDictNode = tess.edgeDictionary.InsertBefore(regAbove.upperHalfEdgeDictNode, regNew); - regNew.fixUpperEdge = false; - regNew.sentinel = false; - regNew.dirty = false; - eNewUp.regionThisIsUpperEdgeOf = regNew; + regNew._upperHalfEdgeDictNode = tess._edgeDictionary.InsertBefore(regAbove._upperHalfEdgeDictNode, regNew); + regNew._fixUpperEdge = false; + regNew._sentinel = false; + regNew._dirty = false; + eNewUp._regionThisIsUpperEdgeOf = regNew; return regNew; } static void ComputeWinding(Tesselator tess, ActiveRegion reg) { - reg.windingNumber = reg.RegionAbove().windingNumber + reg.upperHalfEdge.winding; - reg.inside = tess.IsWindingInside(reg.windingNumber); + reg._windingNumber = reg.RegionAbove()._windingNumber + reg._upperHalfEdge._winding; + reg._inside = tess.IsWindingInside(reg._windingNumber); } @@ -353,10 +357,10 @@ static void FinishRegion(Tesselator tess, ActiveRegion reg) * changing, this face may not have even existed until now). */ { - HalfEdge e = reg.upperHalfEdge; - Face f = e.leftFace; - f.isInterior = reg.inside; - f.halfEdgeThisIsLeftFaceOf = e; // optimization for mesh.TessellateMonoRegion() + HalfEdge e = reg._upperHalfEdge; + Face f = e._leftFace; + f._isInterior = reg._inside; + f._halfEdgeThisIsLeftFaceOf = e; // optimization for mesh.TessellateMonoRegion() DeleteRegion(reg); } @@ -379,15 +383,15 @@ static HalfEdge FinishLeftRegions(Tesselator tess, ActiveRegion reg, regPrev; HalfEdge e, ePrev; regPrev = regFirst; - ePrev = regFirst.upperHalfEdge; + ePrev = regFirst._upperHalfEdge; while (regPrev != regLast) { - regPrev.fixUpperEdge = false; /* placement was OK */ + regPrev._fixUpperEdge = false; /* placement was OK */ reg = RegionBelow(regPrev); - e = reg.upperHalfEdge; - if (e.originVertex != ePrev.originVertex) + e = reg._upperHalfEdge; + if (e._originVertex != ePrev._originVertex) { - if (!reg.fixUpperEdge) + if (!reg._fixUpperEdge) { /* Remove the last left-going edge. Even though there are no further * edges in the dictionary with this origin, there may be further @@ -401,18 +405,18 @@ static HalfEdge FinishLeftRegions(Tesselator tess, /* If the edge below was a temporary edge introduced by * ConnectRightVertex, now is the time to fix it. */ - e = Mesh.meshConnect(ePrev.Lprev, e.otherHalfOfThisEdge); + e = Mesh.meshConnect(ePrev.Lprev, e._otherHalfOfThisEdge); FixUpperEdge(reg, e); } /* Relink edges so that ePrev.Onext == e */ - if (ePrev.nextEdgeCCWAroundOrigin != e) + if (ePrev._nextEdgeCCWAroundOrigin != e) { Mesh.meshSplice(e.Oprev, e); Mesh.meshSplice(ePrev, e); } FinishRegion(tess, regPrev); /* may change reg.eUp */ - ePrev = reg.upperHalfEdge; + ePrev = reg._upperHalfEdge; regPrev = reg; } return ePrev; @@ -440,41 +444,43 @@ static void AddRightEdges(Tesselator tess, ActiveRegion regUp, e = eFirst; do { - if (!e.originVertex.VertLeq(e.directionVertex)) + if (!e._originVertex.VertLeq(e.DirectionVertex)) { throw new Exception(); } - AddRegionBelow(tess, regUp, e.otherHalfOfThisEdge); - e = e.nextEdgeCCWAroundOrigin; + AddRegionBelow(tess, regUp, e._otherHalfOfThisEdge); + e = e._nextEdgeCCWAroundOrigin; } while (e != eLast); /* Walk *all* right-going edges from e.Org, in the dictionary order, - * updating the winding numbers of each region, and re-linking the mesh + * updating the winding numbers of ea + * + * ch region, and re-linking the mesh * edges to match the dictionary ordering (if necessary). */ if (eTopLeft == null) { - eTopLeft = RegionBelow(regUp).upperHalfEdge.Rprev; + eTopLeft = RegionBelow(regUp)._upperHalfEdge.Rprev; } regPrev = regUp; ePrev = eTopLeft; for (; ; ) { reg = RegionBelow(regPrev); - e = reg.upperHalfEdge.otherHalfOfThisEdge; - if (e.originVertex != ePrev.originVertex) break; - if (e.nextEdgeCCWAroundOrigin != ePrev) + e = reg._upperHalfEdge._otherHalfOfThisEdge; + if (e._originVertex != ePrev._originVertex) break; + if (e._nextEdgeCCWAroundOrigin != ePrev) { /* Unlink e from its current position, and relink below ePrev */ Mesh.meshSplice(e.Oprev, e); Mesh.meshSplice(ePrev.Oprev, e); } /* Compute the winding number and "inside" flag for the new regions */ - reg.windingNumber = regPrev.windingNumber - e.winding; - reg.inside = tess.IsWindingInside(reg.windingNumber); + reg._windingNumber = regPrev._windingNumber - e._winding; + reg._inside = tess.IsWindingInside(reg._windingNumber); /* Check for two outgoing edges with same slope -- process these * before any intersection tests (see example in __gl_computeInterior). */ - regPrev.dirty = true; + regPrev._dirty = true; if (!firstTime && CheckForRightSplice(tess, regPrev)) { AddWinding(e, ePrev); @@ -485,8 +491,8 @@ static void AddRightEdges(Tesselator tess, ActiveRegion regUp, regPrev = reg; ePrev = e; } - regPrev.dirty = true; - if (regPrev.windingNumber - e.winding != reg.windingNumber) + regPrev._dirty = true; + if (regPrev._windingNumber - e._winding != reg._windingNumber) { throw new Exception(); } @@ -502,16 +508,17 @@ static void AddRightEdges(Tesselator tess, ActiveRegion regUp, static void CallCombine(Tesselator tess, ContourVertex intersectionVertex, ref Tesselator.CombineParameters combinePars, 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, ref combinePars, out intersectionVertex.clientIndex); - if (intersectionVertex.clientIndex == 0) + double c0 = intersectionVertex._C_0; + double c1 = intersectionVertex._C_1; + double c2 = intersectionVertex._C_2; + intersectionVertex._clientIndex = 0; + + tess.CallCombine(c0, c1, c2, ref combinePars, out intersectionVertex._clientIndex); + if (intersectionVertex._clientIndex == 0) { if (!needed) { - intersectionVertex.clientIndex = combinePars.d0; + intersectionVertex._clientIndex = combinePars.d0; } else { @@ -530,12 +537,17 @@ static void SpliceMergeVertices(Tesselator tess, HalfEdge e1, HalfEdge e2) * e1.Org is kept, while e2.Org is discarded. */ { + //int[] data4 = new int[4]; + //double[] weights4 = new double[] { 0.5f, 0.5f, 0, 0 }; + //data4[0] = e1.originVertex.clientIndex; + //data4[1] = e2.originVertex.clientIndex; + var combinePars = new Tesselator.CombineParameters(); combinePars.w0 = 0.5f; combinePars.w1 = 0.5f; - combinePars.d0 = e1.originVertex.clientIndex; - combinePars.d1 = e2.originVertex.clientIndex; + combinePars.d0 = e1._originVertex._clientIndex; + combinePars.d1 = e2._originVertex._clientIndex; - CallCombine(tess, e1.originVertex, ref combinePars, false); + CallCombine(tess, e1._originVertex, ref combinePars, false); Mesh.meshSplice(e1, e2); } @@ -558,9 +570,9 @@ static void VertexWeights(ContourVertex isect, ContourVertex org, ContourVertex 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; + 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; } @@ -573,14 +585,20 @@ static void GetIntersectData(Tesselator tess, ContourVertex isect, * 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; var combinePars = new Tesselator.CombineParameters(); - combinePars.d0 = orgUp.clientIndex; - combinePars.d1 = dstUp.clientIndex; - combinePars.d2 = orgLo.clientIndex; - combinePars.d3 = dstLo.clientIndex; + combinePars.d0 = orgUp._clientIndex; + combinePars.d1 = dstUp._clientIndex; + combinePars.d2 = orgLo._clientIndex; + combinePars.d3 = dstLo._clientIndex; - isect.C_0 = isect.C_1 = isect.C_2 = 0; + isect._C_0 = isect._C_1 = isect._C_2 = 0; VertexWeights(isect, orgUp, dstUp, out combinePars.w0, out combinePars.w1); VertexWeights(isect, orgLo, dstLo, out combinePars.w2, out combinePars.w3); CallCombine(tess, isect, ref combinePars, true); @@ -614,41 +632,41 @@ static bool CheckForRightSplice(Tesselator tess, ActiveRegion regUp) */ { ActiveRegion regLo = RegionBelow(regUp); - HalfEdge eUp = regUp.upperHalfEdge; - HalfEdge eLo = regLo.upperHalfEdge; - if (eUp.originVertex.VertLeq(eLo.originVertex)) + HalfEdge eUp = regUp._upperHalfEdge; + HalfEdge eLo = regLo._upperHalfEdge; + if (eUp._originVertex.VertLeq(eLo._originVertex)) { - if (ContourVertex.EdgeSign(eLo.directionVertex, eUp.originVertex, eLo.originVertex) > 0) + if (ContourVertex.EdgeSign(eLo.DirectionVertex, eUp._originVertex, eLo._originVertex) > 0) { return false; } /* eUp.Org appears to be below eLo */ - if (!eUp.originVertex.VertEq(eLo.originVertex)) + if (!eUp._originVertex.VertEq(eLo._originVertex)) { /* Splice eUp.Org into eLo */ - Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); + Mesh.meshSplitEdge(eLo._otherHalfOfThisEdge); Mesh.meshSplice(eUp, eLo.Oprev); - regUp.dirty = regLo.dirty = true; + regUp._dirty = regLo._dirty = true; } - else if (eUp.originVertex != eLo.originVertex) + else if (eUp._originVertex != eLo._originVertex) { /* merge the two vertices, discarding eUp.Org */ - tess.vertexPriorityQue.Delete(eUp.originVertex.priorityQueueHandle); + tess._vertexPriorityQue.Delete(eUp._originVertex._priorityQueueHandle); //pqDelete(tess.pq, eUp.Org.pqHandle); /* __gl_pqSortDelete */ SpliceMergeVertices(tess, eLo.Oprev, eUp); } } else { - if (ContourVertex.EdgeSign(eUp.directionVertex, eLo.originVertex, eUp.originVertex) < 0) + if (ContourVertex.EdgeSign(eUp.DirectionVertex, eLo._originVertex, eUp._originVertex) < 0) { return false; } /* eLo.Org appears to be above eUp, so splice eLo.Org into eUp */ - regUp.RegionAbove().dirty = regUp.dirty = true; - Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); + regUp.RegionAbove()._dirty = regUp._dirty = true; + Mesh.meshSplitEdge(eUp._otherHalfOfThisEdge); Mesh.meshSplice(eLo.Oprev, eUp); } return true; @@ -675,35 +693,35 @@ static bool CheckForLeftSplice(Tesselator tess, ActiveRegion regUp) */ { ActiveRegion regLo = RegionBelow(regUp); - HalfEdge eUp = regUp.upperHalfEdge; - HalfEdge eLo = regLo.upperHalfEdge; + HalfEdge eUp = regUp._upperHalfEdge; + HalfEdge eLo = regLo._upperHalfEdge; HalfEdge e; - if (eUp.directionVertex.VertEq(eLo.directionVertex)) + if (eUp.DirectionVertex.VertEq(eLo.DirectionVertex)) { throw new Exception(); } - if (eUp.directionVertex.VertLeq(eLo.directionVertex)) + if (eUp.DirectionVertex.VertLeq(eLo.DirectionVertex)) { - if (ContourVertex.EdgeSign(eUp.directionVertex, eLo.directionVertex, eUp.originVertex) < 0) + if (ContourVertex.EdgeSign(eUp.DirectionVertex, eLo.DirectionVertex, eUp._originVertex) < 0) { return false; } /* eLo.Dst is above eUp, so splice eLo.Dst into eUp */ - regUp.RegionAbove().dirty = regUp.dirty = true; + regUp.RegionAbove()._dirty = regUp._dirty = true; e = Mesh.meshSplitEdge(eUp); - Mesh.meshSplice(eLo.otherHalfOfThisEdge, e); - e.leftFace.isInterior = regUp.inside; + Mesh.meshSplice(eLo._otherHalfOfThisEdge, e); + e._leftFace._isInterior = regUp._inside; } else { - if (ContourVertex.EdgeSign(eLo.directionVertex, eUp.directionVertex, eLo.originVertex) > 0) return false; + if (ContourVertex.EdgeSign(eLo.DirectionVertex, eUp.DirectionVertex, eLo._originVertex) > 0) return false; /* eUp.Dst is below eLo, so splice eUp.Dst into eLo */ - regUp.dirty = regLo.dirty = true; + regUp._dirty = regLo._dirty = true; e = Mesh.meshSplitEdge(eLo); - Mesh.meshSplice(eUp.nextEdgeCCWAroundLeftFace, eLo.otherHalfOfThisEdge); - e.rightFace.isInterior = regUp.inside; + Mesh.meshSplice(eUp._nextEdgeCCWAroundLeftFace, eLo._otherHalfOfThisEdge); + e.rightFace._isInterior = regUp._inside; } return true; } @@ -882,12 +900,12 @@ static bool CheckForIntersect(Tesselator tess, ActiveRegion regUp) */ { 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; + 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; @@ -908,7 +926,7 @@ static bool CheckForIntersect(Tesselator tess, ActiveRegion regUp) { throw new Exception(); } - if (regUp.fixUpperEdge || regLo.fixUpperEdge) + if (regUp._fixUpperEdge || regLo._fixUpperEdge) { throw new Exception(); } @@ -1002,10 +1020,10 @@ static bool CheckForIntersect(Tesselator tess, ActiveRegion regUp) if (dstLo == tess.currentSweepVertex) { /* Splice dstLo into eUp, and process the new region(s) */ - Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); - Mesh.meshSplice(eLo.otherHalfOfThisEdge, eUp); + Mesh.meshSplitEdge(eUp._otherHalfOfThisEdge); + Mesh.meshSplice(eLo._otherHalfOfThisEdge, eUp); regUp = TopLeftRegion(regUp); - eUp = RegionBelow(regUp).upperHalfEdge; + eUp = RegionBelow(regUp)._upperHalfEdge; FinishLeftRegions(tess, RegionBelow(regUp), regLo); AddRightEdges(tess, regUp, eUp.Oprev, eUp, eUp, true); return true; @@ -1013,14 +1031,14 @@ static bool CheckForIntersect(Tesselator tess, ActiveRegion regUp) if (dstUp == tess.currentSweepVertex) { /* Splice dstUp into eLo, and process the new region(s) */ - Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); - Mesh.meshSplice(eUp.nextEdgeCCWAroundLeftFace, eLo.Oprev); + Mesh.meshSplitEdge(eLo._otherHalfOfThisEdge); + Mesh.meshSplice(eUp._nextEdgeCCWAroundLeftFace, eLo.Oprev); regLo = regUp; regUp = TopRightRegion(regUp); - e = RegionBelow(regUp).upperHalfEdge.Rprev; - regLo.upperHalfEdge = eLo.Oprev; + e = RegionBelow(regUp)._upperHalfEdge.Rprev; + regLo._upperHalfEdge = eLo.Oprev; eLo = FinishLeftRegions(tess, regLo, null); - AddRightEdges(tess, regUp, eLo.nextEdgeCCWAroundOrigin, eUp.Rprev, e, true); + AddRightEdges(tess, regUp, eLo._nextEdgeCCWAroundOrigin, eUp.Rprev, e, true); return true; } @@ -1030,17 +1048,17 @@ static bool CheckForIntersect(Tesselator tess, ActiveRegion regUp) */ 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; + 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; + 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; @@ -1054,14 +1072,14 @@ static bool CheckForIntersect(Tesselator tess, ActiveRegion regUp) * 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.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; + 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; } @@ -1080,25 +1098,25 @@ static void WalkDirtyRegions(Tesselator tess, ActiveRegion regUp) for (; ; ) { /* Find the lowest dirty region (we walk from the bottom up). */ - while (regLo.dirty) + while (regLo._dirty) { regUp = regLo; regLo = RegionBelow(regLo); } - if (!regUp.dirty) + if (!regUp._dirty) { regLo = regUp; regUp = regUp.RegionAbove(); - if (regUp == null || !regUp.dirty) + if (regUp == null || !regUp._dirty) { /* We've walked all the dirty regions */ return; } } - regUp.dirty = false; - eUp = regUp.upperHalfEdge; - eLo = regLo.upperHalfEdge; - if (eUp.directionVertex != eLo.directionVertex) + regUp._dirty = false; + eUp = regUp._upperHalfEdge; + eLo = regLo._upperHalfEdge; + if (eUp.DirectionVertex != eLo.DirectionVertex) { /* Check that the edge ordering is obeyed at the Dst vertices. */ if (CheckForLeftSplice(tess, regUp)) @@ -1107,27 +1125,27 @@ static void WalkDirtyRegions(Tesselator tess, ActiveRegion regUp) * we no longer need it (since these edges are needed only for * vertices which otherwise have no right-going edges). */ - if (regLo.fixUpperEdge) + if (regLo._fixUpperEdge) { DeleteRegion(regLo); Mesh.DeleteHalfEdge(eLo); regLo = RegionBelow(regUp); - eLo = regLo.upperHalfEdge; + eLo = regLo._upperHalfEdge; } - else if (regUp.fixUpperEdge) + else if (regUp._fixUpperEdge) { DeleteRegion(regUp); Mesh.DeleteHalfEdge(eUp); regUp = regLo.RegionAbove(); - eUp = regUp.upperHalfEdge; + eUp = regUp._upperHalfEdge; } } } - if (eUp.originVertex != eLo.originVertex) + if (eUp._originVertex != eLo._originVertex) { - if (eUp.directionVertex != eLo.directionVertex - && !regUp.fixUpperEdge && !regLo.fixUpperEdge - && (eUp.directionVertex == tess.currentSweepVertex || eLo.directionVertex == tess.currentSweepVertex)) + if (eUp.DirectionVertex != eLo.DirectionVertex + && !regUp._fixUpperEdge && !regLo._fixUpperEdge + && (eUp.DirectionVertex == tess.currentSweepVertex || eLo.DirectionVertex == tess.currentSweepVertex)) { /* When all else fails in CheckForIntersect(), it uses tess.currentSweepVertex * as the intersection location. To make this possible, it requires @@ -1151,7 +1169,7 @@ static void WalkDirtyRegions(Tesselator tess, ActiveRegion regUp) CheckForRightSplice(tess, regUp); } } - if (eUp.originVertex == eLo.originVertex && eUp.directionVertex == eLo.directionVertex) + if (eUp._originVertex == eLo._originVertex && eUp.DirectionVertex == eLo.DirectionVertex) { /* A degenerate loop consisting of only two edges -- delete it. */ AddWinding(eLo, eUp); @@ -1197,12 +1215,12 @@ static void ConnectRightVertex(Tesselator tess, ActiveRegion regUp, HalfEdge eBo */ { HalfEdge eNew; - HalfEdge eTopLeft = eBottomLeft.nextEdgeCCWAroundOrigin; + HalfEdge eTopLeft = eBottomLeft._nextEdgeCCWAroundOrigin; ActiveRegion regLo = RegionBelow(regUp); - HalfEdge eUp = regUp.upperHalfEdge; - HalfEdge eLo = regLo.upperHalfEdge; + HalfEdge eUp = regUp._upperHalfEdge; + HalfEdge eLo = regLo._upperHalfEdge; bool degenerate = false; - if (eUp.directionVertex != eLo.directionVertex) + if (eUp.DirectionVertex != eLo.DirectionVertex) { CheckForIntersect(tess, regUp); } @@ -1210,15 +1228,15 @@ static void ConnectRightVertex(Tesselator tess, ActiveRegion regUp, HalfEdge eBo /* Possible new degeneracies: upper or lower edge of regUp may pass * through vEvent, or may coincide with new intersection vertex */ - if (eUp.originVertex.VertEq(tess.currentSweepVertex)) + if (eUp._originVertex.VertEq(tess.currentSweepVertex)) { Mesh.meshSplice(eTopLeft.Oprev, eUp); regUp = TopLeftRegion(regUp); - eTopLeft = RegionBelow(regUp).upperHalfEdge; + eTopLeft = RegionBelow(regUp)._upperHalfEdge; FinishLeftRegions(tess, RegionBelow(regUp), regLo); degenerate = true; } - if (eLo.originVertex.VertEq(tess.currentSweepVertex)) + if (eLo._originVertex.VertEq(tess.currentSweepVertex)) { Mesh.meshSplice(eBottomLeft, eLo.Oprev); eBottomLeft = FinishLeftRegions(tess, regLo, null); @@ -1226,14 +1244,14 @@ static void ConnectRightVertex(Tesselator tess, ActiveRegion regUp, HalfEdge eBo } if (degenerate) { - AddRightEdges(tess, regUp, eBottomLeft.nextEdgeCCWAroundOrigin, eTopLeft, eTopLeft, true); + AddRightEdges(tess, regUp, eBottomLeft._nextEdgeCCWAroundOrigin, eTopLeft, eTopLeft, true); return; } /* Non-degenerate situation -- need to add a temporary, fixable edge. * Connect to the closer of eLo.Org, eUp.Org. */ - if (eLo.originVertex.VertLeq(eUp.originVertex)) + if (eLo._originVertex.VertLeq(eUp._originVertex)) { eNew = eLo.Oprev; } @@ -1245,8 +1263,8 @@ static void ConnectRightVertex(Tesselator tess, ActiveRegion regUp, HalfEdge eBo /* Prevent cleanup, otherwise eNew might disappear before we've even * had a chance to mark it as a temporary edge. */ - AddRightEdges(tess, regUp, eNew, eNew.nextEdgeCCWAroundOrigin, eNew.nextEdgeCCWAroundOrigin, false); - eNew.otherHalfOfThisEdge.regionThisIsUpperEdgeOf.fixUpperEdge = true; + AddRightEdges(tess, regUp, eNew, eNew._nextEdgeCCWAroundOrigin, eNew._nextEdgeCCWAroundOrigin, false); + eNew._otherHalfOfThisEdge._regionThisIsUpperEdgeOf._fixUpperEdge = true; WalkDirtyRegions(tess, regUp); } @@ -1259,27 +1277,27 @@ static void ConnectLeftDegenerate(Tesselator tess, ActiveRegion regUp, ContourVe { HalfEdge e, eTopLeft, eTopRight, eLast; ActiveRegion reg; - e = regUp.upperHalfEdge; - if (e.originVertex.VertEq(vEvent)) + 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); + SpliceMergeVertices(tess, e, vEvent._edgeThisIsOriginOf); return; } - if (!e.directionVertex.VertEq(vEvent)) + if (!e.DirectionVertex.VertEq(vEvent)) { /* General case -- splice vEvent into edge e which passes through it */ - Mesh.meshSplitEdge(e.otherHalfOfThisEdge); - if (regUp.fixUpperEdge) + 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.DeleteHalfEdge(e._nextEdgeCCWAroundOrigin); + regUp._fixUpperEdge = false; } - Mesh.meshSplice(vEvent.edgeThisIsOriginOf, e); + Mesh.meshSplice(vEvent._edgeThisIsOriginOf, e); SweepEvent(tess, vEvent); /* recurse */ return; } @@ -1289,9 +1307,9 @@ static void ConnectLeftDegenerate(Tesselator tess, ActiveRegion regUp, ContourVe */ regUp = TopRightRegion(regUp); reg = RegionBelow(regUp); - eTopRight = reg.upperHalfEdge.otherHalfOfThisEdge; - eTopLeft = eLast = eTopRight.nextEdgeCCWAroundOrigin; - if (reg.fixUpperEdge) + 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. @@ -1304,13 +1322,13 @@ static void ConnectLeftDegenerate(Tesselator tess, ActiveRegion regUp, ContourVe Mesh.DeleteHalfEdge(eTopRight); eTopRight = eTopLeft.Oprev; } - Mesh.meshSplice(vEvent.edgeThisIsOriginOf, eTopRight); + 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); + AddRightEdges(tess, regUp, eTopRight._nextEdgeCCWAroundOrigin, eLast, eTopLeft, true); } static void ConnectLeftVertex(Tesselator tess, ContourVertex vEvent) @@ -1336,15 +1354,15 @@ static void ConnectLeftVertex(Tesselator tess, ContourVertex vEvent) /* assert( vEvent.anEdge.Onext.Onext == vEvent.anEdge ); */ /* Get a pointer to the active region containing vEvent */ - tmp.upperHalfEdge = vEvent.edgeThisIsOriginOf.otherHalfOfThisEdge; + tmp._upperHalfEdge = vEvent._edgeThisIsOriginOf._otherHalfOfThisEdge; /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */ - regUp = Dictionary.dictSearch(tess.edgeDictionary, tmp).Key; + regUp = Dictionary.dictSearch(tess._edgeDictionary, tmp).Key; regLo = RegionBelow(regUp); - eUp = regUp.upperHalfEdge; - eLo = regLo.upperHalfEdge; + eUp = regUp._upperHalfEdge; + eLo = regLo._upperHalfEdge; /* Try merging with U or L first */ - if (ContourVertex.EdgeSign(eUp.directionVertex, vEvent, eUp.originVertex) == 0) + if (ContourVertex.EdgeSign(eUp.DirectionVertex, vEvent, eUp._originVertex) == 0) { ConnectLeftDegenerate(tess, regUp, vEvent); return; @@ -1353,19 +1371,19 @@ static void ConnectLeftVertex(Tesselator tess, ContourVertex vEvent) /* 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) + reg = eLo.DirectionVertex.VertLeq(eUp.DirectionVertex) ? regUp : regLo; + if (regUp._inside || reg._fixUpperEdge) { if (reg == regUp) { - eNew = Mesh.meshConnect(vEvent.edgeThisIsOriginOf.otherHalfOfThisEdge, eUp.nextEdgeCCWAroundLeftFace); + eNew = Mesh.meshConnect(vEvent._edgeThisIsOriginOf._otherHalfOfThisEdge, eUp._nextEdgeCCWAroundLeftFace); } else { - HalfEdge tempHalfEdge = Mesh.meshConnect(eLo.Dnext, vEvent.edgeThisIsOriginOf); - eNew = tempHalfEdge.otherHalfOfThisEdge; + HalfEdge tempHalfEdge = Mesh.meshConnect(eLo.Dnext, vEvent._edgeThisIsOriginOf); + eNew = tempHalfEdge._otherHalfOfThisEdge; } - if (reg.fixUpperEdge) + if (reg._fixUpperEdge) { FixUpperEdge(reg, eNew); } @@ -1380,7 +1398,7 @@ static void ConnectLeftVertex(Tesselator tess, ContourVertex vEvent) /* 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); + AddRightEdges(tess, regUp, vEvent._edgeThisIsOriginOf, vEvent._edgeThisIsOriginOf, null, true); } } @@ -1398,11 +1416,11 @@ static void SweepEvent(Tesselator tess, ContourVertex vEvent) * 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 = vEvent._edgeThisIsOriginOf; + while (e._regionThisIsUpperEdgeOf == null) { - e = e.nextEdgeCCWAroundOrigin; - if (e == vEvent.edgeThisIsOriginOf) + e = e._nextEdgeCCWAroundOrigin; + if (e == vEvent._edgeThisIsOriginOf) { /* All edges go right -- not incident to any processed edges */ ConnectLeftVertex(tess, vEvent); @@ -1417,23 +1435,23 @@ static void SweepEvent(Tesselator tess, ContourVertex vEvent) * 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); + regUp = TopLeftRegion(e._regionThisIsUpperEdgeOf); reg = RegionBelow(regUp); - eTopLeft = reg.upperHalfEdge; + 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) + 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); + AddRightEdges(tess, regUp, eBottomLeft._nextEdgeCCWAroundOrigin, eTopLeft, eTopLeft, true); } } @@ -1443,7 +1461,7 @@ static void SweepEvent(Tesselator tess, ContourVertex vEvent) * input contour and the maximum tolerance of 1.0, no merging will be * done with coordinates larger than 3 * GLU_TESS_MAX_COORD). */ - static double SENTINEL_COORD = (4 * Tesselator.MAX_COORD); + const double SENTINEL_COORD = (4 * Tesselator.MAX_COORD); static void AddSentinel(Tesselator tess, double t) /* * We add two sentinel edges above and below all other edges, @@ -1452,19 +1470,19 @@ static void AddSentinel(Tesselator tess, double t) { HalfEdge halfEdge; ActiveRegion activeRedion = new ActiveRegion(); - halfEdge = tess.mesh.MakeEdge(); - halfEdge.originVertex.x = SENTINEL_COORD; - halfEdge.originVertex.y = t; - halfEdge.directionVertex.x = -SENTINEL_COORD; - halfEdge.directionVertex.y = t; - tess.currentSweepVertex = halfEdge.directionVertex; /* initialize it */ - activeRedion.upperHalfEdge = halfEdge; - activeRedion.windingNumber = 0; - activeRedion.inside = false; - activeRedion.fixUpperEdge = false; - activeRedion.sentinel = true; - activeRedion.dirty = false; - activeRedion.upperHalfEdgeDictNode = tess.edgeDictionary.Insert(activeRedion); /* __gl_dictListInsertBefore */ + halfEdge = tess._mesh.MakeEdge(); + halfEdge._originVertex.x = SENTINEL_COORD; + halfEdge._originVertex.y = t; + halfEdge.DirectionVertex.x = -SENTINEL_COORD; + halfEdge.DirectionVertex.y = t; + tess.currentSweepVertex = halfEdge.DirectionVertex; /* initialize it */ + activeRedion._upperHalfEdge = halfEdge; + activeRedion._windingNumber = 0; + activeRedion._inside = false; + activeRedion._fixUpperEdge = false; + activeRedion._sentinel = true; + activeRedion._dirty = false; + activeRedion._upperHalfEdgeDictNode = tess._edgeDictionary.Insert(activeRedion); /* __gl_dictListInsertBefore */ } @@ -1475,7 +1493,7 @@ static void InitEdgeDict(Tesselator tess) */ { /* __gl_dictListNewDict */ - tess.edgeDictionary = new Dictionary(tess); + tess._edgeDictionary = new Dictionary(tess); AddSentinel(tess, -SENTINEL_COORD); AddSentinel(tess, SENTINEL_COORD); } @@ -1487,16 +1505,16 @@ static void DoneEdgeDict(Tesselator tess) int fixedEdges = 0; /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ - while ((reg = tess.edgeDictionary.GetMinNode().Key) != null) + while ((reg = tess._edgeDictionary.GetMinNode().Key) != null) { /* * At the end of all processing, the dictionary should contain * only the two sentinel edges, plus at most one "fixable" edge * created by ConnectRightVertex(). */ - if (!reg.sentinel) + if (!reg._sentinel) { - if (!reg.fixUpperEdge) + if (!reg._fixUpperEdge) { throw new System.Exception(); } @@ -1505,48 +1523,48 @@ static void DoneEdgeDict(Tesselator tess) throw new System.Exception(); } } - if (reg.windingNumber != 0) + if (reg._windingNumber != 0) { throw new Exception(); } DeleteRegion(reg); } - tess.edgeDictionary = null; + tess._edgeDictionary = null; } static void RemoveDegenerateEdges(Tesselator tess) { // Remove zero-length edges, and contours with fewer than 3 vertices. - HalfEdge edgeHead = tess.mesh.halfEdgeHead; + HalfEdge edgeHead = tess._mesh._halfEdgeHead; HalfEdge nextHalfEdge; - for (HalfEdge currentEdge = edgeHead.nextHalfEdge; currentEdge != edgeHead; currentEdge = nextHalfEdge) + for (HalfEdge currentEdge = edgeHead._nextHalfEdge; currentEdge != edgeHead; currentEdge = nextHalfEdge) { - nextHalfEdge = currentEdge.nextHalfEdge; - HalfEdge nextEdgeCCWAroundLeftFace = currentEdge.nextEdgeCCWAroundLeftFace; - if (currentEdge.originVertex.VertEq(currentEdge.directionVertex) - && currentEdge.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace != currentEdge) + nextHalfEdge = currentEdge._nextHalfEdge; + HalfEdge nextEdgeCCWAroundLeftFace = currentEdge._nextEdgeCCWAroundLeftFace; + if (currentEdge._originVertex.VertEq(currentEdge.DirectionVertex) + && currentEdge._nextEdgeCCWAroundLeftFace._nextEdgeCCWAroundLeftFace != currentEdge) { // Zero-length edge, contour has at least 3 edges SpliceMergeVertices(tess, nextEdgeCCWAroundLeftFace, currentEdge); /* deletes e.Org */ Mesh.DeleteHalfEdge(currentEdge); /* e is a self-loop */ currentEdge = nextEdgeCCWAroundLeftFace; - nextEdgeCCWAroundLeftFace = currentEdge.nextEdgeCCWAroundLeftFace; + nextEdgeCCWAroundLeftFace = currentEdge._nextEdgeCCWAroundLeftFace; } - if (nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace == currentEdge) + if (nextEdgeCCWAroundLeftFace._nextEdgeCCWAroundLeftFace == currentEdge) { // Degenerate contour (one or two edges) if (nextEdgeCCWAroundLeftFace != currentEdge) { - if (nextEdgeCCWAroundLeftFace == nextHalfEdge || nextEdgeCCWAroundLeftFace == nextHalfEdge.otherHalfOfThisEdge) + if (nextEdgeCCWAroundLeftFace == nextHalfEdge || nextEdgeCCWAroundLeftFace == nextHalfEdge._otherHalfOfThisEdge) { - nextHalfEdge = nextHalfEdge.nextHalfEdge; + nextHalfEdge = nextHalfEdge._nextHalfEdge; } Mesh.DeleteHalfEdge(nextEdgeCCWAroundLeftFace); } - if (currentEdge == nextHalfEdge || currentEdge == nextHalfEdge.otherHalfOfThisEdge) { nextHalfEdge = nextHalfEdge.nextHalfEdge; } + if (currentEdge == nextHalfEdge || currentEdge == nextHalfEdge._otherHalfOfThisEdge) { nextHalfEdge = nextHalfEdge._nextHalfEdge; } Mesh.DeleteHalfEdge(currentEdge); } } @@ -1558,18 +1576,18 @@ static void InitPriorityQue(Tesselator tess) * order in which vertices cross the sweep line. */ { - MaxFirstList priorityQue = tess.vertexPriorityQue = new MaxFirstList(); - ContourVertex vertexHead = tess.mesh.vertexHead; - for (ContourVertex curVertex = vertexHead.nextVertex; curVertex != vertexHead; curVertex = curVertex.nextVertex) + MaxFirstList priorityQue = tess._vertexPriorityQue = new MaxFirstList(); + ContourVertex vertexHead = tess._mesh._vertexHead; + for (ContourVertex curVertex = vertexHead._nextVertex; curVertex != vertexHead; curVertex = curVertex._nextVertex) { - priorityQue.Add(out curVertex.priorityQueueHandle, curVertex); + priorityQue.Add(out curVertex._priorityQueueHandle, curVertex); } } static void DonePriorityQ(Tesselator tess) { - tess.vertexPriorityQue = null; /* __gl_pqSortDeletePriorityQ */ + tess._vertexPriorityQue = null; /* __gl_pqSortDeletePriorityQ */ } @@ -1591,19 +1609,19 @@ static bool RemoveDegenerateFaces(Mesh mesh) { Face f, fNext; HalfEdge e; - for (f = mesh.faceHead.nextFace; f != mesh.faceHead; f = fNext) + for (f = mesh._faceHead._nextFace; f != mesh._faceHead; f = fNext) { - fNext = f.nextFace; - e = f.halfEdgeThisIsLeftFaceOf; - if (e.nextEdgeCCWAroundLeftFace == e) + fNext = f._nextFace; + e = f._halfEdgeThisIsLeftFaceOf; + if (e._nextEdgeCCWAroundLeftFace == e) { throw new Exception(); } - if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace == e) + if (e._nextEdgeCCWAroundLeftFace._nextEdgeCCWAroundLeftFace == e) { /* A face with only two edges */ - AddWinding(e.nextEdgeCCWAroundOrigin, e); + AddWinding(e._nextEdgeCCWAroundOrigin, e); Mesh.DeleteHalfEdge(e); } } diff --git a/Demo/Shared/Tesselate/ContourVertex.cs b/Demo/Shared/Tesselate/ContourVertex.cs index 4f39c5b2..b113a7b0 100644 --- a/Demo/Shared/Tesselate/ContourVertex.cs +++ b/Demo/Shared/Tesselate/ContourVertex.cs @@ -40,23 +40,24 @@ ** */ -using System; +using System; namespace Tesselate { public class ContourVertex : IComparable { - public ContourVertex nextVertex; /* next vertex (never null) */ - public ContourVertex prevVertex; /* previous vertex (never null) */ - public HalfEdge edgeThisIsOriginOf; /* a half-edge with this origin */ - public int clientIndex; /* client's data */ + internal ContourVertex _nextVertex; /* next vertex (never null) */ + internal ContourVertex _prevVertex; /* previous vertex (never null) */ + internal HalfEdge _edgeThisIsOriginOf; /* a half-edge with this origin */ + internal int _clientIndex; /* client's data */ /* Internal data (keep hidden) */ /* vertex location in 3D */ - internal double C_0; - internal double C_1; - internal double C_2; - public double x, y; /* projection onto the sweep plane */ - public RefItem priorityQueueHandle; /* to allow deletion from priority queue */ + internal double _C_0; + internal double _C_1; + internal double _C_2; + internal double x, y; /* projection onto the sweep plane */ + internal RefItem _priorityQueueHandle; /* to allow deletion from priority queue */ + public int CompareTo(ContourVertex otherVertex) { if (VertEq(otherVertex)) @@ -70,10 +71,8 @@ public int CompareTo(ContourVertex otherVertex) return 1; } - public bool Equal2D(ContourVertex OtherVertex) - { - return (this.x == OtherVertex.x && this.y == OtherVertex.y); - } + public bool Equal2D(ContourVertex OtherVertex) => (x == OtherVertex.x && y == OtherVertex.y); + public static double EdgeEval(ContourVertex u, ContourVertex v, ContourVertex w) { @@ -133,20 +132,12 @@ static public double EdgeSign(ContourVertex u, ContourVertex v, ContourVertex w) return 0; } - public bool VertEq(ContourVertex v) - { - return ((this.x == v.x) && this.y == v.y); - } + public bool VertEq(ContourVertex v) => ((x == v.x) && y == v.y); - public bool VertLeq(ContourVertex v) - { - return ((this.x < v.x) || (this.x == v.x && this.y <= v.y)); - } + public bool VertLeq(ContourVertex v) => ((x < v.x) || (x == v.x && y <= v.y)); + + public bool TransLeq(ContourVertex v) => ((y < v.y) || (y == v.y && x <= v.x)); - public bool TransLeq(ContourVertex v) - { - return ((this.y < v.y) || (this.y == v.y && this.x <= v.x)); - } public static bool VertCCW(ContourVertex u, ContourVertex v, ContourVertex w) { @@ -162,7 +153,7 @@ public static bool VertCCW(ContourVertex u, ContourVertex v, ContourVertex w) #if DEBUG public override string ToString() { - return this.C_0 + "," + this.C_1 + "," + this.C_2; + return _C_0 + "," + _C_1 + "," + _C_2; } #endif } diff --git a/Demo/Shared/Tesselate/Dictionary.cs b/Demo/Shared/Tesselate/Dictionary.cs index 885d5dd3..43222353 100644 --- a/Demo/Shared/Tesselate/Dictionary.cs +++ b/Demo/Shared/Tesselate/Dictionary.cs @@ -42,41 +42,35 @@ using DictKey = Tesselate.ActiveRegion; namespace Tesselate { - public class Dictionary + class Dictionary { - public Node head = new Node(); - public Tesselator tesseator; + Node _head = new Node(); + Tesselator _tess; public class Node { - DictKey key = new DictKey(); + public DictKey Key = new DictKey(); public Node next; public Node prev; - public DictKey Key - { - get { return this.key; } - set { this.key = value; } - } - public void Delete() { this.next.prev = this.prev; this.prev.next = this.next; } - }; + } public Dictionary(Tesselator tesselator) { Node nodeHead; - nodeHead = this.head; + nodeHead = _head; nodeHead.Key = null; nodeHead.next = nodeHead; nodeHead.prev = nodeHead; - this.tesseator = tesselator; + _tess = tesselator; } public static void dictDeleteDict(Dictionary dict) { Node node, next; - for (node = dict.head.next; node != dict.head; node = next) + for (node = dict._head.next; node != dict._head; node = next) { next = node.next; node = null; @@ -87,7 +81,7 @@ public static void dictDeleteDict(Dictionary dict) public Dictionary.Node Insert(DictKey k) { - return this.InsertBefore(head, k); + return this.InsertBefore(_head, k); } public Node InsertBefore(Node node, DictKey key) @@ -96,8 +90,8 @@ public Node InsertBefore(Node node, DictKey key) do { node = node.prev; - } while (node.Key != null && - !ActiveRegion.EdgeLeq(this.tesseator, node.Key, key)); + } while (node.Key != null && !ActiveRegion.EdgeLeq(_tess, node.Key, key)); + newNode = new Node(); newNode.Key = key; newNode.next = node.next; @@ -109,16 +103,16 @@ public Node InsertBefore(Node node, DictKey key) public Dictionary.Node GetMinNode() { - return this.head.next; + return _head.next; } public static Node dictSearch(Dictionary dict, DictKey key) { - Node node = dict.head; + Node node = dict._head; do { node = node.next; - } while (node.Key != null && !ActiveRegion.EdgeLeq(dict.tesseator, key, node.Key)); + } while (node.Key != null && !ActiveRegion.EdgeLeq(dict._tess, key, node.Key)); return node; } } diff --git a/Demo/Shared/Tesselate/Face.cs b/Demo/Shared/Tesselate/Face.cs index d6b0269c..d50c4e31 100644 --- a/Demo/Shared/Tesselate/Face.cs +++ b/Demo/Shared/Tesselate/Face.cs @@ -45,14 +45,16 @@ namespace Tesselate { public class Face { - public int indexDebug; - public Face nextFace; /* next face (never null) */ - public Face prevFace; /* previous face (never null) */ - public HalfEdge halfEdgeThisIsLeftFaceOf; /* a half edge with this left face */ +#if DEBUG + public int dbugIndex; +#endif + internal Face _nextFace; /* next face (never null) */ + internal Face _prevFace; /* previous face (never null) */ + internal HalfEdge _halfEdgeThisIsLeftFaceOf; /* a half edge with this left face */ /* Internal data (keep hidden) */ - public Face trail; /* "stack" for conversion to strips */ - public bool marked; /* flag for conversion to strips */ - public bool isInterior; /* this face is in the polygon interior */ + internal Face _trail; /* "stack" for conversion to strips */ + internal bool _marked; /* flag for conversion to strips */ + internal bool _isInterior; /* this face is in the polygon interior */ /* Macros which keep track of faces we have marked temporarily, and allow * us to backtrack when necessary. With triangle fans, this is not * really necessary, since the only awkward case is a loop of triangles @@ -60,24 +62,21 @@ public class Face * more complicated, and we need a general tracking method like the * one here. */ - public bool Marked() - { - return (!this.isInterior || this.marked); - } + public bool Marked() => (!_isInterior || _marked); public static void AddToTrail(ref Face f, ref Face t) { - f.trail = t; + f._trail = t; t = f; - f.marked = true; + f._marked = true; } static public void FreeTrail(ref Face t) { while (t != null) { - t.marked = false; - t = t.trail; + t._marked = false; + t = t._trail; } } @@ -115,56 +114,56 @@ internal bool TessellateMonoRegion() * Since the sweep goes from left to right, face.anEdge should * be close to the edge we want. */ - HalfEdge up = this.halfEdgeThisIsLeftFaceOf; - if (up.nextEdgeCCWAroundLeftFace == up || up.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace == up) + HalfEdge up = _halfEdgeThisIsLeftFaceOf; + if (up._nextEdgeCCWAroundLeftFace == up || up._nextEdgeCCWAroundLeftFace._nextEdgeCCWAroundLeftFace == up) { throw new Exception(); } - for (; up.directionVertex.VertLeq(up.originVertex); up = up.Lprev) + for (; up.DirectionVertex.VertLeq(up._originVertex); up = up.Lprev) ; - for (; up.originVertex.VertLeq(up.directionVertex); up = up.nextEdgeCCWAroundLeftFace) + for (; up._originVertex.VertLeq(up.DirectionVertex); up = up._nextEdgeCCWAroundLeftFace) ; HalfEdge lo = up.Lprev; - while (up.nextEdgeCCWAroundLeftFace != lo) + while (up._nextEdgeCCWAroundLeftFace != lo) { - if (up.directionVertex.VertLeq(lo.originVertex)) + if (up.DirectionVertex.VertLeq(lo._originVertex)) { /* up.Dst is on the left. It is safe to form triangles from lo.Org. * The EdgeGoesLeft test guarantees progress even when some triangles * are CW, given that the upper and lower chains are truly monotone. */ - while (lo.nextEdgeCCWAroundLeftFace != up && (lo.nextEdgeCCWAroundLeftFace.EdgeGoesLeft() - || ContourVertex.EdgeSign(lo.originVertex, lo.directionVertex, lo.nextEdgeCCWAroundLeftFace.directionVertex) <= 0)) + while (lo._nextEdgeCCWAroundLeftFace != up && (lo._nextEdgeCCWAroundLeftFace.EdgeGoesLeft() + || ContourVertex.EdgeSign(lo._originVertex, lo.DirectionVertex, lo._nextEdgeCCWAroundLeftFace.DirectionVertex) <= 0)) { - HalfEdge tempHalfEdge = Mesh.meshConnect(lo.nextEdgeCCWAroundLeftFace, lo); - lo = tempHalfEdge.otherHalfOfThisEdge; + HalfEdge tempHalfEdge = Mesh.meshConnect(lo._nextEdgeCCWAroundLeftFace, lo); + lo = tempHalfEdge._otherHalfOfThisEdge; } lo = lo.Lprev; } else { /* lo.Org is on the left. We can make CCW triangles from up.Dst. */ - while (lo.nextEdgeCCWAroundLeftFace != up && (up.Lprev.EdgeGoesRight() - || ContourVertex.EdgeSign(up.directionVertex, up.originVertex, up.Lprev.originVertex) >= 0)) + while (lo._nextEdgeCCWAroundLeftFace != up && (up.Lprev.EdgeGoesRight() + || ContourVertex.EdgeSign(up.DirectionVertex, up._originVertex, up.Lprev._originVertex) >= 0)) { HalfEdge tempHalfEdge = Mesh.meshConnect(up, up.Lprev); - up = tempHalfEdge.otherHalfOfThisEdge; + up = tempHalfEdge._otherHalfOfThisEdge; } - up = up.nextEdgeCCWAroundLeftFace; + up = up._nextEdgeCCWAroundLeftFace; } } // Now lo.Org == up.Dst == the leftmost vertex. The remaining region // can be tessellated in a fan from this leftmost vertex. - if (lo.nextEdgeCCWAroundLeftFace == up) + if (lo._nextEdgeCCWAroundLeftFace == up) { throw new Exception(); } - while (lo.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace != up) + while (lo._nextEdgeCCWAroundLeftFace._nextEdgeCCWAroundLeftFace != up) { - HalfEdge tempHalfEdge = Mesh.meshConnect(lo.nextEdgeCCWAroundLeftFace, lo); - lo = tempHalfEdge.otherHalfOfThisEdge; + HalfEdge tempHalfEdge = Mesh.meshConnect(lo._nextEdgeCCWAroundLeftFace, lo); + lo = tempHalfEdge._otherHalfOfThisEdge; } return true; diff --git a/Demo/Shared/Tesselate/HalfEdge.cs b/Demo/Shared/Tesselate/HalfEdge.cs index 9ec28599..ed10656c 100644 --- a/Demo/Shared/Tesselate/HalfEdge.cs +++ b/Demo/Shared/Tesselate/HalfEdge.cs @@ -44,33 +44,37 @@ namespace Tesselate { public class HalfEdge { +#if DEBUG public int debugIndex; - public bool isFirstHalfEdge; - public HalfEdge nextHalfEdge; /* doubly-linked list (prev==Sym.next) */ - public HalfEdge otherHalfOfThisEdge; /* same edge, opposite direction */ - public HalfEdge nextEdgeCCWAroundOrigin; /* next edge CCW around origin */ - public HalfEdge nextEdgeCCWAroundLeftFace; /* next edge CCW around left face */ - public ContourVertex originVertex; /* origin vertex */ - public Face leftFace; /* left face */ +#endif + internal bool _isFirstHalfEdge; + internal HalfEdge _nextHalfEdge; /* doubly-linked list (prev==Sym.next) */ + internal HalfEdge _otherHalfOfThisEdge; /* same edge, opposite direction */ + internal HalfEdge _nextEdgeCCWAroundOrigin; /* next edge CCW around origin */ + internal HalfEdge _nextEdgeCCWAroundLeftFace; /* next edge CCW around left face */ + internal ContourVertex _originVertex; /* origin vertex */ + internal Face _leftFace; /* left face */ /* Internal data (keep hidden) */ - public ActiveRegion regionThisIsUpperEdgeOf; /* a region with this upper edge (sweep.c) */ - public int winding; // change in winding number when crossing from the right face to the left face - public Face rightFace { get { return otherHalfOfThisEdge.leftFace; } set { otherHalfOfThisEdge.leftFace = value; } } - public HalfEdge Lprev { get { return nextEdgeCCWAroundOrigin.otherHalfOfThisEdge; } set { nextEdgeCCWAroundOrigin.otherHalfOfThisEdge = value; } } - public HalfEdge Oprev { get { return otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace; } } - public ContourVertex directionVertex { get { return otherHalfOfThisEdge.originVertex; } set { otherHalfOfThisEdge.originVertex = value; } } - public HalfEdge Dnext { get { return Rprev.otherHalfOfThisEdge; } } - public HalfEdge Dprev { get { return nextEdgeCCWAroundLeftFace.otherHalfOfThisEdge; } } - public HalfEdge Rprev { get { return otherHalfOfThisEdge.nextEdgeCCWAroundOrigin; } } + internal ActiveRegion _regionThisIsUpperEdgeOf; /* a region with this upper edge (sweep.c) */ + internal int _winding; // change in winding number when crossing from the right face to the left face + internal Face rightFace { get => _otherHalfOfThisEdge._leftFace; set => _otherHalfOfThisEdge._leftFace = value; } - public bool EdgeGoesLeft() + internal HalfEdge Lprev { get => _nextEdgeCCWAroundOrigin._otherHalfOfThisEdge; set => _nextEdgeCCWAroundOrigin._otherHalfOfThisEdge = value; } + internal HalfEdge Oprev => _otherHalfOfThisEdge._nextEdgeCCWAroundLeftFace; + + internal ContourVertex DirectionVertex { get => _otherHalfOfThisEdge._originVertex; set => _otherHalfOfThisEdge._originVertex = value; } + internal HalfEdge Dnext => Rprev._otherHalfOfThisEdge; + internal HalfEdge Dprev => _nextEdgeCCWAroundLeftFace._otherHalfOfThisEdge; + internal HalfEdge Rprev => _otherHalfOfThisEdge._nextEdgeCCWAroundOrigin; + + internal bool EdgeGoesLeft() { - return this.directionVertex.VertLeq(this.originVertex); + return this.DirectionVertex.VertLeq(_originVertex); } - public bool EdgeGoesRight() + internal bool EdgeGoesRight() { - return this.originVertex.VertLeq(this.directionVertex); + return _originVertex.VertLeq(this.DirectionVertex); } } } \ No newline at end of file diff --git a/Demo/Shared/Tesselate/MaxFirstList.cs b/Demo/Shared/Tesselate/MaxFirstList.cs index 1aa02e3e..63e6af6f 100644 --- a/Demo/Shared/Tesselate/MaxFirstList.cs +++ b/Demo/Shared/Tesselate/MaxFirstList.cs @@ -1,15 +1,17 @@ -//BSD, 2014-2017, WinterDev +//BSD, 2014-present, WinterDev using System; using System.Collections.Generic; namespace Tesselate { + + //TODO: review this again.... //----- //design for our tess only //not for general use. //----- - public class RefItem - where T : IComparable + class RefItem + where T : IComparable { public RefItem(T data) { @@ -25,71 +27,67 @@ public override string ToString() #endif } - public class MaxFirstList - where T : IComparable + class MaxFirstList + where T : IComparable { - List> innerList = new List>(); - bool isSorted = false; + List> _innerList = new List>(); + bool _isSorted = false; public MaxFirstList() { } - public bool IsEmpty - { - get - { - return innerList.Count == 0; - } - } + // + public bool IsEmpty => _innerList.Count == 0; + // static int MaxFirstSort(RefItem t1, RefItem t2) { return t2.Data.CompareTo(t1.Data); } void SortData() { - innerList.Sort(MaxFirstSort); - for (int i = innerList.Count - 1; i >= 0; --i) + _innerList.Sort(MaxFirstSort); + for (int i = _innerList.Count - 1; i >= 0; --i) { - innerList[i].NodeNumber = i; + _innerList[i].NodeNumber = i; } - isSorted = true; + _isSorted = true; } public T DeleteMin() { //find min and delete - if (!isSorted) + if (!_isSorted) { SortData(); } - int last = innerList.Count - 1; - var tmp = innerList[last]; - innerList.RemoveAt(last); + int last = _innerList.Count - 1; + var tmp = _innerList[last]; + _innerList.RemoveAt(last); return tmp.Data; } public T FindMin() { - if (!isSorted) + if (!_isSorted) { SortData(); } - return innerList[innerList.Count - 1].Data; + return _innerList[_innerList.Count - 1].Data; } public void Add(out RefItem refItem, T data) { RefItem item = new RefItem(data); - innerList.Add(item); - isSorted = false; + _innerList.Add(item); + _isSorted = false; refItem = item; } public void Add(T data) { RefItem item = new RefItem(data); - innerList.Add(item); - isSorted = false; + _innerList.Add(item); + _isSorted = false; } int BinSearch(RefItem refItem, int begin, int end) { int pos = begin + ((end - begin) / 2); - RefItem sample = innerList[pos]; + RefItem sample = _innerList[pos]; if (refItem == sample) { } @@ -123,24 +121,24 @@ public void Delete(RefItem refItem) { //delete specfic node - if (isSorted) + if (_isSorted) { //use binary search to find node //1. find middle point int removeAt = refItem.NodeNumber; - for (int i = innerList.Count - 1; i > removeAt; --i) + for (int i = _innerList.Count - 1; i > removeAt; --i) { - innerList[i].NodeNumber = i - 1; + _innerList[i].NodeNumber = i - 1; } - innerList.RemoveAt(removeAt); + _innerList.RemoveAt(removeAt); } else { - for (int i = innerList.Count - 1; i >= 0; --i) + for (int i = _innerList.Count - 1; i >= 0; --i) { - if (innerList[i] == refItem) + if (_innerList[i] == refItem) { - this.innerList.RemoveAt(i); + _innerList.RemoveAt(i); break; } } diff --git a/Demo/Shared/Tesselate/Tesselator.cs b/Demo/Shared/Tesselate/Tesselator.cs deleted file mode 100644 index d70693a5..00000000 --- a/Demo/Shared/Tesselate/Tesselator.cs +++ /dev/null @@ -1,978 +0,0 @@ -/* -** License Applicability. Except to the extent portions of this file are -** made subject to an alternative license as permitted in the SGI Free -** Software License B, Version 1.1 (the "License"), the contents of this -** file are subject only to the provisions of the License. You may not use -** this file except in compliance with the License. You may obtain a copy -** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 -** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: -** -** http://oss.sgi.com/projects/FreeB -** -** Note that, as provided in the License, the Software is distributed on an -** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS -** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND -** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A -** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. -** -** Original Code. The Original Code is: OpenGL Sample Implementation, -** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, -** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. -** Copyright in any portions created by third parties is as indicated -** elsewhere herein. All Rights Reserved. -** -** Additional Notice Provisions: The application programming interfaces -** established by SGI in conjunction with the Original Code are The -** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released -** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version -** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X -** Window System(R) (Version 1.3), released October 19, 1998. This software -** was created using the OpenGL(R) version 1.2.1 Sample Implementation -** published by SGI, but has not been independently verified as being -** compliant with the OpenGL(R) version 1.2.1 Specification. -** -*/ -/* -** Author: Eric Veach, July 1994. -// C# Port port by: Lars Brubaker -// larsbrubaker@gmail.com -// Copyright (C) 2007 -** -*/ -//MIT,2014- present, WinterDev -using System; -namespace Tesselate -{ - public class Tesselator - { - // The begin/end calls must be properly nested. We keep track of - // the current state to enforce the ordering. - enum ProcessingState - { - Dormant, InPolygon, InContour - } - // We cache vertex data for single-contour polygons so that we can - // try a quick-and-dirty decomposition first. - const int MAX_CACHE_SIZE = 100; - internal const double MAX_COORD = 1.0e150; - - struct Vertex - { - public double x; - public double y; - } - - - public struct CombineParameters - { - public int d0, d1, d2, d3; - public double w0, w1, w2, w3; - } - public enum TriangleListType - { - LineLoop, - Triangles, - TriangleStrip, - TriangleFan - } - - public enum WindingRuleType - { - Odd, - NonZero, - Positive, - Negative, - ABS_GEQ_Two, - } - - ProcessingState _processingState; /* what begin/end calls have we seen? */ - HalfEdge _lastHalfEdge; /* lastEdge.Org is the most recent vertex */ - public Mesh mesh; /* stores the input contours, and eventually the tessellation itself */ - WindingRuleType _windingRule; // rule for determining polygon interior - - public Dictionary edgeDictionary; /* edge dictionary for sweep line */ - - internal MaxFirstList vertexPriorityQue = new MaxFirstList(); - - public ContourVertex currentSweepVertex; /* current sweep event being processed */ - - public delegate void CallCombineDelegate( - double c1, double c2, double c3, ref CombineParameters combinePars, out int outData); - - public CallCombineDelegate callCombine; - /*** state needed for rendering callbacks (see render.c) ***/ - - bool _boundaryOnly; /* Extract contours, not triangles */ - Face _lonelyTriList; - /* list of triangles which could not be rendered as strips or fans */ - - public delegate void CallBeginDelegate(TriangleListType type); - public CallBeginDelegate callBegin; - public delegate void CallEdgeFlagDelegate(bool boundaryEdge); - public CallEdgeFlagDelegate callEdgeFlag; - public delegate void CallVertexDelegate(int data); - public CallVertexDelegate callVertex; - public delegate void CallEndDelegate(); - public CallEndDelegate callEnd; - public delegate void CallMeshDelegate(Mesh mesh); - public CallMeshDelegate callMesh; - /*** state needed to cache single-contour polygons for renderCache() */ - - bool _emptyCache; /* empty cache on next vertex() call */ - int _cacheCount; /* number of cached vertices */ - Vertex[] _simpleVertexCache = new Vertex[MAX_CACHE_SIZE]; /* the vertex data */ - int[] _indexCached = new int[MAX_CACHE_SIZE]; - public Tesselator() - { - /* Only initialize fields which can be changed by the api. Other fields - * are initialized where they are used. - */ - _processingState = ProcessingState.Dormant; - _windingRule = Tesselator.WindingRuleType.Odd;//default - _boundaryOnly = false; - } - - ~Tesselator() - { - RequireState(ProcessingState.Dormant); - } - - public bool EdgeCallBackSet => callEdgeFlag != null; - - public WindingRuleType WindingRule - { - get => _windingRule; - set => _windingRule = value; - } - - public bool BoundaryOnly - { - get => _boundaryOnly; - set => _boundaryOnly = value; - } - - public bool IsWindingInside(int numCrossings) - { - switch (_windingRule) - { - case Tesselator.WindingRuleType.Odd: - return (numCrossings & 1) != 0; - case Tesselator.WindingRuleType.NonZero: - return (numCrossings != 0); - case Tesselator.WindingRuleType.Positive: - return (numCrossings > 0); - case Tesselator.WindingRuleType.Negative: - return (numCrossings < 0); - case Tesselator.WindingRuleType.ABS_GEQ_Two: - return (numCrossings >= 2) || (numCrossings <= -2); - } - - throw new Exception(); - } - - public void CallBegin(TriangleListType triangleType) - { - callBegin?.Invoke(triangleType); - } - public void CallVertex(int vertexData) - { - callVertex?.Invoke(vertexData); - } - public void CallEdgeFlag(bool edgeState) - { - callEdgeFlag?.Invoke(edgeState); - } - - public void CallEnd() - { - callEnd?.Invoke(); - } - - public void CallCombine(double v0, - double v1, double v2, - ref CombineParameters combinePars, - out int outData) - { - outData = 0; - callCombine?.Invoke(v0, v1, v2, ref combinePars, out outData); - } - - void GotoState(ProcessingState newProcessingState) - { - while (_processingState != newProcessingState) - { - /* We change the current state one level at a time, to get to - * the desired state. - */ - if (_processingState < newProcessingState) - { - switch (_processingState) - { - case ProcessingState.Dormant: - throw new Exception("MISSING_BEGIN_POLYGON"); - case ProcessingState.InPolygon: - throw new Exception("MISSING_BEGIN_CONTOUR"); - default: - break; - } - } - else - { - switch (_processingState) - { - case ProcessingState.InContour: - throw new Exception("MISSING_END_CONTOUR"); - case ProcessingState.InPolygon: - throw new Exception("MISSING_END_POLYGON"); - default: - break; - } - } - } - } - - void RequireState(ProcessingState state) - { - if (_processingState != state) - { - GotoState(state); - } - } - - public virtual void BeginPolygon() - { - RequireState(ProcessingState.Dormant); - _processingState = ProcessingState.InPolygon; - _cacheCount = 0; - _emptyCache = false; - mesh = null; - } - - public void BeginContour() - { - RequireState(ProcessingState.InPolygon); - _processingState = ProcessingState.InContour; - _lastHalfEdge = null; - if (_cacheCount > 0) - { - // Just set a flag so we don't get confused by empty contours - _emptyCache = true; - } - } - - bool AddVertex(double x, double y, int data) - { - HalfEdge e; - e = _lastHalfEdge; - if (e == null) - { - /* Make a self-loop (one vertex, one edge). */ - e = this.mesh.MakeEdge(); - Mesh.meshSplice(e, e.otherHalfOfThisEdge); - } - else - { - /* Create a new vertex and edge which immediately follow e - * in the ordering around the left face. - */ - if (Mesh.meshSplitEdge(e) == null) - { - return false; - } - e = e.nextEdgeCCWAroundLeftFace; - } - - /* The new vertex is now e.Org. */ - e.originVertex.clientIndex = data; - e.originVertex.C_0 = x; - e.originVertex.C_1 = y; - /* The winding of an edge says how the winding number changes as we - * cross from the edge''s right face to its left face. We add the - * vertices in such an order that a CCW contour will add +1 to - * the winding number of the region inside the contour. - */ - e.winding = 1; - e.otherHalfOfThisEdge.winding = -1; - _lastHalfEdge = e; - return true; - } - - void EmptyCache() - { - Vertex[] vCaches = _simpleVertexCache; - int[] index_caches = _indexCached; - this.mesh = new Mesh(); - int count = _cacheCount; - for (int i = 0; i < count; i++) - { - Vertex v = vCaches[i]; - this.AddVertex(v.x, v.y, index_caches[i]); - } - _cacheCount = 0; - _emptyCache = false; - } - - void CacheVertex(double x, double y, double z, int data) - { - Vertex v = new Vertex(); - v.x = x; - v.y = y; - _simpleVertexCache[_cacheCount] = v; - _indexCached[_cacheCount] = data; - ++_cacheCount; - } - void CacheVertex(double x, double y, int data) - { - Vertex v = new Vertex(); - v.x = x; - v.y = y; - _simpleVertexCache[_cacheCount] = v; - _indexCached[_cacheCount] = data; - ++_cacheCount; - } - public void AddVertex(double x, double y, double z, int data) - { - - RequireState(ProcessingState.InContour); - if (_emptyCache) - { - EmptyCache(); - _lastHalfEdge = null; - } - //============================= - //expand to 3 times - double tmp = x; - if (tmp < -MAX_COORD) - { - throw new Exception("Your coordinate exceeded -" + MAX_COORD.ToString() + "."); - } - if (tmp > MAX_COORD) - { - throw new Exception("Your coordinate exceeded " + MAX_COORD.ToString() + "."); - } - - //============================= - tmp = y; - if (tmp < -MAX_COORD) - { - throw new Exception("Your coordinate exceeded -" + MAX_COORD.ToString() + "."); - } - if (tmp > MAX_COORD) - { - throw new Exception("Your coordinate exceeded " + MAX_COORD.ToString() + "."); - } - //============================= - tmp = z; - if (tmp < -MAX_COORD) - { - throw new Exception("Your coordinate exceeded -" + MAX_COORD.ToString() + "."); - } - if (tmp > MAX_COORD) - { - throw new Exception("Your coordinate exceeded " + MAX_COORD.ToString() + "."); - } - //============================= - - - if (mesh == null) - { - if (_cacheCount < MAX_CACHE_SIZE) - { - CacheVertex(x, y, data); - return; - } - EmptyCache(); - } - AddVertex(x, y, data); - } - - public void EndContour() - { - RequireState(ProcessingState.InContour); - _processingState = ProcessingState.InPolygon; - } - - void CheckOrientation() - { - double area = 0; - Face curFace, faceHead = this.mesh.faceHead; - ContourVertex vHead = this.mesh.vertexHead; - HalfEdge curHalfEdge; - /* When we compute the normal automatically, we choose the orientation - * so that the sum of the signed areas of all contours is non-negative. - */ - for (curFace = faceHead.nextFace; curFace != faceHead; curFace = curFace.nextFace) - { - curHalfEdge = curFace.halfEdgeThisIsLeftFaceOf; - if (curHalfEdge.winding <= 0) - { - continue; - } - - do - { - area += (curHalfEdge.originVertex.x - curHalfEdge.directionVertex.x) - * (curHalfEdge.originVertex.y + curHalfEdge.directionVertex.y); - curHalfEdge = curHalfEdge.nextEdgeCCWAroundLeftFace; - } while (curHalfEdge != curFace.halfEdgeThisIsLeftFaceOf); - } - - if (area < 0) - { - /* Reverse the orientation by flipping all the t-coordinates */ - for (ContourVertex curVertex = vHead.nextVertex; curVertex != vHead; curVertex = curVertex.nextVertex) - { - curVertex.y = -curVertex.y; - } - } - } - - void ProjectPolygon() - { - ContourVertex v, vHead = this.mesh.vertexHead; - // Project the vertices onto the sweep plane - for (v = vHead.nextVertex; v != vHead; v = v.nextVertex) - { - v.x = v.C_0; - v.y = -v.C_1; - } - - CheckOrientation(); - } - - public void EndPolygon() - { - RequireState(ProcessingState.InPolygon); - _processingState = ProcessingState.Dormant; - if (this.mesh == null) - { - if (!this.EdgeCallBackSet && this.callMesh == null) - { - /* Try some special code to make the easy cases go quickly - * (eg. convex polygons). This code does NOT handle multiple contours, - * intersections, edge flags, and of course it does not generate - * an explicit mesh either. - */ - if (RenderCache()) - { - return; - } - } - - EmptyCache(); /* could've used a label*/ - } - - /* Determine the polygon normal and project vertices onto the plane - * of the polygon. - */ - ProjectPolygon(); - /* __gl_computeInterior( this ) computes the planar arrangement specified - * by the given contours, and further subdivides this arrangement - * into regions. Each region is marked "inside" if it belongs - * to the polygon, according to the rule given by this.windingRule. - * Each interior region is guaranteed to be monotone. - */ - ActiveRegion.ComputeInterior(this); - bool rc = true; - /* If the user wants only the boundary contours, we throw away all edges - * except those which separate the interior from the exterior. - * Otherwise we tessellate all the regions marked "inside". - */ - if (_boundaryOnly) - { - rc = this.mesh.SetWindingNumber(1, true); - } - else - { - rc = this.mesh.TessellateInterior(); - } - - this.mesh.CheckMesh(); - if (this.callBegin != null || this.callEnd != null - || this.callVertex != null || this.callEdgeFlag != null) - { - if (_boundaryOnly) - { - RenderBoundary(mesh); /* output boundary contours */ - } - else - { - RenderMesh(mesh); /* output strips and fans */ - } - } - if (this.callMesh != null) - { - /* Throw away the exterior faces, so that all faces are interior. - * This way the user doesn't have to check the "inside" flag, - * and we don't need to even reveal its existence. It also leaves - * the freedom for an implementation to not generate the exterior - * faces in the first place. - */ - mesh.DiscardExterior(); - callMesh(mesh); /* user wants the mesh itself */ - this.mesh = null; - return; - } - this.mesh = null; - } - - class FaceCount - { - public FaceCount(int _size, HalfEdge _eStart, RenderDelegate _render) - { - size = _size; - eStart = _eStart; - render = _render; - } - - public int size; /* number of triangles used */ - public HalfEdge eStart; /* edge where this primitive starts */ - public delegate void RenderDelegate(Tesselator tess, HalfEdge edge, int data); - event RenderDelegate render; - // routine to render this primitive - - public void CallRender(Tesselator tess, HalfEdge edge, int data) - { - render(tess, edge, data); - } - } - - /************************ Strips and Fans decomposition ******************/ - - /* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle - * fans, strips, and separate triangles. A substantial effort is made - * to use as few rendering primitives as possible (ie. to make the fans - * and strips as large as possible). - * - * The rendering output is provided as callbacks (see the api). - */ - public void RenderMesh(Mesh mesh) - { - Face f; - /* Make a list of separate triangles so we can render them all at once */ - _lonelyTriList = null; - for (f = mesh.faceHead.nextFace; f != mesh.faceHead; f = f.nextFace) - { - f.marked = false; - } - for (f = mesh.faceHead.nextFace; f != mesh.faceHead; f = f.nextFace) - { - /* We examine all faces in an arbitrary order. Whenever we find - * an unprocessed face F, we output a group of faces including F - * whose size is maximum. - */ - if (f.isInterior && !f.marked) - { - RenderMaximumFaceGroup(f); - if (!f.marked) - { - throw new System.Exception(); - } - } - } - if (_lonelyTriList != null) - { - RenderLonelyTriangles(_lonelyTriList); - _lonelyTriList = null; - } - } - - - void RenderMaximumFaceGroup(Face fOrig) - { - /* We want to find the largest triangle fan or strip of unmarked faces - * which includes the given face fOrig. There are 3 possible fans - * passing through fOrig (one centered at each vertex), and 3 possible - * strips (one for each CCW permutation of the vertices). Our strategy - * is to try all of these, and take the primitive which uses the most - * triangles (a greedy approach). - */ - HalfEdge e = fOrig.halfEdgeThisIsLeftFaceOf; - FaceCount max = new FaceCount(1, e, new FaceCount.RenderDelegate(RenderTriangle)); - FaceCount newFace; - max.size = 1; - max.eStart = e; - if (!this.EdgeCallBackSet) - { - newFace = MaximumFan(e); if (newFace.size > max.size) { max = newFace; } - newFace = MaximumFan(e.nextEdgeCCWAroundLeftFace); if (newFace.size > max.size) { max = newFace; } - newFace = MaximumFan(e.Lprev); if (newFace.size > max.size) { max = newFace; } - - newFace = MaximumStrip(e); if (newFace.size > max.size) { max = newFace; } - newFace = MaximumStrip(e.nextEdgeCCWAroundLeftFace); if (newFace.size > max.size) { max = newFace; } - newFace = MaximumStrip(e.Lprev); if (newFace.size > max.size) { max = newFace; } - } - - max.CallRender(this, max.eStart, max.size); - } - - FaceCount MaximumFan(HalfEdge eOrig) - { - /* eOrig.Lface is the face we want to render. We want to find the size - * of a maximal fan around eOrig.Org. To do this we just walk around - * the origin vertex as far as possible in both directions. - */ - FaceCount newFace = new FaceCount(0, null, new FaceCount.RenderDelegate(RenderFan)); - Face trail = null; - HalfEdge e; - for (e = eOrig; !e.leftFace.Marked(); e = e.nextEdgeCCWAroundOrigin) - { - Face.AddToTrail(ref e.leftFace, ref trail); - ++newFace.size; - } - for (e = eOrig; !e.rightFace.Marked(); e = e.Oprev) - { - Face f = e.rightFace; - Face.AddToTrail(ref f, ref trail); - e.rightFace = f; - ++newFace.size; - } - newFace.eStart = e; - Face.FreeTrail(ref trail); - return newFace; - } - - - static bool IsEven(int n) - { - return (((n) & 1) == 0); - } - - FaceCount MaximumStrip(HalfEdge eOrig) - { - /* Here we are looking for a maximal strip that contains the vertices - * eOrig.Org, eOrig.Dst, eOrig.Lnext.Dst (in that order or the - * reverse, such that all triangles are oriented CCW). - * - * Again we walk forward and backward as far as possible. However for - * strips there is a twist: to get CCW orientations, there must be - * an *even* number of triangles in the strip on one side of eOrig. - * We walk the strip starting on a side with an even number of triangles; - * if both side have an odd number, we are forced to shorten one side. - */ - FaceCount newFace = new FaceCount(0, null, RenderStrip); - int headSize = 0, tailSize = 0; - Face trail = null; - HalfEdge e, eTail, eHead; - for (e = eOrig; !e.leftFace.Marked(); ++tailSize, e = e.nextEdgeCCWAroundOrigin) - { - Face.AddToTrail(ref e.leftFace, ref trail); - ++tailSize; - e = e.Dprev; - if (e.leftFace.Marked()) break; - Face.AddToTrail(ref e.leftFace, ref trail); - } - eTail = e; - for (e = eOrig; !e.rightFace.Marked(); ++headSize, e = e.Dnext) - { - Face f = e.rightFace; - Face.AddToTrail(ref f, ref trail); - e.rightFace = f; - ++headSize; - e = e.Oprev; - if (e.rightFace.Marked()) break; - f = e.rightFace; - Face.AddToTrail(ref f, ref trail); - e.rightFace = f; - } - eHead = e; - newFace.size = tailSize + headSize; - if (IsEven(tailSize)) - { - newFace.eStart = eTail.otherHalfOfThisEdge; - } - else if (IsEven(headSize)) - { - newFace.eStart = eHead; - } - else - { - /* Both sides have odd length, we must shorten one of them. In fact, - * we must start from eHead to guarantee inclusion of eOrig.Lface. - */ - --newFace.size; - newFace.eStart = eHead.nextEdgeCCWAroundOrigin; - } - - Face.FreeTrail(ref trail); - return newFace; - } - - - void RenderTriangle(Tesselator tess, HalfEdge e, int size) - { - /* Just add the triangle to a triangle list, so we can render all - * the separate triangles at once. - */ - if (size != 1) - { - throw new Exception(); - } - Face.AddToTrail(ref e.leftFace, ref _lonelyTriList); - } - - - void RenderLonelyTriangles(Face f) - { - /* Now we render all the separate triangles which could not be - * grouped into a triangle fan or strip. - */ - HalfEdge e; - bool newState = false; - bool edgeState = false; /* force edge state output for first vertex */ - bool sentFirstEdge = false; - this.CallBegin(Tesselator.TriangleListType.Triangles); - for (; f != null; f = f.trail) - { - /* Loop once for each edge (there will always be 3 edges) */ - - e = f.halfEdgeThisIsLeftFaceOf; - do - { - if (this.EdgeCallBackSet) - { - /* Set the "edge state" to TRUE just before we output the - * first vertex of each edge on the polygon boundary. - */ - newState = !e.rightFace.isInterior; - if (edgeState != newState || !sentFirstEdge) - { - sentFirstEdge = true; - edgeState = newState; - this.CallEdgeFlag(edgeState); - } - } - - this.CallVertex(e.originVertex.clientIndex); - e = e.nextEdgeCCWAroundLeftFace; - } while (e != f.halfEdgeThisIsLeftFaceOf); - } - - this.CallEnd(); - } - - - static void RenderFan(Tesselator tess, HalfEdge e, int size) - { - /* Render as many CCW triangles as possible in a fan starting from - * edge "e". The fan *should* contain exactly "size" triangles - * (otherwise we've goofed up somewhere). - */ - tess.CallBegin(Tesselator.TriangleListType.TriangleFan); - tess.CallVertex(e.originVertex.clientIndex); - tess.CallVertex(e.directionVertex.clientIndex); - while (!e.leftFace.Marked()) - { - e.leftFace.marked = true; - --size; - e = e.nextEdgeCCWAroundOrigin; - tess.CallVertex(e.directionVertex.clientIndex); - } - - if (size != 0) - { - throw new Exception(); - } - tess.CallEnd(); - } - - - static void RenderStrip(Tesselator tess, HalfEdge halfEdge, int size) - { - /* Render as many CCW triangles as possible in a strip starting from - * edge "e". The strip *should* contain exactly "size" triangles - * (otherwise we've goofed up somewhere). - */ - tess.CallBegin(Tesselator.TriangleListType.TriangleStrip); - tess.CallVertex(halfEdge.originVertex.clientIndex); - tess.CallVertex(halfEdge.directionVertex.clientIndex); - while (!halfEdge.leftFace.Marked()) - { - halfEdge.leftFace.marked = true; - --size; - halfEdge = halfEdge.Dprev; - tess.CallVertex(halfEdge.originVertex.clientIndex); - if (halfEdge.leftFace.Marked()) break; - halfEdge.leftFace.marked = true; - --size; - halfEdge = halfEdge.nextEdgeCCWAroundOrigin; - tess.CallVertex(halfEdge.directionVertex.clientIndex); - } - - if (size != 0) - { - throw new Exception(); - } - tess.CallEnd(); - } - - - /************************ Boundary contour decomposition ******************/ - - /* Takes a mesh, and outputs one - * contour for each face marked "inside". The rendering output is - * provided as callbacks. - */ - public void RenderBoundary(Mesh mesh) - { - for (Face curFace = mesh.faceHead.nextFace; curFace != mesh.faceHead; curFace = curFace.nextFace) - { - if (curFace.isInterior) - { - this.CallBegin(Tesselator.TriangleListType.LineLoop); - HalfEdge curHalfEdge = curFace.halfEdgeThisIsLeftFaceOf; - do - { - this.CallVertex(curHalfEdge.originVertex.clientIndex); - curHalfEdge = curHalfEdge.nextEdgeCCWAroundLeftFace; - } while (curHalfEdge != curFace.halfEdgeThisIsLeftFaceOf); - this.CallEnd(); - } - } - } - - - /************************ Quick-and-dirty decomposition ******************/ - - const int SIGN_INCONSISTENT = 2; - int ComputeNormal(ref double nx, ref double ny, ref double nz) - /* - * Check that each triangle in the fan from v0 has a - * consistent orientation with respect to norm3[]. If triangles are - * consistently oriented CCW, return 1; if CW, return -1; if all triangles - * are degenerate return 0; otherwise (no consistent orientation) return - * SIGN_INCONSISTENT. - */ - { - var vCache = _simpleVertexCache; - Vertex v0 = vCache[0]; - int vcIndex; - double dot, xc, yc, xp, yp; - double n0; - double n1; - double n2; - int sign = 0; - /* Find the polygon normal. It is important to get a reasonable - * normal even when the polygon is self-intersecting (eg. a bowtie). - * Otherwise, the computed normal could be very tiny, but perpendicular - * to the true plane of the polygon due to numerical noise. Then all - * the triangles would appear to be degenerate and we would incorrectly - * decompose the polygon as a fan (or simply not render it at all). - * - * We use a sum-of-triangles normal algorithm rather than the more - * efficient sum-of-trapezoids method (used in CheckOrientation() - * in normal.c). This lets us explicitly reverse the signed area - * of some triangles to get a reasonable normal in the self-intersecting - * case. - */ - vcIndex = 1; - var v = vCache[vcIndex]; - xc = v.x - v0.x; - yc = v.y - v0.y; - int c_count = _cacheCount; - while (++vcIndex < c_count) - { - xp = xc; yp = yc; - v = vCache[vcIndex]; - xc = v.x - v0.x; - yc = v.y - v0.y; - /* Compute (vp - v0) cross (vc - v0) */ - n0 = 0; - n1 = 0; - n2 = xp * yc - yp * xc; - dot = n0 * nx + n1 * ny + n2 * nz; - if (dot != 0) - { - /* Check the new orientation for consistency with previous triangles */ - if (dot > 0) - { - if (sign < 0) - { - return SIGN_INCONSISTENT; - } - sign = 1; - } - else - { - if (sign > 0) - { - return SIGN_INCONSISTENT; - } - sign = -1; - } - } - } - - return sign; - } - - /* Takes a single contour and tries to render it - * as a triangle fan. This handles convex polygons, as well as some - * non-convex polygons if we get lucky. - * - * Returns TRUE if the polygon was successfully rendered. The rendering - * output is provided as callbacks (see the api). - */ - public bool RenderCache() - { - int sign; - if (_cacheCount < 3) - { - /* Degenerate contour -- no output */ - return true; - } - double normal_x = 0; - double normal_y = 0; - double normal_z = 1; - sign = this.ComputeNormal(ref normal_x, ref normal_y, ref normal_z); - if (sign == SIGN_INCONSISTENT) - { - // Fan triangles did not have a consistent orientation - return false; - } - if (sign == 0) - { - // All triangles were degenerate - return true; - } - - /* Make sure we do the right thing for each winding rule */ - switch (_windingRule) - { - case Tesselator.WindingRuleType.Odd: - case Tesselator.WindingRuleType.NonZero: - break; - case Tesselator.WindingRuleType.Positive: - if (sign < 0) return true; - break; - case Tesselator.WindingRuleType.Negative: - if (sign > 0) return true; - break; - case Tesselator.WindingRuleType.ABS_GEQ_Two: - return true; - } - - this.CallBegin(this.BoundaryOnly ? Tesselator.TriangleListType.LineLoop - : (_cacheCount > 3) ? Tesselator.TriangleListType.TriangleFan - : Tesselator.TriangleListType.Triangles); - this.CallVertex(_indexCached[0]); - if (sign > 0) - { - int c_count = _cacheCount; - for (int vcIndex = 1; vcIndex < c_count; ++vcIndex) - { - this.CallVertex(_indexCached[vcIndex]); - } - } - else - { - for (int vcIndex = _cacheCount - 1; vcIndex > 0; --vcIndex) - { - this.CallVertex(_indexCached[vcIndex]); - } - } - this.CallEnd(); - return true; - } - } -} diff --git a/Demo/Shared/Tesselate/mesh.cs b/Demo/Shared/Tesselate/mesh.cs index eb2d3a1d..134fc0ad 100644 --- a/Demo/Shared/Tesselate/mesh.cs +++ b/Demo/Shared/Tesselate/mesh.cs @@ -189,40 +189,40 @@ namespace Tesselate public class Mesh { - public ContourVertex vertexHead = new ContourVertex(); /* dummy header for vertex list */ - public Face faceHead = new Face(); /* dummy header for face list */ - public HalfEdge halfEdgeHead = new HalfEdge(); /* dummy header for edge list */ - HalfEdge otherHalfOfThisEdgeHead = new HalfEdge(); /* and its symmetric counterpart */ + internal ContourVertex _vertexHead = new ContourVertex(); /* dummy header for vertex list */ + internal Face _faceHead = new Face(); /* dummy header for face list */ + internal HalfEdge _halfEdgeHead = new HalfEdge(); /* dummy header for edge list */ + HalfEdge _otherHalfOfThisEdgeHead = new HalfEdge(); /* and its symmetric counterpart */ /* Creates a new mesh with no edges, no vertices, * and no loops (what we usually call a "face"). */ public Mesh() { - HalfEdge otherHalfOfThisEdge = this.otherHalfOfThisEdgeHead; - vertexHead.nextVertex = vertexHead.prevVertex = vertexHead; - vertexHead.edgeThisIsOriginOf = null; - vertexHead.clientIndex = 0; - faceHead.nextFace = faceHead.prevFace = faceHead; - faceHead.halfEdgeThisIsLeftFaceOf = null; - faceHead.trail = null; - faceHead.marked = false; - faceHead.isInterior = false; - halfEdgeHead.nextHalfEdge = halfEdgeHead; - halfEdgeHead.otherHalfOfThisEdge = otherHalfOfThisEdge; - halfEdgeHead.nextEdgeCCWAroundOrigin = null; - halfEdgeHead.nextEdgeCCWAroundLeftFace = null; - halfEdgeHead.originVertex = null; - halfEdgeHead.leftFace = null; - halfEdgeHead.winding = 0; - halfEdgeHead.regionThisIsUpperEdgeOf = null; - otherHalfOfThisEdge.nextHalfEdge = otherHalfOfThisEdge; - otherHalfOfThisEdge.otherHalfOfThisEdge = halfEdgeHead; - otherHalfOfThisEdge.nextEdgeCCWAroundOrigin = null; - otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace = null; - otherHalfOfThisEdge.originVertex = null; - otherHalfOfThisEdge.leftFace = null; - otherHalfOfThisEdge.winding = 0; - otherHalfOfThisEdge.regionThisIsUpperEdgeOf = null; + HalfEdge otherHalfOfThisEdge = _otherHalfOfThisEdgeHead; + _vertexHead._nextVertex = _vertexHead._prevVertex = _vertexHead; + _vertexHead._edgeThisIsOriginOf = null; + _vertexHead._clientIndex = 0; + _faceHead._nextFace = _faceHead._prevFace = _faceHead; + _faceHead._halfEdgeThisIsLeftFaceOf = null; + _faceHead._trail = null; + _faceHead._marked = false; + _faceHead._isInterior = false; + _halfEdgeHead._nextHalfEdge = _halfEdgeHead; + _halfEdgeHead._otherHalfOfThisEdge = otherHalfOfThisEdge; + _halfEdgeHead._nextEdgeCCWAroundOrigin = null; + _halfEdgeHead._nextEdgeCCWAroundLeftFace = null; + _halfEdgeHead._originVertex = null; + _halfEdgeHead._leftFace = null; + _halfEdgeHead._winding = 0; + _halfEdgeHead._regionThisIsUpperEdgeOf = null; + otherHalfOfThisEdge._nextHalfEdge = otherHalfOfThisEdge; + otherHalfOfThisEdge._otherHalfOfThisEdge = _halfEdgeHead; + otherHalfOfThisEdge._nextEdgeCCWAroundOrigin = null; + otherHalfOfThisEdge._nextEdgeCCWAroundLeftFace = null; + otherHalfOfThisEdge._originVertex = null; + otherHalfOfThisEdge._leftFace = null; + otherHalfOfThisEdge._winding = 0; + otherHalfOfThisEdge._regionThisIsUpperEdgeOf = null; } /* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left @@ -231,32 +231,37 @@ public Mesh() * the new face *before* fNext so that algorithms which walk the face * list will not see the newly created faces. */ - static int faceIndex = 0; + +#if DEBUG + static int s_dbugFaceIndexTotal = 0; +#endif static void MakeFace(Face newFace, HalfEdge eOrig, Face fNext) { HalfEdge e; Face fPrev; Face fNew = newFace; - fNew.indexDebug = faceIndex++; +#if DEBUG + fNew.dbugIndex = s_dbugFaceIndexTotal++; +#endif // insert in circular doubly-linked list before fNext - fPrev = fNext.prevFace; - fNew.prevFace = fPrev; - fPrev.nextFace = fNew; - fNew.nextFace = fNext; - fNext.prevFace = fNew; - fNew.halfEdgeThisIsLeftFaceOf = eOrig; - fNew.trail = null; - fNew.marked = false; + fPrev = fNext._prevFace; + fNew._prevFace = fPrev; + fPrev._nextFace = fNew; + fNew._nextFace = fNext; + fNext._prevFace = fNew; + fNew._halfEdgeThisIsLeftFaceOf = eOrig; + fNew._trail = null; + fNew._marked = false; // The new face is marked "inside" if the old one was. This is a // convenience for the common case where a face has been split in two. - fNew.isInterior = fNext.isInterior; + fNew._isInterior = fNext._isInterior; // fix other edges on this face loop e = eOrig; do { - e.leftFace = fNew; - e = e.nextEdgeCCWAroundLeftFace; + e._leftFace = fNew; + e = e._nextEdgeCCWAroundLeftFace; } while (e != eOrig); } @@ -268,10 +273,10 @@ public HalfEdge MakeEdge() 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); + e = MakeEdge(_halfEdgeHead); + MakeVertex(newVertex1, e, _vertexHead); + MakeVertex(newVertex2, e._otherHalfOfThisEdge, _vertexHead); + MakeFace(newFace, e, _faceHead); return e; } @@ -287,21 +292,21 @@ static void MakeVertex(ContourVertex newVertex, HalfEdge eOrig, ContourVertex vN 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; + 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; + e._originVertex = vNew; + e = e._nextEdgeCCWAroundOrigin; } while (e != eOrig); } @@ -310,20 +315,20 @@ static void MakeVertex(ContourVertex newVertex, HalfEdge eOrig, ContourVertex vN */ static void KillVertex(ContourVertex vDel, ContourVertex newOrg) { - HalfEdge e, eStart = vDel.edgeThisIsOriginOf; + HalfEdge e, eStart = vDel._edgeThisIsOriginOf; ContourVertex vPrev, vNext; /* change the origin of all affected edges */ e = eStart; do { - e.originVertex = newOrg; - e = e.nextEdgeCCWAroundOrigin; + 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; + vPrev = vDel._prevVertex; + vNext = vDel._nextVertex; + vNext._prevVertex = vPrev; + vPrev._nextVertex = vNext; } /* KillFace( fDel ) destroys a face and removes it from the global face @@ -331,20 +336,20 @@ static void KillVertex(ContourVertex vDel, ContourVertex newOrg) */ static void KillFace(Face fDel, Face newLface) { - HalfEdge e, eStart = fDel.halfEdgeThisIsLeftFaceOf; + HalfEdge e, eStart = fDel._halfEdgeThisIsLeftFaceOf; Face fPrev, fNext; /* change the left face of all affected edges */ e = eStart; do { - e.leftFace = newLface; - e = e.nextEdgeCCWAroundLeftFace; + e._leftFace = newLface; + e = e._nextEdgeCCWAroundLeftFace; } while (e != eStart); /* delete from circular doubly-linked list */ - fPrev = fDel.prevFace; - fNext = fDel.nextFace; - fNext.prevFace = fPrev; - fPrev.nextFace = fNext; + fPrev = fDel._prevFace; + fNext = fDel._nextFace; + fNext._prevFace = fPrev; + fPrev._nextFace = fNext; } /* Splice( a, b ) is best described by the Guibas/Stolfi paper or the @@ -355,12 +360,12 @@ static void KillFace(Face fDel, Face newLface) */ static void Splice(HalfEdge a, HalfEdge b) { - HalfEdge aOnext = a.nextEdgeCCWAroundOrigin; - HalfEdge bOnext = b.nextEdgeCCWAroundOrigin; - aOnext.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace = b; - bOnext.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace = a; - a.nextEdgeCCWAroundOrigin = bOnext; - b.nextEdgeCCWAroundOrigin = aOnext; + HalfEdge aOnext = a._nextEdgeCCWAroundOrigin; + HalfEdge bOnext = b._nextEdgeCCWAroundOrigin; + aOnext._otherHalfOfThisEdge._nextEdgeCCWAroundLeftFace = b; + bOnext._otherHalfOfThisEdge._nextEdgeCCWAroundLeftFace = a; + a._nextEdgeCCWAroundOrigin = bOnext; + b._nextEdgeCCWAroundOrigin = aOnext; } /* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the @@ -391,17 +396,17 @@ public static void meshSplice(HalfEdge eOrg, HalfEdge eDst) bool joiningLoops = false; bool joiningVertices = false; if (eOrg == eDst) return; - if (eDst.originVertex != eOrg.originVertex) + if (eDst._originVertex != eOrg._originVertex) { /* We are merging two disjoint vertices -- destroy eDst.Org */ joiningVertices = true; - KillVertex(eDst.originVertex, eOrg.originVertex); + KillVertex(eDst._originVertex, eOrg._originVertex); } - if (eDst.leftFace != eOrg.leftFace) + if (eDst._leftFace != eOrg._leftFace) { /* We are connecting two disjoint loops -- destroy eDst.Lface */ joiningLoops = true; - KillFace(eDst.leftFace, eOrg.leftFace); + KillFace(eDst._leftFace, eOrg._leftFace); } /* Change the edge structure */ @@ -412,8 +417,8 @@ public static void meshSplice(HalfEdge eOrg, HalfEdge eDst) /* 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; + MakeVertex(newVertex, eDst, eOrg._originVertex); + eOrg._originVertex._edgeThisIsOriginOf = eOrg; } if (!joiningLoops) { @@ -421,8 +426,8 @@ public static void meshSplice(HalfEdge eOrg, HalfEdge eDst) /* 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; + MakeFace(newFace, eDst, eOrg._leftFace); + eOrg._leftFace._halfEdgeThisIsLeftFaceOf = eOrg; } } @@ -433,16 +438,16 @@ static void KillEdge(HalfEdge eDel) { HalfEdge ePrev, eNext; /* Half-edges are allocated in pairs, see EdgePair above */ - if (eDel.otherHalfOfThisEdge.isFirstHalfEdge) + if (eDel._otherHalfOfThisEdge._isFirstHalfEdge) { - eDel = eDel.otherHalfOfThisEdge; + eDel = eDel._otherHalfOfThisEdge; } /* delete from circular doubly-linked list */ - eNext = eDel.nextHalfEdge; - ePrev = eDel.otherHalfOfThisEdge.nextHalfEdge; - eNext.otherHalfOfThisEdge.nextHalfEdge = ePrev; - ePrev.otherHalfOfThisEdge.nextHalfEdge = eNext; + eNext = eDel._nextHalfEdge; + ePrev = eDel._otherHalfOfThisEdge._nextHalfEdge; + eNext._otherHalfOfThisEdge._nextHalfEdge = ePrev; + ePrev._otherHalfOfThisEdge._nextHalfEdge = eNext; } /* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: @@ -457,47 +462,47 @@ static void KillEdge(HalfEdge eDel) */ public static void DeleteHalfEdge(HalfEdge edgeToDelete) { - HalfEdge otherHalfOfEdgeToDelete = edgeToDelete.otherHalfOfThisEdge; + HalfEdge otherHalfOfEdgeToDelete = edgeToDelete._otherHalfOfThisEdge; bool joiningLoops = false; // First step: disconnect the origin vertex eDel.Org. We make all // changes to get a consistent mesh in this "intermediate" state. - if (edgeToDelete.leftFace != edgeToDelete.rightFace) + if (edgeToDelete._leftFace != edgeToDelete.rightFace) { // We are joining two loops into one -- remove the left face joiningLoops = true; - KillFace(edgeToDelete.leftFace, edgeToDelete.rightFace); + KillFace(edgeToDelete._leftFace, edgeToDelete.rightFace); } - if (edgeToDelete.nextEdgeCCWAroundOrigin == edgeToDelete) + if (edgeToDelete._nextEdgeCCWAroundOrigin == edgeToDelete) { - KillVertex(edgeToDelete.originVertex, null); + KillVertex(edgeToDelete._originVertex, null); } else { // Make sure that eDel.Org and eDel.Rface point to valid half-edges - edgeToDelete.rightFace.halfEdgeThisIsLeftFaceOf = edgeToDelete.Oprev; - edgeToDelete.originVertex.edgeThisIsOriginOf = edgeToDelete.nextEdgeCCWAroundOrigin; + edgeToDelete.rightFace._halfEdgeThisIsLeftFaceOf = edgeToDelete.Oprev; + edgeToDelete._originVertex._edgeThisIsOriginOf = edgeToDelete._nextEdgeCCWAroundOrigin; Splice(edgeToDelete, edgeToDelete.Oprev); if (!joiningLoops) { Face newFace = new Face(); // We are splitting one loop into two -- create a new loop for eDel. - MakeFace(newFace, edgeToDelete, edgeToDelete.leftFace); + MakeFace(newFace, edgeToDelete, edgeToDelete._leftFace); } } // Claim: the mesh is now in a consistent state, except that eDel.Org // may have been deleted. Now we disconnect eDel.Dst. - if (otherHalfOfEdgeToDelete.nextEdgeCCWAroundOrigin == otherHalfOfEdgeToDelete) + if (otherHalfOfEdgeToDelete._nextEdgeCCWAroundOrigin == otherHalfOfEdgeToDelete) { - KillVertex(otherHalfOfEdgeToDelete.originVertex, null); - KillFace(otherHalfOfEdgeToDelete.leftFace, null); + KillVertex(otherHalfOfEdgeToDelete._originVertex, null); + KillFace(otherHalfOfEdgeToDelete._leftFace, null); } else { // Make sure that eDel.Dst and eDel.Lface point to valid half-edges - edgeToDelete.leftFace.halfEdgeThisIsLeftFaceOf = otherHalfOfEdgeToDelete.Oprev; - otherHalfOfEdgeToDelete.originVertex.edgeThisIsOriginOf = otherHalfOfEdgeToDelete.nextEdgeCCWAroundOrigin; + edgeToDelete._leftFace._halfEdgeThisIsLeftFaceOf = otherHalfOfEdgeToDelete.Oprev; + otherHalfOfEdgeToDelete._originVertex._edgeThisIsOriginOf = otherHalfOfEdgeToDelete._nextEdgeCCWAroundOrigin; Splice(otherHalfOfEdgeToDelete, otherHalfOfEdgeToDelete.Oprev); } @@ -513,16 +518,16 @@ static HalfEdge meshAddEdgeVertex(HalfEdge eOrg) { HalfEdge eNewSym; HalfEdge eNew = MakeEdge(eOrg); - eNewSym = eNew.otherHalfOfThisEdge; + eNewSym = eNew._otherHalfOfThisEdge; /* Connect the new edge appropriately */ - Splice(eNew, eOrg.nextEdgeCCWAroundLeftFace); + Splice(eNew, eOrg._nextEdgeCCWAroundLeftFace); /* Set the vertex and face information */ - eNew.originVertex = eOrg.directionVertex; + eNew._originVertex = eOrg.DirectionVertex; { ContourVertex newVertex = new ContourVertex(); - MakeVertex(newVertex, eNewSym, eNew.originVertex); + MakeVertex(newVertex, eNewSym, eNew._originVertex); } - eNew.leftFace = eNewSym.leftFace = eOrg.leftFace; + eNew._leftFace = eNewSym._leftFace = eOrg._leftFace; return eNew; } @@ -534,16 +539,16 @@ public static HalfEdge meshSplitEdge(HalfEdge eOrg) { HalfEdge eNew; HalfEdge tempHalfEdge = meshAddEdgeVertex(eOrg); - eNew = tempHalfEdge.otherHalfOfThisEdge; + eNew = tempHalfEdge._otherHalfOfThisEdge; /* Disconnect eOrg from eOrg.Dst and connect it to eNew.Org */ - Splice(eOrg.otherHalfOfThisEdge, eOrg.otherHalfOfThisEdge.Oprev); - Splice(eOrg.otherHalfOfThisEdge, eNew); + Splice(eOrg._otherHalfOfThisEdge, eOrg._otherHalfOfThisEdge.Oprev); + Splice(eOrg._otherHalfOfThisEdge, eNew); /* Set the vertex and face information */ - eOrg.directionVertex = eNew.originVertex; - eNew.directionVertex.edgeThisIsOriginOf = eNew.otherHalfOfThisEdge; /* may have pointed to eOrg.Sym */ + eOrg.DirectionVertex = eNew._originVertex; + eNew.DirectionVertex._edgeThisIsOriginOf = eNew._otherHalfOfThisEdge; /* may have pointed to eOrg.Sym */ eNew.rightFace = eOrg.rightFace; - eNew.winding = eOrg.winding; /* copy old winding information */ - eNew.otherHalfOfThisEdge.winding = eOrg.otherHalfOfThisEdge.winding; + eNew._winding = eOrg._winding; /* copy old winding information */ + eNew._otherHalfOfThisEdge._winding = eOrg._otherHalfOfThisEdge._winding; return eNew; } @@ -574,35 +579,35 @@ static HalfEdge MakeEdge(HalfEdge eNext) HalfEdge ePrev; EdgePair pair = new EdgePair(); /* Make sure eNext points to the first edge of the edge pair */ - if (eNext.otherHalfOfThisEdge.isFirstHalfEdge) + if (eNext._otherHalfOfThisEdge._isFirstHalfEdge) { - eNext = eNext.otherHalfOfThisEdge; + eNext = eNext._otherHalfOfThisEdge; } /* Insert in circular doubly-linked list before eNext. * Note that the prev pointer is stored in Sym.next. */ - ePrev = eNext.otherHalfOfThisEdge.nextHalfEdge; - pair.eSym.nextHalfEdge = ePrev; - ePrev.otherHalfOfThisEdge.nextHalfEdge = pair.e; - pair.e.nextHalfEdge = eNext; - eNext.otherHalfOfThisEdge.nextHalfEdge = pair.eSym; - pair.e.isFirstHalfEdge = true; - pair.e.otherHalfOfThisEdge = pair.eSym; - pair.e.nextEdgeCCWAroundOrigin = pair.e; - pair.e.nextEdgeCCWAroundLeftFace = pair.eSym; - pair.e.originVertex = null; - pair.e.leftFace = null; - pair.e.winding = 0; - pair.e.regionThisIsUpperEdgeOf = null; - pair.eSym.isFirstHalfEdge = false; - pair.eSym.otherHalfOfThisEdge = pair.e; - pair.eSym.nextEdgeCCWAroundOrigin = pair.eSym; - pair.eSym.nextEdgeCCWAroundLeftFace = pair.e; - pair.eSym.originVertex = null; - pair.eSym.leftFace = null; - pair.eSym.winding = 0; - pair.eSym.regionThisIsUpperEdgeOf = null; + ePrev = eNext._otherHalfOfThisEdge._nextHalfEdge; + pair.eSym._nextHalfEdge = ePrev; + ePrev._otherHalfOfThisEdge._nextHalfEdge = pair.e; + pair.e._nextHalfEdge = eNext; + eNext._otherHalfOfThisEdge._nextHalfEdge = pair.eSym; + pair.e._isFirstHalfEdge = true; + pair.e._otherHalfOfThisEdge = pair.eSym; + pair.e._nextEdgeCCWAroundOrigin = pair.e; + pair.e._nextEdgeCCWAroundLeftFace = pair.eSym; + pair.e._originVertex = null; + pair.e._leftFace = null; + pair.e._winding = 0; + pair.e._regionThisIsUpperEdgeOf = null; + pair.eSym._isFirstHalfEdge = false; + pair.eSym._otherHalfOfThisEdge = pair.e; + pair.eSym._nextEdgeCCWAroundOrigin = pair.eSym; + pair.eSym._nextEdgeCCWAroundLeftFace = pair.e; + pair.eSym._originVertex = null; + pair.eSym._leftFace = null; + pair.eSym._winding = 0; + pair.eSym._regionThisIsUpperEdgeOf = null; return pair.e; } @@ -621,28 +626,28 @@ public static HalfEdge meshConnect(HalfEdge eOrg, HalfEdge eDst) HalfEdge eNewSym; bool joiningLoops = false; HalfEdge eNew = MakeEdge(eOrg); - eNewSym = eNew.otherHalfOfThisEdge; - if (eDst.leftFace != eOrg.leftFace) + eNewSym = eNew._otherHalfOfThisEdge; + if (eDst._leftFace != eOrg._leftFace) { /* We are connecting two disjoint loops -- destroy eDst.Lface */ joiningLoops = true; - KillFace(eDst.leftFace, eOrg.leftFace); + KillFace(eDst._leftFace, eOrg._leftFace); } /* Connect the new edge appropriately */ - Splice(eNew, eOrg.nextEdgeCCWAroundLeftFace); + Splice(eNew, eOrg._nextEdgeCCWAroundLeftFace); Splice(eNewSym, eDst); /* Set the vertex and face information */ - eNew.originVertex = eOrg.directionVertex; - eNewSym.originVertex = eDst.originVertex; - eNew.leftFace = eNewSym.leftFace = eOrg.leftFace; + eNew._originVertex = eOrg.DirectionVertex; + eNewSym._originVertex = eDst._originVertex; + eNew._leftFace = eNewSym._leftFace = eOrg._leftFace; /* Make sure the old face points to a valid half-edge */ - eOrg.leftFace.halfEdgeThisIsLeftFaceOf = eNewSym; + eOrg._leftFace._halfEdgeThisIsLeftFaceOf = eNewSym; if (!joiningLoops) { Face newFace = new Face(); /* We split one loop into two -- the new loop is eNew.Lface */ - MakeFace(newFace, eNew, eOrg.leftFace); + MakeFace(newFace, eNew, eOrg._leftFace); } return eNew; } @@ -652,35 +657,35 @@ public static HalfEdge meshConnect(HalfEdge eOrg, HalfEdge eDst) */ Mesh meshUnion(Mesh mesh1, Mesh mesh2) { - Face f1 = mesh1.faceHead; - ContourVertex v1 = mesh1.vertexHead; - HalfEdge e1 = mesh1.halfEdgeHead; - Face f2 = mesh2.faceHead; - ContourVertex v2 = mesh2.vertexHead; - HalfEdge e2 = mesh2.halfEdgeHead; + Face f1 = mesh1._faceHead; + ContourVertex v1 = mesh1._vertexHead; + HalfEdge e1 = mesh1._halfEdgeHead; + Face f2 = mesh2._faceHead; + ContourVertex v2 = mesh2._vertexHead; + HalfEdge e2 = mesh2._halfEdgeHead; /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */ - if (f2.nextFace != f2) + if (f2._nextFace != f2) { - f1.prevFace.nextFace = f2.nextFace; - f2.nextFace.prevFace = f1.prevFace; - f2.prevFace.nextFace = f1; - f1.prevFace = f2.prevFace; + f1._prevFace._nextFace = f2._nextFace; + f2._nextFace._prevFace = f1._prevFace; + f2._prevFace._nextFace = f1; + f1._prevFace = f2._prevFace; } - if (v2.nextVertex != v2) + if (v2._nextVertex != v2) { - v1.prevVertex.nextVertex = v2.nextVertex; - v2.nextVertex.prevVertex = v1.prevVertex; - v2.prevVertex.nextVertex = v1; - v1.prevVertex = v2.prevVertex; + v1._prevVertex._nextVertex = v2._nextVertex; + v2._nextVertex._prevVertex = v1._prevVertex; + v2._prevVertex._nextVertex = v1; + v1._prevVertex = v2._prevVertex; } - if (e2.nextHalfEdge != e2) + if (e2._nextHalfEdge != e2) { - e1.otherHalfOfThisEdge.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e2.nextHalfEdge; - e2.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e1.otherHalfOfThisEdge.nextHalfEdge; - e2.otherHalfOfThisEdge.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e1; - e1.otherHalfOfThisEdge.nextHalfEdge = e2.otherHalfOfThisEdge.nextHalfEdge; + e1._otherHalfOfThisEdge._nextHalfEdge._otherHalfOfThisEdge._nextHalfEdge = e2._nextHalfEdge; + e2._nextHalfEdge._otherHalfOfThisEdge._nextHalfEdge = e1._otherHalfOfThisEdge._nextHalfEdge; + e2._otherHalfOfThisEdge._nextHalfEdge._otherHalfOfThisEdge._nextHalfEdge = e1; + e1._otherHalfOfThisEdge._nextHalfEdge = e2._otherHalfOfThisEdge._nextHalfEdge; } mesh2 = null; @@ -696,49 +701,49 @@ Mesh meshUnion(Mesh mesh1, Mesh mesh2) */ public static void meshZapFace(Face fZap) { - HalfEdge eStart = fZap.halfEdgeThisIsLeftFaceOf; + HalfEdge eStart = fZap._halfEdgeThisIsLeftFaceOf; HalfEdge e, eNext, eSym; Face fPrev, fNext; /* walk around face, deleting edges whose right face is also null */ - eNext = eStart.nextEdgeCCWAroundLeftFace; + eNext = eStart._nextEdgeCCWAroundLeftFace; do { e = eNext; - eNext = e.nextEdgeCCWAroundLeftFace; - e.leftFace = null; + eNext = e._nextEdgeCCWAroundLeftFace; + e._leftFace = null; if (e.rightFace == null) { /* delete the edge -- see __gl_MeshDelete above */ - if (e.nextEdgeCCWAroundOrigin == e) + if (e._nextEdgeCCWAroundOrigin == e) { - KillVertex(e.originVertex, null); + KillVertex(e._originVertex, null); } else { /* Make sure that e.Org points to a valid half-edge */ - e.originVertex.edgeThisIsOriginOf = e.nextEdgeCCWAroundOrigin; + e._originVertex._edgeThisIsOriginOf = e._nextEdgeCCWAroundOrigin; Splice(e, e.Oprev); } - eSym = e.otherHalfOfThisEdge; - if (eSym.nextEdgeCCWAroundOrigin == eSym) + eSym = e._otherHalfOfThisEdge; + if (eSym._nextEdgeCCWAroundOrigin == eSym) { - KillVertex(eSym.originVertex, null); + KillVertex(eSym._originVertex, null); } else { /* Make sure that eSym.Org points to a valid half-edge */ - eSym.originVertex.edgeThisIsOriginOf = eSym.nextEdgeCCWAroundOrigin; + eSym._originVertex._edgeThisIsOriginOf = eSym._nextEdgeCCWAroundOrigin; Splice(eSym, eSym.Oprev); } KillEdge(e); } } while (e != eStart); /* delete from circular doubly-linked list */ - fPrev = fZap.prevFace; - fNext = fZap.nextFace; - fNext.prevFace = fPrev; - fPrev.nextFace = fNext; + fPrev = fZap._prevFace; + fNext = fZap._nextFace; + fNext._prevFace = fPrev; + fPrev._nextFace = fNext; fZap = null; } @@ -746,125 +751,125 @@ public static void meshZapFace(Face fZap) */ public void CheckMesh() { - Face fHead = this.faceHead; - ContourVertex vHead = this.vertexHead; - HalfEdge eHead = this.halfEdgeHead; + Face fHead = _faceHead; + ContourVertex vHead = _vertexHead; + HalfEdge eHead = _halfEdgeHead; Face f, fPrev; ContourVertex v, vPrev; HalfEdge e, ePrev; fPrev = fHead; - for (fPrev = fHead; (f = fPrev.nextFace) != fHead; fPrev = f) + for (fPrev = fHead; (f = fPrev._nextFace) != fHead; fPrev = f) { - if (f.prevFace != fPrev) + if (f._prevFace != fPrev) { throw new Exception(); } - e = f.halfEdgeThisIsLeftFaceOf; + e = f._halfEdgeThisIsLeftFaceOf; do { - if (e.otherHalfOfThisEdge == e) + if (e._otherHalfOfThisEdge == e) { throw new Exception(); } - if (e.otherHalfOfThisEdge.otherHalfOfThisEdge != e) + if (e._otherHalfOfThisEdge._otherHalfOfThisEdge != e) { throw new Exception(); } - if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge != e) + if (e._nextEdgeCCWAroundLeftFace._nextEdgeCCWAroundOrigin._otherHalfOfThisEdge != e) { throw new Exception(); } - if (e.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace != e) + if (e._nextEdgeCCWAroundOrigin._otherHalfOfThisEdge._nextEdgeCCWAroundLeftFace != e) { throw new Exception(); } - if (e.leftFace != f) + if (e._leftFace != f) { throw new Exception(); } - e = e.nextEdgeCCWAroundLeftFace; - } while (e != f.halfEdgeThisIsLeftFaceOf); + e = e._nextEdgeCCWAroundLeftFace; + } while (e != f._halfEdgeThisIsLeftFaceOf); } - if (f.prevFace != fPrev || f.halfEdgeThisIsLeftFaceOf != null) + if (f._prevFace != fPrev || f._halfEdgeThisIsLeftFaceOf != null) { throw new Exception(); } vPrev = vHead; - for (vPrev = vHead; (v = vPrev.nextVertex) != vHead; vPrev = v) + for (vPrev = vHead; (v = vPrev._nextVertex) != vHead; vPrev = v) { - if (v.prevVertex != vPrev) + if (v._prevVertex != vPrev) { throw new Exception(); } - e = v.edgeThisIsOriginOf; + e = v._edgeThisIsOriginOf; do { - if (e.otherHalfOfThisEdge == e) + if (e._otherHalfOfThisEdge == e) { throw new Exception(); } - if (e.otherHalfOfThisEdge.otherHalfOfThisEdge != e) + if (e._otherHalfOfThisEdge._otherHalfOfThisEdge != e) { throw new Exception(); } - if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge != e) + if (e._nextEdgeCCWAroundLeftFace._nextEdgeCCWAroundOrigin._otherHalfOfThisEdge != e) { throw new Exception(); } - if (e.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace != e) + if (e._nextEdgeCCWAroundOrigin._otherHalfOfThisEdge._nextEdgeCCWAroundLeftFace != e) { throw new Exception(); } - if (e.originVertex != v) + if (e._originVertex != v) { throw new Exception(); } - e = e.nextEdgeCCWAroundOrigin; - } while (e != v.edgeThisIsOriginOf); + e = e._nextEdgeCCWAroundOrigin; + } while (e != v._edgeThisIsOriginOf); } - if (v.prevVertex != vPrev || v.edgeThisIsOriginOf != null || v.clientIndex != 0) + if (v._prevVertex != vPrev || v._edgeThisIsOriginOf != null || v._clientIndex != 0) { throw new Exception(); } ePrev = eHead; - for (ePrev = eHead; (e = ePrev.nextHalfEdge) != eHead; ePrev = e) + for (ePrev = eHead; (e = ePrev._nextHalfEdge) != eHead; ePrev = e) { - if (e.otherHalfOfThisEdge.nextHalfEdge != ePrev.otherHalfOfThisEdge) + if (e._otherHalfOfThisEdge._nextHalfEdge != ePrev._otherHalfOfThisEdge) { throw new Exception(); } - if (e.otherHalfOfThisEdge == e) + if (e._otherHalfOfThisEdge == e) { throw new Exception(); } - if (e.otherHalfOfThisEdge.otherHalfOfThisEdge != e) + if (e._otherHalfOfThisEdge._otherHalfOfThisEdge != e) { throw new Exception(); } - if (e.originVertex == null) + if (e._originVertex == null) { throw new Exception(); } - if (e.directionVertex == null) + if (e.DirectionVertex == null) { throw new Exception(); } - if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge != e) + if (e._nextEdgeCCWAroundLeftFace._nextEdgeCCWAroundOrigin._otherHalfOfThisEdge != e) { throw new Exception(); } - if (e.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace != e) + if (e._nextEdgeCCWAroundOrigin._otherHalfOfThisEdge._nextEdgeCCWAroundLeftFace != e) { throw new Exception(); } } - if (e.otherHalfOfThisEdge.nextHalfEdge != ePrev.otherHalfOfThisEdge - || e.otherHalfOfThisEdge != this.otherHalfOfThisEdgeHead - || e.otherHalfOfThisEdge.otherHalfOfThisEdge != e - || e.originVertex != null || e.directionVertex != null - || e.leftFace != null || e.rightFace != null) + if (e._otherHalfOfThisEdge._nextHalfEdge != ePrev._otherHalfOfThisEdge + || e._otherHalfOfThisEdge != _otherHalfOfThisEdgeHead + || e._otherHalfOfThisEdge._otherHalfOfThisEdge != e + || e._originVertex != null || e.DirectionVertex != null + || e._leftFace != null || e.rightFace != null) { throw new Exception(); } @@ -881,20 +886,20 @@ public void CheckMesh() public bool SetWindingNumber(int value, bool keepOnlyBoundary) { HalfEdge e, eNext; - for (e = this.halfEdgeHead.nextHalfEdge; e != this.halfEdgeHead; e = eNext) + for (e = _halfEdgeHead._nextHalfEdge; e != _halfEdgeHead; e = eNext) { - eNext = e.nextHalfEdge; - if (e.rightFace.isInterior != e.leftFace.isInterior) + eNext = e._nextHalfEdge; + if (e.rightFace._isInterior != e._leftFace._isInterior) { /* This is a boundary edge (one side is interior, one is exterior). */ - e.winding = (e.leftFace.isInterior) ? value : -value; + e._winding = (e._leftFace._isInterior) ? value : -value; } else { /* Both regions are interior, or both are exterior. */ if (!keepOnlyBoundary) { - e.winding = 0; + e._winding = 0; } else { @@ -914,11 +919,11 @@ public bool SetWindingNumber(int value, bool keepOnlyBoundary) public void DiscardExterior() { Face f, next; - for (f = this.faceHead.nextFace; f != this.faceHead; f = next) + for (f = _faceHead._nextFace; f != _faceHead; f = next) { /* Since f will be destroyed, save its next pointer. */ - next = f.nextFace; - if (!f.isInterior) + next = f._nextFace; + if (!f._isInterior) { Mesh.meshZapFace(f); } @@ -932,11 +937,11 @@ public void DiscardExterior() public bool TessellateInterior() { Face f, next; - for (f = this.faceHead.nextFace; f != this.faceHead; f = next) + for (f = _faceHead._nextFace; f != _faceHead; f = next) { /* Make sure we don''t try to tessellate the new triangles. */ - next = f.nextFace; - if (f.isInterior) + next = f._nextFace; + if (f._isInterior) { if (!f.TessellateMonoRegion()) { diff --git a/Demo/Windows/GlyphTess.WinForms/Form1.cs b/Demo/Windows/GlyphTess.WinForms/Form1.cs index cb9d8137..e05ddc94 100644 --- a/Demo/Windows/GlyphTess.WinForms/Form1.cs +++ b/Demo/Windows/GlyphTess.WinForms/Form1.cs @@ -11,7 +11,7 @@ using DrawingGL; using DrawingGL.Text; // - +using Tesselate; namespace Test_WinForm_TessGlyph { @@ -187,8 +187,8 @@ void DrawOutput() { //extra coord (newly created) TessVertex2d extraVertex = tempVertexList[index - orgVertexCount]; - vtx[n] = (float)extraVertex.m_X; - vtx[n + 1] = (float)extraVertex.m_Y; + vtx[n] = (float)extraVertex.x; + vtx[n + 1] = (float)extraVertex.y; } else { diff --git a/Demo/Windows/GlyphTess.WinForms/GlyphTess.WinForms.csproj b/Demo/Windows/GlyphTess.WinForms/GlyphTess.WinForms.csproj index 87b06239..cf350c20 100644 --- a/Demo/Windows/GlyphTess.WinForms/GlyphTess.WinForms.csproj +++ b/Demo/Windows/GlyphTess.WinForms/GlyphTess.WinForms.csproj @@ -78,9 +78,6 @@ Tess\mesh.cs - - Tess\Tesselator.cs - Form