feat: Implement additional Geometry Applet commands toward Book I (#45)
Reviewed-on: #45 Co-authored-by: Glen Whitney <glen@studioinfinity.org> Co-committed-by: Glen Whitney <glen@studioinfinity.org>
This commit is contained in:
parent
277d9b0a8c
commit
5b1ca40b26
@ -34,12 +34,12 @@
|
||||
url: 'https://code.studioinfinity.org/glen/archematics.git',
|
||||
},
|
||||
devDependencies: {
|
||||
'@danielx/civet': '^0.6.43',
|
||||
'@types/firefox-webext-browser': '^111.0.2',
|
||||
'@types/jquery': '^3.5.22',
|
||||
'@danielx/civet': '^0.6.46',
|
||||
'@types/firefox-webext-browser': '^111.0.3',
|
||||
'@types/jquery': '^3.5.24',
|
||||
'@webcomponents/custom-elements': '^1.6.0',
|
||||
'http-server': '^14.1.1',
|
||||
rollup: '^4.0.2',
|
||||
rollup: '^4.1.4',
|
||||
typescript: '^5.2.2',
|
||||
'webextension-polyfill': '^0.10.0',
|
||||
},
|
||||
|
104
pnpm-lock.yaml
104
pnpm-lock.yaml
@ -14,14 +14,14 @@ dependencies:
|
||||
|
||||
devDependencies:
|
||||
'@danielx/civet':
|
||||
specifier: ^0.6.43
|
||||
version: 0.6.43(typescript@5.2.2)
|
||||
specifier: ^0.6.46
|
||||
version: 0.6.46(typescript@5.2.2)
|
||||
'@types/firefox-webext-browser':
|
||||
specifier: ^111.0.2
|
||||
version: 111.0.2
|
||||
specifier: ^111.0.3
|
||||
version: 111.0.3
|
||||
'@types/jquery':
|
||||
specifier: ^3.5.22
|
||||
version: 3.5.22
|
||||
specifier: ^3.5.24
|
||||
version: 3.5.24
|
||||
'@webcomponents/custom-elements':
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0
|
||||
@ -29,8 +29,8 @@ devDependencies:
|
||||
specifier: ^14.1.1
|
||||
version: 14.1.1
|
||||
rollup:
|
||||
specifier: ^4.0.2
|
||||
version: 4.0.2
|
||||
specifier: ^4.1.4
|
||||
version: 4.1.4
|
||||
typescript:
|
||||
specifier: ^5.2.2
|
||||
version: 5.2.2
|
||||
@ -47,8 +47,8 @@ packages:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
dev: true
|
||||
|
||||
/@danielx/civet@0.6.43(typescript@5.2.2):
|
||||
resolution: {integrity: sha512-K4G9Rq4J6TY1H+XJOXCxY7IZqlLK+roL1ufvJQaOIrzx+KJ7F8zdn/4GKM7F7NKD4BgJl3avBqHAMzuUWRkzRg==}
|
||||
/@danielx/civet@0.6.46(typescript@5.2.2):
|
||||
resolution: {integrity: sha512-8WjtFrnK9TbBg5nVIQWFkFeWvDFBv/jFYYaasjiaT4+Vwr6TpZfbXnB/A4uj1sETagPtdNJPgasVpE1LHdrm6A==}
|
||||
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@ -78,108 +78,108 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
dev: true
|
||||
|
||||
/@rollup/rollup-android-arm-eabi@4.0.2:
|
||||
resolution: {integrity: sha512-xDvk1pT4vaPU2BOLy0MqHMdYZyntqpaBf8RhBiezlqG9OjY8F50TyctHo8znigYKd+QCFhCmlmXHOL/LoaOl3w==}
|
||||
/@rollup/rollup-android-arm-eabi@4.1.4:
|
||||
resolution: {integrity: sha512-WlzkuFvpKl6CLFdc3V6ESPt7gq5Vrimd2Yv9IzKXdOpgbH4cdDSS1JLiACX8toygihtH5OlxyQzhXOph7Ovlpw==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-android-arm64@4.0.2:
|
||||
resolution: {integrity: sha512-lqCglytY3E6raze27DD9VQJWohbwCxzqs9aSHcj5X/8hJpzZfNdbsr4Ja9Hqp6iPyF53+5PtPx0pKRlkSvlHZg==}
|
||||
/@rollup/rollup-android-arm64@4.1.4:
|
||||
resolution: {integrity: sha512-D1e+ABe56T9Pq2fD+R3ybe1ylCDzu3tY4Qm2Mj24R9wXNCq35+JbFbOpc2yrroO2/tGhTobmEl2Bm5xfE/n8RA==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-arm64@4.0.2:
|
||||
resolution: {integrity: sha512-nkBKItS6E6CCzvRwgiKad+j+1ibmL7SIInj7oqMWmdkCjiSX6VeVZw2mLlRKIUL+JjsBgpATTfo7BiAXc1v0jA==}
|
||||
/@rollup/rollup-darwin-arm64@4.1.4:
|
||||
resolution: {integrity: sha512-7vTYrgEiOrjxnjsgdPB+4i7EMxbVp7XXtS+50GJYj695xYTTEMn3HZVEvgtwjOUkAP/Q4HDejm4fIAjLeAfhtg==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-x64@4.0.2:
|
||||
resolution: {integrity: sha512-vX2C8xvWPIbpEgQht95+dY6BReKAvtDgPDGi0XN0kWJKkm4WdNmq5dnwscv/zxvi+n6jUTBhs6GtpkkWT4q8Gg==}
|
||||
/@rollup/rollup-darwin-x64@4.1.4:
|
||||
resolution: {integrity: sha512-eGJVZScKSLZkYjhTAESCtbyTBq9SXeW9+TX36ki5gVhDqJtnQ5k0f9F44jNK5RhAMgIj0Ht9+n6HAgH0gUUyWQ==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm-gnueabihf@4.0.2:
|
||||
resolution: {integrity: sha512-DVFIfcHOjgmeHOAqji4xNz2wczt1Bmzy9MwBZKBa83SjBVO/i38VHDR+9ixo8QpBOiEagmNw12DucG+v55tCrg==}
|
||||
/@rollup/rollup-linux-arm-gnueabihf@4.1.4:
|
||||
resolution: {integrity: sha512-HnigYSEg2hOdX1meROecbk++z1nVJDpEofw9V2oWKqOWzTJlJf1UXVbDE6Hg30CapJxZu5ga4fdAQc/gODDkKg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm64-gnu@4.0.2:
|
||||
resolution: {integrity: sha512-GCK/a9ItUxPI0V5hQEJjH4JtOJO90GF2Hja7TO+EZ8rmkGvEi8/ZDMhXmcuDpQT7/PWrTT9RvnG8snMd5SrhBQ==}
|
||||
/@rollup/rollup-linux-arm64-gnu@4.1.4:
|
||||
resolution: {integrity: sha512-TzJ+N2EoTLWkaClV2CUhBlj6ljXofaYzF/R9HXqQ3JCMnCHQZmQnbnZllw7yTDp0OG5whP4gIPozR4QiX+00MQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm64-musl@4.0.2:
|
||||
resolution: {integrity: sha512-cLuBp7rOjIB1R2j/VazjCmHC7liWUur2e9mFflLJBAWCkrZ+X0+QwHLvOQakIwDymungzAKv6W9kHZnTp/Mqrg==}
|
||||
/@rollup/rollup-linux-arm64-musl@4.1.4:
|
||||
resolution: {integrity: sha512-aVPmNMdp6Dlo2tWkAduAD/5TL/NT5uor290YvjvFvCv0Q3L7tVdlD8MOGDL+oRSw5XKXKAsDzHhUOPUNPRHVTQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-x64-gnu@4.0.2:
|
||||
resolution: {integrity: sha512-Zqw4iVnJr2naoyQus0yLy7sLtisCQcpdMKUCeXPBjkJtpiflRime/TMojbnl8O3oxUAj92mxr+t7im/RbgA20w==}
|
||||
/@rollup/rollup-linux-x64-gnu@4.1.4:
|
||||
resolution: {integrity: sha512-77Fb79ayiDad0grvVsz4/OB55wJRyw9Ao+GdOBA9XywtHpuq5iRbVyHToGxWquYWlEf6WHFQQnFEttsAzboyKg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-x64-musl@4.0.2:
|
||||
resolution: {integrity: sha512-jJRU9TyUD/iMqjf8aLAp7XiN3pIj5v6Qcu+cdzBfVTKDD0Fvua4oUoK8eVJ9ZuKBEQKt3WdlcwJXFkpmMLk6kg==}
|
||||
/@rollup/rollup-linux-x64-musl@4.1.4:
|
||||
resolution: {integrity: sha512-/t6C6niEQTqmQTVTD9TDwUzxG91Mlk69/v0qodIPUnjjB3wR4UA3klg+orR2SU3Ux2Cgf2pWPL9utK80/1ek8g==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-arm64-msvc@4.0.2:
|
||||
resolution: {integrity: sha512-ZkS2NixCxHKC4zbOnw64ztEGGDVIYP6nKkGBfOAxEPW71Sji9v8z3yaHNuae/JHPwXA+14oDefnOuVfxl59SmQ==}
|
||||
/@rollup/rollup-win32-arm64-msvc@4.1.4:
|
||||
resolution: {integrity: sha512-ZY5BHHrOPkMbCuGWFNpJH0t18D2LU6GMYKGaqaWTQ3CQOL57Fem4zE941/Ek5pIsVt70HyDXssVEFQXlITI5Gg==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-ia32-msvc@4.0.2:
|
||||
resolution: {integrity: sha512-3SKjj+tvnZ0oZq2BKB+fI+DqYI83VrRzk7eed8tJkxeZ4zxJZcLSE8YDQLYGq1tZAnAX+H076RHHB4gTZXsQzw==}
|
||||
/@rollup/rollup-win32-ia32-msvc@4.1.4:
|
||||
resolution: {integrity: sha512-XG2mcRfFrJvYyYaQmvCIvgfkaGinfXrpkBuIbJrTl9SaIQ8HumheWTIwkNz2mktCKwZfXHQNpO7RgXLIGQ7HXA==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-x64-msvc@4.0.2:
|
||||
resolution: {integrity: sha512-MBdJIOxRauKkry7t2q+rTHa3aWjVez2eioWg+etRVS3dE4tChhmt5oqZYr48R6bPmcwEhxQr96gVRfeQrLbqng==}
|
||||
/@rollup/rollup-win32-x64-msvc@4.1.4:
|
||||
resolution: {integrity: sha512-ANFqWYPwkhIqPmXw8vm0GpBEHiPpqcm99jiiAp71DbCSqLDhrtr019C5vhD0Bw4My+LmMvciZq6IsWHqQpl2ZQ==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@types/firefox-webext-browser@111.0.2:
|
||||
resolution: {integrity: sha512-NS7izfYOnQI/Opf3YdZSKkI5Ox89SqEffJHK2zfGY2BYEVuWuM6pSwDRglGl4W0SM84oUQfvLyYH4X6EQZAJ2w==}
|
||||
/@types/firefox-webext-browser@111.0.3:
|
||||
resolution: {integrity: sha512-yboaUEJfY6AGB1hJIWzRGNa/qQUCC/2CAgBSWkdJ8rml4k0bXIyxlWYLSRTr6jHT29gdHkdHsPVuF5McSNERfw==}
|
||||
dev: true
|
||||
|
||||
/@types/jquery@3.5.22:
|
||||
resolution: {integrity: sha512-ISQFeUK5GwRftLK4PVvKTWEVCxZ2BpaqBz0TWkIq5w4vGojxZP9+XkqgcPjxoqmPeew+HLyWthCBvK7GdF5NYA==}
|
||||
/@types/jquery@3.5.24:
|
||||
resolution: {integrity: sha512-V/TG69ge5amcr8Ap7vY3SObqKfZlV7ttqcYnNcYnndI77ySIRi05+3GjvfwRtE2qalAC2ySLIL1ker512sI20g==}
|
||||
dependencies:
|
||||
'@types/sizzle': 2.3.3
|
||||
dev: true
|
||||
@ -544,23 +544,23 @@ packages:
|
||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||
dev: true
|
||||
|
||||
/rollup@4.0.2:
|
||||
resolution: {integrity: sha512-MCScu4usMPCeVFaiLcgMDaBQeYi1z6vpWxz0r0hq0Hv77Y2YuOTZldkuNJ54BdYBH3e+nkrk6j0Rre/NLDBYzg==}
|
||||
/rollup@4.1.4:
|
||||
resolution: {integrity: sha512-U8Yk1lQRKqCkDBip/pMYT+IKaN7b7UesK3fLSTuHBoBJacCE+oBqo/dfG/gkUdQNNB2OBmRP98cn2C2bkYZkyw==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-android-arm-eabi': 4.0.2
|
||||
'@rollup/rollup-android-arm64': 4.0.2
|
||||
'@rollup/rollup-darwin-arm64': 4.0.2
|
||||
'@rollup/rollup-darwin-x64': 4.0.2
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.0.2
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.0.2
|
||||
'@rollup/rollup-linux-arm64-musl': 4.0.2
|
||||
'@rollup/rollup-linux-x64-gnu': 4.0.2
|
||||
'@rollup/rollup-linux-x64-musl': 4.0.2
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.0.2
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.0.2
|
||||
'@rollup/rollup-win32-x64-msvc': 4.0.2
|
||||
'@rollup/rollup-android-arm-eabi': 4.1.4
|
||||
'@rollup/rollup-android-arm64': 4.1.4
|
||||
'@rollup/rollup-darwin-arm64': 4.1.4
|
||||
'@rollup/rollup-darwin-x64': 4.1.4
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.1.4
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.1.4
|
||||
'@rollup/rollup-linux-arm64-musl': 4.1.4
|
||||
'@rollup/rollup-linux-x64-gnu': 4.1.4
|
||||
'@rollup/rollup-linux-x64-musl': 4.1.4
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.1.4
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.1.4
|
||||
'@rollup/rollup-win32-x64-msvc': 4.1.4
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
|
@ -196,6 +196,12 @@ function dispatchJcommand(
|
||||
Text("${value}", TitlePoint + (2,5))`
|
||||
'pivot'
|
||||
return // already handled in postApplets
|
||||
'align'
|
||||
console.warn
|
||||
'Label alignment is not available in GeoGebra'
|
||||
'translation, as there is no facility for automatically'
|
||||
'positioning labels. However, they can be dragged manually.'
|
||||
return
|
||||
/e\[\d+\]/
|
||||
num := parseInt(name.slice(2))
|
||||
{commands, callbacks, parts} :=
|
||||
@ -491,7 +497,7 @@ function geoname(
|
||||
return .= 'Geo' + numCode.toString(36);
|
||||
return += '1' while return.value in elements
|
||||
|
||||
// Helper for similar point/line functions:
|
||||
// Helpers for some corresponding point/line functions:
|
||||
function cutoffExtend(
|
||||
method: string, pt: string[], point?: string[], line?: string[]
|
||||
): [string, string]
|
||||
@ -502,6 +508,35 @@ function cutoffExtend(
|
||||
source := method is 'cutoff' ? pt[0] : pt[1]
|
||||
[source, displacement]
|
||||
|
||||
function proportionSimilar(
|
||||
method: string, args: JoyceArguments, cdata: ConstructionData,
|
||||
aux: string, commands: string[], auxiliaries: string[]
|
||||
): readonly [string, string]
|
||||
bad := ['', ''] as const
|
||||
pt .= args.subpoints
|
||||
unless pt then return bad
|
||||
// reduce the similar case to general proportion
|
||||
if method is 'similar'
|
||||
unless pt.length is 5 then return bad
|
||||
sourcePlane .= ''
|
||||
destPlane .= ''
|
||||
if cdata.is3d
|
||||
unless args.plane then return bad
|
||||
destPlane = `, ${args.plane[0]}`
|
||||
if args.plane.length > 1
|
||||
sourcePlane = `, ${args.plane[1]}`
|
||||
else
|
||||
sourcePlane = `, Plane(${pt[2]}, ${pt[3]}, ${pt[4]})`
|
||||
angle := `Angle(${pt[3]}, ${pt[2]}, ${pt[4]}${sourcePlane})`
|
||||
commands.push
|
||||
`${aux} = Rotate(${pt[1]}, ${angle}, ${pt[0]}${destPlane})`
|
||||
auxiliaries.push aux
|
||||
pt = [pt[2], pt[3], pt[2], pt[4], pt[0], pt[1], pt[0], aux]
|
||||
len := `Distance(${pt[2]},${pt[3]})*Distance(${pt[4]},${pt[5]})`
|
||||
+ `/ Distance(${pt[0]},${pt[1]})`
|
||||
direction := `UnitVector(Vector(${pt[6]}, ${pt[7]}))`
|
||||
return [pt[6], `${len}*${direction}`]
|
||||
|
||||
// All of the detailed semantics of each available command lies in this
|
||||
// function.
|
||||
classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
@ -514,24 +549,10 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
parts[0].push name
|
||||
switch method
|
||||
/angle(?:Bisector|Divider)/
|
||||
// Note we just ignore a possible plane argument; it's irrelevant
|
||||
unless args.subpoints return
|
||||
[start, center, end] := args.subpoints
|
||||
// see if we need to make the destination segment from start to end
|
||||
destination .= ''
|
||||
unless args.line?.length is 1 and args.point?[0] is center
|
||||
destination = aux + '1'
|
||||
auxiliaries.push destination
|
||||
commands.push `${destination} = Segment(${start}, ${end})`
|
||||
else destination = args.line[0]
|
||||
n := method is 'angleBisector' ? 2 : args.scalar?[0]
|
||||
inPlane := cdata.is3d ? `, Plane(${start}, ${center}, ${end})` : ''
|
||||
commands.push
|
||||
`${aux}2 = Angle(${start}, ${center}, ${end}${inPlane})`
|
||||
`${aux}3 = If(${aux}2 > pi, ${aux}2 - 2*pi, ${aux}2)`
|
||||
`${aux}4 = Rotate(${start}, ${aux}3/${n}, ${center}${inPlane})`
|
||||
`${name} = Intersect(${destination}, Ray(${center}, ${aux}4))`
|
||||
auxiliaries.push ...[2..4].map (i) => `${aux}${i}`
|
||||
{center, foot} :=
|
||||
makeAngDiv(method, args, cdata, aux, auxiliaries, commands)
|
||||
unless foot return
|
||||
commands.push `${name} = ${foot}`
|
||||
'circleSlider'
|
||||
unless args.circle then return
|
||||
circ := args.circle[0]
|
||||
@ -592,12 +613,23 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
if args.scalar and args.scalar.length
|
||||
callbacks.push (api: AppletObject) =>
|
||||
api.setCoords name, ...args.scalar as XYZ
|
||||
'lineSlider'
|
||||
pt := args.subpoints
|
||||
unless pt and pt.length is 2 then return
|
||||
commands.push `${name} = Point(Line(${pt[0]}, ${pt[1]}))`
|
||||
if args.scalar and args.scalar.length
|
||||
callbacks.push (api: AppletObject) =>
|
||||
api.setCoords name, ...args.scalar as XYZ
|
||||
'midpoint'
|
||||
if args.line
|
||||
commands.push `${name} = Midpoint(${args.line[0]})`
|
||||
else
|
||||
commands.push
|
||||
`${name} = Midpoint(${args.point?[0]},${args.point?[1]})`
|
||||
'parallelogram'
|
||||
pt := args.subpoints
|
||||
unless pt then return
|
||||
commands.push `${name} = ${pt[0]} + ${pt[2]} - ${pt[1]}`
|
||||
'perpendicular'
|
||||
// Note only the two-point option implemented so far
|
||||
unless args.subpoints return
|
||||
@ -605,29 +637,10 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
// Note clockwise 90° rotation (3π/2) confirmed in Joyce source
|
||||
commands.push `${name} = Rotate(${direction}, 3*pi/2, ${center})`
|
||||
/proportion|similar/
|
||||
pt .= args.subpoints
|
||||
unless pt then return
|
||||
// reduce the similar case to general proportion
|
||||
if method is 'similar'
|
||||
unless pt.length is 5 then return
|
||||
sourcePlane .= ''
|
||||
destPlane .= ''
|
||||
if cdata.is3d
|
||||
unless args.plane then return
|
||||
destPlane = `, ${args.plane[0]}`
|
||||
if args.plane.length > 1
|
||||
sourcePlane = `, ${args.plane[1]}`
|
||||
else
|
||||
sourcePlane = `, Plane(${pt[2]}, ${pt[3]}, ${pt[4]})`
|
||||
angle := `Angle(${pt[3]}, ${pt[2]}, ${pt[4]}${sourcePlane})`
|
||||
commands.push
|
||||
`${aux} = Rotate(${pt[1]}, ${angle}, ${pt[0]}${destPlane})`
|
||||
auxiliaries.push aux
|
||||
pt = [pt[2], pt[3], pt[2], pt[4], pt[0], pt[1], pt[0], aux]
|
||||
len := `Distance(${pt[2]},${pt[3]})*Distance(${pt[4]},${pt[5]})`
|
||||
+ `/ Distance(${pt[0]},${pt[1]})`
|
||||
direction := `UnitVector(Vector(${pt[6]}, ${pt[7]}))`
|
||||
commands.push `${name} = Translate(${pt[6]}, ${len}*${direction})`
|
||||
[source, displacement] :=
|
||||
proportionSimilar method, args, cdata, aux, commands, auxiliaries
|
||||
unless source then return
|
||||
commands.push `${name} = Translate(${source}, ${displacement})`
|
||||
'vertex'
|
||||
commands.push
|
||||
`${name} = Vertex(${args.polygon?[0]},${args.scalar?[0]})`
|
||||
@ -640,6 +653,15 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
parts[1].push name
|
||||
madeSegment .= false
|
||||
switch method
|
||||
/angle(?:Bisector|Divider)/
|
||||
{center, foot} :=
|
||||
makeAngDiv(method, args, cdata, aux, auxiliaries, commands)
|
||||
unless foot return
|
||||
auxiliaries.push aux + 'F'
|
||||
commands.push
|
||||
`${aux}F = ${foot}`
|
||||
ends[0] = center
|
||||
ends[1] = aux+'F'
|
||||
'bichord'
|
||||
// To match Joyce, we need to get the ordering here correct.
|
||||
// we want the order so that start -> end sweeping past the
|
||||
@ -649,7 +671,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
unless cr return
|
||||
commands.push ...[1..2].map (n) =>
|
||||
`${aux}${n} = Intersect(${cr[0]}, ${cr[1]}, ${n})`
|
||||
inPlane := cdata.is3d ? `Plane(${cr[0]})` : ''
|
||||
inPlane := cdata.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})`)
|
||||
@ -745,6 +767,14 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
commands.push
|
||||
`${aux}2 = Translate(${pt[0]}, ${dist}*${unitVec})`
|
||||
else return
|
||||
/proportion|similar/
|
||||
[source, displacement] :=
|
||||
proportionSimilar method, args, cdata, aux, commands, auxiliaries
|
||||
unless source then return
|
||||
ends[0] = source
|
||||
commands.push `${aux}1 = Translate(${source},${displacement})`
|
||||
auxiliaries.push aux+1
|
||||
ends[1] = aux+1
|
||||
|
||||
unless madeSegment
|
||||
commands.push `${name} = Segment(${ends[0]},${ends[1]})`
|
||||
@ -755,9 +785,14 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
return := freshCommander()
|
||||
return.value.ends = ['', '']
|
||||
{commands, callbacks, parts, auxiliaries, ends} := return.value
|
||||
parts[2].push name
|
||||
aux := name + 'aUx'
|
||||
parts[1].push name
|
||||
circle .= ''
|
||||
switch method
|
||||
'circumcircle'
|
||||
pt := args.subpoints
|
||||
unless pt and pt.length is 3 then return
|
||||
circle = `Circle(${pt.join ','})`
|
||||
'radius'
|
||||
pt := args.subpoints
|
||||
unless pt then return
|
||||
@ -766,30 +801,37 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
when 2
|
||||
[center, point] := pt
|
||||
ends[0] = center
|
||||
commands.push
|
||||
`${name} = Circle(${center}, ${point}${inPlane})`
|
||||
circle = `Circle(${center}, ${point}${inPlane})`
|
||||
when 3
|
||||
center := pt[0]
|
||||
ends[0] = center
|
||||
radius := `Distance(${pt[1]}, ${pt[2]})`
|
||||
commands.push
|
||||
`${name} = Circle(${center}, ${radius}${inPlane})`
|
||||
circle = `Circle(${center}, ${radius}${inPlane})`
|
||||
commands.push
|
||||
`${aux} = ${circle}` // for the filling
|
||||
`${name} = ${circle}` // for the perimeter
|
||||
parts[2].push aux
|
||||
makeLinesInvisible callbacks, aux
|
||||
callbacks.push (api: AppletObject) => api.setLabelVisible name, true
|
||||
|
||||
polygon: (name, method, args, index) =>
|
||||
polygon: (name, method, args, index, cdata) =>
|
||||
return := freshCommander()
|
||||
{commands, callbacks, parts, auxiliaries} := return.value
|
||||
parts[2].push name
|
||||
// what to push for edges?
|
||||
aux := name + 'aUx'
|
||||
switch method
|
||||
'equilateralTriangle'
|
||||
/equilateralTriangle|square|regularPolygon/
|
||||
pt := args.subpoints
|
||||
unless pt then return
|
||||
N .= 3
|
||||
if method is 'square' then N = 4
|
||||
else if method is 'regularPolygon' and args.scalar
|
||||
N = args.scalar[0]
|
||||
commands.push '' // hack, make sure there is a command
|
||||
parts[0].push pt[0], pt[1]
|
||||
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
||||
made:= api.evalCommandGetLabels
|
||||
`${name} = Polygon(${pt[1]},${pt[0]}, 3)`
|
||||
`${name} = Polygon(${pt[0]},${pt[1]}, ${N})`
|
||||
if not made return
|
||||
for each obj of made.split ','
|
||||
if obj is name continue
|
||||
@ -801,10 +843,42 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
'point'
|
||||
moreParts[0].push newObj
|
||||
api.setVisible newObj, false
|
||||
/triangle|quadrilateral/
|
||||
pt := args.subpoints
|
||||
unless pt then return
|
||||
commands.push ''
|
||||
/triangle|similar|parallelogram|application|quadrilateral|octagon/
|
||||
unless args.subpoints then return
|
||||
pt .= args.subpoints
|
||||
if method is 'parallelogram'
|
||||
unless pt.length is 3 then return
|
||||
commands.push `${aux} = ${pt[0]} + ${pt[2]} - ${pt[1]}`
|
||||
auxiliaries.push aux
|
||||
pt = [...pt, aux]
|
||||
else if method is 'application'
|
||||
unless pt.length is 3 then return
|
||||
unless args.polygon?.length is 1 then return
|
||||
direction := `UnitVector(${pt[2]} - ${pt[0]})`
|
||||
angle := `Angle(${pt[1]},${pt[0]},${pt[2]})`
|
||||
length := `Area(${args.polygon})`
|
||||
+ `/(Distance(${pt[0]},${pt[1]})*sin(${angle}))`
|
||||
commands.push ...[0..1].map (n) =>
|
||||
`${aux}${n} = ${pt[n]} + ${length}*${direction}`
|
||||
auxiliaries.push aux+0, aux+1
|
||||
pt = [pt[0], pt[1], aux+1, aux+0]
|
||||
else if method is 'similar'
|
||||
unless pt.length is 5 then return
|
||||
if cdata.is3d and not args.plane then return
|
||||
inSourcePlane := cdata.is3d
|
||||
? `, Plane(${pt[2]},${pt[3]},${pt[4]})`
|
||||
: ''
|
||||
inDestPlane := cdata.is3d ? (', ' + args.plane?[0]) : ''
|
||||
factor :=
|
||||
`Distance(${pt[2]},${pt[4]})/Distance(${pt[2]},${pt[3]})`
|
||||
commands.push
|
||||
`${aux}1 = Angle(${pt[3]},${pt[2]},${pt[4]}${inSourcePlane})`
|
||||
`${aux}2 = Rotate(${pt[1]},${aux}1,${pt[0]}${inDestPlane})`
|
||||
`${aux}3 = ${pt[0]} + (${aux}2 - ${pt[0]})*${factor}`
|
||||
auxiliaries.push ...[1..3].map (n) => aux + n
|
||||
pt = [pt[0], pt[1], aux+3]
|
||||
else
|
||||
commands.push ''
|
||||
parts[0].push ...pt
|
||||
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
||||
made := api.evalCommandGetLabels
|
||||
@ -823,32 +897,25 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
aux := name + 'aUx'
|
||||
parts[2].push name
|
||||
switch method
|
||||
'sector'
|
||||
/arc|sector/
|
||||
unless args.subpoints?.length is 3 return
|
||||
parts[0].push ...args.subpoints
|
||||
[center, end, start] := args.subpoints
|
||||
[center, end, start] .= args.subpoints
|
||||
parms .= center + ', ' + start + ', ' + end
|
||||
prefix .= 'Circular'
|
||||
if method is 'arc'
|
||||
temp := end
|
||||
end = center
|
||||
center = temp
|
||||
parms = start + ', ' + center + ', ' + end
|
||||
prefix = 'Circumcircular'
|
||||
ends[0] = start
|
||||
ends[1] = end
|
||||
parms := center + ', ' + start + ', ' + end
|
||||
commands.push
|
||||
`${name} = CircularSector(${parms})`
|
||||
`${aux}1 = CircularArc(${parms})`
|
||||
`${name} = ${prefix}Sector(${parms})`
|
||||
`${aux}1 = ${prefix}Arc(${parms})`
|
||||
parts[1].push aux + 1
|
||||
callbacks.push (api: AppletObject) =>
|
||||
api.setLineThickness name, 1
|
||||
// The rest of this function is a weird roundabout way to make
|
||||
// the lines of the sector have zero opacity.
|
||||
// I got it from
|
||||
// https://www.reddit.com/r/geogebra/comments/12cbr85/setlineopacity_command/
|
||||
// I don't really understand how/why it works, but it seems to
|
||||
// So that's good enough for me
|
||||
xml .= api.getXML name
|
||||
xml = xml.replace /opacity="\d+"/, 'opacity="0"'
|
||||
api.evalXML xml
|
||||
// This last step is especially confusing... I think
|
||||
// evaluating the modified XML created a sort of second
|
||||
// copy of the entity, and so we have to hide the original one
|
||||
api.setVisible name, false
|
||||
makeLinesInvisible callbacks, name
|
||||
|
||||
plane: (name, method, args) =>
|
||||
return := freshCommander()
|
||||
@ -900,3 +967,47 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
||||
moreParts[1].push newObj
|
||||
'triangle'
|
||||
moreParts[2].push newObj
|
||||
|
||||
// Helper for dividing an angle
|
||||
function makeAngDiv(
|
||||
method:string,
|
||||
args: JoyceArguments,
|
||||
cdata: ConstructionData,
|
||||
aux: string,
|
||||
auxiliaries: string[],
|
||||
commands: string[])
|
||||
// Note we just ignore a possible plane argument; it's irrelevant
|
||||
unless args.subpoints return center: '', foot: ''
|
||||
[start, center, end] := args.subpoints
|
||||
// see if we need to make the destination segment from start to end
|
||||
destination .= ''
|
||||
unless args.line?.length is 1 and args.point?[0] is center
|
||||
destination = aux + '1'
|
||||
auxiliaries.push destination
|
||||
commands.push `${destination} = Segment(${start}, ${end})`
|
||||
else destination = args.line[0]
|
||||
n := method is 'angleBisector' ? 2 : args.scalar?[0]
|
||||
inPlane := cdata.is3d ? `, Plane(${start}, ${center}, ${end})` : ''
|
||||
commands.push
|
||||
`${aux}2 = Angle(${start}, ${center}, ${end}${inPlane})`
|
||||
`${aux}3 = If(${aux}2 > pi, ${aux}2 - 2*pi, ${aux}2)`
|
||||
`${aux}4 = Rotate(${start}, ${aux}3/${n}, ${center}${inPlane})`
|
||||
auxiliaries.push ...[2..4].map (i) => `${aux}${i}`
|
||||
return {center, foot: `Intersect(${destination}, Ray(${center}, ${aux}4))`}
|
||||
|
||||
// helper for separating color of perimeter and interior:
|
||||
function makeLinesInvisible(callbacks: GeogebraCallback[], name: string)
|
||||
callbacks.push (api: AppletObject) =>
|
||||
api.setLineThickness name, 1
|
||||
// The rest of this function is a weird roundabout way to make
|
||||
// the lines of the sector have zero opacity. I got it from
|
||||
// https://www.reddit.com/r/geogebra/comments/12cbr85/setlineopacity_command/
|
||||
// I don't really understand how/why it works, but it seems to
|
||||
// So that's good enough for me
|
||||
xml .= api.getXML name
|
||||
xml = xml.replace /opacity="\d+"/, 'opacity="0"'
|
||||
api.evalXML xml
|
||||
// This last step is especially confusing... I think
|
||||
// evaluating the modified XML created a sort of second
|
||||
// copy of the entity, and so we have to hide the original one
|
||||
api.setVisible name, false
|
||||
|
Loading…
Reference in New Issue
Block a user