Permalink
Browse files

Add support for forced orientation (CW/CCW)

  • Loading branch information...
speps committed Mar 10, 2012
1 parent 6bf2fc9 commit bfc78aa6219b0d5ede009e33a6c6ac7a61c3c635
@@ -108,7 +108,7 @@ public class Vertex
internal Vec3 _coords;
internal float _s, _t;
internal PQHandle _pqHandle;
- internal int _n, _idx;
+ internal int _n;
internal object _data;
}
@@ -360,7 +360,6 @@ private void VertexWeights(MeshUtils.Vertex isect, MeshUtils.Vertex org, MeshUti
private void GetIntersectData(MeshUtils.Vertex isect, MeshUtils.Vertex orgUp, MeshUtils.Vertex dstUp, MeshUtils.Vertex orgLo, MeshUtils.Vertex dstLo)
{
isect._coords = Vec3.Zero;
- isect._idx = MeshUtils.Undef;
float w0, w1, w2, w3;
VertexWeights(isect, orgUp, dstUp, out w0, out w1);
VertexWeights(isect, orgLo, dstLo, out w2, out w3);
@@ -51,6 +51,13 @@ public enum ElementType
BoundaryContours
}
+ public enum ContourOrientation
+ {
+ Original,
+ Clockwise,
+ CounterClockwise
+ }
+
public struct ContourVertex
{
public Vec3 Position;
@@ -75,10 +82,8 @@ public partial class Tess
private MeshUtils.Vertex _event;
private CombineCallback _combineCallback;
- private int _vertexIndexCounter;
private ContourVertex[] _vertices;
- private int[] _vertexIndices;
private int _vertexCount;
private int[] _elements;
private int _elementCount;
@@ -89,7 +94,6 @@ public partial class Tess
public float SUnitY = 1.0f;
public ContourVertex[] Vertices { get { return _vertices; } }
- public int[] VertexIndices { get { return _vertexIndices; } }
public int VertexCount { get { return _vertexCount; } }
public int[] Elements { get { return _elements; } }
@@ -103,10 +107,7 @@ public Tess()
_windingRule = WindingRule.EvenOdd;
_mesh = null;
- _vertexIndexCounter = 0;
-
_vertices = null;
- _vertexIndices = null;
_vertexCount = 0;
_elements = null;
_elementCount = 0;
@@ -474,7 +475,6 @@ private void OutputPolymesh(ElementType elementType, int polySize)
_vertexCount = maxVertexCount;
_vertices = new ContourVertex[_vertexCount];
- _vertexIndices = new int[_vertexCount];
// Output vertices.
for (v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
@@ -485,8 +485,6 @@ private void OutputPolymesh(ElementType elementType, int polySize)
int n = v._n;
_vertices[v._n].Position = v._coords;
_vertices[v._n].Data = v._data;
- // Store vertex index.
- _vertexIndices[v._n] = v._idx;
}
}
@@ -556,11 +554,9 @@ private void OutputContours()
_elements = new int[_elementCount * 2];
_vertices = new ContourVertex[_vertexCount];
- _vertexIndices = new int[_vertexCount];
int vertIndex = 0;
int elementIndex = 0;
- int vertIndsIndex = 0;
startVert = 0;
@@ -570,15 +566,12 @@ private void OutputContours()
vertCount = 0;
start = edge = f._anEdge;
- do
- {
+ do {
_vertices[vertIndex++].Position = edge._Org._coords;
_vertices[vertIndex++].Data = edge._Org._data;
- _vertexIndices[vertIndsIndex++] = edge._Org._idx;
++vertCount;
edge = edge._Lnext;
- }
- while (edge != start);
+ } while (edge != start);
_elements[elementIndex++] = startVert;
_elements[elementIndex++] = vertCount;
@@ -587,41 +580,67 @@ private void OutputContours()
}
}
+ private float SignedArea(ContourVertex[] vertices)
+ {
+ float area = 0.0f;
+
+ for (int i = 0; i < vertices.Length; i++)
+ {
+ var v0 = vertices[i];
+ var v1 = vertices[(i + 1) % vertices.Length];
+
+ area += v0.Position.X * v1.Position.Y;
+ area -= v0.Position.Y * v1.Position.X;
+ }
+
+ return area * 0.5f;
+ }
+
public void AddContour(ContourVertex[] vertices)
+ {
+ AddContour(vertices, ContourOrientation.Original);
+ }
+
+ public void AddContour(ContourVertex[] vertices, ContourOrientation forceOrientation)
{
if (_mesh == null)
{
_mesh = new Mesh();
}
+ bool reverse = false;
+ if (forceOrientation != ContourOrientation.Original)
+ {
+ float area = SignedArea(vertices);
+ reverse = (forceOrientation == ContourOrientation.Clockwise && area < 0.0f) || (forceOrientation == ContourOrientation.CounterClockwise && area > 0.0f);
+ }
+
MeshUtils.Edge e = null;
for (int i = 0; i < vertices.Length; ++i)
{
if (e == null)
{
e = _mesh.MakeEdge();
- _mesh.Splice(e, e._Sym);
- }
- else
- {
- // Create a new vertex and edge which immediately follow e
+ _mesh.Splice(e, e._Sym);
+ }
+ else
+ {
+ // Create a new vertex and edge which immediately follow e
// in the ordering around the left face.
- _mesh.SplitEdge(e);
- e = e._Lnext;
- }
-
+ _mesh.SplitEdge(e);
+ e = e._Lnext;
+ }
+
+ int index = reverse ? vertices.Length - 1 - i : i;
// The new vertex is now e._Org.
- e._Org._coords = vertices[i].Position;
- e._Org._data = vertices[i].Data;
-
- // Store the insertion number so that the vertex can be later recognized.
- e._Org._idx = _vertexIndexCounter++;
-
- // 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._Org._coords = vertices[index].Position;
+ e._Org._data = vertices[index].Data;
+
+ // 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._Sym._winding = -1;
}
}
@@ -635,9 +654,6 @@ public void Tessellate(WindingRule windingRule, ElementType elementType, int pol
{
_vertices = null;
_elements = null;
- _vertexIndices = null;
-
- _vertexIndexCounter = 0;
_windingRule = windingRule;
_combineCallback = combineCallback;
View
@@ -18,7 +18,9 @@ Provide a robust and fast tessellator (polygons with N vertices in the output) f
- self-intersecting (see "star-intersect" sample)
- with coincident vertices (see "clipper" sample)
- advanced winding rules : even/odd, non zero, positive, negative, |winding| >= 2 (see "redbook-winding" sample)
-* Custom vertex attributes (eg. UV coordinates) with merging callback
+* Custom input
+ - Custom vertex attributes (eg. UV coordinates) with merging callback
+ - Force orientation of input contour (clockwise/counterclockwise, eg. for GIS systems, see "force-winding" sample)
* Choice of output
- polygons with N vertices (with N >= 3)
- connected polygons (didn't quite tried this yet, but should work)
@@ -37,7 +39,7 @@ TBD
* Better performance timing (eg. multiple loops instead of one)
* Profile GC allocations
-* Comparison with Poly2Tri
+* Comparison with [Poly2Tri](http://code.google.com/p/poly2tri/)
### License
@@ -46,4 +48,7 @@ More information in LICENSE.txt.
### Links
* [Reference implementation](http://oss.sgi.com/projects/ogl-sample) - the original SGI reference implementation
-* [libtess2](http://code.google.com/p/libtess2/) - Mikko Mononen cleaned up the original GLU tesselator
+* [libtess2](http://code.google.com/p/libtess2/) - Mikko Mononen cleaned up the original GLU tesselator
+* [Poly2Tri](http://code.google.com/p/poly2tri/) - another triangulation library for .NET (other ports also available)
+ - Does not support polygons from Clipper, more specifically vertices with same coordinates (coincident)
+* [Clipper](http://www.angusj.com/delphi/clipper.php) - an open source freeware polygon clipping library
View
@@ -2,6 +2,7 @@
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
+using LibTessDotNet;
namespace TessBed
{
@@ -17,6 +18,22 @@ public Canvas()
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
}
+ private float SignedArea(Polygon polygon)
+ {
+ float area = 0.0f;
+
+ for (int i = 0; i < polygon.Count; i++)
+ {
+ var v0 = polygon[i];
+ var v1 = polygon[(i + 1) % polygon.Count];
+
+ area += v0.X * v1.Y;
+ area -= v0.Y * v1.X;
+ }
+
+ return area * 0.5f;
+ }
+
protected override void OnPaint(PaintEventArgs pe)
{
var g = pe.Graphics;
@@ -60,10 +77,18 @@ protected override void OnPaint(PaintEventArgs pe)
{
foreach (var polygon in Input)
{
+ bool reverse = false;
+ if (polygon.Orientation != ContourOrientation.Original)
+ {
+ float area = SignedArea(polygon);
+ reverse = (polygon.Orientation == ContourOrientation.Clockwise && area < 0.0f) || (polygon.Orientation == ContourOrientation.CounterClockwise && area > 0.0f);
+ }
+
for (int i = 0; i < polygon.Count; i++)
{
- var p0 = f(polygon[i]);
- var p1 = f(polygon[(i + 1) % polygon.Count]);
+ int index = reverse ? polygon.Count - 1 - i : i;
+ var p0 = f(polygon[(index + (reverse ? 1 : 0)) % polygon.Count]);
+ var p1 = f(polygon[(index + (reverse ? 0 : 1)) % polygon.Count]);
g.DrawLine(ShowWinding ? penWinding : penContour, p0, p1);
g.FillEllipse(brushPoint, p0.X - 2.0f, p0.Y - 2.0f, 4.0f, 4.0f);
@@ -0,0 +1,17 @@
+force cw
+ 50.0, 50.0
+300.0, 50.0
+300.0, 300.0
+ 50.0, 300.0
+
+force ccw
+100.0, 100.0
+200.0, 100.0
+200.0, 250.0
+100.0, 250.0
+
+force ccw
+150.0, 100.0
+250.0, 100.0
+250.0, 250.0
+150.0, 250.0
View
@@ -4,6 +4,7 @@
using System.Globalization;
using System.IO;
using System.Reflection;
+using LibTessDotNet;
namespace TessBed
{
@@ -15,6 +16,8 @@ public struct PolygonPoint
public class Polygon : List<PolygonPoint>
{
+ public ContourOrientation Orientation = ContourOrientation.Original;
+
public Polygon()
{
@@ -46,8 +49,8 @@ public PolygonSet LoadDat(Stream fileStream)
var polys = new PolygonSet();
int lineNum = 0;
string line;
- bool skipLine = false;
Color currentColor = Color.White;
+ ContourOrientation currentOrientation = ContourOrientation.Original;
using (var stream = new StreamReader(fileStream))
{
while ((line = stream.ReadLine()) != null)
@@ -58,7 +61,8 @@ public PolygonSet LoadDat(Stream fileStream)
{
if (points.Count > 0)
{
- var p = new Polygon(points);
+ var p = new Polygon(points) { Orientation = currentOrientation };
+ currentOrientation = ContourOrientation.Original;
polys.Add(p);
points.Clear();
}
@@ -70,20 +74,22 @@ public PolygonSet LoadDat(Stream fileStream)
{
continue;
}
- if (!skipLine && line.StartsWith("/*"))
+ if (line.StartsWith("force", true, CultureInfo.InvariantCulture))
{
- skipLine = true;
- continue;
- }
- else if (skipLine)
- {
- if (line.StartsWith("*/"))
+ var force = line.Split(new[] { ' ', ',', '\t' }, StringSplitOptions.RemoveEmptyEntries);
+ if (force.Length == 2)
{
- skipLine = false;
+ if (string.Compare(force[1], "cw", true) == 0)
+ {
+ currentOrientation = ContourOrientation.Clockwise;
+ }
+ if (string.Compare(force[1], "ccw", true) == 0)
+ {
+ currentOrientation = ContourOrientation.CounterClockwise;
+ }
}
- continue;
}
- if (line.StartsWith("color", true, CultureInfo.InvariantCulture))
+ else if (line.StartsWith("color", true, CultureInfo.InvariantCulture))
{
var rgba = line.Split(new[] { ' ', ',', '\t' }, StringSplitOptions.RemoveEmptyEntries);
int r = 255, g = 255, b = 255, a = 255;
@@ -128,7 +134,7 @@ public PolygonSet LoadDat(Stream fileStream)
if (points.Count > 0)
{
- Polygon p = new Polygon(points);
+ Polygon p = new Polygon(points) { Orientation = currentOrientation };
polys.Add(p);
}
}
View
@@ -167,7 +167,7 @@ private void RefreshAsset(string name)
v[i].Data = poly[i].Color;
}
_sw.Start();
- _tess.AddContour(v);
+ _tess.AddContour(v, poly.Orientation);
_sw.Stop();
}
View
Binary file not shown.
Oops, something went wrong.

1 comment on commit bfc78aa

@speps

This comment has been minimized.

Show comment Hide comment
@speps

speps Mar 10, 2012

Owner

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

Owner

speps commented on bfc78aa Mar 10, 2012

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.