feat: joyce commands needed for BookEleven #57

Merged
glen merged 3 commits from BookEleven into main 2024-02-10 07:01:21 +00:00
2 changed files with 116 additions and 32 deletions
Showing only changes of commit a4f3a96d6e - Show all commits

View File

@ -13,7 +13,8 @@ obs := new MutationObserver (mutationList) =>
newNode := (newGenericNode as HTMLElement) newNode := (newGenericNode as HTMLElement)
newParent := (change.target as HTMLElement) newParent := (change.target as HTMLElement)
unless newNode.tagName is 'APPLET' then continue unless newNode.tagName is 'APPLET' then continue
unless newNode.getAttribute('code') is 'Geometry' then continue code := newNode.getAttribute('code')
unless code is 'Geometry' or code is 'Geometry.class' then continue
id .= newParent.getAttribute 'id' id .= newParent.getAttribute 'id'
unless id unless id
id = 'joyceApplet' + joyceApplets.length id = 'joyceApplet' + joyceApplets.length

View File

@ -56,7 +56,7 @@ function vertFlipped(coords: number[], cdata: ConstructionData): XYZ
coords = coords.slice() coords = coords.slice()
if cdata.is3d if cdata.is3d
if coords[Z] then coords[Z] = -coords[Z] if coords[Z] then coords[Z] = -coords[Z]
else coords[Y] = cdata.height - coords[Y] coords[Y] = cdata.height - coords[Y]
return coords as XYZ return coords as XYZ
type ConstructionData type ConstructionData
@ -128,11 +128,12 @@ function postApplets(jApplets: AppletDescription[], codebase = '')
if is3d if is3d
depth .= jApp.width depth .= jApp.width
if jApp.height > depth then depth = jApp.height if jApp.height > depth then depth = jApp.height
depth /= 8
api.setCoordSystem api.setCoordSystem
-10, 10 + jApp.width, -10 + jApp.width/6, 10 + 4*jApp.width/6,
-10, 10 + jApp.height, -10 + jApp.height/6, 10 + 4*jApp.height/6,
-depth - 10, depth + 10, -2*depth - 10, depth + 10,
false true
api.setAxesVisible 3, false, false, false api.setAxesVisible 3, false, false, false
api.setGridVisible 3, false api.setGridVisible 3, false
else else
@ -207,8 +208,7 @@ function dispatchJcommand(
'title' 'title'
if adapParams.config?.commands if adapParams.config?.commands
console.log 'Setting title to', value console.log 'Setting title to', value
corner := cdata.is3d ? 'Corner(-1,1)' : 'Corner(1,1)' api.evalCommand `TitlePoint = Corner(1,1)
api.evalCommand `TitlePoint = ${corner}
Text("${value}", TitlePoint + (2,5))` Text("${value}", TitlePoint + (2,5))`
'pivot' 'pivot'
return // already handled in postApplets return // already handled in postApplets
@ -485,7 +485,7 @@ function joyce2rgb(cname: string, backgroundRGB?: RGB): RGB
/yellow/i /yellow/i
[255,255,0] [255,255,0]
/random/i /random/i
colorsea.random().lighten(40).rgb() colorsea.random().lighten(30).rgb()
/background/i /background/i
bg bg
/brighter/i /brighter/i
@ -572,6 +572,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
return := freshCommander() return := freshCommander()
{commands, callbacks, parts, auxiliaries} := return.value {commands, callbacks, parts, auxiliaries} := return.value
zeroVector := cdata.is3d ? 'Vector((0,0,0))' : 'Vector((0,0))' zeroVector := cdata.is3d ? 'Vector((0,0,0))' : 'Vector((0,0))'
defaultPlane := cdata.is3d ? ', xOyPlane' : ''
aux := name + 'aUx' aux := name + 'aUx'
pivotable := cdata.pivot and name !== pivotData[cdata.pivot].pivot pivotable := cdata.pivot and name !== pivotData[cdata.pivot].pivot
parts[0].push name parts[0].push name
@ -684,13 +685,22 @@ classHandler: Record<JoyceClass, ClassHandler> :=
'perpendicular' 'perpendicular'
// 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
inPlane := args.plane ? `,${args.plane[0]}` : defaultPlane
[center, direction] := args.subpoints [center, direction] := args.subpoints
commands.push `${name} = Rotate(${direction}, pi/2, ${center})` commands.push
`${name} = Rotate(${direction}, pi/2, ${center}${inPlane})`
/proportion|similar/ /proportion|similar/
[source, displacement] := [source, displacement] :=
proportionSimilar method, args, cdata, aux, commands, auxiliaries proportionSimilar method, args, cdata, aux, commands, auxiliaries
unless source then return unless source then return
commands.push `${name} = Translate(${source}, ${displacement})` commands.push `${name} = Translate(${source}, ${displacement})`
'sphereSlider'
sph := args.sphere?[0]
unless sph then return
commands.push `${name} = PointIn(${sph})`
if args.scalar and args.scalar.length
callbacks.push (api: AppletObject) =>
api.setCoords name, ...vertFlipped(args.scalar or [], cdata)
'vertex' 'vertex'
commands.push commands.push
`${name} = Vertex(${args.polygon?[0]},${args.scalar?[0]})` `${name} = Vertex(${args.polygon?[0]},${args.scalar?[0]})`
@ -836,22 +846,27 @@ classHandler: Record<JoyceClass, ClassHandler> :=
callbacks.push (api: AppletObject) => api.setLabelVisible name, true callbacks.push (api: AppletObject) => api.setLabelVisible name, true
parts[0].push ...ends parts[0].push ...ends
circle: (name, method, args) => circle: (name, method, args, index, cdata) =>
return := freshCommander() return := freshCommander()
return.value.ends = ['', ''] return.value.ends = ['', '']
{commands, callbacks, parts, auxiliaries, ends} := return.value {commands, callbacks, parts, auxiliaries, ends} := return.value
aux := name + 'aUx' aux := name + 'aUx'
parts[1].push name parts[1].push name
circle .= '' circle .= ''
defaultPlane := cdata.is3d ? ', xOyPlane' : ''
switch method switch method
'circumcircle' 'circumcircle'
pt := args.subpoints pt := args.subpoints
unless pt and pt.length is 3 then return unless pt and pt.length is 3 then return
circle = `Circle(${pt.join ','})` circle = `Circle(${pt.join ','})`
'intersection'
sph := args.sphere
unless sph and sph.length is 2 then return
circle = `IntersectConic(${sph[0]}, ${sph[1]})`
'radius' 'radius'
pt := args.subpoints pt := args.subpoints
unless pt then return unless pt then return
inPlane := args.plane ? `, ${args.plane[0]}` : '' inPlane := args.plane ? `, ${args.plane[0]}` : defaultPlane
switch pt.length switch pt.length
when 2 when 2
[center, point] := pt [center, point] := pt
@ -949,12 +964,14 @@ classHandler: Record<JoyceClass, ClassHandler> :=
api.renameObject obj, newObj api.renameObject obj, newObj
moreParts[1].push newObj moreParts[1].push newObj
sector: (name, method, args, index) => sector: (name, method, args, index, cdata) =>
return := freshCommander() return := freshCommander()
return.value.ends = ['', ''] return.value.ends = ['', '']
{commands, callbacks, parts, auxiliaries, ends} := return.value {commands, callbacks, parts, auxiliaries, ends} := return.value
aux := name + 'aUx' aux := name + 'aUx'
parts[2].push name parts[2].push name
defaultPlane := cdata.is3d ? ', xOyPlane' : ''
inPlane := args.plane ? `, ${args.plane[0]}` : defaultPlane
switch method switch method
/arc|sector/ /arc|sector/
unless args.subpoints?.length is 3 return unless args.subpoints?.length is 3 return
@ -971,9 +988,10 @@ classHandler: Record<JoyceClass, ClassHandler> :=
ends[0] = start ends[0] = start
ends[1] = end ends[1] = end
commands.push commands.push
`${name} = ${prefix}Sector(${parms})` `${name} = ${prefix}Sector(${parms}${inPlane})`
`${aux}1 = ${prefix}Arc(${parms})` `${aux}1 = ${prefix}Arc(${parms}${inPlane})`
parts[1].push aux + 1 parts[1].push aux + 1
auxiliaries.push aux + 1
makeLinesInvisible callbacks, name makeLinesInvisible callbacks, name
plane: (name, method, args) => plane: (name, method, args) =>
@ -990,22 +1008,86 @@ classHandler: Record<JoyceClass, ClassHandler> :=
commands.push commands.push
`${name} = PerpendicularPlane(${thru}, Line(${thru}, ${perp}))` `${name} = PerpendicularPlane(${thru}, Line(${thru}, ${perp}))`
sphere: (name, method, args) => freshCommander() sphere: (name, method, args) =>
polyhedron: (name, method, args, index) =>
return := freshCommander() return := freshCommander()
return.value.ends = ['', ''] return.value.ends = ['', '']
{commands, callbacks, parts, auxiliaries, ends} := return.value {commands, callbacks, parts, auxiliaries, ends} := return.value
aux := name + 'aUx' parts[2].push name
switch method
'tetrahedron'
pt := args.subpoints pt := args.subpoints
if method is 'radius' and pt
switch pt.length
when 2
[center, point] := pt
ends[0] = center
commands.push `${name} = Sphere(${center}, ${point})`
when 3
center := pt[0]
ends[0] = center
radius := `Distance(${pt[1]}, ${pt[2]})`
commands.push `${name} = Sphere(${center}, ${radius})`
polyhedron: (name, method, args, index, cdata) =>
return := freshCommander()
return.value.ends = ['', '']
{commands, callbacks, parts, auxiliaries, ends} := return.value
aux := geoname name + 'aUx', cdata.elements, 'point'
switch method
'parallelepiped'
pt .= args.subpoints
unless pt and pt.length is 4 then return unless pt and pt.length is 4 then return
// create all of the vertices we will need:
commands.push
`${aux}4 = ${pt[2]} + ${pt[1]} - ${pt[0]}`
`${aux}5 = ${pt[3]} + ${pt[1]} - ${pt[0]}`
`${aux}6 = ${pt[3]} + ${pt[2]} - ${pt[0]}`
`${aux}7 = ${pt[3]} + ${pt[2]} + ${pt[1]} - 2*${pt[0]}`
for i of [4..7]
auxiliaries.push aux+i
pt = [...pt, ...auxiliaries]
parts[0].push ...pt
generalRecipe :=
A: [0,1,4,2]
B: [0,1,5,3]
C: [0,2,6,3]
D: [3,5,7,6]
E: [2,4,7,6]
F: [1,4,7,5]
letters := ['A'..'F'] as const
recipe: Record<string, string[]> := {}
for ltr of letters
auxlet := aux + ltr
auxiliaries.push auxlet
parts[2].push auxlet
recipe[auxlet] = (pt[i] for each i of generalRecipe[ltr])
ends[0] = aux + 'A'
ends[1] = aux + 'D'
callbacks.push (api: AppletObject, moreParts: DimParts) =>
ix .= 0
for piece in recipe
madeIt := api.evalCommandGetLabels
`${piece} = Polygon(${recipe[piece].join ','})`
if not madeIt return
for each obj of madeIt.split ','
if obj is piece continue
newObj := 'GeoAux' + index + obj + ix
api.renameObject obj, newObj
moreParts[1].push newObj
ix += 1
/pyramid|tetrahedron/
base .= args.polygon?[0]
ends[0] = base or aux + 1
pt := args.subpoints
unless pt and pt.length > 0 then return
// A tetrahedron is just a pyramid where we have to build the
// base from three points ourselves. But it has to be done in the
// callback, since we have to capture the edges.
if method is 'tetrahedron' and pt.length !== 4 then return
commands.push '' // hack, make sure there is a command commands.push '' // hack, make sure there is a command
parts[0].push ...pt parts[0].push ...pt
ends[0] = aux + 1 parts[2].push ends[0]
ends[1] = pt[3] ends[1] = pt.at(-1) or ''
callbacks.push (api: AppletObject, moreParts: DimParts) => callbacks.push (api: AppletObject, moreParts: DimParts) =>
if not base
madeBase := api.evalCommandGetLabels madeBase := api.evalCommandGetLabels
`${ends[0]} = Polygon(${pt[0]},${pt[1]},${pt[2]})` `${ends[0]} = Polygon(${pt[0]},${pt[1]},${pt[2]})`
if not madeBase return if not madeBase return
@ -1014,8 +1096,9 @@ classHandler: Record<JoyceClass, ClassHandler> :=
newObj := 'GeoAux' + index + obj newObj := 'GeoAux' + index + obj
api.renameObject obj, newObj api.renameObject obj, newObj
moreParts[1].push newObj moreParts[1].push newObj
base = ends[0]
made := api.evalCommandGetLabels made := api.evalCommandGetLabels
`${name} = Pyramid(${aux}1, ${pt[3]})` `${name} = Pyramid(${base}, ${ends[1]})`
if not made return if not made return
for each obj of made.split ',' for each obj of made.split ','
if obj is name continue if obj is name continue