(function() { var analyze_convexity, analyze_orientation, refresh; window.main = function() { var drag, height, polygon, svg, vis, width; width = 960; height = 500; svg = d3.select('body').append('svg').attr('width', width).attr('height', height); vis = svg.append('g').attr('transform', 'translate(250,85)'); /* define a drag behavior */ drag = d3.behavior.drag().on('drag', function(d) { /* move the circle */ d.x = d3.event.x; d.y = d3.event.y; return refresh(vis, polygon, drag); }); polygon = { points: [ { x: 30, y: 30 }, { x: 20, y: 130 }, { x: 100, y: 280 }, { x: 200, y: 320 }, { x: 330, y: 260 }, { x: 430, y: 130 }, { x: 430, y: 80 }, { x: 380, y: 30 }, { x: 230, y: 20 } ] }; /* draw the base shapes */ vis.selectAll('.polygon').data([polygon]).enter().append('path').attr('class', 'polygon'); vis.selectAll('.vertex').data(polygon.points).enter().append('circle').attr('class', 'vertex').attr('r', 4); /* update the shapes according to the analysis */ return refresh(vis, polygon, drag); }; refresh = function(vis, polygon, drag) { analyze_convexity(polygon); vis.selectAll('.polygon').attr('d', function(d) { return "M" + (d.points.map(function(p) { return p.x + ' ' + p.y; }).join('L')) + "Z"; }).attr('stroke', function(d) { if (d.orientation === 'cw') { return '#FF8900'; } else { return '#086FA1'; } }).attr('fill', function(d) { if (d.convex) { if (d.orientation === 'cw') { return '#FF8900'; } else { return '#086FA1'; } } else { return 'white'; } }); return vis.selectAll('.vertex').attr('cx', function(d) { return d.x; }).attr('cy', function(d) { return d.y; }).attr('stroke', function(d) { if (d.orientation === 'cw') { return '#FF8900'; } else { return '#086FA1'; } }).attr('fill', function(d) { if (d.convex) { if (d.orientation === 'cw') { return '#FF8900'; } else { return '#086FA1'; } } else { return 'white'; } }).call(drag); }; /* write into the polygon whether it's in clockwise or counterclockwise orientation */ analyze_orientation = function(polygon) { var i, j, z, z_sum, _ref; z_sum = 0; for (i = 0, _ref = polygon.points.length; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { j = (i + 1) % polygon.points.length; z = (polygon.points[j].x - polygon.points[i].x) * (polygon.points[j].y + polygon.points[i].y); z_sum += z; } return polygon.orientation = z_sum > 0 ? 'ccw' : 'cw'; }; /* write into the polygon wheter it is convex or concave */ /* also, for each vertex, store its orientation (cw or ccw) and if it determines the concavity (i.e. if it falls inside the convex hull) */ analyze_convexity = function(polygon) { var i, j, k, last_orientation, orientation, z, _ref, _results; analyze_orientation(polygon); /* WARNING no complex polygons, no less than 3 vertices, no colinear points */ polygon.convex = true; _results = []; for (i = 0, _ref = polygon.points.length; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { j = (i + 1) % polygon.points.length; k = (i + 2) % polygon.points.length; z = (polygon.points[j].x - polygon.points[i].x) * (polygon.points[k].y - polygon.points[j].y); z -= (polygon.points[j].y - polygon.points[i].y) * (polygon.points[k].x - polygon.points[j].x); orientation = z > 0 ? 'cw' : 'ccw'; polygon.points[j].orientation = orientation; polygon.points[j].convex = polygon.points[j].orientation === polygon.orientation; if ((typeof last_orientation !== "undefined" && last_orientation !== null) && orientation !== last_orientation) { polygon.convex = false; } _results.push(last_orientation = orientation); } return _results; }; }).call(this);