feat: joyce commands needed for BookEleven (#57)
Reviewed-on: #57 Co-authored-by: Glen Whitney <glen@studioinfinity.org> Co-committed-by: Glen Whitney <glen@studioinfinity.org>
This commit is contained in:
parent
9e69613425
commit
0b241e010e
@ -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
|
||||||
|
@ -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
|
||||||
@ -67,6 +67,7 @@ type ConstructionData
|
|||||||
height: number
|
height: number
|
||||||
elements: JoyceElements
|
elements: JoyceElements
|
||||||
pivot: string
|
pivot: string
|
||||||
|
title?: string
|
||||||
|
|
||||||
// Global data setup for pivoting (ugh, but necessary because the api
|
// Global data setup for pivoting (ugh, but necessary because the api
|
||||||
// is not passed to the callback, so we have to look up the slider in
|
// is not passed to the callback, so we have to look up the slider in
|
||||||
@ -128,11 +129,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
|
||||||
@ -185,7 +187,8 @@ type ClassHandler = (
|
|||||||
method: string,
|
method: string,
|
||||||
args: JoyceArguments,
|
args: JoyceArguments,
|
||||||
index: number,
|
index: number,
|
||||||
cdata: ConstructionData) => Commander
|
cdata: ConstructionData,
|
||||||
|
colors: string[]) => Commander
|
||||||
type RGB = [number, number, number]
|
type RGB = [number, number, number]
|
||||||
type XYZ = RGB
|
type XYZ = RGB
|
||||||
|
|
||||||
@ -207,8 +210,8 @@ 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)'
|
cdata.title = value
|
||||||
api.evalCommand `TitlePoint = ${corner}
|
api.evalCommand `TitlePoint = Corner(1,1)
|
||||||
Text("${value}", TitlePoint + (2,5))`
|
Text("${value}", TitlePoint + (2,5))`
|
||||||
'pivot'
|
'pivot'
|
||||||
return // already handled in postApplets
|
return // already handled in postApplets
|
||||||
@ -257,7 +260,7 @@ function jToG(
|
|||||||
args: JoyceArguments := {}
|
args: JoyceArguments := {}
|
||||||
usesCaptions := []
|
usesCaptions := []
|
||||||
for each jdep of data.split ','
|
for each jdep of data.split ','
|
||||||
scalar := parseFloat jdep
|
scalar := Number jdep
|
||||||
if scalar is scalar // not NaN
|
if scalar is scalar // not NaN
|
||||||
(args.scalar ?= []).push scalar
|
(args.scalar ?= []).push scalar
|
||||||
continue
|
continue
|
||||||
@ -271,7 +274,7 @@ function jToG(
|
|||||||
(args.subpoints ?= []).push depGeo
|
(args.subpoints ?= []).push depGeo
|
||||||
else if depKlass is 'line'
|
else if depKlass is 'line'
|
||||||
(args.subpoints ?= []).push ...ends ?? []
|
(args.subpoints ?= []).push ...ends ?? []
|
||||||
cmdr = classHandler[klass] name, method, args, index, cdata
|
cmdr = classHandler[klass] name, method, args, index, cdata, colors
|
||||||
unless name is jname then cmdr.callbacks.push (api: AppletObject) =>
|
unless name is jname then cmdr.callbacks.push (api: AppletObject) =>
|
||||||
api.setCaption name, jname
|
api.setCaption name, jname
|
||||||
api.setLabelStyle name, 3 // style CAPTION = 3
|
api.setLabelStyle name, 3 // style CAPTION = 3
|
||||||
@ -312,23 +315,26 @@ function jToG(
|
|||||||
// we can adjust components after setting overall color, etc.
|
// we can adjust components after setting overall color, etc.
|
||||||
|
|
||||||
// Color the "Faces"; they default to 'brighter':
|
// Color the "Faces"; they default to 'brighter':
|
||||||
if invisible colors[3]
|
if invisible(colors[3]) and (klass !== 'sphere' or invisible colors[2])
|
||||||
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 traceC
|
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 traceC
|
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)
|
surface .= colors[3]
|
||||||
|
if klass is 'sphere' and invisible surface
|
||||||
|
surface = colors[2] // for Joyce, spheres had one circular "edge"
|
||||||
|
faceRGB := joyce2rgb(surface 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]
|
||||||
if traceC
|
if traceC
|
||||||
console.log 'Coloring face', face, 'to',
|
console.log 'Coloring face', face, 'to',
|
||||||
colors[3], '=', faceRGB
|
surface, '=', faceRGB
|
||||||
api.setVisible face, true
|
api.setVisible face, true
|
||||||
api.setFilling face, filling
|
api.setFilling face, filling
|
||||||
api.setColor face, ...faceRGB
|
api.setColor face, ...faceRGB
|
||||||
@ -485,7 +491,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
|
||||||
@ -514,7 +520,7 @@ function pointDefaultColorName(
|
|||||||
|
|
||||||
function geoname(
|
function geoname(
|
||||||
jname: JoyceName, elements: JoyceElements, klass: JoyceClass): GeoName
|
jname: JoyceName, elements: JoyceElements, klass: JoyceClass): GeoName
|
||||||
unless jname.substring(0,3) is 'Geo' // those might clash
|
unless jname is 'floor' or jname.substring(0,3) is 'Geo' // those might clash
|
||||||
// Names with word characters starting with a capital are always good:
|
// Names with word characters starting with a capital are always good:
|
||||||
if /^[A-Z]['\w]*$/.test jname then return jname
|
if /^[A-Z]['\w]*$/.test jname then return jname
|
||||||
// If it's not a point, can start with any letter:
|
// If it's not a point, can start with any letter:
|
||||||
@ -568,13 +574,30 @@ function proportionSimilar(
|
|||||||
// 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, cdata): Commander =>
|
point: (name, method, args, index, cdata, colors): Commander =>
|
||||||
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
|
||||||
|
// HACK: Special-case corrections for Joyce Elements Bk XI
|
||||||
|
if cdata.title is 'XI.4'
|
||||||
|
if name is 'Z' and method is 'fixed' and args.scalar?.length is 3
|
||||||
|
method = 'perpendicular'
|
||||||
|
args.subpoints = ['E','P2','A']
|
||||||
|
args.plane = ['baseplane']
|
||||||
|
colors[0] = '0'
|
||||||
|
colors[1] = '0'
|
||||||
|
if name is 'F' and method is 'lineSlider' and args.subpoints?[0] is 'E'
|
||||||
|
args.scalar = [160,40,60]
|
||||||
|
if cdata.title is 'XI.5'
|
||||||
|
if name is 'A' and method is 'free'
|
||||||
|
method = 'perpendicular'
|
||||||
|
args.subpoints = ['B','P1','P3']
|
||||||
|
args.plane = ['xOyPlane']
|
||||||
|
commands.push 'B=(80,140)'
|
||||||
switch method
|
switch method
|
||||||
/angle(?:Bisector|Divider)/
|
/angle(?:Bisector|Divider)/
|
||||||
{center, foot} :=
|
{center, foot} :=
|
||||||
@ -604,6 +627,12 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
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, ...vertFlipped(args.scalar or [], cdata)
|
api.setCoords name, ...vertFlipped(args.scalar or [], cdata)
|
||||||
|
'circumcenter'
|
||||||
|
unless args.subpoints?.length is 3 then return
|
||||||
|
commands.push
|
||||||
|
`${aux} = Circle(${args.subpoints.join ','})`
|
||||||
|
`${name} = Center(${aux})`
|
||||||
|
auxiliaries.push aux
|
||||||
/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
|
||||||
@ -612,9 +641,9 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
commands.push `${name} = Translate(${source}, ${displacement})`
|
commands.push `${name} = Translate(${source}, ${displacement})`
|
||||||
'first'
|
'first'
|
||||||
unless args.subpoints then return
|
unless args.subpoints then return
|
||||||
// HACK: Special-case correction for Joyce Elements Bk II, prop 14
|
|
||||||
index .= 0
|
index .= 0
|
||||||
if name === 'H' and args.line and args.line[0] === 'HH2'
|
// HACK: Special-case correction for Joyce Elements Bk II, prop 14
|
||||||
|
if cdata.title is 'II.14' and name is 'H' and args.line?[0] is 'HH2'
|
||||||
index = 1
|
index = 1
|
||||||
commands.push `${name} = ${args.subpoints[index]}`
|
commands.push `${name} = ${args.subpoints[index]}`
|
||||||
/fixed|free/
|
/fixed|free/
|
||||||
@ -638,9 +667,9 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
// Checking Joyce source, means intersection of lines, not
|
// Checking Joyce source, means intersection of lines, not
|
||||||
// intersection of line segments
|
// intersection of line segments
|
||||||
pt := args.subpoints
|
pt := args.subpoints
|
||||||
unless pt then return
|
unless pt and pt.length > 1 then return
|
||||||
l1 := `Line(${pt[0]},${pt[1]})`
|
l1 := `Line(${pt[0]},${pt[1]})`
|
||||||
e2 := args.plane ? args.plane[0] : `Line(${pt[2]},${pt[3]})`
|
e2 := pt.length < 3 ? args.plane?[0] : `Line(${pt[2]},${pt[3]})`
|
||||||
commands.push `${name} = Intersect(${l1},${e2})`
|
commands.push `${name} = Intersect(${l1},${e2})`
|
||||||
'last'
|
'last'
|
||||||
unless args.subpoints then return
|
unless args.subpoints then return
|
||||||
@ -682,15 +711,49 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
unless pt then return
|
unless pt then return
|
||||||
commands.push `${name} = ${pt[0]} + ${pt[2]} - ${pt[1]}`
|
commands.push `${name} = ${pt[0]} + ${pt[2]} - ${pt[1]}`
|
||||||
'perpendicular'
|
'perpendicular'
|
||||||
// Note only the two-point option implemented so far
|
pt := args.subpoints
|
||||||
unless args.subpoints return
|
unless pt return
|
||||||
[center, direction] := args.subpoints
|
inPlane := args.plane ? `,${args.plane[0]}` : defaultPlane
|
||||||
commands.push `${name} = Rotate(${direction}, pi/2, ${center})`
|
center := pt[0]
|
||||||
|
switch pt.length
|
||||||
|
when 2
|
||||||
|
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]})`
|
||||||
|
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]
|
||||||
|
when 4
|
||||||
|
commands.push
|
||||||
|
`${aux}1 = Ray(${center}, Rotate(${pt[1]}, pi/2, ${center}${inPlane}))`
|
||||||
|
`${aux}2 = Circle(${center}, Distance(${pt[2]},${pt[3]})${inPlane})`
|
||||||
|
`${name} = Intersect(${aux}1, ${aux}2)`
|
||||||
|
auxiliaries.push aux+1, aux+2
|
||||||
|
'planeSlider'
|
||||||
|
pln := args.plane?[0]
|
||||||
|
unless pln then return
|
||||||
|
commands.push `${name} = PointIn(${pln})`
|
||||||
|
if args.scalar and args.scalar.length
|
||||||
|
callbacks.push (api: AppletObject) =>
|
||||||
|
api.setCoords name, ...vertFlipped(args.scalar or [], cdata)
|
||||||
/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]})`
|
||||||
@ -731,7 +794,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
`${aux}4 = If(${condition}, ${aux}2, ${aux}1)`
|
`${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 aux+n for n of [1..4]
|
||||||
'chord'
|
'chord'
|
||||||
// To match Joyce, we need to get the ordering here correct.
|
// To match Joyce, we need to get the ordering here correct.
|
||||||
// The complicated condition about distances is modeled after
|
// The complicated condition about distances is modeled after
|
||||||
@ -755,7 +818,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
`${aux}4 = If(${condition}, ${aux}1, ${aux}2)`
|
`${aux}4 = If(${condition}, ${aux}1, ${aux}2)`
|
||||||
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 aux+n for n of [1..4]
|
||||||
'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]
|
||||||
@ -810,7 +873,17 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
commands.push
|
commands.push
|
||||||
`${aux} = Rotate(${pt[1]}, pi/2, ${pt[0]}${inPlane})`
|
`${aux} = Rotate(${pt[1]}, pi/2, ${pt[0]}${inPlane})`
|
||||||
when 3
|
when 3
|
||||||
return // TODO: line perpendicular to plane
|
radius := `Distance(${pt[1]}, ${pt[2]})`
|
||||||
|
commands.push
|
||||||
|
`${aux}1 = PerpendicularLine(${pt[1]}${inPlane})`
|
||||||
|
`${aux}2 = Intersect(${aux}1${inPlane})`
|
||||||
|
`${aux}3 = Circle(${aux}2, ${radius}${inPlane})`
|
||||||
|
`${aux}4 = PointIn(${aux}3)`
|
||||||
|
`${aux}5 = Plane(${aux}4, ${aux}1)`
|
||||||
|
`${aux}6 = Rotate(${aux}4, pi/2, ${aux}2, ${aux}5)`
|
||||||
|
ends[0] = aux + 2
|
||||||
|
ends[1] = aux + 6
|
||||||
|
auxiliaries.push aux+n for n of [1..6]
|
||||||
when 4
|
when 4
|
||||||
ends[0] = pt[0]
|
ends[0] = pt[0]
|
||||||
ends[1] = aux + 2
|
ends[1] = aux + 2
|
||||||
@ -836,22 +909,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
|
||||||
@ -931,7 +1009,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
`${aux}1 = Angle(${pt[3]},${pt[2]},${pt[4]}${inSourcePlane})`
|
`${aux}1 = Angle(${pt[3]},${pt[2]},${pt[4]}${inSourcePlane})`
|
||||||
`${aux}2 = Rotate(${pt[1]},${aux}1,${pt[0]}${inDestPlane})`
|
`${aux}2 = Rotate(${pt[1]},${aux}1,${pt[0]}${inDestPlane})`
|
||||||
`${aux}3 = ${pt[0]} + (${aux}2 - ${pt[0]})*${factor}`
|
`${aux}3 = ${pt[0]} + (${aux}2 - ${pt[0]})*${factor}`
|
||||||
auxiliaries.push ...[1..3].map (n) => aux + n
|
auxiliaries.push aux+n for n of [1..3]
|
||||||
pt = [pt[0], pt[1], aux+3]
|
pt = [pt[0], pt[1], aux+3]
|
||||||
else
|
else
|
||||||
commands.push ''
|
commands.push ''
|
||||||
@ -949,12 +1027,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
|
||||||
@ -968,12 +1048,14 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
center = temp
|
center = temp
|
||||||
parms = start + ', ' + center + ', ' + end
|
parms = start + ', ' + center + ', ' + end
|
||||||
prefix = 'Circumcircular'
|
prefix = 'Circumcircular'
|
||||||
|
inPlane = '' // not needed in 3-point case
|
||||||
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) =>
|
||||||
@ -984,38 +1066,133 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
'3points'
|
'3points'
|
||||||
unless args.subpoints?.length is 3 then return
|
unless args.subpoints?.length is 3 then return
|
||||||
commands.push `${name} = Plane(${args.subpoints.join ','})`
|
commands.push `${name} = Plane(${args.subpoints.join ','})`
|
||||||
|
'parallel'
|
||||||
|
unless args.subpoints?.length is 1 then return
|
||||||
|
unless args.plane?.length is 1 then return
|
||||||
|
commands.push `${name} = Plane(${args.subpoints[0]}, ${args.plane[0]})`
|
||||||
'perpendicular'
|
'perpendicular'
|
||||||
unless args.subpoints?.length is 2 then return
|
unless args.subpoints?.length is 2 then return
|
||||||
[thru, perp] := args.subpoints
|
[thru, perp] := args.subpoints
|
||||||
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
|
||||||
|
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
|
switch method
|
||||||
'tetrahedron'
|
'parallelepiped'
|
||||||
pt := args.subpoints
|
pt .= args.subpoints
|
||||||
unless pt and pt.length is 4 then return
|
unless pt and pt.length is 4 then return
|
||||||
commands.push '' // hack, make sure there is a command
|
// 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]}`
|
||||||
|
auxiliaries.push aux+i for i of [4..7]
|
||||||
|
pt = [...pt, ...auxiliaries]
|
||||||
parts[0].push ...pt
|
parts[0].push ...pt
|
||||||
ends[0] = aux + 1
|
generalRecipe :=
|
||||||
ends[1] = pt[3]
|
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) =>
|
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
||||||
madeBase := api.evalCommandGetLabels
|
ix .= 0
|
||||||
`${ends[0]} = Polygon(${pt[0]},${pt[1]},${pt[2]})`
|
for piece in recipe
|
||||||
if not madeBase return
|
madeIt := api.evalCommandGetLabels
|
||||||
for each obj of madeBase.split ','
|
`${piece} = Polygon(${recipe[piece].join ','})`
|
||||||
if obj is ends[0] continue
|
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
|
||||||
|
'prism'
|
||||||
|
unless args.polygon?.length is 1 then return
|
||||||
|
base := args.polygon[0]
|
||||||
|
ends[0] = base
|
||||||
|
pt := args.subpoints
|
||||||
|
unless pt and pt.length is 2 then return
|
||||||
|
commands.push
|
||||||
|
`${aux}1 = Vertex(${base},1) + ${pt[1]} - ${pt[0]}`
|
||||||
|
auxiliaries.push aux+1
|
||||||
|
ends[1] = aux+1
|
||||||
|
parts[0].push aux+1
|
||||||
|
parts[2].push ends[0]
|
||||||
|
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
||||||
|
made := api.evalCommandGetLabels
|
||||||
|
`${name} = Prism(${base}, ${aux}1)`
|
||||||
|
if not made return
|
||||||
|
for each obj of made.split ','
|
||||||
|
if obj is name continue
|
||||||
newObj := 'GeoAux' + index + obj
|
newObj := 'GeoAux' + index + obj
|
||||||
api.renameObject obj, newObj
|
api.renameObject obj, newObj
|
||||||
moreParts[1].push newObj
|
switch api.getObjectType newObj
|
||||||
|
'point'
|
||||||
|
moreParts[0].push newObj
|
||||||
|
'segment'
|
||||||
|
moreParts[1].push newObj
|
||||||
|
else
|
||||||
|
moreParts[2].push newObj
|
||||||
|
/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
|
||||||
|
parts[0].push ...pt
|
||||||
|
parts[2].push ends[0]
|
||||||
|
ends[1] = pt.at(-1) or ''
|
||||||
|
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
||||||
|
if not base
|
||||||
|
madeBase := api.evalCommandGetLabels
|
||||||
|
`${ends[0]} = Polygon(${pt[0]},${pt[1]},${pt[2]})`
|
||||||
|
if not madeBase return
|
||||||
|
for each obj of madeBase.split ','
|
||||||
|
if obj is ends[0] continue
|
||||||
|
newObj := 'GeoAux' + index + obj
|
||||||
|
api.renameObject obj, 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
|
||||||
@ -1051,7 +1228,7 @@ function makeAngDiv(
|
|||||||
`${aux}2 = Angle(${start}, ${center}, ${end}${inPlane})`
|
`${aux}2 = Angle(${start}, ${center}, ${end}${inPlane})`
|
||||||
`${aux}3 = If(${aux}2 > pi, ${aux}2 - 2*pi, ${aux}2)`
|
`${aux}3 = If(${aux}2 > pi, ${aux}2 - 2*pi, ${aux}2)`
|
||||||
`${aux}4 = Rotate(${start}, ${aux}3/${n}, ${center}${inPlane})`
|
`${aux}4 = Rotate(${start}, ${aux}3/${n}, ${center}${inPlane})`
|
||||||
auxiliaries.push ...[2..4].map (i) => `${aux}${i}`
|
auxiliaries.push aux+i for i of [2..4]
|
||||||
return {center, foot: `Intersect(${destination}, Ray(${center}, ${aux}4))`}
|
return {center, foot: `Intersect(${destination}, Ray(${center}, ${aux}4))`}
|
||||||
|
|
||||||
// helper for separating color of perimeter and interior:
|
// helper for separating color of perimeter and interior:
|
||||||
|
Loading…
Reference in New Issue
Block a user