Triangulation of polygons is the procedure that divides a polygon into triangles. The algorithm discussed below accepts a polygon in the form of an array of Point2D objects that contain the polygon vertices in counter-clockwise order, and return an array of type Triangle2D objects, if the given polygon is valid.
If the given polygon is valid and has n vertices, the result triangle array should have length n - 2. The algorithm works as follows. Traversing the vertices of the polygon in counter-clockwise order, for every three successive vertices P, Q and R of which Q is a convex vertex (with an angle less than 180°), we cut the triangle PQR off the polygon if this triangle does not contain any of the other polygon vertices.
The Silverlight plugin is used to visualise the given polygon and its constituent triangles, which appear in different colors.
The algorithm is started by specifying a CircularLink class that implementation a circularly-linked list. The initializefunction build the circular list by assign the next property of each element, thanks for the advanced Javascript Object-Oriented features.
The CircularLink class also has a remove function that is used when removing vertices from the polygon. The linear circular list is maintained after an element is removed.
CircularLink = Class.create(); CircularLink.prototype = { initialize: function(nodes) { this.nodes = nodes; var n = this.nodes.length; for(var i= 0;i<n-1;i++) { this.nodes[i].next = this.nodes[i+1]; } this.nodes[n-1].next = this.nodes[0]; }, remove: function(node) { var n = this.nodes.length; for(var i= 0;i<n;i++){ if (this.nodes[i] == node) { if (i == 0) { this.nodes[n-1].next = node.next; } else { this.nodes[i-1].next = node.next; } this.nodes.splice(i,1); return true; } } return false; }, get: function(index) { return this.nodes[index]; } };
The Polygon2D class implements the above triangulation algorithm in its triangulate function.
Point2D = Class.create(); Point2D.prototype = { initialize: function(x, y) { this.x = x; this.y = y; }, }; Polygon2D = Class.create(); Polygon2D.prototype = { initialize: function(vertices) { this.vertices = vertices; }, triangulate: function() { if (!((this.vertices) && (this.vertices.length > 0))) { return false; } var result = new Array(); var link = new CircularLink(this.vertices); var n = this.vertices.length; var A = link.get(0); for(var k =0; k<n-2;k++) { var triaFound = false; var count = 0; while (!triaFound && ++count < n) { var B = A.next; var C = B.next; var t = new Triangle2D([A, B, C]); if (t.area() > 0) { var p = C.next; while (p != A) { if (t.contain(p)) { break; } p = p.next; } if (p == A) { result.push(t); link.remove(B); triaFound = true; } } A = A.next; } if (count == n) { return false; } } return result; } };
The Triangle2D class, which is a subclass of Polygon2D, defines two useful tools: area computes the area of the triangle, and contain checks if the triangle contains the given point p.
Triangle2D = Class.create(); Triangle2D.prototype = Object.extend(new Polygon2D(), { area: function() { var a = this.vertices[0]; var b = this.vertices[1]; var c = this.vertices[2]; return ((a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x))/2.0; }, contain: function(p) { var a = this.vertices[0]; var b = this.vertices[1]; var c = this.vertices[2]; var t1 = new Triangle2D([a,b,p]); var t2 = new Triangle2D([b,c,p]); var t3 = new Triangle2D([c,a,p]); return ( t1.area() >= 0 && t2.area() >= 0 && t3.area() >= 0 ); } });
The Silverlight onLoad function testTriangulate creates Silverlight Polygon objects for each of the result triangle using different Fill color based on given test data set.
function testTriangulate(plugin, context, sender) { var vertices = new Array(); vertices.push(new Point2D(20,0)); vertices.push(new Point2D(40,40)); vertices.push(new Point2D(0,20)); vertices.push(new Point2D(-40,40)); vertices.push(new Point2D(-20,0)); vertices.push(new Point2D(-40,-40)); vertices.push(new Point2D(0,-20)); vertices.push(new Point2D(40,-40)); var center = new Point2D(150,150); var colors = ['#FFff5555', '#FF5555ff', '#FF55ff55', '#FFffff55', '#FFff55ff', '#FF55ffff', '#FFffafaf', '#FF808080']; var polygon = new Polygon2D(vertices); var result = polygon.triangulate(); for(var i = 0; i<result.length; i++) { var plugin = sender.getHost(); var p = plugin.content.createFromXaml("<Polygon/>"); var x0 = center.x + result[i].vertices[0].x; var y0 = center.y - result[i].vertices[0].y; var x1 = center.x + result[i].vertices[1].x; var y1 = center.y - result[i].vertices[1].y; var x2 = center.x + result[i].vertices[2].x; var y2 = center.y - result[i].vertices[2].y; var s = x0 + "," + y0 + "," + x1 + "," + y1 + "," + x2 + "," + y2 + "," + x0 + "," + y0; p.Points = s; p.Fill = colors[i]; p.Stroke = "White"; p.StrokeThickness = "2"; sender.children.add(p); } }