Skip to content

# Khan/khan-exercises

Add exercises: Law of sines and Law of cosines

Test Plan: Tested multiple seeds for each new problem type outside devappserver

Reviewers: stephanie, yunfangjuan

Reviewed By: yunfangjuan

Differential Revision: http://phabricator.khanacademy.org/D776
• Loading branch information...
1 parent 96591ea commit a5882e34cf6d6b534147013653d1af0655e63cf4 beneater committed Oct 2, 2012
Showing with 909 additions and 1 deletion.
1. +331 −0 exercises/law_of_cosines.html
2. +308 −0 exercises/law_of_sines.html
3. +2 −1 khan-exercise.js
4. +268 −0 utils/graphie-geometry.js
331 exercises/law_of_cosines.html
 @@ -0,0 +1,331 @@ + + + + + Law of cosines + + + +
+
+
+
+ shuffle([null]. + concat(randRangeUnique(5, 16, 2))) + _.map(SIDES, function(s) { + if (s === null) { + return randRange(20, 140); + } + return null; + }) + solveTriangle({ + sides: SIDES.slice(), + angles: ANGLES.slice(), + sideLabels: SIDES.slice(), + angleLabels: _.map(ANGLES, function(a) { + return a == null ? a : a + "^\\circ"; + }), + vertexLabels: ["A", "B", "C"] + }) + SIDES.indexOf(null) + ["BC", "AC", "AB"][UNKNOWN] + "abc"[UNKNOWN] + SIDES[(UNKNOWN + 1) % 3] + SIDES[(UNKNOWN + 2) % 3] + roundTo(1, sqrt(KNOWN_SIDE_1 * KNOWN_SIDE_1 + + KNOWN_SIDE_2 * KNOWN_SIDE_2 - 2 * KNOWN_SIDE_1 * + KNOWN_SIDE_2 * cos(ANGLES[UNKNOWN] * PI / 180))) + + + + rand(2) ? randRange(-20, 20) : randRange(160, 200) + +
+ +

+ Find UNKNOWN_MEASURE. +

+ +
+

+ Round to the nearest tenth. +

+
+ TRIANGLE = addTriangle(_.extend(TRIANGLE, { + xPos: 1, + yPos: 1, + width: 10, + height: 6, + rot: ROTATION + })); + init({ + range: [[0, TRIANGLE.width + 2], + [0, TRIANGLE.height + 2]] + }); + + TRIANGLE.draw(); +
+
+
SOLUTION +
+ +
+
+

You can use the law of cosines:

+

\qquad + \pink{UNKNOWN_SIDE}^2 \quad = \quad + \blue{"abc"[(UNKNOWN + 1) % 3]}^2 + + \green{"abc"[(UNKNOWN + 2) % 3]}^2 - + 2\blue{"abc"[(UNKNOWN + 1) % 3]} + \green{"abc"[(UNKNOWN + 2) % 3]} + \space\cos(\pink{"ABC"[UNKNOWN]}) +

+
+ TRIANGLE.sideLabels = _.map(SIDES, function(s, n) { + return s == null ? "abc"[n] : + "abc"[n] + " = " + s; + }); + TRIANGLE.sideLabels[UNKNOWN] = "\\pink{" + + TRIANGLE.sideLabels[UNKNOWN] + "}"; + TRIANGLE.angleLabels[UNKNOWN] = "\\pink{" + + TRIANGLE.angleLabels[UNKNOWN] + "}"; + TRIANGLE.sideLabels[(UNKNOWN + 1) % 3] = "\\blue{" + + TRIANGLE.sideLabels[(UNKNOWN + 1) % 3] + "}"; + TRIANGLE.sideLabels[(UNKNOWN + 2) % 3] = "\\green{" + + TRIANGLE.sideLabels[(UNKNOWN + 2) % 3] + "}"; + TRIANGLE.color = GRAY; + TRIANGLE.draw(); +
+
+ +
+

Plug in the known values:

+

\qquad + \pink{UNKNOWN_SIDE}^2 \quad = \quad + \blue{KNOWN_SIDE_1}^2 + + \green{KNOWN_SIDE_2}^2 - + 2(\blue{KNOWN_SIDE_1}) + (\green{KNOWN_SIDE_2}) + \space\cos(\pink{ANGLES[UNKNOWN]^\circ}) +

+
+ +
+

\qquad + \pink{UNKNOWN_SIDE}^2 \quad = \quad + + KNOWN_SIDE_1 * KNOWN_SIDE_1 + + KNOWN_SIDE_2 * KNOWN_SIDE_2 + - + 2 * KNOWN_SIDE_1 * KNOWN_SIDE_2 + + \cdot\cos(\pink{ANGLES[UNKNOWN]^\circ}) +

+
+ +
+

Evaluate and simplify the right side:

+

\qquad + \pink{UNKNOWN_SIDE}^2 \quad \approx \quad + roundTo(9, + KNOWN_SIDE_1 * KNOWN_SIDE_1 + + KNOWN_SIDE_2 * KNOWN_SIDE_2 - + 2 * KNOWN_SIDE_1 * KNOWN_SIDE_2 * + cos(ANGLES[UNKNOWN] * Math.PI / 180)) + +

+
+ +
+

Take the positive square root of both sides (we only + need to worry about the positive square root because the + side of a triangle can't have negative length):

+

\qquad + \pink{UNKNOWN_SIDE} \quad \approx \quad + \sqrt{roundTo(9, + KNOWN_SIDE_1 * KNOWN_SIDE_1 + + KNOWN_SIDE_2 * KNOWN_SIDE_2 - + 2 * KNOWN_SIDE_1 * KNOWN_SIDE_2 * + cos(ANGLES[UNKNOWN] * Math.PI / 180)) + } +

+
+ +
+

Evaluate and round to the nearest tenth:

+

+ \qquad \pink{UNKNOWN_MEASURE} + \quad = \quad \pink{UNKNOWN_SIDE} + \quad \approx \quad SOLUTION +

+
+ TRIANGLE.sideLabels[UNKNOWN] = "\\pink{" + + UNKNOWN_SIDE + " \\approx " + SOLUTION + "}"; + TRIANGLE.draw(); +
+
+
+
+ +
+
+ randRange(5, 15, 3) + solveTriangle({ + sides: SIDES.slice(), + angles: [null, null, null], + sideLabels: SIDES.slice(), + vertexLabels: ["A", "B", "C"] + }) + randRange(0, 2) + "ABC"[UNKNOWN] + SIDES[(UNKNOWN + 1) % 3] + SIDES[(UNKNOWN + 2) % 3] + fraction( + KNOWN_SIDE_1 * KNOWN_SIDE_1 + + KNOWN_SIDE_2 * KNOWN_SIDE_2 - + SIDES[UNKNOWN] * SIDES[UNKNOWN], + 2 * KNOWN_SIDE_1 * KNOWN_SIDE_2) + + roundTo(1, acos( + (KNOWN_SIDE_1 * KNOWN_SIDE_1 + + KNOWN_SIDE_2 * KNOWN_SIDE_2 - + SIDES[UNKNOWN] * SIDES[UNKNOWN]) / (2 * + KNOWN_SIDE_1 * KNOWN_SIDE_2)) / + PI * 180) + + + + rand(2) ? randRange(-20, 20) : randRange(160, 200) + +
+ +

+ Find m\angle UNKNOWN_ANGLE. +

+ +
+

+ Round to the nearest degree. +

+
+ TRIANGLE = addTriangle(_.extend(TRIANGLE, { + xPos: 1, + yPos: 1, + width: 10, + height: 6, + rot: ROTATION + })); + init({ + range: [[0, TRIANGLE.width + 2], + [0, TRIANGLE.height + 2]] + }); + + TRIANGLE.draw(); +
+
+
+ SOLUTION + \Large{^\circ} +
+ +
+
+

You can use the law of cosines:

+

\qquad + \pink{"abc"[UNKNOWN]}^2 \quad = \quad + \blue{"abc"[(UNKNOWN + 1) % 3]}^2 + + \green{"abc"[(UNKNOWN + 2) % 3]}^2 - + 2\blue{"abc"[(UNKNOWN + 1) % 3]} + \green{"abc"[(UNKNOWN + 2) % 3]} + \space\cos(\pink{"ABC"[UNKNOWN]}) +

+
+ TRIANGLE.sideLabels = _.map(SIDES, function(s, n) { + return "abc"[n] + " = " + s; + }); + TRIANGLE.sideLabels[UNKNOWN] = "\\pink{" + + TRIANGLE.sideLabels[UNKNOWN] + "}"; + TRIANGLE.vertexLabels[UNKNOWN] = "\\pink{" + + "ABC"[UNKNOWN] + "}"; + TRIANGLE.sideLabels[(UNKNOWN + 1) % 3] = "\\blue{" + + TRIANGLE.sideLabels[(UNKNOWN + 1) % 3] + "}"; + TRIANGLE.sideLabels[(UNKNOWN + 2) % 3] = "\\green{" + + TRIANGLE.sideLabels[(UNKNOWN + 2) % 3] + "}"; + TRIANGLE.color = GRAY; + TRIANGLE.draw(); +
+
+ +
+

+ Rewrite the law of cosines to solve for + \cos(\pink{"ABC"[UNKNOWN]}): +

+

\qquad + \cos(\pink{"ABC"[UNKNOWN]}) \quad = \quad + \dfrac{ + \blue{"abc"[(UNKNOWN + 1) % 3]}^2 + + \green{"abc"[(UNKNOWN + 2) % 3]}^2 - + \pink{"abc"[UNKNOWN]}^2 + }{2\blue{"abc"[(UNKNOWN + 1) % 3]} + \green{"abc"[(UNKNOWN + 2) % 3]}} +

+
+ +
+

Plug in the known values:

+

\qquad + \cos(\pink{"ABC"[UNKNOWN]}) \quad = \quad + \dfrac{ + \blue{KNOWN_SIDE_1}^2 + + \green{KNOWN_SIDE_2}^2 - + \pink{SIDES[UNKNOWN]}^2 + }{2(\blue{KNOWN_SIDE_1}) + (\green{KNOWN_SIDE_2})} +

+
+ +
+

Simplify the right side:

+

\qquad + \cos(\pink{"ABC"[UNKNOWN]}) \quad = \quad + COS_UNKNOWN +

+
+ +
+

+ Evaluate the inverse cosine to find + m\angle UNKNOWN_ANGLE + and round to the nearest degree: +

+

\qquad + \pink{m\angle UNKNOWN_ANGLE} + \quad = \quad \cos^{-1}\left(COS_UNKNOWN\right) + \quad \approx \quad \pink{SOLUTION^\circ} +

+
+ TRIANGLE.angleLabels[UNKNOWN] = "\\pink{" + + SOLUTION + "^\\circ}"; + TRIANGLE.draw(); +
+
+
+
+ +
+
308 exercises/law_of_sines.html
 @@ -0,0 +1,308 @@ + + + + + Law of sines + + + +
+
+ +
+
+ randRange(20, 140) + randRange(20, 160 - ANGLE1) + randRange(5, 25) + shuffle([0, 1, 2]) + [ + KNOWN === 0 ? SIDELEN : null, + KNOWN === 1 ? SIDELEN : null, + KNOWN === 2 ? SIDELEN : null + ] + !!rand(2) + + [ + KNOWN === 0 ? ANGLE1 : + (UNKNOWN === 0 ^ GIVE_OPPOSITE ? null : ANGLE2), + KNOWN === 1 ? ANGLE1 : + (UNKNOWN === 1 ^ GIVE_OPPOSITE ? null : ANGLE2), + KNOWN === 2 ? ANGLE1 : + (UNKNOWN === 2 ^ GIVE_OPPOSITE ? null : ANGLE2) + ] + solveTriangle({ + sides: SIDES.slice(), + angles: ANGLES.slice(), + sideLabels: SIDES.slice(), + angleLabels: _.map(ANGLES, function(a) { + return a == null ? a : a + "^\\circ"; + }), + vertexLabels: ["A", "B", "C"] + }) + ["BC", "AC", "AB"][UNKNOWN] + roundTo(1,TRIANGLE.sides[UNKNOWN]) + + + rand(2) ? randRange(-20, 20) : randRange(160, 200) + +
+ +

+ Find UNKNOWN_MEASURE. +

+ +
+

+ Round to the nearest tenth. +

+
+ TRIANGLE = addTriangle(_.extend(TRIANGLE, { + xPos: 1, + yPos: 1, + width: 10, + height: 6, + rot: ROTATION + })); + init({ + range: [[0, TRIANGLE.width + 2], + [0, TRIANGLE.height + 2]] + }); + + TRIANGLE.draw(); + +
+
+
SOLUTION +
+ +
+
+

You can use the law of sines:

+

+ \qquad \dfrac{BC}{\sin(m\angle A)} \quad = + \quad \dfrac{AC}{\sin(m\angle B)} \quad = + \quad \dfrac{AB}{\sin(m\angle C)} +

+
+ +
+

+ Fill in the unknown angle using the fact that the + angles of a triangle always sum to + 180^\circ. +

+

+ \qquad 180^\circ - ANGLE1^\circ - + ANGLE2^\circ \quad = \quad + \pink{TRIANGLE.angles[UNKNOWN]^\circ} +

+
+ TRIANGLE.angleLabels[UNKNOWN] = "\\pink{" + + TRIANGLE.angles[UNKNOWN] + "^\\circ}"; + TRIANGLE.draw(); +
+
+ +
+

+ Set up a useful relationship using the law of sines: +

+

+ \qquad \dfrac{\pink{UNKNOWN_MEASURE}}{\sin( + \pink{TRIANGLE.angles[UNKNOWN]^\circ})} + \quad = \quad\dfrac{\blue{ + TRIANGLE.sides[KNOWN]}}{\sin(\blue{ + TRIANGLE.angles[KNOWN]^\circ})} +

+
+ TRIANGLE.sideLabels[KNOWN] = "\\blue{" + + TRIANGLE.sides[KNOWN] + "}"; + TRIANGLE.angleLabels[KNOWN] = "\\blue{" + + TRIANGLE.angles[KNOWN] + "^\\circ}"; + TRIANGLE.sideLabels[UNKNOWN] = "\\pink{" + + UNKNOWN_MEASURE + "}"; + TRIANGLE.angleLabels[UNKNOWN] = "\\pink{" + + TRIANGLE.angles[UNKNOWN] + "^\\circ}"; + TRIANGLE.color = GRAY; + TRIANGLE.draw(); +
+
+ +
+

+ Solve for the unknown side: +

+

+ \qquad \pink{UNKNOWN_MEASURE} \quad = \quad + \dfrac{\blue{TRIANGLE.sides[KNOWN]} + \cdot + \sin(\pink{TRIANGLE.angles[UNKNOWN]^\circ}) + }{ + \sin(\blue{TRIANGLE.angles[KNOWN]^\circ})} +

+
+ +
+

Evaluate and round to the nearest tenth:

+

+ \qquad \pink{UNKNOWN_MEASURE} + \quad \approx \quad SOLUTION +

+
+ TRIANGLE.sideLabels[UNKNOWN] = "\\pink{" + + SOLUTION + "}"; + TRIANGLE.draw(); +
+
+
+
+ +
+
+ randRange(5, 15, 3) + shuffle([0, 1, 2]) + solveTriangle({ + sides: SIDES.slice(), + angles: [null, null, null], + sideLabels: SIDES.slice(), + vertexLabels: ["A", "B", "C"] + }) + _.map(TRIANGLE.angles, round) + "ABC"[UNKNOWN] + round(Math.asin((TRIANGLE.sides[UNKNOWN] * + sin(ANGLES[KNOWN] * Math.PI / 180)) / + TRIANGLE.sides[KNOWN]) / Math.PI * 180) + + + + rand(2) ? randRange(-20, 20) : randRange(160, 200) + +
+ +

+ Find m\angle UNKNOWN_MEASURE. +

+ +
+

+ Round to the nearest degree. +

+
+ TRIANGLE.angleLabels = [ + KNOWN === 0 ? ANGLES[0] + "^\\circ" : null, + KNOWN === 1 ? ANGLES[1] + "^\\circ" : null, + KNOWN === 2 ? ANGLES[2] + "^\\circ" : null + ]; + TRIANGLE = addTriangle(_.extend(TRIANGLE, { + xPos: 1, + yPos: 1, + width: 10, + height: 6, + rot: ROTATION + })); + init({ + range: [[0, TRIANGLE.width + 2], + [0, TRIANGLE.height + 2]] + }); + + TRIANGLE.draw(); +
+
+
+ SOLUTION + \Large{^\circ} +
+ +
+
+

You can use the law of sines:

+

+ \qquad \dfrac{BC}{\sin(m\angle A)} \quad = + \quad \dfrac{AC}{\sin(m\angle B)} \quad = + \quad \dfrac{AB}{\sin(m\angle C)} +

+
+ +
+

+ Set up a useful relationship using the law of sines: +

+

+ \qquad \dfrac{\pink{SIDES[UNKNOWN]}}{\sin( + \pink{m\angle UNKNOWN_MEASURE})} + \quad = \quad\dfrac{\blue{ + TRIANGLE.sides[KNOWN]}}{\sin(\blue{ + ANGLES[KNOWN]^\circ})} +

+
+ TRIANGLE.sideLabels[KNOWN] = "\\blue{" + + TRIANGLE.sides[KNOWN] + "}"; + TRIANGLE.angleLabels[KNOWN] = "\\blue{" + + ANGLES[KNOWN] + "^\\circ}"; + TRIANGLE.sideLabels[UNKNOWN] = "\\pink{" + + TRIANGLE.sides[UNKNOWN] + "}"; + TRIANGLE.angleLabels[UNKNOWN] = "\\pink{?}"; + TRIANGLE.color = GRAY; + TRIANGLE.draw(); +
+
+ +
+

+ Solve for the sine of the unknown angle: +

+

+ \qquad \sin(\pink{m\angle UNKNOWN_MEASURE}) + \quad = \quad + \dfrac{\pink{TRIANGLE.sides[UNKNOWN]} + \cdot + \sin(\blue{ANGLES[KNOWN]^\circ}) + }{\blue{TRIANGLE.sides[KNOWN]}} +

+
+ +
+

Evaluate the right side:

+

+ \qquad \sin(\pink{m\angle UNKNOWN_MEASURE}) + \quad \approx \quad + roundTo(9, (TRIANGLE.sides[UNKNOWN] * + sin(ANGLES[KNOWN] * Math.PI / 180)) / + TRIANGLE.sides[KNOWN]) +

+
+ +
+

+ Evaluate the inverse sine to find + m\angle UNKNOWN_MEASURE and round to + the nearest degree: +

+

+ \qquad \pink{m\angle UNKNOWN_MEASURE} + \quad \approx \quad SOLUTION^\circ +

+
+ TRIANGLE.angleLabels[UNKNOWN] = "\\pink{" + + SOLUTION + "^\\circ}"; + TRIANGLE.draw(); +
+
+
+
+ +
+
3 khan-exercise.js
 @@ -316,7 +316,8 @@ var Khan = (function() { "math-model": ["ast"], "simplify": ["math-model", "ast", "expr-helpers", "expr-normal-form", "steps-helpers"], "congruency": ["angles", "interactive"], - "graphie-3d": ["graphie", "matrix"] + "graphie-3d": ["graphie", "matrix"], + "graphie-geometry": ["graphie", "matrix"] }, warnTimeout: function() {
268 utils/graphie-geometry.js
 @@ -970,3 +970,271 @@ $.extend(KhanUtil, { return shape; } }); + + +// The following triangley code creates hopefully more legible triangles +// TODO(eater): Collapse this stuff into the existing triangle code above +// without breaking a bunch of stuff (and refactor things so +// Quadrilateral no longer inherits from Triangle) +// +// angles[0] +// /\ +// / \ +// sides[1] / \ sides[2] +// / \ +// /________\ +// angles[2] sides[0] angles[1] +// + +// The following solves the triangle by filling in any side and angle measures +// that were not included. +// +// The sides and angles included must unambiguously specify one triangle. duh. +// If not, behavior is undefined. +// +KhanUtil.solveTriangle = function(triangle) { + var sides = triangle.sides; + var angles = triangle.angles; + var numSides = _.reduce(sides, function(n, side) { + return n + (typeof side === "number" ? 1 : 0); + }, 0); + var numAngles = _.reduce(angles, function(n, angle) { + return n + (typeof angle === "number" ? 1 : 0); + }, 0); + + // If we have 2 sides, we must have the angle opposite the unknown side. + // (SAS is the only valid postulate in this case) + // We can use the law of cosines to find the third side + if (numSides === 2) { + var missingSide = sides.indexOf(null); + var side1 = sides[(missingSide + 1) % 3]; + var side2 = sides[(missingSide + 2) % 3]; + sides[missingSide] = Math.sqrt(side1 * side1 + side2 * side2 - 2 * + side1 * side2 * Math.cos(angles[missingSide] * Math.PI / 180)); + numSides = 3; + } + + // If we have all three sides, we can use the law of cosines to find all + // the angles + if (numSides === 3) { + // Use law of cosines to find all the angles + angles = _.map(angles, function(angle, n) { + var oppSide = sides[n]; + var adjSide1 = sides[(n + 1) % 3]; + var adjSide2 = sides[(n + 2) % 3]; + return Math.acos((adjSide1 * adjSide1 + adjSide2 * adjSide2 - + oppSide * oppSide) / (2 * adjSide1 * adjSide2)); + }); + angles = _.map(angles, KhanUtil.toDegrees); + } + + // If we have 2 angles, we can easily fill in the third angle + if (numAngles === 2) { + var missingAngle = angles.indexOf(null); + angles[missingAngle] = 180 - angles[(missingAngle + 1) % 3] - + angles[(missingAngle + 2) % 3]; + numAngles = 3; + } + + // 3 angles and 1 side is enough to figure out the rest of the sides using + // the law of sines + if (numAngles === 3 && numSides >= 1) { + var knownSide = sides.indexOf(sides[0] || sides[1] || sides[2]); + sides[(knownSide + 1) % 3] = (sides[knownSide] * + Math.sin(angles[(knownSide + 1) % 3] * Math.PI / 180)) + / Math.sin(angles[knownSide] * Math.PI / 180); + sides[(knownSide + 2) % 3] = (sides[knownSide] * + Math.sin(angles[(knownSide + 2) % 3] * Math.PI / 180)) + / Math.sin(angles[knownSide] * Math.PI / 180); + } + + triangle.sides = sides; + triangle.angles = angles; + + triangle.isRight = function() { + return (this.angles[0] === 90 || this.angles[1] === 90 || + this.angles[2] === 90); + }; + + triangle.isScalene = function() { + return (this.angles[0] !== this.angles[1] && + this.angles[1] !== this.angles[2] && + this.angles[0] !== this.angles[2]); + } + + triangle.isNotDegenerate = function() { + return (this.sides[1] + this.sides[2] > this.sides[0] && + this.sides[0] + this.sides[2] > this.sides[1] && + this.sides[0] + this.sides[1] > this.sides[2]); + }; + + return triangle; +}; + + +KhanUtil.addTriangle = function(triangle) { + triangle =$.extend({ + sides: [], + angles: [], + points: [], + sideLabels: [], + angleLabels: [], + vertexLabels: [], + labels: [], + rot: 0, + xPos: 0, + yPos: 0, + width: 10, + height: 10, + color: KhanUtil.BLUE + }, triangle); + + var getDistance = function(point1, point2) { + return Math.sqrt((point1[0] - point2[0]) * (point1[0] - point2[0]) + + (point1[1] - point2[1]) * (point1[1] - point2[1])); + }; + + var rotatePoint = function(point, angle) { + var matrix = KhanUtil.makeMatrix([ + [Math.cos(angle), -Math.sin(angle), 0], + [Math.sin(angle), Math.cos(angle), 0], + [0, 0, 1] + ]); + var vector = KhanUtil.makeMatrix([[point[0]], [point[1]], [1]]); + var prod = KhanUtil.matrixMult(matrix, vector); + return [prod[0], prod[1]]; + }; + + var findCenterPoints = function(triangle, points) { + var Ax = points[0][0]; + var Ay = points[0][1]; + var Bx = points[1][0]; + var By = points[1][1]; + var Cx = points[2][0]; + var Cy = points[2][1]; + var D = 2 * (Ax * (By - Cy) + Bx * (Cy - Ay) + Cx * (Ay - By)); + var a = triangle.sides[0]; + var b = triangle.sides[1]; + var c = triangle.sides[2]; + var P = a + b + c; + var x1 = (a * Ax + b * Bx + c * Cx) / P; + var y1 = (a * Ay + b * By + c * Cy) / P; + var x = ((Ay * Ay + Ax * Ax) * (By - Cy) + (By * By + Bx * Bx) * + (Cy - Ay) + (Cy * Cy + Cx * Cx) * (Ay - By)) / D; + var y = ((Ay * Ay + Ax * Ax) * (Cx - Bx) + (By * By + Bx * Bx) * + (Ax - Cx) + (Cy * Cy + Cx * Cx) * (Bx - Ax)) / D; + return { + circumCenter: [x, y], + centroid: [1 / 3 * (Ax + Bx + Cx), 1 / 3 * (Ay + By + Cy)], + inCenter: [x1, y1] + }; + }; + + triangle.draw = function() { + var graphie = KhanUtil.currentGraph; + if (triangle.set != null) { + triangle.set.remove(); + } + _.each(triangle.labels, function(lbl) { + lbl.remove(); + }); + triangle.set = graphie.raphael.set(); + triangle.set.push(graphie.path(triangle.points.concat([true]),{ + stroke: triangle.color + })); + + var centerPoints = findCenterPoints(triangle, triangle.points); + _(3).times(function(i) { + if (triangle.angleLabels[i] != null) { + var ang = Math.atan2(centerPoints.inCenter[1] - + triangle.points[i][1], centerPoints.inCenter[0] - + triangle.points[i][0]); + + // The angle measure label needs to be further from the vertex + // for small angles and closer for large angles. This is an + // empirically determined formula for figuring out how far. + var labelDist = (3.51470560176242 - 0.5687298702748785) * + Math.exp(-0.037587715462826674 * triangle.angles[i]) + + 0.5687298702748785; + + triangle.labels.push(graphie.label([ + triangle.points[i][0] + Math.cos(ang) * labelDist, + triangle.points[i][1] + Math.sin(ang) * labelDist], + triangle.angleLabels[i], "center")); + } + }); + + _(3).times(function(i) { + if (triangle.sideLabels[i] != null) { + var x = (triangle.points[(i + 1) % 3][0] + + triangle.points[(i + 2) % 3][0]) / 2; + var y = (triangle.points[(i + 1) % 3][1] + + triangle.points[(i + 2) % 3][1]) / 2; + var ang; + var labelDist = 0.4; + if (triangle.angles[i] < 90) { + ang = Math.atan2(y - centerPoints.circumCenter[1], + x - centerPoints.circumCenter[0]); + } else if (triangle.angles[i] > 90) { + ang = Math.atan2(centerPoints.circumCenter[1] - y, + centerPoints.circumCenter[0] - x); + } else { + ang = Math.atan2(y - centerPoints.centroid[1], + x - centerPoints.centroid[0]); + } + + triangle.labels.push(graphie.label([x, y], + triangle.sideLabels[i], ang)); + } + }); + + _(3).times(function(i) { + if (triangle.vertexLabels[i] != null) { + var ang = Math.atan2(triangle.points[i][1] - + centerPoints.inCenter[1], triangle.points[i][0] - + centerPoints.inCenter[0]); + var labelDist = 0.4; + + var lbl = graphie.label([ + triangle.points[i][0] + Math.cos(ang) * labelDist, + triangle.points[i][1] + Math.sin(ang) * labelDist], + triangle.vertexLabels[i], "center"); + lbl.css("color", triangle.color); + triangle.labels.push(lbl); + } + }); + }; + + triangle.points[2] = [0, 0]; + triangle.points[1] = [triangle.sides[0], 0]; + triangle.points[0] = [Math.cos(triangle.angles[2] * Math.PI / 180) * + triangle.sides[1], Math.sin(triangle.angles[2] * Math.PI / 180) * + triangle.sides[1]]; + + // Rotate the triangle + triangle.points[0] = rotatePoint(triangle.points[0], triangle.rot * + Math.PI / 180); + triangle.points[1] = rotatePoint(triangle.points[1], triangle.rot * + Math.PI / 180); + triangle.points[2] = rotatePoint(triangle.points[2], triangle.rot * + Math.PI / 180); + + // Scale and translate the triangle such that it fits within the + // specified width/height constraint and is positioned at xPos, yPos + var minX = _.min(_.map(triangle.points, function(p) { return p[0]; })); + var maxX = _.max(_.map(triangle.points, function(p) { return p[0]; })); + var minY = _.min(_.map(triangle.points, function(p) { return p[1]; })); + var maxY = _.max(_.map(triangle.points, function(p) { return p[1]; })); + var xScale = triangle.width / (maxX - minX); + var yScale = triangle.height / (maxY - minY); + var scale = _.min([xScale, yScale]); + triangle.width = (maxX - minX) * scale; + triangle.height = (maxY - minY) * scale; + + triangle.points = _.map(triangle.points, function(p) { + return [(p[0] - minX) * scale + triangle.xPos, + (p[1] - minY) * scale + triangle.yPos]; + }); + + return triangle; +};

#### 0 comments on commit a5882e3

Please sign in to comment.
Something went wrong with that request. Please try again.