|
window.main = () -> |
|
width = 960 |
|
height = 500 |
|
|
|
svg = d3.select('body').append('svg') |
|
.attr('width', width) |
|
.attr('height', height) |
|
|
|
svg.append('text') |
|
.text('CCW') |
|
.attr('class', 'ccw') |
|
.attr('x', 50) |
|
.attr('y', 160) |
|
|
|
svg.append('text') |
|
.text('CW') |
|
.attr('class', 'cw') |
|
.attr('x', 50) |
|
.attr('y', 360) |
|
|
|
vis = svg.append('g') |
|
.attr('transform', 'translate(150,65)') |
|
|
|
polygons = [{ |
|
points: [{x:30, y:30}, {x:30, y:130}, {x:130, y:130}, {x:130, y:30}] |
|
},{ |
|
points: [{x:30, y:230}, {x:130, y:230}, {x:130, y:330}, {x:30, y:330}] |
|
},{ |
|
points: [{x:230, y:30}, {x:230, y:130}, {x:330, y:130}, {x:330, y:80}, {x:280, y:80}, {x:280, y:30}] |
|
},{ |
|
points: [{x:230, y:230}, {x:280, y:230}, {x:280, y:280}, {x:330, y:280}, {x:330, y:330}, {x:230, y:330}] |
|
},{ |
|
points: [{x:430, y:30}, {x:430, y:130}, {x:530, y:130}, {x:530, y:80}, {x:480, y:30}] |
|
},{ |
|
points: [{x:430, y:230}, {x:480, y:230}, {x:530, y:280}, {x:530, y:330}, {x:430, y:330}] |
|
},{ |
|
points: [{x:630, y:30}, {x:660, y:80}, {x:630, y:130}, {x:680, y:100}, {x:730, y:130}, {x:700, y:80}, {x:730, y:30}, {x:680, y:60}] |
|
},{ |
|
points: [{x:630, y:230}, {x:680, y:260}, {x:730, y:230}, {x:700, y:280}, {x:730, y:330}, {x:680, y:300}, {x:630, y:330}, {x:660, y:280}] |
|
}] |
|
|
|
analyze_convexity(polygon) for polygon in polygons |
|
|
|
### draw the results ### |
|
vis.selectAll('.polygon') |
|
.data(polygons) |
|
.enter().append('path') |
|
.attr('class', 'polygon') |
|
.attr('d', (d) -> "M#{d.points.map((p)->p.x+' '+p.y).join('L')}Z") |
|
.attr('stroke', (d) -> if d.orientation is 'cw' then '#FF8900' else '#086FA1') |
|
.attr('fill', (d) -> if d.convex then (if d.orientation is 'cw' then '#FF8900' else '#086FA1') else 'white') |
|
|
|
all_points = polygons.reduce(((p,c) -> p.concat c.points),[]) |
|
vis.selectAll('.vertex') |
|
.data(all_points) |
|
.enter().append('circle') |
|
.attr('class', 'vertex') |
|
.attr('r', 4) |
|
.attr('cx', (d) -> d.x) |
|
.attr('cy', (d) -> d.y) |
|
.attr('stroke', (d) -> if d.orientation is 'cw' then '#FF8900' else '#086FA1') |
|
.attr('fill', (d) -> if d.convex then (if d.orientation is 'cw' then '#FF8900' else '#086FA1') else 'white') |
|
|
|
### write into the polygon whether it's in clockwise or counterclockwise orientation ### |
|
analyze_orientation = (polygon) -> |
|
z_sum = 0 |
|
|
|
for i in [0...polygon.points.length] |
|
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 |
|
|
|
polygon.orientation = if z_sum > 0 then 'ccw' else '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 = (polygon) -> |
|
analyze_orientation(polygon) |
|
|
|
### WARNING no complex polygons, no less than 3 vertices, no colinear points ### |
|
polygon.convex = true |
|
|
|
for i in [0...polygon.points.length] |
|
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 = if z > 0 then 'cw' else 'ccw' |
|
|
|
polygon.points[j].orientation = orientation |
|
polygon.points[j].convex = polygon.points[j].orientation is polygon.orientation |
|
|
|
if last_orientation? and orientation isnt last_orientation |
|
polygon.convex = false |
|
|
|
last_orientation = orientation |
|
|