From d2c872020cd87ffe4cdbd510c23e1f7854eb0150 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sun, 11 Feb 2024 23:34:43 +0000 Subject: [PATCH] fix: hacks and bugfixes to get Book 13 working (#59) Reviewed-on: https://code.studioinfinity.org/glen/archematics/pulls/59 Co-authored-by: Glen Whitney Co-committed-by: Glen Whitney --- src/adapptlet.civet | 101 +++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 33 deletions(-) diff --git a/src/adapptlet.civet b/src/adapptlet.civet index bfa902d..aacc327 100644 --- a/src/adapptlet.civet +++ b/src/adapptlet.civet @@ -266,16 +266,21 @@ function jToG( if scalar is scalar // not NaN (args.scalar ?= []).push scalar continue - unless jdep in cdata.elements - console.warn `Reference to unknown geometric entity ${jdep} in $jCom}` - return cmdr - usesCaptions.push jdep - {klass: depKlass, otherName: depGeo, ends} := cdata.elements[jdep] - (args[depKlass] ?= []).push depGeo - if depKlass is 'point' - (args.subpoints ?= []).push depGeo - else if depKlass is 'line' - (args.subpoints ?= []).push ...ends ?? [] + if jdep is 'screen' + // special case for Joyce; assume xOyPlane + (args.plane ?= []).push 'xOyPlane' + else + unless jdep in cdata.elements + console.warn + `Reference to unknown geometric entity ${jdep} in ${jCom}` + return cmdr + usesCaptions.push jdep + {klass: depKlass, otherName: depGeo, ends} := cdata.elements[jdep] + (args[depKlass] ?= []).push depGeo + if depKlass is 'point' + (args.subpoints ?= []).push depGeo + else if depKlass is 'line' + (args.subpoints ?= []).push ...ends ?? [] cmdr = classHandler[klass] name, method, args, index, cdata, colors, jname unless name is jname then cmdr.callbacks.push (api: AppletObject) => api.setCaption name, jname @@ -586,7 +591,7 @@ classHandler: Record := aux := name + 'aUx' pivotable := cdata.pivot and name !== pivotData[cdata.pivot].pivot parts[0].push name - // HACK: Special-case corrections for Joyce Elements Bk XI & XII + // HACK: Special-case corrections for Joyce Elements Bk XI -- XIII if cdata.title is 'XI.4' if name is 'Z' and method is 'fixed' and args.scalar?.length is 3 method = 'perpendicular' @@ -614,6 +619,23 @@ classHandler: Record := if (jname is 'a' and method is 'lineSlider' and args.subpoints?[1] is cdata.elements['1Y'].otherName) args.subpoints[1] = cdata.elements['1Z'].otherName + if cdata.title is 'XIII.17' + // Joyce's point perpendicular to a plane is ambiguous between + // two possible positions, and Geogebra's choice does not + // always agree with what Joyce's code did. So we just have to flip + // one of his choices in the dodecahedron + if name is 'U' and method is 'extend' and args.subpoints?[0] is "U'" + method = 'midpoint' + args.point = ["U'", "U'"] // hacky way to make U = U' + if cdata.title is 'Dodecahedron and cube' + // similar issues to XIII.17 + if (name is 'VAB' or name is 'VBC') and method is 'perpendicular' + args.scalar = [1] + if cdata.title is 'XIII.18' + // Joyce just mistook where N is + if name is 'N' and method is 'intersection' + method = 'golden' + args.subpoints = ['B', 'F'] switch method /angle(?:Bisector|Divider)/ {center, foot} := @@ -679,6 +701,12 @@ classHandler: Record := : `Line(${pt[1]},${pt[2]})` commands.push `${name} = ClosestPoint(${destination}, ${pt[0]})` + 'golden' + // Added in this implementation to ease fix of XIII.18 + pt := args.subpoints + unless pt and pt.length is 2 then return + commands.push + `${name} = ${pt[0]} + ((sqrt(5)-1)/2)*(${pt[1]}-${pt[0]})` 'intersection' // Checking Joyce source, means intersection of lines, not // intersection of line segments @@ -736,15 +764,13 @@ classHandler: Record := commands.push `${name} = Rotate(${pt[1]}, pi/2, ${center}${inPlane})` when 3 // perpendicular **to** the plane - // Uses lots of auxiliaries radius := `Distance(${pt[1]}, ${pt[2]})` + which := args.scalar ? args.scalar[0] : 2 //HACK: 2 vs 1?? commands.push - `${aux}1 = Circle(${center}, ${radius}${inPlane})` - `${aux}2 = PointIn(${aux}1)` - `${aux}3 = PerpendicularLine(${center}${inPlane})` - `${aux}4 = Plane(${aux}2, ${aux}3)` - `${name} = Rotate(${aux}2, pi/2, ${center}, ${aux}4)` - auxiliaries.push aux+n for n of [1..4] + `${aux}1 = Sphere(${center}, ${radius})` + `${aux}2 = PerpendicularLine(${center}${inPlane})` + `${name} = Intersect(${aux}2,${aux}1,${which})` + auxiliaries.push aux+1, aux+2 when 4 commands.push `${aux}1 = Ray(${center}, Rotate(${pt[1]}, pi/2, ${center}${inPlane}))` @@ -932,7 +958,6 @@ classHandler: Record := aux := name + 'aUx' parts[1].push name circle .= '' - defaultPlane := cdata.is3d ? ', xOyPlane' : '' switch method 'circumcircle' pt := args.subpoints @@ -944,18 +969,17 @@ classHandler: Record := circle = `IntersectConic(${sph[0]}, ${sph[1]})` 'radius' pt := args.subpoints - unless pt then return - inPlane := args.plane ? `, ${args.plane[0]}` : defaultPlane - switch pt.length - when 2 - [center, point] := pt - ends[0] = center - circle = `Circle(${center}, ${point}${inPlane})` - when 3 - center := pt[0] - ends[0] = center - radius := `Distance(${pt[1]}, ${pt[2]})` - circle = `Circle(${center}, ${radius}${inPlane})` + unless pt and pt.length > 1 and pt.length < 4 then return + ends[0] = pt[0] + if pt.length is 2 and not cdata.is3d + circle = `Circle(${pt[0]}, ${pt[1]})` + else // 3d or three points + radIx := pt.length - 2 + inPlane := (cdata.is3d + ? args.plane ? `, ${args.plane[0]}` : ', xOyPlane' + : '') + radius := `Distance(${pt[radIx]}, ${pt[radIx+1]})` + circle = `Circle(${pt[0]}, ${radius}${inPlane})` commands.push `${aux} = ${circle}` // for the filling `${name} = ${circle}` // for the perimeter @@ -978,9 +1002,14 @@ classHandler: Record := N = args.scalar[0] commands.push '' // hack, make sure there is a command parts[0].push pt[0], pt[1] + inPlane := cdata.is3d + ? args.plane ? `, ${args.plane[0]}` : ', xOyPlane' + : '' callbacks.push (api: AppletObject, moreParts: DimParts) => - made:= api.evalCommandGetLabels - `${name} = Polygon(${pt[0]},${pt[1]}, ${N})` + command := `${name} = Polygon(${pt[0]},${pt[1]}, ${N}${inPlane})` + made:= api.evalCommandGetLabels command + if adapParams.config?.commands + console.log 'Finishing with', command, 'producing', made if not made return for each obj of made.split ',' if obj is name continue @@ -1212,6 +1241,12 @@ classHandler: Record := api.renameObject obj, newObj moreParts[1].push newObj base = ends[0] + else // need to grab the parts of the base, which become + // parts of the polyhedron + baseParts := cdata.elements[base].parts + if baseParts + parts[0].push ...baseParts[0] + parts[1].push ...baseParts[1] made := api.evalCommandGetLabels `${name} = Pyramid(${base}, ${ends[1]})` if not made return