|
|
@ -35,6 +35,20 @@ type Description
|
|
|
|
// with the otherName property:
|
|
|
|
// with the otherName property:
|
|
|
|
type JoyceElements = Record<AnyName, Description>
|
|
|
|
type JoyceElements = Record<AnyName, Description>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
adapptScript := findAdappt() as HTMLScriptElement
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function findAdappt()
|
|
|
|
|
|
|
|
scripts := document.querySelectorAll 'script'
|
|
|
|
|
|
|
|
for scrip of scripts
|
|
|
|
|
|
|
|
src := scrip.getAttribute 'src'
|
|
|
|
|
|
|
|
if src and src.includes 'adapptlet'
|
|
|
|
|
|
|
|
return scrip
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
adapParams: AdapParams :=
|
|
|
|
|
|
|
|
typeof GGBApplet is 'undefined'
|
|
|
|
|
|
|
|
? {loader: 'https://www.geogebra.org/apps/deployggb.js', joyceApplets: []}
|
|
|
|
|
|
|
|
: JSON.parse(adapptScript.dataset.params ?? '') as AdapParams
|
|
|
|
|
|
|
|
|
|
|
|
function postApplets(jApplets: AppletDescription[], codebase = '')
|
|
|
|
function postApplets(jApplets: AppletDescription[], codebase = '')
|
|
|
|
for each jApp of jApplets
|
|
|
|
for each jApp of jApplets
|
|
|
|
params := {
|
|
|
|
params := {
|
|
|
@ -48,7 +62,7 @@ function postApplets(jApplets: AppletDescription[], codebase = '')
|
|
|
|
backgroundRGB := [255, 255, 255] as RGB
|
|
|
|
backgroundRGB := [255, 255, 255] as RGB
|
|
|
|
config3d := contains3d jApp.params
|
|
|
|
config3d := contains3d jApp.params
|
|
|
|
if config3d
|
|
|
|
if config3d
|
|
|
|
worked := api.enable3D true
|
|
|
|
api.enable3D true
|
|
|
|
api.setPerspective 'T'
|
|
|
|
api.setPerspective 'T'
|
|
|
|
// Get rid of the xy-plane indicator
|
|
|
|
// Get rid of the xy-plane indicator
|
|
|
|
xml .= api.getXML()
|
|
|
|
xml .= api.getXML()
|
|
|
@ -56,6 +70,8 @@ function postApplets(jApplets: AppletDescription[], codebase = '')
|
|
|
|
api.setXML xml
|
|
|
|
api.setXML xml
|
|
|
|
else if codebase.includes 'web3d'
|
|
|
|
else if codebase.includes 'web3d'
|
|
|
|
api.setPerspective 'G'
|
|
|
|
api.setPerspective 'G'
|
|
|
|
|
|
|
|
if adapParams.config?.algebra
|
|
|
|
|
|
|
|
api.setPerspective '+A'
|
|
|
|
for name, value in jApp.params
|
|
|
|
for name, value in jApp.params
|
|
|
|
dispatchJcommand
|
|
|
|
dispatchJcommand
|
|
|
|
api, name, value, elements, backgroundRGB, config3d
|
|
|
|
api, name, value, elements, backgroundRGB, config3d
|
|
|
@ -78,22 +94,6 @@ function postApplets(jApplets: AppletDescription[], codebase = '')
|
|
|
|
if codebase then geoApp.setHTML5Codebase codebase
|
|
|
|
if codebase then geoApp.setHTML5Codebase codebase
|
|
|
|
geoApp.inject jApp.id
|
|
|
|
geoApp.inject jApp.id
|
|
|
|
|
|
|
|
|
|
|
|
adapptScript := findAdappt() as HTMLScriptElement
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function findAdappt()
|
|
|
|
|
|
|
|
scripts := document.querySelectorAll 'script'
|
|
|
|
|
|
|
|
for scrip of scripts
|
|
|
|
|
|
|
|
src := scrip.getAttribute 'src'
|
|
|
|
|
|
|
|
if src and src.includes 'adapptlet'
|
|
|
|
|
|
|
|
return scrip
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log 'In script', document.currentScript, adapptScript
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
adapParams: AdapParams :=
|
|
|
|
|
|
|
|
typeof GGBApplet is 'undefined'
|
|
|
|
|
|
|
|
? {loader: 'https://www.geogebra.org/apps/deployggb.js', joyceApplets: []}
|
|
|
|
|
|
|
|
: JSON.parse(adapptScript.dataset.params ?? '') as AdapParams
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Always use the final joyceApplets if there are any:
|
|
|
|
// Always use the final joyceApplets if there are any:
|
|
|
|
if joyceApplets.length
|
|
|
|
if joyceApplets.length
|
|
|
|
adapParams.joyceApplets = joyceApplets
|
|
|
|
adapParams.joyceApplets = joyceApplets
|
|
|
@ -412,12 +412,24 @@ function geoname(jname: JoyceName, elements: JoyceElements): GeoName
|
|
|
|
return .= 'Geo' + numCode.toString(36);
|
|
|
|
return .= 'Geo' + numCode.toString(36);
|
|
|
|
return += '1' while return.value in elements
|
|
|
|
return += '1' while return.value in elements
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Helper for similar point/line functions:
|
|
|
|
|
|
|
|
function cutoffExtend(
|
|
|
|
|
|
|
|
method: string, pt: string[], point?: string[], line?: string[]
|
|
|
|
|
|
|
|
): [string, string]
|
|
|
|
|
|
|
|
direction .= `UnitVector(Vector(${pt[0]},${pt[1]}))`
|
|
|
|
|
|
|
|
if line and (not point or point[0] !== pt[0])
|
|
|
|
|
|
|
|
direction = `UnitVector(${line[0]})`
|
|
|
|
|
|
|
|
displacement := `Distance(${pt[2]}, ${pt[3]})*${direction}`
|
|
|
|
|
|
|
|
source := method is 'cutoff' ? pt[0] : pt[1]
|
|
|
|
|
|
|
|
[source, displacement]
|
|
|
|
|
|
|
|
|
|
|
|
// All of the detailed semantics of each available command lies in this
|
|
|
|
// All of the detailed semantics of each available command lies in this
|
|
|
|
// function.
|
|
|
|
// function.
|
|
|
|
classHandler: Record<JoyceClass, ClassHandler> :=
|
|
|
|
classHandler: Record<JoyceClass, ClassHandler> :=
|
|
|
|
point: (name, method, args, index, is3d): Commander =>
|
|
|
|
point: (name, method, args, index, is3d): Commander =>
|
|
|
|
return := freshCommander()
|
|
|
|
return := freshCommander()
|
|
|
|
{commands, callbacks, parts, auxiliaries} := return.value
|
|
|
|
{commands, callbacks, parts, auxiliaries} := return.value
|
|
|
|
|
|
|
|
zeroVector := is3d ? 'Vector((0,0,0))' : 'Vector((0,0))'
|
|
|
|
aux := name + 'aUx'
|
|
|
|
aux := name + 'aUx'
|
|
|
|
parts[0].push name
|
|
|
|
parts[0].push name
|
|
|
|
switch method
|
|
|
|
switch method
|
|
|
@ -440,22 +452,25 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|
|
|
`${aux}4 = Rotate(${start}, ${aux}3/${n}, ${center}${inPlane})`
|
|
|
|
`${aux}4 = Rotate(${start}, ${aux}3/${n}, ${center}${inPlane})`
|
|
|
|
`${name} = Intersect(${destination}, Ray(${center}, ${aux}4))`
|
|
|
|
`${name} = Intersect(${destination}, Ray(${center}, ${aux}4))`
|
|
|
|
auxiliaries.push ...[2..4].map (i) => `${aux}${i}`
|
|
|
|
auxiliaries.push ...[2..4].map (i) => `${aux}${i}`
|
|
|
|
'extend'
|
|
|
|
'circleSlider'
|
|
|
|
sp := args.subpoints
|
|
|
|
unless args.circle then return
|
|
|
|
unless sp then return
|
|
|
|
commands.push `${name} = Point(${args.circle[0]})`
|
|
|
|
direction .= `UnitVector(Vector(${sp[0]},${sp[1]}))`
|
|
|
|
if args.scalar and args.scalar.length
|
|
|
|
if args.line and (
|
|
|
|
callbacks.push (api: AppletObject) =>
|
|
|
|
not args.point or args.point[0] !== sp[0])
|
|
|
|
api.setCoords name, ...args.scalar as XYZ
|
|
|
|
direction = `UnitVector(${args.line[0]})`
|
|
|
|
/cutoff|extend/
|
|
|
|
displacement := `Distance(${sp[2]}, ${sp[3]})*${direction}`
|
|
|
|
pt := args.subpoints
|
|
|
|
commands.push `${name} = Translate(${sp[1]}, ${displacement})`
|
|
|
|
unless pt and pt.length is 4 then return
|
|
|
|
|
|
|
|
[source, displacement] :=
|
|
|
|
|
|
|
|
cutoffExtend method, pt, args.point, args.line
|
|
|
|
|
|
|
|
commands.push `${name} = Translate(${source}, ${displacement})`
|
|
|
|
'first'
|
|
|
|
'first'
|
|
|
|
unless args.subpoints then return
|
|
|
|
unless args.subpoints then return
|
|
|
|
commands.push `${name} = ${args.subpoints[0]}`
|
|
|
|
commands.push `${name} = Point(${args.subpoints[0]},${zeroVector})`
|
|
|
|
/fixed|free/
|
|
|
|
/fixed|free/
|
|
|
|
coords := args.scalar
|
|
|
|
coords := args.scalar
|
|
|
|
unless coords then return
|
|
|
|
unless coords then return
|
|
|
|
commands.push `${name} = (${coords.join ','})`
|
|
|
|
commands.push `${name} = Point({${coords.join ','}})`
|
|
|
|
if method is 'fixed'
|
|
|
|
if method is 'fixed'
|
|
|
|
callbacks.push (api: AppletObject) => api.setFixed name, true
|
|
|
|
callbacks.push (api: AppletObject) => api.setFixed name, true
|
|
|
|
'foot'
|
|
|
|
'foot'
|
|
|
@ -476,7 +491,8 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|
|
|
commands.push `${name} = Intersect(${l1},${e2})`
|
|
|
|
commands.push `${name} = Intersect(${l1},${e2})`
|
|
|
|
'last'
|
|
|
|
'last'
|
|
|
|
unless args.subpoints then return
|
|
|
|
unless args.subpoints then return
|
|
|
|
commands.push `${name} = ${args.subpoints.at(-1)}`
|
|
|
|
commands.push
|
|
|
|
|
|
|
|
`${name} = Point(${args.subpoints.at(-1)}, ${zeroVector})`
|
|
|
|
'lineSegmentSlider'
|
|
|
|
'lineSegmentSlider'
|
|
|
|
segment .= args.line?[0]
|
|
|
|
segment .= args.line?[0]
|
|
|
|
unless segment
|
|
|
|
unless segment
|
|
|
@ -528,7 +544,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|
|
|
commands.push
|
|
|
|
commands.push
|
|
|
|
`${name} = Vertex(${args.polygon?[0]},${args.scalar?[0]})`
|
|
|
|
`${name} = Vertex(${args.polygon?[0]},${args.scalar?[0]})`
|
|
|
|
|
|
|
|
|
|
|
|
line: (name, method, args) =>
|
|
|
|
line: (name, method, args, index, is3d) =>
|
|
|
|
return := freshCommander()
|
|
|
|
return := freshCommander()
|
|
|
|
return.value.ends = ['', '']
|
|
|
|
return.value.ends = ['', '']
|
|
|
|
{commands, callbacks, parts, auxiliaries, ends} := return.value
|
|
|
|
{commands, callbacks, parts, auxiliaries, ends} := return.value
|
|
|
@ -536,10 +552,62 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|
|
|
parts[1].push name
|
|
|
|
parts[1].push name
|
|
|
|
madeSegment .= false
|
|
|
|
madeSegment .= false
|
|
|
|
switch method
|
|
|
|
switch method
|
|
|
|
|
|
|
|
'bichord'
|
|
|
|
|
|
|
|
// To match Joyce, we need to get the ordering here correct.
|
|
|
|
|
|
|
|
// we want the order so that start -> end sweeping past the
|
|
|
|
|
|
|
|
// center of the other circle is counterclockwise in the first
|
|
|
|
|
|
|
|
// circle
|
|
|
|
|
|
|
|
cr := args.circle
|
|
|
|
|
|
|
|
unless cr return
|
|
|
|
|
|
|
|
commands.push ...[1..2].map (n) =>
|
|
|
|
|
|
|
|
`${aux}${n} = Intersect(${cr[0]}, ${cr[1]}, ${n})`
|
|
|
|
|
|
|
|
inPlane := is3d ? `Plane(${cr[0]})` : ''
|
|
|
|
|
|
|
|
ctr := cr.map (c) => `Center(${c})`
|
|
|
|
|
|
|
|
condition := (`Angle(${aux}1,${ctr[0]},${ctr[1]}${inPlane})`
|
|
|
|
|
|
|
|
+ `< Angle(${aux}2,${ctr[0]},${ctr[1]}${inPlane})`)
|
|
|
|
|
|
|
|
commands.push
|
|
|
|
|
|
|
|
`${aux}3 = If(${condition}, ${aux}2, ${aux}1)`
|
|
|
|
|
|
|
|
`${aux}4 = If(${condition}, ${aux}1, ${aux}2)`
|
|
|
|
|
|
|
|
ends[0] = aux + 3
|
|
|
|
|
|
|
|
ends[1] = aux + 4
|
|
|
|
|
|
|
|
auxiliaries.push ...[1..4].map (n) => aux + n
|
|
|
|
|
|
|
|
'chord'
|
|
|
|
|
|
|
|
// To match Joyce, we need to get the ordering here correct.
|
|
|
|
|
|
|
|
// The complicated condition about distances is modeled after
|
|
|
|
|
|
|
|
// Joyce's code, but it boils down to: take the endpoint of the
|
|
|
|
|
|
|
|
// chord closest to the first subpoint, unless that first
|
|
|
|
|
|
|
|
// subpoint is essentially at the midpoint of the chord, in
|
|
|
|
|
|
|
|
// which case start with the endpoint nearest the second subpoint.
|
|
|
|
|
|
|
|
unless args.subpoints and args.circle then return
|
|
|
|
|
|
|
|
// We intersect with the whole line, not just the segment:
|
|
|
|
|
|
|
|
line := `Line(${args.subpoints.join ','})`
|
|
|
|
|
|
|
|
pA := args.subpoints[0]
|
|
|
|
|
|
|
|
pB := args.subpoints[1]
|
|
|
|
|
|
|
|
commands.push ...[1..2].map (n) =>
|
|
|
|
|
|
|
|
`${aux}${n} = Intersect(${args.circle}, ${line}, ${n})`
|
|
|
|
|
|
|
|
s := `Distance(${aux}1,${aux}2)`
|
|
|
|
|
|
|
|
d := `Distance(${aux}1,${pA}) - Distance(${aux}2,${pA})`
|
|
|
|
|
|
|
|
condition := (`If(${s}/10^9 < abs(${d}), ${d} > 0,`
|
|
|
|
|
|
|
|
+ `Distance(${aux}2,${pB}) < Distance(${aux}1,${pB}))`)
|
|
|
|
|
|
|
|
commands.push
|
|
|
|
|
|
|
|
`${aux}3 = If(${condition}, ${aux}2, ${aux}1)`
|
|
|
|
|
|
|
|
`${aux}4 = If(${condition}, ${aux}1, ${aux}2)`
|
|
|
|
|
|
|
|
ends[0] = aux + 3
|
|
|
|
|
|
|
|
ends[1] = aux + 4
|
|
|
|
|
|
|
|
auxiliaries.push ...[1..4].map (n) => aux + n
|
|
|
|
'connect'
|
|
|
|
'connect'
|
|
|
|
unless args.subpoints and args.subpoints.length is 2 then return
|
|
|
|
unless args.subpoints and args.subpoints.length is 2 then return
|
|
|
|
ends[0] = args.subpoints[0]
|
|
|
|
ends[0] = args.subpoints[0]
|
|
|
|
ends[1] = args.subpoints[1]
|
|
|
|
ends[1] = args.subpoints[1]
|
|
|
|
|
|
|
|
/cutoff|extend/
|
|
|
|
|
|
|
|
pt := args.subpoints
|
|
|
|
|
|
|
|
unless pt and pt.length is 4 then return
|
|
|
|
|
|
|
|
[source, displacement] :=
|
|
|
|
|
|
|
|
cutoffExtend method, pt, args.point, args.line
|
|
|
|
|
|
|
|
ends[0] = source
|
|
|
|
|
|
|
|
commands.push `${aux} = Translate(${source}, ${displacement})`
|
|
|
|
|
|
|
|
auxiliaries.push aux
|
|
|
|
|
|
|
|
ends[1] = aux
|
|
|
|
'parallel'
|
|
|
|
'parallel'
|
|
|
|
unless args.subpoints then return
|
|
|
|
unless args.subpoints then return
|
|
|
|
[newStart, oldStart, oldEnd] := args.subpoints
|
|
|
|
[newStart, oldStart, oldEnd] := args.subpoints
|
|
|
@ -555,24 +623,6 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|
|
|
madeSegment = true
|
|
|
|
madeSegment = true
|
|
|
|
else
|
|
|
|
else
|
|
|
|
commands.push `${aux}2 = Translate(${oldEnd}, ${aux}1)`
|
|
|
|
commands.push `${aux}2 = Translate(${oldEnd}, ${aux}1)`
|
|
|
|
'chord'
|
|
|
|
|
|
|
|
// To match Joyce, we need to get the ordering here correct.
|
|
|
|
|
|
|
|
// ends[0] should be the one closer to args.subpoints[0]
|
|
|
|
|
|
|
|
unless args.subpoints and args.circle then return
|
|
|
|
|
|
|
|
line := `Line(${args.subpoints.join ','})`
|
|
|
|
|
|
|
|
pt := args.subpoints[0]
|
|
|
|
|
|
|
|
commands.push ...[1..2].map (n) =>
|
|
|
|
|
|
|
|
`${aux}${n} = Intersect(${args.circle}, ${line}, ${n})`
|
|
|
|
|
|
|
|
condition := `Distance(${aux}2,${pt}) < Distance(${aux}1,${pt})`
|
|
|
|
|
|
|
|
// NOTE: Joyce's code has special case for when pt is almost
|
|
|
|
|
|
|
|
// at midpoint of chord; in that case, it starts at endpoint
|
|
|
|
|
|
|
|
// closer to the second subpoint... postponing that nicety
|
|
|
|
|
|
|
|
commands.push
|
|
|
|
|
|
|
|
`${aux}3 = If(${condition}, ${aux}2, ${aux}1)`
|
|
|
|
|
|
|
|
`${aux}4 = If(${condition}, ${aux}1, ${aux}2)`
|
|
|
|
|
|
|
|
ends[0] = aux + 3
|
|
|
|
|
|
|
|
ends[1] = aux + 4
|
|
|
|
|
|
|
|
auxiliaries.push ...[1..4].map (n) => aux + n
|
|
|
|
|
|
|
|
unless madeSegment
|
|
|
|
unless madeSegment
|
|
|
|
commands.push `${name} = Segment(${ends[0]},${ends[1]})`
|
|
|
|
commands.push `${name} = Segment(${ends[0]},${ends[1]})`
|
|
|
|
callbacks.push (api: AppletObject) => api.setLabelVisible name, true
|
|
|
|
callbacks.push (api: AppletObject) => api.setLabelVisible name, true
|
|
|
@ -585,9 +635,19 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|
|
|
parts[1].push name
|
|
|
|
parts[1].push name
|
|
|
|
switch method
|
|
|
|
switch method
|
|
|
|
'radius'
|
|
|
|
'radius'
|
|
|
|
unless args.subpoints then return
|
|
|
|
pt := args.subpoints
|
|
|
|
[center, point] := args.subpoints
|
|
|
|
unless pt then return
|
|
|
|
commands.push `${name} = Circle(${center}, ${point})`
|
|
|
|
inPlane := args.plane ? `, ${args.plane[0]}` : ''
|
|
|
|
|
|
|
|
switch pt.length
|
|
|
|
|
|
|
|
when 2
|
|
|
|
|
|
|
|
[center, point] := pt
|
|
|
|
|
|
|
|
commands.push
|
|
|
|
|
|
|
|
`${name} = Circle(${center}, ${point}${inPlane})`
|
|
|
|
|
|
|
|
when 3
|
|
|
|
|
|
|
|
center := pt[0]
|
|
|
|
|
|
|
|
radius := `Distance(${pt[1]}, ${pt[2]})`
|
|
|
|
|
|
|
|
commands.push
|
|
|
|
|
|
|
|
`${name} = Circle(${center}, ${radius}${inPlane})`
|
|
|
|
callbacks.push (api: AppletObject) => api.setLabelVisible name, true
|
|
|
|
callbacks.push (api: AppletObject) => api.setLabelVisible name, true
|
|
|
|
|
|
|
|
|
|
|
|
polygon: (name, method, args, index) =>
|
|
|
|
polygon: (name, method, args, index) =>
|
|
|
|