2 randRange(1, 360) ANGLE * Math.PI / 180 [SIZE * cos(ANGLE * Math.PI / 180), SIZE * sin(ANGLE * Math.PI / 180)] [P[0] * 2, P[1] * 2] ["right", "above", "left", "below", "right"][round(ANGLE / 90)] ["left", "below", "right", "above", "left"][round(ANGLE / 90)]

Construct a line going through `P`, tangent to the circle.

init({ range: [[-5, 5], [-5, 5]], scale: 50 }); addMouseLayer(); addConstruction("construction"); addDummyCircle([0, 0], SIZE); addDummyPoint([0, 0]); addDummyPoint(P); label(P, "P", POSITION);
getToolProperties(construction)
if (guess.length === 0) { return ""; } // Get lines going through P var lines = _.filter(guess, function(tool) { if (tool.first != null) { return isPointOnLineSegment([tool.first.coord, tool.second.coord], P, 0.2); } }); // Of the lines going through P, does one have the correct angle? var tangentLine = _.filter(lines, function(tool) { return angleEqual(tool, (ANGLE + 90) % 180, 5); }); if (tangentLine.length === 0) { return false; } // Of the lines going through P, does one go through C too? var connectingLine = _.filter(lines, function(tool) { return isPointOnLineSegment([tool.first.coord, tool.second.coord], [0, 0], 0.2); }); if (connectingLine.length === 0) { return "Make sure you keep the straightedges you used in place."; } // Are there two compasses on the line segment CQ but not on P? var compasses = _.filter(guess, function(tool) { if (tool.center != null) { return !distEqual(P, tool.center.coord, 0, 0.15) && isPointOnLineSegment([connectingLine[0].first.coord, connectingLine[0].second.coord], tool.center.coord, 0.2); } }); // Do two of these compasses have and have the same length radii? for (var i = 0; i < compasses.length; i++) { for (var j = i + 1; j < compasses.length; j++) { if (abs(compasses[i].radius - compasses[j].radius) < 0.15) { return true; } } } return "Make sure you keep the compasses you used in place.";
showConstructionGuess(guess);
var angle = (ANGLE + 90) * Math.PI / 180; var dx = 10 * cos(angle); var dy = 10 * sin(angle); graph.hintLines = raphael.set(); graph.hintLines.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 through `P` and try to get it right, but then we have no guarantee that it's actually perfectly perpendicular.

How can you guarantee that a line is really perpendicular?

var dAngle = 30 * Math.PI / 180; var d = SIZE / cos(dAngle); graph.hintLines.push(drawHintLine([0, 0], [d * cos(RAD - dAngle), d * sin(RAD - dAngle)], 2)).toBack(); graph.hintLines.push(drawHintLine([0, 0], [d * cos(RAD + dAngle), d * sin(RAD + dAngle)], 2)).toBack(); graph.hintLines.push(drawHintLine([0, 0], P)).toBack();

If we pick two points on the tangent line that are an equal distance from `P`, they will also be the same distance from the center of the circle.

graph.hintLines.remove(); line([0, 0], [SIZE * 2.5 * cos(RAD), SIZE * 2.5 * sin(RAD)], { strokeWidth: 2, stroke: GRAY }).toBack(); label([0, 0], "C", POSITION2);

Draw a line that passes through the center of the circle, `C`, and `P`.

circle(P, SIZE, { fill: "none", strokeDasharray: "- ", strokeWidth: 1, stroke: GRAY }); circle(Q, 0.08, { fill: GRAY, stroke: null }); label(Q, "Q", POSITION);

Use a compass with the same radius as the original circle to find a point on the line, `Q`, such `\overline{PQ} = \overline{PC}`.

Point `P` bisects the line `\overline{QC}`, so if we construct a perpendicular bisector of the line it will pass through point `P` and be tangent to the circle.

graph.d = SIZE * 1.6; circle([0, 0], graph.d, { fill: "none", strokeDasharray: "- ", strokeWidth: 1, stroke: GRAY }); circle(Q, graph.d, { fill: "none", strokeDasharray: "- ", strokeWidth: 1, stroke: GRAY });

Create two compasses of the same size, one centered at `C` and one centered at `Q`.

var dAngle = acos(SIZE / graph.d); var p1 = [graph.d * cos(RAD - dAngle), graph.d * sin(RAD - dAngle)]; var p2 = [graph.d * cos(RAD + dAngle), graph.d * sin(RAD + dAngle)]; line(p1, p2, { strokeWidth: 2, stroke: GRAY }); circle(p1, 0.08, { fill: GRAY, stroke: null }); circle(p2, 0.08, { fill: GRAY, stroke: null });

Join the points where these two compasses intersect.

2 randRange(1, 360) ANGLE * Math.PI / 180 2 * SIZE + randRange(-1, 2) D * randRange(3, 7) / 10 D1 - D (D1 + D2) / 2 [ ((RAD - asin(SIZE / D)) * 180 / Math.PI) % 180, ((RAD + asin(SIZE / D)) * 180 / Math.PI) % 180] [D1 * cos(ANGLE * Math.PI / 180), D1 * sin(ANGLE * Math.PI / 180)] [D2 * cos(ANGLE * Math.PI / 180), D2 * sin(ANGLE * Math.PI / 180)] [DP * cos(ANGLE * Math.PI / 180), DP * sin(ANGLE * Math.PI / 180)] ["right", "above", "left", "below", "right"][round(ANGLE / 90)] ["left", "below", "right", "above", "left"][round(ANGLE / 90)]

Construct a line going through `P`, tangent to the circle.

init({ range: [[-5, 5], [-5, 5]], scale: 50 }); addMouseLayer(); addConstruction("construction"); addDummyCircle(C, SIZE); addDummyPoint(C); addDummyPoint(P); label(P, "P", POSITION); label(C, "C", POSITION2);
getToolProperties(construction)
if (guess.length === 0) { return ""; } // Find tangent line var tangent = _.filter(guess, function(tool) { if (tool.first != null) { if (isPointOnLineSegment([tool.first.coord, tool.second.coord], P, 0.2)) { return angleEqual(tool, TANGENT_ANGLES[0], 4) || angleEqual(tool, TANGENT_ANGLES[1], 5); } } }); if (tangent.length < 1) { return false; } // Get lines going through X var lines = _.filter(guess, function(tool) { if (tool.first != null) { return isPointOnLineSegment([tool.first.coord, tool.second.coord], X, 0.2); } }); if (lines.length < 2) { return "Make sure you keep the straightedges you used in place."; } // Check there is a line going through P and C var connectingLine = _.filter(lines, function(tool) { return isPointOnLineSegment([tool.first.coord, tool.second.coord], P, 0.2) && isPointOnLineSegment([tool.first.coord, tool.second.coord], C, 0.2); }); // Check there is a perpendicular bisector of the line PC var bisectingLine = _.filter(lines, function(tool) { return angleEqual(tool, (ANGLE + 90) % 180, 5); }); if (connectingLine.length * bisectingLine.length === 0) { return "Make sure you keep the straightedges you used in place.";; } // Test whether there is a compass centered on X with the correct radius var compass = findCompass(guess, {center: X, radius: D / 2}); if (compass.length === 0) { return "Make sure you keep the compasses you used in place."; } else { return true; }
showConstructionGuess(guess);
var angle = TANGENT_ANGLES[0] * Math.PI / 180; graph.dx = 10 * cos(angle); graph.dy = 10 * sin(angle); graph.hintLines = raphael.set(); graph.hintLines.push(line([P[0] - graph.dx, P[1] - graph.dy], [P[0] + graph.dx, P[1] + graph.dy],{ strokeWidth: 1, stroke: BLUE })).toBack();

We could just draw a line through `P` and try to get it right, but then we have no guarantee that it's actually perfectly tangent.

How can you guarantee that a line is really tangent?

var angle1 = RAD - asin(SIZE / D); var angle2 = RAD + asin(SIZE / D); var d = Math.sqrt(D * D - SIZE * SIZE) graph.tangent1 = [P[0] + d * cos(angle1), P[1] + d * sin(angle1)]; graph.tangent2 = [P[0] + d * cos(angle2), P[1] + d * sin(angle2)]; graph.hintLines.push(drawHintLine(C, P)).toBack(); style({ strokeWidth: 1, stroke: BLUE }, function() { graph.hintLines.push(line(C, graph.tangent1)); graph.hintLines.push(line(C, graph.tangent2)); graph.hintLines.push(line(P, graph.tangent2)); }); graph.hintLines.push(circle(X, D / 2, { stroke: BLUE, strokeWidth: 1, fill: "none", strokeDasharray: "- " })); graph.hintLines.push(circle(X, 0.08, { fill: BLUE, stroke: null }));

If we can draw a quadrilateral inscribed in a circle, its opposite angles must sum to `180^\circ`. If the two angles are equal, then both must be `90^\circ`.

Therefore we need to find a circle for which `P` and `C` are on opposite sides.

graph.hintLines.remove(); line(C, P, { strokeWidth: 2, stroke: GRAY }).toBack();

First draw a line connecting the center of the circle, `C`, to `P`.

graph.d = D * 0.65; circle(C, graph.d, { fill: "none", strokeDasharray: "- ", strokeWidth: 1, stroke: GRAY }); circle(P, graph.d, { fill: "none", strokeDasharray: "- ", strokeWidth: 1, stroke: GRAY });

Now we want to bisect the line `CP`. So create two compasses with the same radius, one centered at `C` and one centered at `P`.

var dAngle = acos(D / graph.d / 2); var p1 = [C[0] - graph.d * cos(RAD - dAngle), C[1] - graph.d * sin(RAD - dAngle)]; var p2 = [C[0] - graph.d * cos(RAD + dAngle), C[1] - graph.d * sin(RAD + dAngle)]; line(p1, p2, { strokeWidth: 2, stroke: GRAY }); circle(p1, 0.08, { fill: GRAY, stroke: null }); circle(p2, 0.08, { fill: GRAY, stroke: null });

Join the points where these two compasses intersect to the bisect line `CP`.

circle(X, 0.08, { fill: GRAY, stroke: null }); circle(X, D / 2, { fill: "none", strokeDasharray: "- ", strokeWidth: 1, stroke: GRAY });

Now we can draw a circle halfway between `C` and `P` with a radius that goes through both.

circle(graph.tangent1, 0.08, { fill: GRAY, stroke: null }); circle(graph.tangent2, 0.08, { fill: GRAY, stroke: null }); if (graph.tangent1[1] > C[1]) { label(graph.tangent1, "A", "above"); } else { label(graph.tangent1, "A", "below"); } if (graph.tangent2[1] > C[1]) { label(graph.tangent2, "B", "above"); } else { label(graph.tangent2, "B", "below"); } path([C, graph.tangent1, P, graph.tangent2, C], { fill: GREEN, 'fill-opacity': 0.4, stroke: GREEN, strokeWidth: 2 }).toBack(); var drawRightAngleMarker = function(p, flip) { var x = p[0]; var y = p[1]; var dx = x - P[0]; var dy = y - P[1]; var norm = sqrt(dx * dx + dy * dy); dx /= norm * 5; dy /= norm * 5; //circle([x - dx, y - dy], 0.1); path([[x, y], [x - dx, y - dy], [x - dx - dy * flip, y + dx * flip - dy], [x - dy * flip, y + dx * flip]], { fill: null, stroke: BLACK, strokeWidth: 1 }) } drawRightAngleMarker(graph.tangent1, 1); drawRightAngleMarker(graph.tangent2, -1);

This circle intersects the original circle at points `A` and `B`. `\green{PACB}` forms a quadrilateral inscribed inside this circle so its opposite angles sum to `180^\circ`. `\triangle PAC` and `\triangle PBC` are congruent triangles, so `\angle PAC = \angle PBC`. Therefore both angles are `90^\circ`.

var angle = TANGENT_ANGLES[1] * Math.PI / 180; var dx = 10 * cos(angle); var dy = 10 * sin(angle); graph.hintLines.push(line([P[0] - graph.dx, P[1] - graph.dy], [P[0] + graph.dx, P[1] + graph.dy],{ strokeWidth: 2, stroke: GRAY })) graph.hintLines.push(line([P[0] - dx, P[1] - dy], [P[0] + dx, P[1] + dy],{ strokeWidth: 2, stroke: GRAY }))

Therefore a line from `P` to either `A` or `B` will be tangent to the original circle.