feat: Implement additional Geometry Applet commands toward Book I #45
@ -34,12 +34,12 @@
|
|||||||
url: 'https://code.studioinfinity.org/glen/archematics.git',
|
url: 'https://code.studioinfinity.org/glen/archematics.git',
|
||||||
},
|
},
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
'@danielx/civet': '^0.6.43',
|
'@danielx/civet': '^0.6.46',
|
||||||
'@types/firefox-webext-browser': '^111.0.2',
|
'@types/firefox-webext-browser': '^111.0.3',
|
||||||
'@types/jquery': '^3.5.22',
|
'@types/jquery': '^3.5.24',
|
||||||
'@webcomponents/custom-elements': '^1.6.0',
|
'@webcomponents/custom-elements': '^1.6.0',
|
||||||
'http-server': '^14.1.1',
|
'http-server': '^14.1.1',
|
||||||
rollup: '^4.0.2',
|
rollup: '^4.1.4',
|
||||||
typescript: '^5.2.2',
|
typescript: '^5.2.2',
|
||||||
'webextension-polyfill': '^0.10.0',
|
'webextension-polyfill': '^0.10.0',
|
||||||
},
|
},
|
||||||
|
104
pnpm-lock.yaml
104
pnpm-lock.yaml
@ -14,14 +14,14 @@ dependencies:
|
|||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@danielx/civet':
|
'@danielx/civet':
|
||||||
specifier: ^0.6.43
|
specifier: ^0.6.46
|
||||||
version: 0.6.43(typescript@5.2.2)
|
version: 0.6.46(typescript@5.2.2)
|
||||||
'@types/firefox-webext-browser':
|
'@types/firefox-webext-browser':
|
||||||
specifier: ^111.0.2
|
specifier: ^111.0.3
|
||||||
version: 111.0.2
|
version: 111.0.3
|
||||||
'@types/jquery':
|
'@types/jquery':
|
||||||
specifier: ^3.5.22
|
specifier: ^3.5.24
|
||||||
version: 3.5.22
|
version: 3.5.24
|
||||||
'@webcomponents/custom-elements':
|
'@webcomponents/custom-elements':
|
||||||
specifier: ^1.6.0
|
specifier: ^1.6.0
|
||||||
version: 1.6.0
|
version: 1.6.0
|
||||||
@ -29,8 +29,8 @@ devDependencies:
|
|||||||
specifier: ^14.1.1
|
specifier: ^14.1.1
|
||||||
version: 14.1.1
|
version: 14.1.1
|
||||||
rollup:
|
rollup:
|
||||||
specifier: ^4.0.2
|
specifier: ^4.1.4
|
||||||
version: 4.0.2
|
version: 4.1.4
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.2.2
|
specifier: ^5.2.2
|
||||||
version: 5.2.2
|
version: 5.2.2
|
||||||
@ -47,8 +47,8 @@ packages:
|
|||||||
'@jridgewell/trace-mapping': 0.3.9
|
'@jridgewell/trace-mapping': 0.3.9
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@danielx/civet@0.6.43(typescript@5.2.2):
|
/@danielx/civet@0.6.46(typescript@5.2.2):
|
||||||
resolution: {integrity: sha512-K4G9Rq4J6TY1H+XJOXCxY7IZqlLK+roL1ufvJQaOIrzx+KJ7F8zdn/4GKM7F7NKD4BgJl3avBqHAMzuUWRkzRg==}
|
resolution: {integrity: sha512-8WjtFrnK9TbBg5nVIQWFkFeWvDFBv/jFYYaasjiaT4+Vwr6TpZfbXnB/A4uj1sETagPtdNJPgasVpE1LHdrm6A==}
|
||||||
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
|
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -78,108 +78,108 @@ packages:
|
|||||||
'@jridgewell/sourcemap-codec': 1.4.15
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@rollup/rollup-android-arm-eabi@4.0.2:
|
/@rollup/rollup-android-arm-eabi@4.1.4:
|
||||||
resolution: {integrity: sha512-xDvk1pT4vaPU2BOLy0MqHMdYZyntqpaBf8RhBiezlqG9OjY8F50TyctHo8znigYKd+QCFhCmlmXHOL/LoaOl3w==}
|
resolution: {integrity: sha512-WlzkuFvpKl6CLFdc3V6ESPt7gq5Vrimd2Yv9IzKXdOpgbH4cdDSS1JLiACX8toygihtH5OlxyQzhXOph7Ovlpw==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-android-arm64@4.0.2:
|
/@rollup/rollup-android-arm64@4.1.4:
|
||||||
resolution: {integrity: sha512-lqCglytY3E6raze27DD9VQJWohbwCxzqs9aSHcj5X/8hJpzZfNdbsr4Ja9Hqp6iPyF53+5PtPx0pKRlkSvlHZg==}
|
resolution: {integrity: sha512-D1e+ABe56T9Pq2fD+R3ybe1ylCDzu3tY4Qm2Mj24R9wXNCq35+JbFbOpc2yrroO2/tGhTobmEl2Bm5xfE/n8RA==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-darwin-arm64@4.0.2:
|
/@rollup/rollup-darwin-arm64@4.1.4:
|
||||||
resolution: {integrity: sha512-nkBKItS6E6CCzvRwgiKad+j+1ibmL7SIInj7oqMWmdkCjiSX6VeVZw2mLlRKIUL+JjsBgpATTfo7BiAXc1v0jA==}
|
resolution: {integrity: sha512-7vTYrgEiOrjxnjsgdPB+4i7EMxbVp7XXtS+50GJYj695xYTTEMn3HZVEvgtwjOUkAP/Q4HDejm4fIAjLeAfhtg==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-darwin-x64@4.0.2:
|
/@rollup/rollup-darwin-x64@4.1.4:
|
||||||
resolution: {integrity: sha512-vX2C8xvWPIbpEgQht95+dY6BReKAvtDgPDGi0XN0kWJKkm4WdNmq5dnwscv/zxvi+n6jUTBhs6GtpkkWT4q8Gg==}
|
resolution: {integrity: sha512-eGJVZScKSLZkYjhTAESCtbyTBq9SXeW9+TX36ki5gVhDqJtnQ5k0f9F44jNK5RhAMgIj0Ht9+n6HAgH0gUUyWQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-arm-gnueabihf@4.0.2:
|
/@rollup/rollup-linux-arm-gnueabihf@4.1.4:
|
||||||
resolution: {integrity: sha512-DVFIfcHOjgmeHOAqji4xNz2wczt1Bmzy9MwBZKBa83SjBVO/i38VHDR+9ixo8QpBOiEagmNw12DucG+v55tCrg==}
|
resolution: {integrity: sha512-HnigYSEg2hOdX1meROecbk++z1nVJDpEofw9V2oWKqOWzTJlJf1UXVbDE6Hg30CapJxZu5ga4fdAQc/gODDkKg==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-arm64-gnu@4.0.2:
|
/@rollup/rollup-linux-arm64-gnu@4.1.4:
|
||||||
resolution: {integrity: sha512-GCK/a9ItUxPI0V5hQEJjH4JtOJO90GF2Hja7TO+EZ8rmkGvEi8/ZDMhXmcuDpQT7/PWrTT9RvnG8snMd5SrhBQ==}
|
resolution: {integrity: sha512-TzJ+N2EoTLWkaClV2CUhBlj6ljXofaYzF/R9HXqQ3JCMnCHQZmQnbnZllw7yTDp0OG5whP4gIPozR4QiX+00MQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-arm64-musl@4.0.2:
|
/@rollup/rollup-linux-arm64-musl@4.1.4:
|
||||||
resolution: {integrity: sha512-cLuBp7rOjIB1R2j/VazjCmHC7liWUur2e9mFflLJBAWCkrZ+X0+QwHLvOQakIwDymungzAKv6W9kHZnTp/Mqrg==}
|
resolution: {integrity: sha512-aVPmNMdp6Dlo2tWkAduAD/5TL/NT5uor290YvjvFvCv0Q3L7tVdlD8MOGDL+oRSw5XKXKAsDzHhUOPUNPRHVTQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-x64-gnu@4.0.2:
|
/@rollup/rollup-linux-x64-gnu@4.1.4:
|
||||||
resolution: {integrity: sha512-Zqw4iVnJr2naoyQus0yLy7sLtisCQcpdMKUCeXPBjkJtpiflRime/TMojbnl8O3oxUAj92mxr+t7im/RbgA20w==}
|
resolution: {integrity: sha512-77Fb79ayiDad0grvVsz4/OB55wJRyw9Ao+GdOBA9XywtHpuq5iRbVyHToGxWquYWlEf6WHFQQnFEttsAzboyKg==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-x64-musl@4.0.2:
|
/@rollup/rollup-linux-x64-musl@4.1.4:
|
||||||
resolution: {integrity: sha512-jJRU9TyUD/iMqjf8aLAp7XiN3pIj5v6Qcu+cdzBfVTKDD0Fvua4oUoK8eVJ9ZuKBEQKt3WdlcwJXFkpmMLk6kg==}
|
resolution: {integrity: sha512-/t6C6niEQTqmQTVTD9TDwUzxG91Mlk69/v0qodIPUnjjB3wR4UA3klg+orR2SU3Ux2Cgf2pWPL9utK80/1ek8g==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-win32-arm64-msvc@4.0.2:
|
/@rollup/rollup-win32-arm64-msvc@4.1.4:
|
||||||
resolution: {integrity: sha512-ZkS2NixCxHKC4zbOnw64ztEGGDVIYP6nKkGBfOAxEPW71Sji9v8z3yaHNuae/JHPwXA+14oDefnOuVfxl59SmQ==}
|
resolution: {integrity: sha512-ZY5BHHrOPkMbCuGWFNpJH0t18D2LU6GMYKGaqaWTQ3CQOL57Fem4zE941/Ek5pIsVt70HyDXssVEFQXlITI5Gg==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-win32-ia32-msvc@4.0.2:
|
/@rollup/rollup-win32-ia32-msvc@4.1.4:
|
||||||
resolution: {integrity: sha512-3SKjj+tvnZ0oZq2BKB+fI+DqYI83VrRzk7eed8tJkxeZ4zxJZcLSE8YDQLYGq1tZAnAX+H076RHHB4gTZXsQzw==}
|
resolution: {integrity: sha512-XG2mcRfFrJvYyYaQmvCIvgfkaGinfXrpkBuIbJrTl9SaIQ8HumheWTIwkNz2mktCKwZfXHQNpO7RgXLIGQ7HXA==}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-win32-x64-msvc@4.0.2:
|
/@rollup/rollup-win32-x64-msvc@4.1.4:
|
||||||
resolution: {integrity: sha512-MBdJIOxRauKkry7t2q+rTHa3aWjVez2eioWg+etRVS3dE4tChhmt5oqZYr48R6bPmcwEhxQr96gVRfeQrLbqng==}
|
resolution: {integrity: sha512-ANFqWYPwkhIqPmXw8vm0GpBEHiPpqcm99jiiAp71DbCSqLDhrtr019C5vhD0Bw4My+LmMvciZq6IsWHqQpl2ZQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@types/firefox-webext-browser@111.0.2:
|
/@types/firefox-webext-browser@111.0.3:
|
||||||
resolution: {integrity: sha512-NS7izfYOnQI/Opf3YdZSKkI5Ox89SqEffJHK2zfGY2BYEVuWuM6pSwDRglGl4W0SM84oUQfvLyYH4X6EQZAJ2w==}
|
resolution: {integrity: sha512-yboaUEJfY6AGB1hJIWzRGNa/qQUCC/2CAgBSWkdJ8rml4k0bXIyxlWYLSRTr6jHT29gdHkdHsPVuF5McSNERfw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/jquery@3.5.22:
|
/@types/jquery@3.5.24:
|
||||||
resolution: {integrity: sha512-ISQFeUK5GwRftLK4PVvKTWEVCxZ2BpaqBz0TWkIq5w4vGojxZP9+XkqgcPjxoqmPeew+HLyWthCBvK7GdF5NYA==}
|
resolution: {integrity: sha512-V/TG69ge5amcr8Ap7vY3SObqKfZlV7ttqcYnNcYnndI77ySIRi05+3GjvfwRtE2qalAC2ySLIL1ker512sI20g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/sizzle': 2.3.3
|
'@types/sizzle': 2.3.3
|
||||||
dev: true
|
dev: true
|
||||||
@ -544,23 +544,23 @@ packages:
|
|||||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/rollup@4.0.2:
|
/rollup@4.1.4:
|
||||||
resolution: {integrity: sha512-MCScu4usMPCeVFaiLcgMDaBQeYi1z6vpWxz0r0hq0Hv77Y2YuOTZldkuNJ54BdYBH3e+nkrk6j0Rre/NLDBYzg==}
|
resolution: {integrity: sha512-U8Yk1lQRKqCkDBip/pMYT+IKaN7b7UesK3fLSTuHBoBJacCE+oBqo/dfG/gkUdQNNB2OBmRP98cn2C2bkYZkyw==}
|
||||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@rollup/rollup-android-arm-eabi': 4.0.2
|
'@rollup/rollup-android-arm-eabi': 4.1.4
|
||||||
'@rollup/rollup-android-arm64': 4.0.2
|
'@rollup/rollup-android-arm64': 4.1.4
|
||||||
'@rollup/rollup-darwin-arm64': 4.0.2
|
'@rollup/rollup-darwin-arm64': 4.1.4
|
||||||
'@rollup/rollup-darwin-x64': 4.0.2
|
'@rollup/rollup-darwin-x64': 4.1.4
|
||||||
'@rollup/rollup-linux-arm-gnueabihf': 4.0.2
|
'@rollup/rollup-linux-arm-gnueabihf': 4.1.4
|
||||||
'@rollup/rollup-linux-arm64-gnu': 4.0.2
|
'@rollup/rollup-linux-arm64-gnu': 4.1.4
|
||||||
'@rollup/rollup-linux-arm64-musl': 4.0.2
|
'@rollup/rollup-linux-arm64-musl': 4.1.4
|
||||||
'@rollup/rollup-linux-x64-gnu': 4.0.2
|
'@rollup/rollup-linux-x64-gnu': 4.1.4
|
||||||
'@rollup/rollup-linux-x64-musl': 4.0.2
|
'@rollup/rollup-linux-x64-musl': 4.1.4
|
||||||
'@rollup/rollup-win32-arm64-msvc': 4.0.2
|
'@rollup/rollup-win32-arm64-msvc': 4.1.4
|
||||||
'@rollup/rollup-win32-ia32-msvc': 4.0.2
|
'@rollup/rollup-win32-ia32-msvc': 4.1.4
|
||||||
'@rollup/rollup-win32-x64-msvc': 4.0.2
|
'@rollup/rollup-win32-x64-msvc': 4.1.4
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -497,7 +497,7 @@ function 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:
|
// Helpers for some corresponding point/line functions:
|
||||||
function cutoffExtend(
|
function cutoffExtend(
|
||||||
method: string, pt: string[], point?: string[], line?: string[]
|
method: string, pt: string[], point?: string[], line?: string[]
|
||||||
): [string, string]
|
): [string, string]
|
||||||
@ -508,6 +508,35 @@ function cutoffExtend(
|
|||||||
source := method is 'cutoff' ? pt[0] : pt[1]
|
source := method is 'cutoff' ? pt[0] : pt[1]
|
||||||
[source, displacement]
|
[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
|
// All of the detailed semantics of each available command lies in this
|
||||||
// function.
|
// function.
|
||||||
classHandler: Record<JoyceClass, ClassHandler> :=
|
classHandler: Record<JoyceClass, ClassHandler> :=
|
||||||
@ -520,24 +549,10 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
parts[0].push name
|
parts[0].push name
|
||||||
switch method
|
switch method
|
||||||
/angle(?:Bisector|Divider)/
|
/angle(?:Bisector|Divider)/
|
||||||
// Note we just ignore a possible plane argument; it's irrelevant
|
{center, foot} :=
|
||||||
unless args.subpoints return
|
makeAngDiv(method, args, cdata, aux, auxiliaries, commands)
|
||||||
[start, center, end] := args.subpoints
|
unless foot return
|
||||||
// see if we need to make the destination segment from start to end
|
commands.push `${name} = ${foot}`
|
||||||
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}`
|
|
||||||
'circleSlider'
|
'circleSlider'
|
||||||
unless args.circle then return
|
unless args.circle then return
|
||||||
circ := args.circle[0]
|
circ := args.circle[0]
|
||||||
@ -611,6 +626,10 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
else
|
else
|
||||||
commands.push
|
commands.push
|
||||||
`${name} = Midpoint(${args.point?[0]},${args.point?[1]})`
|
`${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'
|
'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
|
||||||
@ -618,29 +637,10 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
// Note clockwise 90° rotation (3π/2) confirmed in Joyce source
|
// Note clockwise 90° rotation (3π/2) confirmed in Joyce source
|
||||||
commands.push `${name} = Rotate(${direction}, 3*pi/2, ${center})`
|
commands.push `${name} = Rotate(${direction}, 3*pi/2, ${center})`
|
||||||
/proportion|similar/
|
/proportion|similar/
|
||||||
pt .= args.subpoints
|
[source, displacement] :=
|
||||||
unless pt then return
|
proportionSimilar method, args, cdata, aux, commands, auxiliaries
|
||||||
// reduce the similar case to general proportion
|
unless source then return
|
||||||
if method is 'similar'
|
commands.push `${name} = Translate(${source}, ${displacement})`
|
||||||
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})`
|
|
||||||
'vertex'
|
'vertex'
|
||||||
commands.push
|
commands.push
|
||||||
`${name} = Vertex(${args.polygon?[0]},${args.scalar?[0]})`
|
`${name} = Vertex(${args.polygon?[0]},${args.scalar?[0]})`
|
||||||
@ -653,6 +653,15 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
parts[1].push name
|
parts[1].push name
|
||||||
madeSegment .= false
|
madeSegment .= false
|
||||||
switch method
|
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'
|
'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
|
||||||
@ -662,7 +671,7 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
unless cr return
|
unless cr return
|
||||||
commands.push ...[1..2].map (n) =>
|
commands.push ...[1..2].map (n) =>
|
||||||
`${aux}${n} = Intersect(${cr[0]}, ${cr[1]}, ${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})`
|
ctr := cr.map (c) => `Center(${c})`
|
||||||
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})`)
|
||||||
@ -758,6 +767,14 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
commands.push
|
commands.push
|
||||||
`${aux}2 = Translate(${pt[0]}, ${dist}*${unitVec})`
|
`${aux}2 = Translate(${pt[0]}, ${dist}*${unitVec})`
|
||||||
else return
|
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
|
unless madeSegment
|
||||||
commands.push `${name} = Segment(${ends[0]},${ends[1]})`
|
commands.push `${name} = Segment(${ends[0]},${ends[1]})`
|
||||||
@ -768,9 +785,14 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
return := freshCommander()
|
return := freshCommander()
|
||||||
return.value.ends = ['', '']
|
return.value.ends = ['', '']
|
||||||
{commands, callbacks, parts, auxiliaries, ends} := return.value
|
{commands, callbacks, parts, auxiliaries, ends} := return.value
|
||||||
parts[2].push name
|
aux := name + 'aUx'
|
||||||
parts[1].push name
|
parts[1].push name
|
||||||
|
circle .= ''
|
||||||
switch method
|
switch method
|
||||||
|
'circumcircle'
|
||||||
|
pt := args.subpoints
|
||||||
|
unless pt and pt.length is 3 then return
|
||||||
|
circle = `Circle(${pt.join ','})`
|
||||||
'radius'
|
'radius'
|
||||||
pt := args.subpoints
|
pt := args.subpoints
|
||||||
unless pt then return
|
unless pt then return
|
||||||
@ -779,30 +801,37 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
when 2
|
when 2
|
||||||
[center, point] := pt
|
[center, point] := pt
|
||||||
ends[0] = center
|
ends[0] = center
|
||||||
commands.push
|
circle = `Circle(${center}, ${point}${inPlane})`
|
||||||
`${name} = Circle(${center}, ${point}${inPlane})`
|
|
||||||
when 3
|
when 3
|
||||||
center := pt[0]
|
center := pt[0]
|
||||||
ends[0] = center
|
ends[0] = center
|
||||||
radius := `Distance(${pt[1]}, ${pt[2]})`
|
radius := `Distance(${pt[1]}, ${pt[2]})`
|
||||||
commands.push
|
circle = `Circle(${center}, ${radius}${inPlane})`
|
||||||
`${name} = 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
|
callbacks.push (api: AppletObject) => api.setLabelVisible name, true
|
||||||
|
|
||||||
polygon: (name, method, args, index) =>
|
polygon: (name, method, args, index, cdata) =>
|
||||||
return := freshCommander()
|
return := freshCommander()
|
||||||
{commands, callbacks, parts, auxiliaries} := return.value
|
{commands, callbacks, parts, auxiliaries} := return.value
|
||||||
parts[2].push name
|
parts[2].push name
|
||||||
// what to push for edges?
|
aux := name + 'aUx'
|
||||||
switch method
|
switch method
|
||||||
'equilateralTriangle'
|
/equilateralTriangle|square|regularPolygon/
|
||||||
pt := args.subpoints
|
pt := args.subpoints
|
||||||
unless pt then return
|
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
|
commands.push '' // hack, make sure there is a command
|
||||||
parts[0].push pt[0], pt[1]
|
parts[0].push pt[0], pt[1]
|
||||||
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
||||||
made:= api.evalCommandGetLabels
|
made:= api.evalCommandGetLabels
|
||||||
`${name} = Polygon(${pt[1]},${pt[0]}, 3)`
|
`${name} = Polygon(${pt[0]},${pt[1]}, ${N})`
|
||||||
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
|
||||||
@ -814,10 +843,42 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
'point'
|
'point'
|
||||||
moreParts[0].push newObj
|
moreParts[0].push newObj
|
||||||
api.setVisible newObj, false
|
api.setVisible newObj, false
|
||||||
/triangle|quadrilateral/
|
/triangle|similar|parallelogram|application|quadrilateral|octagon/
|
||||||
pt := args.subpoints
|
unless args.subpoints then return
|
||||||
unless pt then return
|
pt .= args.subpoints
|
||||||
commands.push ''
|
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
|
parts[0].push ...pt
|
||||||
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
||||||
made := api.evalCommandGetLabels
|
made := api.evalCommandGetLabels
|
||||||
@ -836,32 +897,25 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
aux := name + 'aUx'
|
aux := name + 'aUx'
|
||||||
parts[2].push name
|
parts[2].push name
|
||||||
switch method
|
switch method
|
||||||
'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, 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[0] = start
|
||||||
ends[1] = end
|
ends[1] = end
|
||||||
parms := center + ', ' + start + ', ' + end
|
|
||||||
commands.push
|
commands.push
|
||||||
`${name} = CircularSector(${parms})`
|
`${name} = ${prefix}Sector(${parms})`
|
||||||
`${aux}1 = CircularArc(${parms})`
|
`${aux}1 = ${prefix}Arc(${parms})`
|
||||||
parts[1].push aux + 1
|
parts[1].push aux + 1
|
||||||
callbacks.push (api: AppletObject) =>
|
makeLinesInvisible callbacks, name
|
||||||
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
|
|
||||||
|
|
||||||
plane: (name, method, args) =>
|
plane: (name, method, args) =>
|
||||||
return := freshCommander()
|
return := freshCommander()
|
||||||
@ -913,3 +967,47 @@ classHandler: Record<JoyceClass, ClassHandler> :=
|
|||||||
moreParts[1].push newObj
|
moreParts[1].push newObj
|
||||||
'triangle'
|
'triangle'
|
||||||
moreParts[2].push newObj
|
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