fix: conform to Joyce's original axis orientations (#47)

Seems to produce correct diagrams for all of Joyce Euclid up to but not including the Pythagorean Theorem (I.47), so hopefully I have managed to find and correct all of the necessary orientation flips (but I won't be shocked if I have not).

Resolves #46.

Reviewed-on: #47
Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Co-committed-by: Glen Whitney <glen@studioinfinity.org>
This commit is contained in:
Glen Whitney 2023-11-27 03:40:04 +00:00 committed by Glen Whitney
parent 5b1ca40b26
commit 52272ff728

View File

@ -49,6 +49,16 @@ adapParams: AdapParams :=
? {loader: 'https://www.geogebra.org/apps/deployggb.js', joyceApplets: []} ? {loader: 'https://www.geogebra.org/apps/deployggb.js', joyceApplets: []}
: JSON.parse(adapptScript.dataset.params ?? '') as AdapParams : JSON.parse(adapptScript.dataset.params ?? '') as AdapParams
// For accessing/setting the y coordinate of a set of coordinates:
Y := 1
Z := 2
function vertFlipped(coords: number[], cdata: ConstructionData): XYZ
coords = coords.slice()
if cdata.is3d
if coords[Z] then coords[Z] = -coords[Z]
else coords[Y] = cdata.height - coords[Y]
return coords as XYZ
type ConstructionData type ConstructionData
id: string id: string
bg: RGB bg: RGB
@ -285,12 +295,13 @@ function jToG(
api.registerObjectUpdateListener(cdata.pivot, 'pivotListener') api.registerObjectUpdateListener(cdata.pivot, 'pivotListener')
// Create callback to assign colors // Create callback to assign colors
traceC := adapParams.config?.color
console.log 'Considering coloring', name, 'with', colors if traceC
if colors.length is 4 and colors.every (color) => invisible color if colors.length is 4 and colors.every (color) => invisible color
cmdr.callbacks.push (api: AppletObject) => api.setVisible name, false cmdr.callbacks.push (api: AppletObject) => api.setVisible name, false
else // we have to decorate else // we have to decorate
dimension .= cmdr.parts.findLastIndex .includes name dimension .= cmdr.parts.findLastIndex .includes name
cmdr.callbacks.push (api: AppletObject, parts: DimParts) => cmdr.callbacks.push (api: AppletObject, parts: DimParts) =>
trace := adapParams.config?.color
// Operate in order faces, lines, point, caption so that // Operate in order faces, lines, point, caption so that
// we can adjust components after setting overall color, etc. // we can adjust components after setting overall color, etc.
@ -298,18 +309,18 @@ function jToG(
if invisible colors[3] if invisible colors[3]
for each face of parts[2] for each face of parts[2]
if face is name if face is name
console.log 'Fading out interior of', face if trace console.log 'Fading out interior of', face if traceC
// hide the interior by making it transparent // hide the interior by making it transparent
api.setFilling face, 0 api.setFilling face, 0
else if face not in cdata.elements else if face not in cdata.elements
console.log 'Hiding face', face if trace console.log 'Hiding face', face if traceC
api.setVisible face, false api.setVisible face, false
else else
faceRGB := joyce2rgb(colors[3] or 'brighter', cdata.bg) faceRGB := joyce2rgb(colors[3] or 'brighter', cdata.bg)
deep := ['circle', 'polygon', 'sector'] deep := ['circle', 'polygon', 'sector']
filling := deep.includes(klass) ? 0.7 : 0.2 filling := deep.includes(klass) ? 0.7 : 0.2
for each face of parts[2] for each face of parts[2]
console.log 'Coloring face', face, 'to', colors[3] if trace console.log 'Coloring face', face, 'to', colors[3] if traceC
api.setVisible face, true api.setVisible face, true
api.setFilling face, filling api.setFilling face, filling
api.setColor face, ...faceRGB api.setColor face, ...faceRGB
@ -318,17 +329,17 @@ function jToG(
if invisible colors[2] if invisible colors[2]
for each line of parts[1] for each line of parts[1]
if line is name or line not in cdata.elements if line is name or line not in cdata.elements
console.log 'Hiding line', line if trace console.log 'Hiding line', line if traceC
api.setVisible line, false api.setVisible line, false
else else
lineRGB := joyce2rgb(colors[2] or 'black', cdata.bg) lineRGB := joyce2rgb(colors[2] or 'black', cdata.bg)
for each line of parts[1] for each line of parts[1]
console.log 'Coloring line', line, 'to', colors[2] if trace console.log 'Coloring line', line, 'to', colors[2] if traceC
api.setVisible line, true api.setVisible line, true
api.setColor line, ...lineRGB api.setColor line, ...lineRGB
// Now color the points: // Now color the points:
if trace if traceC
console.log console.log
'Considering point colors for', name, 'of dimension', dimension 'Considering point colors for', name, 'of dimension', dimension
if invisible colors[1] if invisible colors[1]
@ -336,19 +347,19 @@ function jToG(
// items: // items:
for each point of parts[0] for each point of parts[0]
if point is name or point not in cdata.elements if point is name or point not in cdata.elements
console.log 'Hiding point', point if trace console.log 'Hiding point', point if traceC
api.setVisible point, false api.setVisible point, false
else if dimension is 0 or colors[1] // Need to color the points else if dimension is 0 or colors[1] // Need to color the points
ptRGB := colors[1] ? joyce2rgb colors[1], cdata.bg ptRGB := colors[1] ? joyce2rgb colors[1], cdata.bg
: pointDefaultRGB name, method, isPivot : pointDefaultRGB name, method, isPivot
for each point of parts[0] for each point of parts[0]
console.log 'Coloring point', point, 'to', colors[1] if trace console.log 'Coloring point', point, 'to', colors[1] if traceC
api.setVisible point, true api.setVisible point, true
api.setColor point, ...ptRGB api.setColor point, ...ptRGB
// Make the caption the correct color // Make the caption the correct color
if invisible colors[0] if invisible colors[0]
console.log 'Hiding label', name if trace console.log 'Hiding label', name if traceC
api.setLabelVisible name, false api.setLabelVisible name, false
else if colors[dimension+1] and colors[dimension+1] is not colors[0] else if colors[dimension+1] and colors[dimension+1] is not colors[0]
// Have to make a text to provide the caption, since GeoGebra // Have to make a text to provide the caption, since GeoGebra
@ -377,7 +388,7 @@ function jToG(
`(4*${ex1}+${ex2})/5` `(4*${ex1}+${ex2})/5`
textCmd := `${textName} = Text("${jname}", ${locationExpr})` textCmd := `${textName} = Text("${jname}", ${locationExpr})`
textCol := joyce2rgb colors[0], cdata.bg textCol := joyce2rgb colors[0], cdata.bg
if trace if traceC
console.log `Making text '${textCmd}' colored`, textCol console.log `Making text '${textCmd}' colored`, textCol
api.evalCommand textCmd api.evalCommand textCmd
api.setColor textName, ...textCol api.setColor textName, ...textCol
@ -386,13 +397,13 @@ function jToG(
else if colors[0] else if colors[0]
// Label gets the correct color from element // Label gets the correct color from element
// but we had better make sure it is visible: // but we had better make sure it is visible:
console.log 'Showing label', name if trace console.log 'Showing label', name if traceC
api.setLabelVisible name, true api.setLabelVisible name, true
else else
// label color is defaulting. Same as element for points, invisible // label color is defaulting. Same as element for points, invisible
// otherwise: // otherwise:
show := klass is 'point' show := klass is 'point'
console.log 'Setting label vis of', name, 'to', show if trace console.log 'Setting label vis of', name, 'to', show if traceC
api.setLabelVisible name, show api.setLabelVisible name, show
cdata.elements[jname] = {otherName: name, usesCaptions, klass, cmdr.ends} cdata.elements[jname] = {otherName: name, usesCaptions, klass, cmdr.ends}
@ -563,7 +574,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
pivotData[cdata.pivot].rotatable.push name pivotData[cdata.pivot].rotatable.push name
if args.scalar and args.scalar.length if args.scalar and args.scalar.length
callbacks.push (api: AppletObject) => callbacks.push (api: AppletObject) =>
api.setCoords name, ...args.scalar as XYZ api.setCoords name, ...vertFlipped(args.scalar or [], cdata)
/cutoff|extend/ /cutoff|extend/
pt := args.subpoints pt := args.subpoints
unless pt and pt.length is 4 then return unless pt and pt.length is 4 then return
@ -574,8 +585,8 @@ classHandler: Record<JoyceClass, ClassHandler> :=
unless args.subpoints then return unless args.subpoints then return
commands.push `${name} = ${args.subpoints[0]}` commands.push `${name} = ${args.subpoints[0]}`
/fixed|free/ /fixed|free/
coords := args.scalar unless args.scalar then return
unless coords then return coords := vertFlipped(args.scalar, cdata)
scoord := coords.join ',' scoord := coords.join ','
if pivotable then pivotData[cdata.pivot].rotatable.push name if pivotable then pivotData[cdata.pivot].rotatable.push name
commands.push `${name} = (${scoord})` commands.push `${name} = (${scoord})`
@ -612,14 +623,14 @@ classHandler: Record<JoyceClass, ClassHandler> :=
commands.push `${name} = Point(${segment})` commands.push `${name} = Point(${segment})`
if args.scalar and args.scalar.length if args.scalar and args.scalar.length
callbacks.push (api: AppletObject) => callbacks.push (api: AppletObject) =>
api.setCoords name, ...args.scalar as XYZ api.setCoords name, ...vertFlipped(args.scalar or [], cdata)
'lineSlider' 'lineSlider'
pt := args.subpoints pt := args.subpoints
unless pt and pt.length is 2 then return unless pt and pt.length is 2 then return
commands.push `${name} = Point(Line(${pt[0]}, ${pt[1]}))` commands.push `${name} = Point(Line(${pt[0]}, ${pt[1]}))`
if args.scalar and args.scalar.length if args.scalar and args.scalar.length
callbacks.push (api: AppletObject) => callbacks.push (api: AppletObject) =>
api.setCoords name, ...args.scalar as XYZ api.setCoords name, ...vertFlipped(args.scalar or [], cdata)
'midpoint' 'midpoint'
if args.line if args.line
commands.push `${name} = Midpoint(${args.line[0]})` commands.push `${name} = Midpoint(${args.line[0]})`
@ -634,8 +645,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
// Note only the two-point option implemented so far // Note only the two-point option implemented so far
unless args.subpoints return unless args.subpoints return
[center, direction] := args.subpoints [center, direction] := args.subpoints
// Note clockwise 90° rotation (3π/2) confirmed in Joyce source commands.push `${name} = Rotate(${direction}, pi/2, ${center})`
commands.push `${name} = Rotate(${direction}, 3*pi/2, ${center})`
/proportion|similar/ /proportion|similar/
[source, displacement] := [source, displacement] :=
proportionSimilar method, args, cdata, aux, commands, auxiliaries proportionSimilar method, args, cdata, aux, commands, auxiliaries
@ -665,7 +675,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
'bichord' 'bichord'
// To match Joyce, we need to get the ordering here correct. // To match Joyce, we need to get the ordering here correct.
// we want the order so that start -> end sweeping past the // we want the order so that start -> end sweeping past the
// center of the other circle is counterclockwise in the first // center of the other circle is clockwise in the first
// circle // circle
cr := args.circle cr := args.circle
unless cr return unless cr return
@ -676,8 +686,8 @@ classHandler: Record<JoyceClass, ClassHandler> :=
condition := (`Angle(${aux}1,${ctr[0]},${ctr[1]}${inPlane})` condition := (`Angle(${aux}1,${ctr[0]},${ctr[1]}${inPlane})`
+ `< Angle(${aux}2,${ctr[0]},${ctr[1]}${inPlane})`) + `< Angle(${aux}2,${ctr[0]},${ctr[1]}${inPlane})`)
commands.push commands.push
`${aux}3 = If(${condition}, ${aux}2, ${aux}1)` `${aux}3 = If(${condition}, ${aux}1, ${aux}2)`
`${aux}4 = If(${condition}, ${aux}1, ${aux}2)` `${aux}4 = If(${condition}, ${aux}2, ${aux}1)`
ends[0] = aux + 3 ends[0] = aux + 3
ends[1] = aux + 4 ends[1] = aux + 4
auxiliaries.push ...[1..4].map (n) => aux + n auxiliaries.push ...[1..4].map (n) => aux + n
@ -748,12 +758,12 @@ classHandler: Record<JoyceClass, ClassHandler> :=
if args.line?.length is 1 if args.line?.length is 1
ln := args.line[0] ln := args.line[0]
commands.push commands.push
`${name} = Rotate(${ln}, 3*pi/2, ${pt[0]}${inPlane})` `${name} = Rotate(${ln}, pi/2, ${pt[0]}${inPlane})`
`${aux} = Vertex(${name}, 2)` `${aux} = Vertex(${name}, 2)`
madeSegment = true madeSegment = true
else else
commands.push commands.push
`${aux} = Rotate(${pt[1]}, 3*pi/2, ${pt[0]}${inPlane})` `${aux} = Rotate(${pt[1]}, pi/2, ${pt[0]}${inPlane})`
when 3 when 3
return // TODO: line perpendicular to plane return // TODO: line perpendicular to plane
when 4 when 4
@ -854,7 +864,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
else if method is 'application' else if method is 'application'
unless pt.length is 3 then return unless pt.length is 3 then return
unless args.polygon?.length is 1 then return unless args.polygon?.length is 1 then return
direction := `UnitVector(${pt[2]} - ${pt[0]})` direction := `UnitVector(${pt[0]} - ${pt[2]})`
angle := `Angle(${pt[1]},${pt[0]},${pt[2]})` angle := `Angle(${pt[1]},${pt[0]},${pt[2]})`
length := `Area(${args.polygon})` length := `Area(${args.polygon})`
+ `/(Distance(${pt[0]},${pt[1]})*sin(${angle}))` + `/(Distance(${pt[0]},${pt[1]})*sin(${angle}))`
@ -900,12 +910,12 @@ classHandler: Record<JoyceClass, ClassHandler> :=
/arc|sector/ /arc|sector/
unless args.subpoints?.length is 3 return unless args.subpoints?.length is 3 return
parts[0].push ...args.subpoints parts[0].push ...args.subpoints
[center, end, start] .= args.subpoints [center, start, end] .= args.subpoints
parms .= center + ', ' + start + ', ' + end parms .= center + ', ' + start + ', ' + end
prefix .= 'Circular' prefix .= 'Circular'
if method is 'arc' if method is 'arc'
temp := end temp := start
end = center start = center
center = temp center = temp
parms = start + ', ' + center + ', ' + end parms = start + ', ' + center + ', ' + end
prefix = 'Circumcircular' prefix = 'Circumcircular'