|
(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); |