javascript - Given one side of a regular polygon, find the remaining sides -
we given:
- the number of sides of regular polygon
- one side defined coordinates (x1, y1) , (x2, y2)
i have find out remaining coordinates. how can this?
suppose have turtle has drawn 1 segment of n
-sided regular polygon moving x1, y1
x2, y2
. draw remaining sides, n-1
times:
- turn angle
- move forward distance
the angle have turn 360/n
degrees. example, if we're drawing triangle, turtle has turn 120 degrees after each segment. if we're drawing triangle clockwise, subtract 120 degrees current orientation of turtle. if we're drawing counterclockwise, add 120 degrees.
the distance move forward length of first segment. can calculate length of segment using pythagorean theorem. in javascript, can implement thus:
var dx = x2-x1, dy = y2-y1, length = math.sqrt(dx*dx + dy*dy);
the initial orientation of turtle same angle of first line segment, can calculate taking inverse cosine of dx/length
:
var angle = math.acos(dx/length); if (dy < 0) { angle = 2*math.pi - angle; }
to make use of turning angle , segment length, have implement following turtle operations:
- set turtle position
x, y
- set turtle orientation
angle
- move turtle
distance
units forward in current orientation - add
delta
turtle orientation - query current position
x, y
of turtle
after operations implemented, can write loop iterates n-1
times. in each iteration, turn , move forward prescribed amounts, query turtle position, , print coordinates.
to implement turtle operations, have store position , orientation of turtle. here easy way so:
var turtle = { x: 0, y: 0, angle: 0 };
to move turtle distance
units forward in current orientation, use basic trigonometry:
turtle.x += math.cos(turtle.angle) * distance; turtle.y += math.sin(turtle.angle) * distance;
note trigonometric functions in javascript work radians rather degrees. there 2π radians in circle, hence π radians in semicircle. if have angle r
expressed in radians, equivalent in degrees r / math.pi * 180
.
when add or subtract value turtle orientation, possible end angle less 0 or greater 2π. not affect our trigonometric calculations, can make program difficult debug. ensure angle in range [0, 2π), can following whenever turtle.angle
gets modified:
turtle.angle -= math.floor(turtle.angle / (2*math.pi)) * 2*math.pi;
i have written code snippet demonstrate turtle approach. run code clicking blue button below, click , drag draw first segment of polygon. can change number of sides clicking on plus , minus symbols.
var polygon = { color: { axes: '#ccc', sides: { hover: { plain: '#dddfa4', special: '#9d9c64' }, final: { plain: '#b0c598', special: '#4f7337' } } } }; polygon.turtle = { x: 0, y: 0, angle: 0 }; polygon.turtle.setposition = function (x, y) { var g = polygon, turtle = g.turtle, context = g.context, origin = g.origin; turtle.x = x; turtle.y = y; context.moveto(origin.left + turtle.x, origin.top - turtle.y); }; polygon.turtle.setangle = function (angle) { var g = polygon, turtle = g.turtle; turtle.angle = angle; }; polygon.turtle.left = function (delta) { var g = polygon, turtle = g.turtle; turtle.angle = g.normalizeangle(turtle.angle + delta); }; polygon.turtle.right = function (delta) { var g = polygon, turtle = g.turtle; turtle.angle = g.normalizeangle(turtle.angle - delta); }; polygon.normalizeangle = function (angle) { angle -= math.floor(angle / (2*math.pi)) * 2*math.pi; return angle; }; polygon.turtle.forward = function (distance) { var g = polygon, turtle = g.turtle, canvas = g.canvas, context = g.context, origin = g.origin; turtle.x += math.cos(turtle.angle) * distance; turtle.y += math.sin(turtle.angle) * distance; context.lineto(origin.left + turtle.x, origin.top - turtle.y); }; polygon.resizecanvas = function() { var g = polygon, canvas = g.canvas, context = g.context, width = canvas.width = window.innerwidth, height = canvas.height = window.innerheight; g.origin = { left: math.floor(width/2), top: math.floor(height/2) }; g.drawaxes(); }; polygon.drawaxes = function() { var g = polygon, canvas = g.canvas, context = g.context, origin = g.origin, color = g.color; context.linewidth = 2; context.strokestyle = color.axes; context.beginpath(); context.moveto(origin.left, 0); context.lineto(origin.left, canvas.height); context.moveto(0, origin.top); context.lineto(canvas.width, origin.top); context.stroke(); }; polygon.drawpolygon = function (situation) { var g = polygon, canvas = g.canvas, context = g.context, turtle = g.turtle, color = g.color, n = parseint(document.getelementbyid('numsides').innerhtml, 10), turn = 2*math.pi / n, x1 = g.x1, y1 = g.y1, x2 = g.x2, y2 = g.y2, dx = x2-x1, dy = y2-y1, length = math.sqrt(dx*dx + dy*dy); var angle = math.acos(dx/length); if (dy < 0) { angle = 2*math.pi - angle; } context.clearrect(0, 0, canvas.width, canvas.height); g.drawaxes(); context.linewidth = 4; context.linecap = 'round'; context.beginpath(); context.strokestyle = color.sides[situation].plain; turtle.setposition(x1, y1); turtle.setangle(angle); (var = 0; < n; ++i) { turtle.forward(length); turtle.left(turn); } context.closepath(); context.stroke(); context.strokestyle = color.sides[situation].special; context.beginpath(); turtle.setposition(x1, y1); turtle.forward(length); context.stroke(); } polygon.load = function () { var g = polygon, canvas = g.canvas = document.getelementbyid('surface'), context = g.context = canvas.getcontext('2d'), display = { begin: document.getelementbyid('begin'), end: document.getelementbyid('end') }, color = g.color; g.resizecanvas(); window.onresize = g.resizecanvas; function makeunselectable(element) { element.classname += ' unselectable'; element.ondragstart = element.onselectstart = function (event) { event.preventdefault(); }; } makeunselectable(canvas); var numsides = document.getelementbyid('numsides'), minus = document.getelementbyid('minus'), plus = document.getelementbyid('plus'); minus.onmousedown = function () { var current = parseint(numsides.innerhtml, 10); if (current == 3) { return; } numsides.innerhtml = current-1; g.drawpolygon('final'); }; plus.onmousedown = function () { var current = parseint(numsides.innerhtml, 10); if (current == 20) { return; } numsides.innerhtml = current+1; g.drawpolygon('final'); }; var controls = [display.begin, display.end, numsides, minus, plus, document.getelementbyid('options')]; (var = 0; < controls.length; ++i) { makeunselectable(controls[i]); } var getposition = function (event) { event = event || window.event; var rect = canvas.getboundingclientrect(), left = event.clientx - rect.left, top = event.clienty - rect.top, origin = g.origin, x = left - origin.left, y = origin.top - top; return { x: x, y: y }; }; canvas.onmousedown = function (event) { document.body.style.cursor = 'default'; var position = getposition(event); g.x1 = g.x2 = position.x; g.y1 = g.y2 = position.y; display.begin.innerhtml = '<span class="label">x1, y1 =</span> '+g.x1+', '+g.y1; display.end.innerhtml = ''; g.drawpolygon('hover'); (var = 0; < controls.length; ++i) { controls[i].style.zindex = -10; } canvas.onmousemove = function (event) { var position = getposition(event); g.x2 = position.x; g.y2 = position.y; display.end.innerhtml = '<span class="label">x2, y2 =</span> '+g.x2+', '+g.y2; g.drawpolygon('hover'); }; }; function noop() { } canvas.onmousemove = noop; canvas.onmouseup = canvas.onmouseout = function (event) { if (canvas.onmousemove === noop) { return; } canvas.onmousemove = noop; g.drawpolygon('final'); (var = 0; < controls.length; ++i) { controls[i].style.zindex = 0; } }; }; window.onload = polygon.load;
body { margin: 0; padding: 0; overflow: hidden; } .unselectable { -webkit-user-select: none; -khtml-user-drag: none; -khtml-user-select: none; -moz-user-select: none; -moz-user-select: -moz-none; -ms-user-select: none; user-select: none; } canvas { width: 100%; height: 100%; } .display { color: #444; position: fixed; left: 40px; font-family: sans-serif; font-size: 20px; } .label { color: #aaa; } #begin { top: 20px; } #end { top: 60px; } #options { position: fixed; left: 40px; top: 100px; font-family: sans-serif; font-size: 28px; } #options div { display: inline; } #options .button { font-size: 32px; cursor: pointer; } #options .button:hover { color: #55838e; } #options .button, #numsides { padding: 0 5px; } #numsides { cursor: default; }
<div class="display" id="begin"></div> <div class="display" id="end"></div> <div id="options"> <div class="button" id="minus">−</div><div id="numsides">6</div><div class="button" id="plus">+</div> </div> <canvas id="surface"></canvas>
Comments
Post a Comment