(random() < 0.5 ? randRange(10, 80): randRange(100, 170)) * PI / 180 tan(ROT) 0

Construct a line perpendicular to the given line.

init({ range: [[-5, 5], [-5, 5]], scale: 50 }); addMouseLayer(); addConstruction("construction"); var a = applyRefFrame([4, 0], ROT); var b = applyRefFrame([-4, 0], ROT); addDummyStraightedge(a, b);
getToolProperties(construction)
// If there's only one element, it's the given line if (guess.length === 0) { return ""; } // first determine if there is a perp. line within 7 degrees perp = null; _.each(guess, function(tool) { if (tool.first != null) { ang = atan2( tool.second.coord[1] - tool.first.coord[1], tool.second.coord[0] - tool.first.coord[0]); deg = ang * 180 / PI; origDeg = ROT * 180 / PI; if (abs(deg-origDeg+90) < 7 || abs(deg-origDeg-90) < 7) { perp = tool; } } }); if (perp == null) { return false; } // next make sure there are two compasses, // each of which are centered on the line comps = _.filter(guess, function(tool) { return tool.center != null; }); if (comps.length < 2) { return false; } onLine = []; _.each(comps, function(comp1) { _.each(comps, function(comp2) { thisSlope = (comp1.center.coord[1] - comp2.center.coord[1]) / (comp1.center.coord[0] - comp2.center.coord[0]); thisYInt = comp1.center.coord[1] - thisSlope*comp1.center.coord[0]; if (abs(thisSlope - SLOPE) < 0.5 && abs(thisYInt) < 0.1) { onLine = [comp1,comp2]; } }); }); // Really, Javascript? [] !== []? Fine. if (onLine.length < 2) { return false; } // now we know that the slope of the straightedge // is good, and the two compasses are an on the line, // so if the straightedge has the same y-intercept // as the y-intercept of the line going between // the two points of intersection of the two compasses var a = onLine[0].center.coord[0]; var b = onLine[0].center.coord[1]; var c = onLine[1].center.coord[0]; var d = onLine[1].center.coord[1]; var r = onLine[0].radius; var s = onLine[1].radius; var e = c - a; var f = d - b; var p = sqrt(pow(e,2) + pow(f,2)); var k = (pow(p,2) + pow(r,2) - pow(s,2))/(2*p); var x1 = a + e * k / p + (f / p) * sqrt(pow(r, 2) - pow(k, 2)); var y1 = b + f * k / p - (e / p) * sqrt(pow(r, 2) - pow(k, 2)); interYInt = y1 + x1 * (1 / SLOPE); perpYInt = perp.first.coord[1] + perp.first.coord[0] * (1 / SLOPE); // give some leeway for the y-int when the slope // is high return abs(interYInt - perpYInt) < 1;
showConstructionGuess(guess);
graph.perp = raphael.set(); graph.perp.push(line( applyRefFrame([0, 10], ROT), applyRefFrame([0, -10], ROT), { strokeWidth: 1, stroke: BLUE })).toBack();

We could just draw a line and try to make it perpendicular, but then we have no guarantee that it's perfectly perpendicular.

How can you guarantee that a line is perpendicular?

graph.perpPoints = raphael.set(); graph.hintLines = raphael.set(); style({ fill: BLUE, stroke: null, }, function() { graph.perpPoints.push(circle( applyRefFrame([0, -1], ROT), 0.08)); graph.perpPoints.push(circle( applyRefFrame([0, 1], ROT), 0.08)); }); graph.perp.push(drawHintLine( applyRefFrame([0, 0], ROT), applyRefFrame([0, 1], ROT), 1)); graph.perp.push(drawHintLine( applyRefFrame([0, 0], ROT), applyRefFrame([0, -1], ROT), 1)); graph.hintLines.push(drawHintLine( applyRefFrame([0, -1], ROT), applyRefFrame([1, 0], ROT), 2)); graph.hintLines.push(drawHintLine( applyRefFrame([0, 1], ROT), applyRefFrame([1, 0], ROT), 2)); graph.hintLines.push(drawHintLine( applyRefFrame([0, -1],ROT), applyRefFrame([-2, 0],ROT), 3)); graph.hintLines.push(drawHintLine( applyRefFrame([0, 1],ROT), applyRefFrame([-2, 0],ROT), 3)); graph.perp.toBack(); graph.hintLines.toBack(); graph.perpPoints.toBack();

If we pick two points on the perpendicular line which are an equal distance from the intersection, they will also be the same distance from every other point on the line we started with.

graph.perp.remove();

If we don't already have the perpendicular line, is there another way to find the blue points?

circle(applyRefFrame([1, 0], ROT), 0.08, { fill: GRAY, stroke: null }); circle(applyRefFrame([1, 0], ROT), eDist(applyRefFrame([0, -1], ROT), applyRefFrame([1, 0], ROT)), { stroke: GRAY, strokeWidth: 1, fill: "none", strokeDasharray: "- " });

If we use the compass to put a circle somewhere on the line, the circle will include all points that are the same distance from that point, including the two blue points.

circle(applyRefFrame([-2, 0], ROT), 0.08, { fill: GRAY, stroke: null }); circle(applyRefFrame([-2, 0], ROT), eDist(applyRefFrame([0, -1], ROT), applyRefFrame([-2, 0], ROT)), { stroke: GRAY, strokeWidth: 1, fill: "none", strokeDasharray: "- " });

We can add a second circle somewhere else on the line that intersects with the first circle.

graph.hintLines.remove();

The points where the two circles intersect can be used to draw a perpendicular line.

graph.perpPoints.attr({fill: GRAY}); line(applyRefFrame([0, 10], ROT), applyRefFrame([0, -10], ROT), { strokeWidth: 1, stroke: GRAY, strokeDasharray: "- " }).toBack();

Use a straightedge to connect the two points where the circles intersect. This line is perpendicular to the given line.

ROT * 180/PI - 180 randRange(3, 5) / 2 applyRefFrame([2, -DY], ROT) applyRefFrame([-2, -DY], ROT) applyRefFrame([0, DY], ROT) 1.2

Construct a line parallel to the given line, going through `P`.

init({ range: [[-5, 5], [-5, 5]], scale: 50 }); addMouseLayer(); addConstruction("construction"); addDummyStraightedge(A, B); addDummyPoint(P); label(P, "P", SLOPE < 0 ? "left" : "below");
getToolProperties(construction)
if (guess.length === 0) { return ""; } // Ways to construct a line a given point P that's parallel to a given line L. // 1. Choose two points, A and B, on L and then use A, B, P to create a parallelogram. // (This can be done using only compasses - no need to draw the unneeded sides.) // 2. Draw a line from P that crosses L. Transfer the angle created to P. // (This is the method suggested in hints) // 3. Construct two perpendicular lines. Messy, but ok. // 4. More? // Common requirements to all(?) good parallel constructions. // 1) Require that there is one parallel line through P. // - i.e., the problem is solved! // 2) There are at least two circles with an intersection point on that line. // - Some construction has been used to create the line. // Note that using the tools given, it is possible to simply draw a line one the given one then parallel transport it to P. // It is also possible to create a "construction" that uses the above parallel transport as part of it. // Q: Are these two conditions too permissive? Will theses checks be gamed or too easily allow false constructions? // Lines that go through P var lines = _.filter(guess, function(tool) { if (tool.first != null) { return isPointOnLineSegment([tool.first.coord, tool.second.coord], P, 0.1); } }); // Ensure at least one of the lines is at the correct angle **within 5 degrees** var parallel = _.filter(lines, function(tool) { var ang = atan2( tool.second.coord[1] - tool.first.coord[1], tool.second.coord[0] - tool.first.coord[0]); // Check angle of line var deltaAngle = abs(abs(ang - ROT) * 180 / PI - 90); if (abs(deltaAngle - 90) > 5) { return false; } else { return true; } }); if (parallel.length === 0) { return false; } var compasses = _.filter(guess, function(tool) { if (tool.radius != null) { return true; } }); // Find intersections of compasses that lie on the parallel line var compassintersections = []; // Circle intersection code from constructions.js for (var i=0; i<compasses.length; i++) { for (var j=0; j<i; j++) { var a = compasses[i].center.coord[0]; var b = compasses[i].center.coord[1]; var c = compasses[j].center.coord[0]; var d = compasses[j].center.coord[1]; var r = compasses[i].radius; var s = compasses[j].radius; var e = c - a; var f = d - b; var p = Math.sqrt(Math.pow(e, 2) + Math.pow(f, 2)); var k = (Math.pow(p, 2) + Math.pow(r, 2) - Math.pow(s, 2)) / (2 * p); var x1 = a + e * k / p + (f / p) * Math.sqrt(Math.pow(r, 2) - Math.pow(k, 2)); var y1 = b + f * k / p - (e / p) * Math.sqrt(Math.pow(r, 2) - Math.pow(k, 2)); var x2 = a + e * k / p - (f / p) * Math.sqrt(Math.pow(r, 2) - Math.pow(k, 2)); var y2 = b + f * k / p + (e / p) * Math.sqrt(Math.pow(r, 2) - Math.pow(k, 2)); if (!isNaN(x1)) {compassintersections.push([x1, y1]);} if (!isNaN(x2)) {compassintersections.push([x2, y2]);} }} var intersections = _.filter(compassintersections, function(compint) { for (var i=0; i<parallel.length; i++) { var tool = parallel[i]; return isPointOnLineSegment([tool.first.coord, tool.second.coord], compint, 0.1); }; }); if (intersections.length > 0) { return true; } return false;
showConstructionGuess(guess);
var dx = 10 * cos(ROT); var dy = 10 * sin(ROT); graph.parallel = raphael.set(); graph.parallel.push(line([P[0] - dx, P[1] - dy], [P[0] + dx, P[1] + dy], { strokeWidth: 1, stroke: BLUE })).toBack();

We could just draw a line at `P` and try to make it parallel, but then we have no guarantee that it's perfectly parallel.

var d = applyRefFrame([-1, DY], ROT); line([A[0] + d[0] * 10, A[1] + d[1] * 10], [A[0] - d[0] * 10, A[1] - d[1] * 10], { strokeWidth: 1, stroke: BLUE }).toBack(); graph.angleAP = atan2(P[1] - A[1], P[0] - A[0]); arc(A, 0.5, graph.angleAP * 180 / PI, DEG, false, { strokeWidth: 1, stroke: BLUE }).toBack(); graph.parallel.push(arc(P, 0.5, graph.angleAP * 180 / PI, DEG, false, { strokeWidth: 1, stroke: BLUE })); addDummyPoint(A); label(A, "A", SLOPE < 0 ? "above" : "below");

If we did have parallel line and drew a line from a point `A` on the given line, through `P`, then the angle at `A` must be the same as the angle at `P`.

Therefore, if we copy the angle at `A` to point `P`, we will be able to construct parallel lines.

var d1 = applyRefFrame([-R, 0], ROT); var d2 = applyRefFrame([R, 0], graph.angleAP); graph.p1 = [A[0] + d1[0], A[1] + d1[1]]; graph.p2 = [A[0] + d2[0], A[1] + d2[1]]; graph.p3 = [P[0] + d1[0], P[1] + d1[1]]; graph.p4 = [P[0] + d2[0], P[1] + d2[1]]; circle(graph.p1, 0.08, { stroke: null, fill: BLUE }); circle(graph.p2, 0.08, { stroke: null, fill: BLUE }); graph.hintPoint1 = circle(graph.p3, 0.08, { stroke: null, fill: BLUE }); graph.hintPoint2 = circle(graph.p4, 0.08, { stroke: null, fill: BLUE }); drawHintLine(A, graph.p1, 2).toBack(); drawHintLine(A, graph.p2, 2).toBack(); graph.parallel.push(drawHintLine(graph.p1, graph.p2, 1)); graph.parallel.push(drawHintLine(graph.p3, graph.p4, 1)); graph.parallel.push(drawHintLine(P, graph.p3, 2)); graph.parallel.push(drawHintLine(P, graph.p4, 2)); graph.parallel.toBack();

If we construct a triangle with a vertex at point `A`, then construct a triangle with the same side lengths at and a vertex at point `P`, the angles at `A` and `P` will be the same.

graph.parallel.remove(); graph.hintPoint1.remove(); graph.hintPoint2.remove(); circle(A, 0.08, { stroke: null, fill: GRAY }); circle(A, R, { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " });

We can use a compass centered at `A` to find all the point a given distance from `A`.

var compassVertex = circle(A, 0.08, { stroke: null, fill: GRAY }); var compassCircumference = circle(A, R, { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " }); compassVertex.animate({ cx: scalePoint(P)[0], cy: scalePoint(P)[1] }, 1000); compassCircumference.animate({ cx: scalePoint(P)[0], cy: scalePoint(P)[1] }, 1000);

We can use a compass with same radius centered at `P` to find all the points the same distance from `P`. We can ensure we have get the same radius by putting the compass at point `A` first, getting the radii equal, then moving the compass to point `P`.

graph.compassVertex = circle(graph.p2, 0.08, { stroke: null, fill: GRAY }).toBack(); graph.compassCircumference = circle(graph.p2, eDist(graph.p1, graph.p2), { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " });

To find the distance between the two points equidistant from `A` we can add center a compass on one point and change the radius to go through the other point.

graph.compassVertex.animate({ cx: scalePoint(graph.p4)[0], cy: scalePoint(graph.p4)[1] }, 1000); graph.compassCircumference.animate({ cx: scalePoint(graph.p4)[0], cy: scalePoint(graph.p4)[1] }, 1000);

Now move the compass to the point where the first compass intersected with the line through `P`.

drawHintLine(graph.p1, graph.p2, 1); drawHintLine(graph.p3, graph.p4, 1); drawHintLine(P, graph.p3, 2); drawHintLine(P, graph.p4, 2); circle(graph.p3, 0.08, { stroke: null, fill: BLUE }); circle(graph.p4, 0.08, { stroke: null, fill: BLUE });

Now we can use the point where the two compasses intersect to construct a triangle with the same side lengths as the triange at point `A`.

var dx = 10 * cos(ROT); var dy = 10 * sin(ROT); line([P[0] + dx, P[1] + dy], [P[0] - dx, P[1] - dy], { strokeWidth: 1, stroke: BLUE }).toBack();

Finally use a straightedge to connect point `P` to where the two compasses intersect. This line will be parallel to the original line.

Construct a perpendicular bisector of the line segment `\overline{AB}`.

init({ range: [[-5,5],[-5,5]], scale: 50 }); addMouseLayer(); addConstruction("construction"); var a = applyRefFrame([1.5, 0], ROT); var b = applyRefFrame([-1.5, 0], ROT); a = [roundToNearest(0.01, a[0]), roundToNearest(0.01, a[1])]; b = [roundToNearest(0.01, b[0]), roundToNearest(0.01, b[1])]; addDummyStraightedge(a, b, false); addDummyPoint(a); addDummyPoint(b); var offset = "above"; if ((ROT * 180 / PI) > 50 && (ROT * 180 / PI) < 90) { offset = "left"; } if ((ROT * 180 / PI) < 130 && (ROT * 180 / PI) > 90) { offset = "right"; } label(a, "A", offset); label(b, "B", offset);
getToolProperties(construction)
// If there's only one element, it's the given line if (guess.length === 0) { return ""; } // first determine if there is a perp. line within 7 degrees perp = null; _.each(guess, function(tool) { if (tool.first != null) { ang = atan2( tool.second.coord[1] - tool.first.coord[1], tool.second.coord[0] - tool.first.coord[0]); deg = ang * 180 / PI; origDeg = ROT * 180 / PI; if ((abs(deg - origDeg + 90) % 360) < 7 || (abs(deg - origDeg - 90) % 360) < 7) { perp = tool; } } }); if (perp == null) { return false; } // next make sure there are two compasses, // each of which are centered on the line, // with equal radius comps = _.filter(guess, function(tool) { return tool.center != null; }); if (comps.length < 2) { return false; } onLine = []; _.each(comps, function(comp1) { _.each(comps, function(comp2) { thisSlope = (comp1.center.coord[1] - comp2.center.coord[1]) / (comp1.center.coord[0] - comp2.center.coord[0]); thisYInt = comp1.center.coord[1] - thisSlope * comp1.center.coord[0]; if (abs(thisSlope - SLOPE) < 0.5 && abs(thisYInt) < 0.1 && abs(comp1.radius - comp2.radius) < 0.1) { onLine = [comp1, comp2]; } }); }); // Really, Javascript? [] !== []? Fine. if (onLine.length < 2) { return false; } // now we know that the slope of the straightedge // is good, and the two compasses are an on the line, // so if the straightedge has the same y-intercept // as the y-intercept of the line going between // the two points of intersection of the two compasses interYInt = 0; perpYInt = perp.first.coord[1] + perp.first.coord[0] * (1/SLOPE); return abs(interYInt - perpYInt) < 0.5;
showConstructionGuess(guess);
graph.perp = raphael.set(); graph.perp.push(line( applyRefFrame([0, 10], ROT), applyRefFrame([0, -10], ROT), { strokeWidth: 1, stroke: BLUE })).toBack();

We could just draw a line and try to get it right, but then we have no guarantee that it's actually perfectly perpendicular or that it bisects the segment at exactly the midpoint.

How can you guarantee that a line is really a perpendicular bisector?

graph.hintLines = raphael.set(); style({ fill: BLUE, stroke: null, }, function() { graph.perpPoint1 = circle( applyRefFrame([0, -1], ROT), 0.08); graph.perpPoint2 = circle( applyRefFrame([0, 1], ROT), 0.08); }); graph.perp.push(drawHintLine( applyRefFrame([0, 0], ROT), applyRefFrame([0, 1], ROT), 1)); graph.perp.push(drawHintLine( applyRefFrame([0, 0], ROT), applyRefFrame([0, -1], ROT), 1)); graph.hintLines.push(drawHintLine( applyRefFrame([0, -1], ROT), applyRefFrame([1.5, 0], ROT), 2)); graph.hintLines.push(drawHintLine( applyRefFrame([0, 1], ROT), applyRefFrame([1.5, 0], ROT), 2)); graph.hintLines.push(drawHintLine( applyRefFrame([0, -1],ROT), applyRefFrame([-1.5, 0],ROT), 2)); graph.hintLines.push(drawHintLine( applyRefFrame([0, 1],ROT), applyRefFrame([-1.5, 0],ROT), 2)); graph.perp.toBack(); graph.hintLines.toBack(); graph.perpPoint1.toBack(); graph.perpPoint2.toBack();

If we pick two points on the perpendicular bisector which are an equal distance from the intersection, they will also be the same distance from both endpoints of the segment we started with.

graph.perp.remove();

If we don't already have the perpendicular bisector, is there another way to find the blue points?

circle(applyRefFrame([1.5, 0], ROT), 0.08, { fill: GRAY, stroke: null }); graph.compass1 = circle(applyRefFrame([1.5, 0], ROT), eDist(applyRefFrame([0, -1], ROT), applyRefFrame([1.5, 0], ROT)), { stroke: GRAY, strokeWidth: 1, fill: "none", strokeDasharray: "- " }).toBack();

If we use the compass to put a circle centered at point `A`, the circle will include all points that are the same distance from point `A`, including the two blue points.

circle(applyRefFrame([-1.5, 0], ROT), 0.08, { fill: GRAY, stroke: null }); graph.compass2 = circle(applyRefFrame([-1.5, 0], ROT), eDist(applyRefFrame([0, -1], ROT), applyRefFrame([-2, 0], ROT)), { stroke: GRAY, strokeWidth: 1, fill: "none", strokeDasharray: "- " }).toBack();

We can add a second circle at point `B` that intersects with the first circle.

graph.hintLines.remove();

But wait! We can use these circles to draw a perpendicular line, but not a bisector! That's because the two circles are different sizes.

graph.compass1.animate({ rx: scaleVector(3)[0], ry: scaleVector(3)[1] }, 250); graph.compass2.animate({ rx: scaleVector(3)[0], ry: scaleVector(3)[1] }, 250); graph.perpPoint1.animate({ cx: scalePoint(applyRefFrame( [0, -3/2 * sqrt(3)], ROT))[0], cy: scalePoint(applyRefFrame( [0, -3/2 * sqrt(3)], ROT))[1] }, 250); graph.perpPoint2.animate({ cx: scalePoint(applyRefFrame( [0, 3/2 * sqrt(3)], ROT))[0], cy: scalePoint(applyRefFrame( [0, 3/2 * sqrt(3)], ROT))[1] }, 250);

One nice way to make the circles the same size is to set the radii equal to the distance between `A` and `B`. You can do this by setting the center at one point and the edge of the circle at the other.

graph.perpPoint1.attr({fill: GRAY}); graph.perpPoint2.attr({fill: GRAY}); line(applyRefFrame([0, 10], ROT), applyRefFrame([0, -10], ROT), { strokeWidth: 1, stroke: GRAY, strokeDasharray: "- " }).toBack();

Use a straightedge to connect the two points where the circles intersect. This line segment is the perpendicular bisector of `\overline{AB}`.

randRange(30, 80) * PI / 180 [randRange(-3, 0), 0] 1 CENTER [CENTER[0] + 4, CENTER[1]] (function() { var c = applyRefFrame([4, 0], ROT); c = [c[0] + CENTER[0], c[1] + CENTER[1]]; c = [roundToNearest(0.01, c[0]), roundToNearest(0.01, c[1])]; return c; })()

Construct an angle bisector for the given angle.

init({ range: [[-5, 5], [-2, 5]], scale: 50 }); addMouseLayer(); addConstruction("construction"); addDummyRay(A, B); addDummyRay(A, C);
getToolProperties(construction)
if (guess.length === 0) { return ""; } // first determine if there is a line bisecting var bisect = null; _.each(guess, function(tool) { if (tool.first != null) { ang = atan2( (tool.second.coord[1] - tool.first.coord[1]), (tool.second.coord[0] - tool.first.coord[0])); ang = ang < 0 ? ang + PI : ang; if (abs((ROT / 2) - ang) < 3 * PI / 180) { bisect = tool; } } }); if (bisect == null) { return false; } // next make sure there is a compass with // center on the center of the angle such that // its intersections with the angle are the // centers of two further compasses (yup) var middle = null; var bottom = null; var top = null; var comps = _.filter(guess, function(tool) { return tool.center != null; }); if (comps.length < 3) { return false; } _.each(comps, function(comp) { if(eDist(comp.center.coord, CENTER) < 0.5) { middle = comp; } }); if (middle == null) { return false; } var botInter = []; _.each(comps, function(comp) { botInter = [CENTER[0] + middle.radius, CENTER[1]]; if (eDist(comp.center.coord, botInter) < 0.5) { bottom = comp; } }); var topInter = []; _.each(comps, function(comp) { topInter = [CENTER[0] + applyRefFrame([middle.radius, 0], ROT)[0], CENTER[1] + applyRefFrame([middle.radius, 0], ROT)[1]]; if( eDist(comp.center.coord, topInter) < 0.5) { top = comp; } }); if (bottom == null || top == null) { return false; } return abs(bottom.radius - top.radius) < 0.5;
showConstructionGuess(guess);
var farPoint = [ applyRefFrame([10, 0], ROT / 2)[0] + CENTER[0], applyRefFrame([10, 0], ROT / 2)[1] + CENTER[1] ]; graph.bisect = line(CENTER, farPoint, { stroke: BLUE, strokeWidth: 1 }); graph.bisect.toBack();

We could just draw a line and try to make it bisect the angle, but that's difficult to do and there is no guarantee it's a perfect bisector.

graph.hintLines = raphael.set(); graph.intersect1 = [CENTER[0] + 2, CENTER[1]]; graph.intersect2 = [ applyRefFrame([2, 0], ROT)[0] + CENTER[0], applyRefFrame([2, 0], ROT)[1] + CENTER[1]]; circle(graph.intersect1, 0.08, { stroke: null, fill: BLUE }); circle(graph.intersect2, 0.08, { stroke: null, fill: BLUE }); graph.hintLines.push(drawHintLine(CENTER, graph.intersect1, 1)); graph.hintLines.push(drawHintLine(CENTER, graph.intersect2, 1)); graph.hintLines.push(drawHintLine(graph.intersect1, [ applyRefFrame([2.5, 0], ROT / 2)[0] + CENTER[0], applyRefFrame([2.5, 0], ROT / 2)[1] + CENTER[1]], 2)); graph.hintLines.push(drawHintLine(graph.intersect2, [ applyRefFrame([2.5, 0], ROT / 2)[0] + CENTER[0], applyRefFrame([2.5, 0], ROT / 2)[1] + CENTER[1]], 2)); graph.hintLines.push(drawHintLine(graph.intersect1, [ applyRefFrame([4, 0], ROT / 2)[0] + CENTER[0], applyRefFrame([4, 0], ROT / 2)[1] + CENTER[1]], 3)); graph.hintLines.push(drawHintLine(graph.intersect2, [ applyRefFrame([4, 0], ROT / 2)[0] + CENTER[0], applyRefFrame([4, 0], ROT / 2)[1] + CENTER[1]], 3)); graph.hintLines.toBack();

If we pick any two points on the given lines that are the same distance from the vertex of the angle, every point on the bisector line will be equidistant from those points.

circle(CENTER, 0.08, { stroke: null, fill: GRAY }); circle(CENTER, 2, { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " });

We can use a compass centered at the vertex to find two points equidistant from the vertex.

graph.bisect.remove(); circle(graph.intersect1, 0.08, { stroke: null, fill: GRAY }); graph.compass1 = circle(graph.intersect1, 1.5, { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " }); circle(graph.intersect2, 0.08, { stroke: null, fill: GRAY }); graph.compass2 = circle(graph.intersect2, 1.8, { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " }); graph.hintLines.remove();

If we use two more compasses centered at each of the two points, we can see that they intersect, but not on the angle bisector! That's because the two circles are not the same size.

graph.compass1.animate({ rx: scaleVector(2)[0], ry: scaleVector(2)[1] }, 250); graph.compass2.animate({ rx: scaleVector(2)[0], ry: scaleVector(2)[1] }, 250);

A nice way to make the circles the same size is to set the edges of both circles so they pass through the vertex.

circle([CENTER[0] + 2 + 2 * cos(ROT), CENTER[1] + 2 * sin(ROT)], 0.08, { stroke: null, fill: GRAY }); line([ applyRefFrame([-10, 0], ROT / 2)[0] + CENTER[0], applyRefFrame([-10 , 0], ROT / 2)[1] + CENTER[1] ], [ applyRefFrame([10, 0], ROT / 2)[0] + CENTER[0], applyRefFrame([10 , 0], ROT / 2)[1] + CENTER[1] ], { stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " });

Use a straightedge to connect the vertex to the point where the last two circles intersect. This line is the angle bisector.

randRange(3, 6) / 2 randRange(2, 4) / 2 applyRefFrame([LENGTH / 2, -DY], ROT) applyRefFrame([-LENGTH / 2, -DY], ROT) applyRefFrame([0, DY], ROT)

Construct a line segment with the same length as `\overline{AB}`, with one end at `P`.

init({ range: [[-5, 5], [-5, 5]], scale: 50 }); addMouseLayer(); addConstruction("construction"); addDummyStraightedge(A, B, false); addDummyPoint(A); addDummyPoint(B); addDummyPoint(P); var label_position = abs(SLOPE) > 1.5 ? "left" : "below" label(A, "A", label_position); label(B, "B", label_position); label(P, "P", "below");
getToolProperties(construction)
if (guess.length === 0) { return ""; } // Ensure there is a line at P of the correct length var line = _.any(guess, function(tool) { return tool.first != null && (eDist(tool.first.coord, P) < 0.5 || eDist(tool.second.coord, P) < 0.5) && abs(eDist(tool.first.coord, tool.second.coord) - LENGTH) < 0.5; }); if (!line) { return false; } // Ensure there is a compass at P of the correct length var compass = findCompass(guess, { cx: P[0], cy: P[1], radius: LENGTH }); return compass.length === 1;
showConstructionGuess(guess);
graph.p1 = [P[0] + LENGTH * cos(ROT), P[1] + LENGTH * sin(ROT)]; graph.segment = line(P, graph.p1, { strokeWidth: 1, stroke: BLUE, extend: false }).toBack(); graph.lineEnd = circle(graph.p1, 0.08, { stroke: null, fill: BLUE });

We could just draw a line at `P` and try to make it the same length as `\overline{AB}`, but then we have no guarantee that it's actually the same length.

How could we find all the points the correct length from `P`?

graph.segment.remove(); graph.lineEnd.remove(); graph.compassVertex = circle(A, 0.08, { stroke: null, fill: GRAY }); graph.compassCircumference = circle(A, LENGTH, { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " });

If we center a compass on `A` and set its radius such that it intersects `B`, then we will have a compass with a radius of the correct length.

graph.compassVertex.animate({ cx: scalePoint(P)[0], cy: scalePoint(P)[1] }, 1000); graph.compassCircumference.animate({ cx: scalePoint(P)[0], cy: scalePoint(P)[1] }, 1000);

Now we can move the compass to `P`.

graph.segment = line(P, graph.p1, { strokeWidth: 1, stroke: BLUE, extend: false }).toBack(); graph.lineEnd = circle(graph.p1, 0.08, { stroke: null, fill: BLUE });

Finally use a straightedge to connect point `P` to any point on the compass circumference.

randRange(30, 80) * PI / 180 [randFromArray([-2, 2]), 0] 1 [CENTER[0] - 1.5, CENTER[1]] [CENTER[0] + 1.5, CENTER[1]] (function() { var c = applyRefFrame([3, 0], ROT); c = [c[0] + A[0], c[1] + CENTER[1]]; c = [roundToNearest(0.01, c[0]), roundToNearest(0.01, c[1])]; return c; })() [-CENTER[0] - 1, 2.5]

Construct a copy of angle `\angle BAC` with the vertex at point `P`.

init({ range: [[-5, 5], [-2, 5]], scale: 50 }); addMouseLayer(); addConstruction("construction"); addDummyRay(A, B); addDummyRay(A, C); addDummyPoint(P); label(A, "A", "below"); label(B, "B", "below"); label(C, "C", "above"); label(P, "P", "below");
getToolProperties(construction)
if (guess.length === 0) { return ""; } // Find two lines going through P var lines = _.filter(guess, function(tool) { return tool.first != null && (eDist(tool.first.coord, P) < 0.5 || eDist(tool.second.coord, P) < 0.5) }); if (lines.length !== 2) { return false; } // Find angles in degrees for the two lines var angles = _.map(lines, function(tool) { var p1, p2; if (eDist(tool.first.coord, P) < 0.5) { p1 = tool.first.coord; p2 = tool.second.coord; } else { p1 = tool.second.coord; p2 = tool.first.coord; } return atan2(p1[1] - p2[1], p1[0] - p2[0]); }); // Find smallest angle between lines var angle1 = angles[0] * 180 / PI; var angle2 = angles[1] * 180 / PI; angle1 += (angle1 < 0) ? 180 : 0; angle2 += (angle2 < 0) ? 180 : 0; var deltaAngle = min(180 - abs(angle1 - angle2), abs(angle1 - angle2)); if (abs(deltaAngle - ROT * 180 / PI) > 3) { return false; } // Ensure there is a compass centered on P and find its radius var compass1 = findCompass(guess, { cx: P[0], cy: P[1] }); if (compass1.length !== 1) { return "Make sure you keep the compasses you used in place."; } // Ensure there is a compass centered on p1 or p2 with correct radius var r = compass1[0].radius; var p1 = [P[0] - r * cos(angles[0]), P[1] - r * sin(angles[0])]; var p2 = [P[0] - r * cos(angles[1]), P[1] - r * sin(angles[1])]; // Radius, r2, will be base of an isosceles with one angle of ROT and two sides of r var angle = (PI - ROT) / 2; var r2 = 2 * r * cos(angle); var compass2 = findCompass(guess, { cx: p1[0], cy: p1[1], radius: r2 }); var compass3 = findCompass(guess, { cx: p2[0], cy: p2[1], radius: r2 }); if (compass2.length === 1 || compass3.length === 1) { return true; } else { return "Make sure you keep the compasses you used in place."; }
showConstructionGuess(guess);
line(P, [P[0] + 2.5, P[1]], { strokeWidth: 1, stroke: BLUE }).toBack(); circle([P[0] + 2.5, P[1]], 0.08, { stroke: null, fill: BLUE });

First we need to draw a line through `P`.

graph.p4 = [applyRefFrame([2, 0], ROT)[0] + P[0], applyRefFrame([2, 0], ROT)[1] + P[1]]; graph.hintLines = raphael.set(); graph.hintLines.push(line(P, graph.p4, { strokeWidth: 1, stroke: BLUE })).toBack(); graph.hintPoint = circle(graph.p4, 0.08, { stroke: null, fill: BLUE });

We could just draw a second line through `P` and try to make the angle the same as `\angle BAC`, but that's difficult to do and there is no guarantee the angle will be exactly the same.

graph.p1 = [A[0] + 2, A[1]]; graph.p2 = [applyRefFrame([2, 0], ROT)[0] + A[0], applyRefFrame([2, 0], ROT)[1] + A[1]]; graph.p3 = [P[0] + 2, P[1]]; circle(graph.p1, 0.08, { stroke: null, fill: BLUE }); circle(graph.p2, 0.08, { stroke: null, fill: BLUE }); drawHintLine(A, graph.p1, 2).toBack(); drawHintLine(A, graph.p2, 2).toBack(); graph.hintLines.push(drawHintLine(graph.p1, graph.p2, 1)); graph.hintLines.push(drawHintLine(graph.p3, graph.p4, 1)); graph.hintLines.push(drawHintLine(P, graph.p3, 2)); graph.hintLines.push(drawHintLine(P, graph.p4, 2)); graph.hintLines.toBack();

If we construct a triangle with a vertex at point `A`, then construct a triangle with the same side lengths and a vertex at point `P`, the angles at `A` and `P` will be the same.

graph.hintLines.remove(); graph.hintPoint.remove(); circle(A, 0.08, { stroke: null, fill: GRAY }); circle(A, 2, { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " });

We can use a compass centered at `A` to find two points equidistant from `A`.

var compassVertex = circle(A, 0.08, { stroke: null, fill: GRAY }); var compassCircumference = circle(A, 2, { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " }); compassVertex.animate({ cx: scalePoint(P)[0], cy: scalePoint(P)[1] }, 1000); compassCircumference.animate({ cx: scalePoint(P)[0], cy: scalePoint(P)[1] }, 1000); // This should appear later circle(graph.p3, 0.08, { stroke: null, fill: BLUE }); drawHintLine(P, graph.p3, 2);

We can use a compass with same radius centered at `P` to find all the points the same distance from `P`. We can ensure we get the same radius by putting the compass at point `A` first, getting the radii equal, then moving the compass to point `P`.

graph.compassVertex = circle(graph.p1, 0.08, { stroke: null, fill: GRAY }); graph.compassCircumference = circle(graph.p1, eDist(graph.p1, graph.p2), { fill: null, stroke: GRAY, strokeWidth: 1, strokeDasharray: "- " });

To find the distance between the two points equidistant from `A` we can center a compass on one point and change the radius to go through the other point.

graph.compassVertex.animate({ cx: scalePoint(graph.p3)[0], cy: scalePoint(graph.p3)[1] }, 1000); graph.compassCircumference.animate({ cx: scalePoint(graph.p3)[0], cy: scalePoint(graph.p3)[1] }, 1000);

Now move the compass to the point where the first compass intersected with the line through `P`.

var dx = 10 * cos(ROT); var dy = 10 * sin(ROT); drawHintLine(P, graph.p4, 2); drawHintLine(graph.p3, graph.p4, 1); line(P, graph.p4, { strokeWidth: 1, stroke: BLUE }).toBack(); line(P, graph.p4, { strokeWidth: 1, stroke: BLUE }).toBack(); circle(graph.p4, 0.08, { stroke: null, fill: BLUE });

Finally use a straightedge to connect point `P` to where the two compasses intersect. The angle formed at point `P` will be the same as `\angle BAC`.