DoubleReflectionQuestion.jsx (3777B)
1 import Plot from "../plotting/Plot"; 2 import Line from "../plotting/Line"; 3 import Polygon from "../plotting/Polygon"; 4 import Question from "../Question"; 5 import Point from "../plotting/Point"; 6 import Equation from "../Equation"; 7 import RotationTextElement from "../RotationTextElement"; 8 import { getRandomChoice, getRandomInt } from "../../scripts/randomUtils"; 9 import { formatLine, formatVec } from "../../scripts/formatUtils"; 10 11 function reflectPoint(point, line = {a: 1, b: 1, c: 0}) { 12 const [px, py] = point 13 const k = (line.a*px + line.b*py + line.c) / (line.a**2 + line.b**2) 14 return [px - 2*line.a*k, py - 2*line.b*k] 15 } 16 17 function getLineAngle(line) { 18 // gradient is -b/a. need -arctan(b/a) 19 return -Math.atan(line.b/line.a) 20 } 21 22 function findIntersect(line1, line2) { 23 const k = line1.a*line2.b - line2.a*line1.b 24 // parallel 25 if (k == 0) { 26 return null 27 } 28 return [(line2.c*line1.b - line1.c*line2.b) / k, (line2.a*line1.c - line1.a*line2.c) / k] 29 } 30 31 function randomLine(seed, lineType, excludedLine = {a: 100, b: 100, c: 100}) { 32 let f = (seed) => {return {a: 0, b: 1, c: 1}} 33 switch (lineType) { 34 case 'horizontal': 35 f = (seed) => {return {a: 0, b: 1, c: getRandomInt(-2, 2, seed)}} 36 break 37 case 'vertical': 38 f = (seed) => {return {a: 1, b: 0, c: getRandomInt(-2, 2, seed)}} 39 break 40 case 'diagonal': 41 f = (seed) => {return {a: 1, b: getRandomChoice([-1, 1], seed), c: 0}} 42 break 43 } 44 45 let output = excludedLine 46 while (output.a == excludedLine.a && output.b == excludedLine.b && output.c == excludedLine.c) { 47 output = f(seed++) 48 } 49 return output 50 } 51 52 export default function DoubleReflectionQuestion({ seed, showAnswer }) { 53 const viewBox = [-7, -7, 14, 14] 54 const originalShape = [[0, 1], [2, 1], [0, 2]] 55 56 const lineTypes = ['horizontal', 'vertical', 'diagonal'] 57 const line1Type = getRandomChoice(lineTypes, seed++) 58 const line2Type = getRandomChoice(lineTypes, seed++) 59 60 const line1 = randomLine(seed++, line1Type) 61 const line2 = randomLine(seed++, line2Type, line1) 62 63 const question = (<> 64 <p> 65 Reflect P in <Equation equation={formatLine(line1)} /> then <Equation equation={formatLine(line2)} />.<br /> 66 What single transformation describes this? 67 </p> 68 <Plot viewBox={viewBox} width="300" height="300"> 69 <Polygon points={originalShape} label="P" colour="1" /> 70 <Line line={line1} colour="2" /> 71 <Line line={line2} colour="3" /> 72 </Plot> 73 </>) 74 75 const midShape = originalShape.map((point) => reflectPoint(point, line1)) 76 const endShape = midShape.map((point) => reflectPoint(point, line2)) 77 78 const intersect = findIntersect(line1, line2) 79 let answerTextElement = <></> 80 let answerGraphElement = <></> 81 if (intersect === null) { // translation, lines parallel 82 const xTrans = 2*(line1.c*line1.a - line2.c*line2.a) 83 const yTrans = 2*(line1.c*line1.b - line2.c*line2.b) 84 answerTextElement = <p>Translation by vector <Equation equation={formatVec([xTrans, yTrans])} /></p> 85 } else { // rotation 86 const angle = 2*(getLineAngle(line2) - getLineAngle(line1)) 87 answerTextElement = <p>Rotation <RotationTextElement angle={angle} /> about <Equation equation={`(${intersect[0]}, ${intersect[1]}).`} /></p> 88 answerGraphElement = <Point point={intersect} /> 89 } 90 const answer = (<> 91 {answerTextElement} 92 <Plot viewBox={viewBox} width="300" height="300"> 93 <Polygon points={originalShape} label="P" colour="1" /> 94 <Polygon points={midShape} label="Q" colour="2" /> 95 <Polygon points={endShape} label="R" colour="3" /> 96 <Line line={line1} colour="2" /> 97 <Line line={line2} colour="3" /> 98 {answerGraphElement} 99 </Plot> 100 </>) 101 102 return <Question question={question} answer={answer} showAnswer={showAnswer} /> 103 }