Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Add support for forced orientation (CW/CCW)

  • Loading branch information...
commit bfc78aa6219b0d5ede009e33a6c6ac7a61c3c635 1 parent 6bf2fc9
Remi Gillig authored
2  LibTessDotNet/Sources/MeshUtils.cs
@@ -108,7 +108,7 @@ public class Vertex
108 108
             internal Vec3 _coords;
109 109
             internal float _s, _t;
110 110
             internal PQHandle _pqHandle;
111  
-            internal int _n, _idx;
  111
+            internal int _n;
112 112
             internal object _data;
113 113
         }
114 114
 
1  LibTessDotNet/Sources/Sweep.cs
@@ -360,7 +360,6 @@ private void VertexWeights(MeshUtils.Vertex isect, MeshUtils.Vertex org, MeshUti
360 360
         private void GetIntersectData(MeshUtils.Vertex isect, MeshUtils.Vertex orgUp, MeshUtils.Vertex dstUp, MeshUtils.Vertex orgLo, MeshUtils.Vertex dstLo)
361 361
         {
362 362
             isect._coords = Vec3.Zero;
363  
-            isect._idx = MeshUtils.Undef;
364 363
             float w0, w1, w2, w3;
365 364
             VertexWeights(isect, orgUp, dstUp, out w0, out w1);
366 365
             VertexWeights(isect, orgLo, dstLo, out w2, out w3);
94  LibTessDotNet/Sources/Tess.cs
@@ -51,6 +51,13 @@ public enum ElementType
51 51
         BoundaryContours
52 52
     }
53 53
 
  54
+    public enum ContourOrientation
  55
+    {
  56
+        Original,
  57
+        Clockwise,
  58
+        CounterClockwise
  59
+    }
  60
+
54 61
     public struct ContourVertex
55 62
     {
56 63
         public Vec3 Position;
@@ -75,10 +82,8 @@ public partial class Tess
75 82
         private MeshUtils.Vertex _event;
76 83
 
77 84
         private CombineCallback _combineCallback;
78  
-        private int _vertexIndexCounter;
79 85
 
80 86
         private ContourVertex[] _vertices;
81  
-        private int[] _vertexIndices;
82 87
         private int _vertexCount;
83 88
         private int[] _elements;
84 89
         private int _elementCount;
@@ -89,7 +94,6 @@ public partial class Tess
89 94
         public float SUnitY = 1.0f;
90 95
 
91 96
         public ContourVertex[] Vertices { get { return _vertices; } }
92  
-        public int[] VertexIndices { get { return _vertexIndices; } }
93 97
         public int VertexCount { get { return _vertexCount; } }
94 98
 
95 99
         public int[] Elements { get { return _elements; } }
@@ -103,10 +107,7 @@ public Tess()
103 107
             _windingRule = WindingRule.EvenOdd;
104 108
             _mesh = null;
105 109
 
106  
-            _vertexIndexCounter = 0;
107  
-
108 110
             _vertices = null;
109  
-            _vertexIndices = null;
110 111
             _vertexCount = 0;
111 112
             _elements = null;
112 113
             _elementCount = 0;
@@ -474,7 +475,6 @@ private void OutputPolymesh(ElementType elementType, int polySize)
474 475
 
475 476
             _vertexCount = maxVertexCount;
476 477
             _vertices = new ContourVertex[_vertexCount];
477  
-            _vertexIndices = new int[_vertexCount];
478 478
 
479 479
             // Output vertices.
480 480
             for (v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
@@ -485,8 +485,6 @@ private void OutputPolymesh(ElementType elementType, int polySize)
485 485
                     int n = v._n;
486 486
                     _vertices[v._n].Position = v._coords;
487 487
                     _vertices[v._n].Data = v._data;
488  
-                    // Store vertex index.
489  
-                    _vertexIndices[v._n] = v._idx;
490 488
                 }
491 489
             }
492 490
 
@@ -556,11 +554,9 @@ private void OutputContours()
556 554
 
557 555
             _elements = new int[_elementCount * 2];
558 556
             _vertices = new ContourVertex[_vertexCount];
559  
-            _vertexIndices = new int[_vertexCount];
560 557
 
561 558
             int vertIndex = 0;
562 559
             int elementIndex = 0;
563  
-            int vertIndsIndex = 0;
564 560
 
565 561
             startVert = 0;
566 562
 
@@ -570,15 +566,12 @@ private void OutputContours()
570 566
 
571 567
                 vertCount = 0;
572 568
                 start = edge = f._anEdge;
573  
-                do
574  
-                {
  569
+                do {
575 570
                     _vertices[vertIndex++].Position = edge._Org._coords;
576 571
                     _vertices[vertIndex++].Data = edge._Org._data;
577  
-                    _vertexIndices[vertIndsIndex++] = edge._Org._idx;
578 572
                     ++vertCount;
579 573
                     edge = edge._Lnext;
580  
-                }
581  
-                while (edge != start);
  574
+                } while (edge != start);
582 575
 
583 576
                 _elements[elementIndex++] = startVert;
584 577
                 _elements[elementIndex++] = vertCount;
@@ -587,41 +580,67 @@ private void OutputContours()
587 580
             }
588 581
         }
589 582
 
  583
+        private float SignedArea(ContourVertex[] vertices)
  584
+        {
  585
+            float area = 0.0f;
  586
+
  587
+            for (int i = 0; i < vertices.Length; i++)
  588
+            {
  589
+                var v0 = vertices[i];
  590
+                var v1 = vertices[(i + 1) % vertices.Length];
  591
+
  592
+                area += v0.Position.X * v1.Position.Y;
  593
+                area -= v0.Position.Y * v1.Position.X;
  594
+            }
  595
+
  596
+            return area * 0.5f;
  597
+        }
  598
+
590 599
         public void AddContour(ContourVertex[] vertices)
591 600
         {
  601
+            AddContour(vertices, ContourOrientation.Original);
  602
+        }
  603
+
  604
+        public void AddContour(ContourVertex[] vertices, ContourOrientation forceOrientation)
  605
+        {
592 606
             if (_mesh == null)
593 607
             {
594 608
                 _mesh = new Mesh();
595 609
             }
596 610
 
  611
+            bool reverse = false;
  612
+            if (forceOrientation != ContourOrientation.Original)
  613
+            {
  614
+                float area = SignedArea(vertices);
  615
+                reverse = (forceOrientation == ContourOrientation.Clockwise && area < 0.0f) || (forceOrientation == ContourOrientation.CounterClockwise && area > 0.0f);
  616
+            }
  617
+
597 618
             MeshUtils.Edge e = null;
598 619
             for (int i = 0; i < vertices.Length; ++i)
599 620
             {
600 621
                 if (e == null)
601 622
                 {
602 623
                     e = _mesh.MakeEdge();
603  
-                    _mesh.Splice(e, e._Sym);
604  
-                }
605  
-                else
606  
-                {
607  
-                    // Create a new vertex and edge which immediately follow e
  624
+                    _mesh.Splice(e, e._Sym);
  625
+                }
  626
+                else
  627
+                {
  628
+                    // Create a new vertex and edge which immediately follow e
608 629
                     // in the ordering around the left face.
609  
-                    _mesh.SplitEdge(e);
610  
-                    e = e._Lnext;
611  
-                }
612  
-
  630
+                    _mesh.SplitEdge(e);
  631
+                    e = e._Lnext;
  632
+                }
  633
+
  634
+                int index = reverse ? vertices.Length - 1 - i : i;
613 635
                 // The new vertex is now e._Org.
614  
-                e._Org._coords = vertices[i].Position;
615  
-                e._Org._data = vertices[i].Data;
616  
-
617  
-                // Store the insertion number so that the vertex can be later recognized.
618  
-                e._Org._idx = _vertexIndexCounter++;
619  
-
620  
-                // The winding of an edge says how the winding number changes as we
621  
-                // cross from the edge''s right face to its left face.  We add the
622  
-                // vertices in such an order that a CCW contour will add +1 to
623  
-                // the winding number of the region inside the contour.
624  
-                e._winding = 1;
  636
+                e._Org._coords = vertices[index].Position;
  637
+                e._Org._data = vertices[index].Data;
  638
+
  639
+                // The winding of an edge says how the winding number changes as we
  640
+                // cross from the edge''s right face to its left face.  We add the
  641
+                // vertices in such an order that a CCW contour will add +1 to
  642
+                // the winding number of the region inside the contour.
  643
+                e._winding = 1;
625 644
                 e._Sym._winding = -1;
626 645
             }
627 646
         }
@@ -635,9 +654,6 @@ public void Tessellate(WindingRule windingRule, ElementType elementType, int pol
635 654
         {
636 655
             _vertices = null;
637 656
             _elements = null;
638  
-            _vertexIndices = null;
639  
-
640  
-            _vertexIndexCounter = 0;
641 657
 
642 658
             _windingRule = windingRule;
643 659
             _combineCallback = combineCallback;
11  README.md
Source Rendered
@@ -18,7 +18,9 @@ Provide a robust and fast tessellator (polygons with N vertices in the output) f
18 18
     - self-intersecting (see "star-intersect" sample)
19 19
     - with coincident vertices (see "clipper" sample)
20 20
     - advanced winding rules : even/odd, non zero, positive, negative, |winding| >= 2 (see "redbook-winding" sample)
21  
-* Custom vertex attributes (eg. UV coordinates) with merging callback
  21
+* Custom input
  22
+    - Custom vertex attributes (eg. UV coordinates) with merging callback
  23
+    - Force orientation of input contour (clockwise/counterclockwise, eg. for GIS systems, see "force-winding" sample)
22 24
 * Choice of output
23 25
     - polygons with N vertices (with N >= 3)
24 26
     - connected polygons (didn't quite tried this yet, but should work)
@@ -37,7 +39,7 @@ TBD
37 39
 
38 40
 * Better performance timing (eg. multiple loops instead of one)
39 41
 * Profile GC allocations
40  
-* Comparison with Poly2Tri
  42
+* Comparison with [Poly2Tri](http://code.google.com/p/poly2tri/)
41 43
 
42 44
 ### License
43 45
 
@@ -46,4 +48,7 @@ More information in LICENSE.txt.
46 48
 
47 49
 ### Links
48 50
 * [Reference implementation](http://oss.sgi.com/projects/ogl-sample) - the original SGI reference implementation
49  
-* [libtess2](http://code.google.com/p/libtess2/) - Mikko Mononen cleaned up the original GLU tesselator
  51
+* [libtess2](http://code.google.com/p/libtess2/) - Mikko Mononen cleaned up the original GLU tesselator
  52
+* [Poly2Tri](http://code.google.com/p/poly2tri/) - another triangulation library for .NET (other ports also available)
  53
+    - Does not support polygons from Clipper, more specifically vertices with same coordinates (coincident)
  54
+* [Clipper](http://www.angusj.com/delphi/clipper.php) - an open source freeware polygon clipping library
29  TessBed/Canvas.cs
@@ -2,6 +2,7 @@
2 2
 using System.Drawing;
3 3
 using System.Drawing.Drawing2D;
4 4
 using System.Windows.Forms;
  5
+using LibTessDotNet;
5 6
 
6 7
 namespace TessBed
7 8
 {
@@ -17,6 +18,22 @@ public Canvas()
17 18
             SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
18 19
         }
19 20
 
  21
+        private float SignedArea(Polygon polygon)
  22
+        {
  23
+            float area = 0.0f;
  24
+
  25
+            for (int i = 0; i < polygon.Count; i++)
  26
+            {
  27
+                var v0 = polygon[i];
  28
+                var v1 = polygon[(i + 1) % polygon.Count];
  29
+
  30
+                area += v0.X * v1.Y;
  31
+                area -= v0.Y * v1.X;
  32
+            }
  33
+
  34
+            return area * 0.5f;
  35
+        }
  36
+
20 37
         protected override void OnPaint(PaintEventArgs pe)
21 38
         {
22 39
             var g = pe.Graphics;
@@ -60,10 +77,18 @@ protected override void OnPaint(PaintEventArgs pe)
60 77
                 {
61 78
                     foreach (var polygon in Input)
62 79
                     {
  80
+                        bool reverse = false;
  81
+                        if (polygon.Orientation != ContourOrientation.Original)
  82
+                        {
  83
+                            float area = SignedArea(polygon);
  84
+                            reverse = (polygon.Orientation == ContourOrientation.Clockwise && area < 0.0f) || (polygon.Orientation == ContourOrientation.CounterClockwise && area > 0.0f);
  85
+                        }
  86
+
63 87
                         for (int i = 0; i < polygon.Count; i++)
64 88
                         {
65  
-                            var p0 = f(polygon[i]);
66  
-                            var p1 = f(polygon[(i + 1) % polygon.Count]);
  89
+                            int index = reverse ? polygon.Count - 1 - i : i;
  90
+                            var p0 = f(polygon[(index + (reverse ? 1 : 0)) % polygon.Count]);
  91
+                            var p1 = f(polygon[(index + (reverse ? 0 : 1)) % polygon.Count]);
67 92
 
68 93
                             g.DrawLine(ShowWinding ? penWinding : penContour, p0, p1);
69 94
                             g.FillEllipse(brushPoint, p0.X - 2.0f, p0.Y - 2.0f, 4.0f, 4.0f);
17  TessBed/Data/force-winding.dat
... ...
@@ -0,0 +1,17 @@
  1
+force cw
  2
+ 50.0,  50.0
  3
+300.0,  50.0 
  4
+300.0, 300.0
  5
+ 50.0, 300.0
  6
+
  7
+force ccw
  8
+100.0, 100.0
  9
+200.0, 100.0
  10
+200.0, 250.0
  11
+100.0, 250.0
  12
+
  13
+force ccw
  14
+150.0, 100.0
  15
+250.0, 100.0
  16
+250.0, 250.0
  17
+150.0, 250.0
32  TessBed/DataLoader.cs
@@ -4,6 +4,7 @@
4 4
 using System.Globalization;
5 5
 using System.IO;
6 6
 using System.Reflection;
  7
+using LibTessDotNet;
7 8
 
8 9
 namespace TessBed
9 10
 {
@@ -15,6 +16,8 @@ public struct PolygonPoint
15 16
 
16 17
     public class Polygon : List<PolygonPoint>
17 18
     {
  19
+        public ContourOrientation Orientation = ContourOrientation.Original;
  20
+
18 21
         public Polygon()
19 22
         {
20 23
 
@@ -46,8 +49,8 @@ public PolygonSet LoadDat(Stream fileStream)
46 49
             var polys = new PolygonSet();
47 50
             int lineNum = 0;
48 51
             string line;
49  
-            bool skipLine = false;
50 52
             Color currentColor = Color.White;
  53
+            ContourOrientation currentOrientation = ContourOrientation.Original;
51 54
             using (var stream = new StreamReader(fileStream))
52 55
             {
53 56
                 while ((line = stream.ReadLine()) != null)
@@ -58,7 +61,8 @@ public PolygonSet LoadDat(Stream fileStream)
58 61
                     {
59 62
                         if (points.Count > 0)
60 63
                         {
61  
-                            var p = new Polygon(points);
  64
+                            var p = new Polygon(points) { Orientation = currentOrientation };
  65
+                            currentOrientation = ContourOrientation.Original;
62 66
                             polys.Add(p);
63 67
                             points.Clear();
64 68
                         }
@@ -70,20 +74,22 @@ public PolygonSet LoadDat(Stream fileStream)
70 74
                     {
71 75
                         continue;
72 76
                     }
73  
-                    if (!skipLine && line.StartsWith("/*"))
  77
+                    if (line.StartsWith("force", true, CultureInfo.InvariantCulture))
74 78
                     {
75  
-                        skipLine = true;
76  
-                        continue;
77  
-                    }
78  
-                    else if (skipLine)
79  
-                    {
80  
-                        if (line.StartsWith("*/"))
  79
+                        var force = line.Split(new[] { ' ', ',', '\t' }, StringSplitOptions.RemoveEmptyEntries);
  80
+                        if (force.Length == 2)
81 81
                         {
82  
-                            skipLine = false;
  82
+                            if (string.Compare(force[1], "cw", true) == 0)
  83
+                            {
  84
+                                currentOrientation = ContourOrientation.Clockwise;
  85
+                            }
  86
+                            if (string.Compare(force[1], "ccw", true) == 0)
  87
+                            {
  88
+                                currentOrientation = ContourOrientation.CounterClockwise;
  89
+                            }
83 90
                         }
84  
-                        continue;
85 91
                     }
86  
-                    if (line.StartsWith("color", true, CultureInfo.InvariantCulture))
  92
+                    else if (line.StartsWith("color", true, CultureInfo.InvariantCulture))
87 93
                     {
88 94
                         var rgba = line.Split(new[] { ' ', ',', '\t' }, StringSplitOptions.RemoveEmptyEntries);
89 95
                         int r = 255, g = 255, b = 255, a = 255;
@@ -128,7 +134,7 @@ public PolygonSet LoadDat(Stream fileStream)
128 134
 
129 135
                 if (points.Count > 0)
130 136
                 {
131  
-                    Polygon p = new Polygon(points);
  137
+                    Polygon p = new Polygon(points) { Orientation = currentOrientation };
132 138
                     polys.Add(p);
133 139
                 }
134 140
             }
2  TessBed/MainForm.cs
@@ -167,7 +167,7 @@ private void RefreshAsset(string name)
167 167
                     v[i].Data = poly[i].Color;
168 168
                 }
169 169
                 _sw.Start();
170  
-                _tess.AddContour(v);
  170
+                _tess.AddContour(v, poly.Orientation);
171 171
                 _sw.Stop();
172 172
             }
173 173
 
BIN  TessBed/Misc/screenshot.png
1  TessBed/TessBed.csproj
@@ -84,6 +84,7 @@
84 84
     <EmbeddedResource Include="Data\letterE.dat" />
85 85
     <EmbeddedResource Include="Data\star-intersect.dat" />
86 86
     <EmbeddedResource Include="Data\redbook-winding.dat" />
  87
+    <EmbeddedResource Include="Data\force-winding.dat" />
87 88
     <None Include="Properties\Settings.settings">
88 89
       <Generator>SettingsSingleFileGenerator</Generator>
89 90
       <LastGenOutput>Settings.Designer.cs</LastGenOutput>

1 note on commit bfc78aa

Remi Gillig
Owner

I definitely need to profile the library, on the screens the difference is huge and I don't know why.

Please sign in to comment.
Something went wrong with that request. Please try again.