Permalink
Fetching contributors…
Cannot retrieve contributors at this time
293 lines (271 sloc) 13.5 KB
<!DOCTYPE html>
<html data-require="math graphie geom interactive">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Rotation of polygons</title>
<script data-main="../local-only/main.js" src="../local-only/require.js"></script>
</head>
<body>
<div class="exercise">
<div class="vars">
<var id="ROTDEG">90 * randRange(1, 3)</var>
<var id="ROTRAD">ROTDEG * PI / 180 </var>
<var id="HULL">Geom.convexhull(_.map(randRange(-4, 4, 6, 2), function(p) {
return { x: p[0], y: p[1] };
}))</var>
<var id="HULL_POINTS">_.map(HULL, function(p) { return [p.x, p.y]; })</var>
<var id="TARGET"> _.map(HULL, function(p) {
return [p.x * cos(ROTRAD) - p.y * sin(ROTRAD),
p.x * sin(ROTRAD) + p.y * cos(ROTRAD)];
})</var>
</div>
<div class="problems">
<div id="drag">
<p class="question">
What is the image of the polygon below after the rotation
<code>T_{{<var>ROTDEG</var>}^\circ{} }</code>?
</p>
<div class="problem">
<div class="graphie" id="grid">
graphInit({
range: 11,
scale: 20,
axisArrows: "&lt;-&gt;",
tickStep: 1,
labelStep: 1,
gridOpacity: 0.05,
axisOpacity: 0.2,
tickOpacity: 0.4,
labelOpacity: 0.5
});
addMouseLayer();
path(HULL_POINTS.concat(true), { stroke: GRAY, strokeDasharray: "- " });
graph.points = _.map(HULL_POINTS, function(point) {
return addMovablePoint({ coord: point });
});
graph.lines = [];
_(graph.points.length).times(function(i) {
graph.lines.push(addMovableLineSegment({
pointA: graph.points[i],
pointZ: graph.points[(i + 1) % graph.points.length],
fixed: true
}));
});
graph.origOffsets = _.map(graph.points, function(point) {
return atan2(point.coord[1], point.coord[0]) -
roundToNearest(PI / 6, atan2(point.coord[1], point.coord[0]));
});
_.each(graph.points, function(point) {
point.onMove = function(x, y) {
var myAng = atan2(this.coord[1], this.coord[0]);
var newAng = atan2(y, x);
graph.updatePolygon(newAng - myAng);
return false;
}
point.onMoveEnd = function(x, y) {
var possibleSnaps = [
graph.origOffsets[_.indexOf(graph.points, this)] +
roundToNearest(PI/6, atan2(y,x)) -
atan2(this.coord[1], this.coord[0]),
graph.origOffsets[_.indexOf(graph.points, this)] +
roundToNearest(PI/6, atan2(y,x)) +
PI / 6 - atan2(this.coord[1], this.coord[0]),
graph.origOffsets[_.indexOf(graph.points, this)] +
roundToNearest(PI/6, atan2(y,x)) -
PI / 6 - atan2(this.coord[1], this.coord[0])
];
var absSnaps = _.map(possibleSnaps, function(rad) { return abs(rad); });
var absMoveTo = min(absSnaps[0], absSnaps[1], absSnaps[2]);
var moveTo = _.find(possibleSnaps, function(rad) { return abs(rad) === absMoveTo; });
graph.updatePolygon(moveTo);
}
});
graph.updatePolygon = function(rad) {
_.each(graph.points, function(point) {
point.setCoord([
point.coord[0] * cos(rad) - point.coord[1] * sin(rad),
point.coord[0] * sin(rad) + point.coord[1] * cos(rad)
]);
point.updateLineEnds();
});
};
</div>
</div>
<div class="solution" data-type="custom">
<div class="instruction"></div>
<div class="guess">_.map(graph.points, function(point) {
return point.coord;
})</div>
<div class="validator-function">
var closeEnough = function(points1, points2) {
// put the two sets of points in the same order (if they're roughly the same)
points1 = _.sortBy(points1, function(p) { return p[0] * 100 + p[1]; });
points2 = _.sortBy(points2, function(p) { return p[0] * 100 + p[1]; });
return _.all(points1, function(el, i) {
return abs(points1[i][0] - points2[i][0]) &lt; 0.01 &amp;&amp;
abs(points1[i][1] - points2[i][1]) &lt; 0.01;
});
};
if (closeEnough(guess, HULL_POINTS)) {
return "";
}
return closeEnough(guess, TARGET);
</div>
<div class="show-guess">
_.each(graph.points, function(point, n) {
point.setCoord(guess[n]);
point.updateLineEnds();
});
</div>
</div>
<div class="hints">
<p>
A rotation <code>T_{\LARGE{r^\circ{}}}</code> rotates points
by <code>r</code> degrees around <code>(0,0)</code>
counter-clockwise.
</p>
<div>
<p>
To see where a rotation moved this polygon, pick one
point and rotate it. For example, what happens to
<code>(<var> HULL[0].x </var>,<var> HULL[0].y </var>)</code>
under this rotation?
</p>
<div class="graphie" data-update="grid">
circle([HULL[0].x, HULL[0].y], { r: 0.2, fill: "black", stroke: "none" });
</div>
</div>
<div>
<p>
Under the rotation <code>T_{<var>ROTDEG</var> {}^\circ{} }</code>,
<code>(<var> HULL[0].x </var>,<var> HULL[0].y </var>)</code>
is translated to <code>(<var> round(TARGET[0][0]) </var>,
<var>round(TARGET[0][1])</var>)</code>. Where is the rest
of the polygon rotated?
</p>
<div class="graphie" data-update="grid">
circle(TARGET[0], { r: 0.2, fill: "black", stroke: "none" });
arc([0,0], sqrt(pow(HULL[0].x, 2) + pow(HULL[0].y, 2)),
atan2(HULL[0].y, HULL[0].x) * 180 / PI,
atan2(TARGET[0][1], TARGET[0][0]) * 180 / PI,
{ stroke: "black", arrows: "-&gt;" });
</div>
</div>
<p>
<span data-if="ROTDEG / 90 === 1">
To get from <code>(<var>HULL[0].x</var>, <var>HULL[0].y</var>)</code>
to <code>(<var>round(TARGET[0][0])</var>, <var>round(TARGET[0][1])</var>)</code>,
we rotated it <code><var>ROTDEG</var>{}^\circ{}</code> counter-clockwise, or through
one quarter of a circle.
</span>
<span data-else-if="ROTDEG / 90 === 2">
To get from <code>(<var>HULL[0].x</var>, <var>HULL[0].y</var>)</code>
to <code>(<var>round(TARGET[0][0])</var>, <var>round(TARGET[0][1])</var>)</code>,
we rotated it <code><var>ROTDEG</var>{}^\circ{}</code> counter-clockwise, or through
one half of a circle.
</span>
<span data-else-if="ROTDEG / 90 === 3">
To get from <code>(<var>HULL[0].x</var>, <var>HULL[0].y</var>)</code>
to <code>(<var>round(TARGET[0][0])</var>, <var>round(TARGET[0][1])</var>)</code>,
we rotated it <code><var>ROTDEG</var>{}^\circ{}</code> counter-clockwise, or through
three quarters of a circle.
</span>
</p>
<div>
<p>
The orange outline shows where the polygon ends up after
the translation.
</p>
<div class="graphie" data-update="grid">
for (var i=0; i &lt; TARGET.length; i++) {
line(TARGET[i], TARGET[(i+1) % TARGET.length], { stroke: ORANGE });
}
</div>
</div>
</div>
</div>
<div id="reverse">
<p class="question">
What is the transformation that rotates the blue solid shape to
the orange dashed shape?
<span class="render-answer-area-here"></span>
</p>
<div class="problem">
<div class="graphie" id="grid">
graphInit({
range: 11,
scale: 20,
axisArrows: "&lt;-&gt;",
tickStep: 1,
labelStep: 1,
gridOpacity: 0.05,
axisOpacity: 0.2,
tickOpacity: 0.4,
labelOpacity: 0.5
});
path(HULL_POINTS.concat(true), { stroke: BLUE });
path(TARGET.concat(true), { stroke: ORANGE, strokeDasharray: "- " });
</div>
</div>
<div class="solution" data-type="multiple">
<code>{\LARGE T}</code>
<span class="sol short30" data-forms="integer" data-type="predicate">
function(guess, maxError) {
var correct = false;
var symmetries = _.map(Geom.rotationalSymmetries(HULL), function(rad) {
return rad / PI * 180;
}).concat(360);
return _.any(symmetries, function(sym) {
return abs((((guess - ROTDEG + sym) % 360) + 360 + 180) % 360 - 180) &lt; maxError;
});
}
</span>
<code>{}^\circ{}</code>
</div>
<div class="hints">
<p>
A rotation <code>T_{\LARGE{r^\circ{}}}</code> rotates points
by <code>r</code> degrees around <code>(0, 0)</code>
counter-clockwise.
</p>
<div>
<p>
To see what rotation moved the blue polygon, pick one
point and rotate it. For example, what happened to
<code>(<var>HULL[0].x</var>, <var>HULL[0].y</var>)</code>
under this rotation?
</p>
<div class="graphie" data-update="grid">
circle([HULL[0].x, HULL[0].y], { r: 0.2, fill: "black", stroke: "none" });
</div>
</div>
<div>
<p>
Under this rotation, <code>(<var>HULL[0].x</var>,
<var>HULL[0].y</var>)</code> was rotated to
<code>(<var>round(TARGET[0][0])</var>, <var>round(TARGET[0][1])</var>)</code>.
What does this tell you about the rotation used?
</p>
<div class="graphie" data-update="grid">
circle(TARGET[0], { r: 0.2, fill: "black", stroke: "none" });
arc([0, 0], sqrt(pow(HULL[0].x, 2) + pow(HULL[0].y, 2)),
atan2(HULL[0].y, HULL[0].x) * 180 / PI,
atan2(TARGET[0][1], TARGET[0][0]) * 180 / PI,
{ stroke: "black", arrows: "-&gt;" });
</div>
</div>
<p>
To get from <code>(<var>HULL[0].x</var>, <var>HULL[0].y</var>)</code>
to <code>(<var>round(TARGET[0][0])</var>, <var>round(TARGET[0][1])</var>)</code>,
we rotated it around <code>(0, 0)</code>
<code><var>ROTDEG</var>{}^\circ{}</code> counterclockwise.
</p>
<div>
The rotation used was <code>T_{<var>ROTDEG</var>^\circ{}}</code>.
</div>
</div>
</div>
</div>
</div>
</body>
</html>