feat: Improve construction element handling (#32)
This change implements several additional construction methods, including the first polygon ones. In particular, it now allows arbitrary strings as entity names, even ones that are not allowed as GeoGebra identifiers, using captions to show the original entity names. In addition, line arguments are interpreted as a pair of point arguments as needed. Resolves #6. Resolves #30. Resolves #31. Reviewed-on: #32 Co-authored-by: Glen Whitney <glen@studioinfinity.org> Co-committed-by: Glen Whitney <glen@studioinfinity.org>
This commit is contained in:
parent
550ce0168c
commit
bab48b25ad
BIN
public/Geometry.zip
Normal file
BIN
public/Geometry.zip
Normal file
Binary file not shown.
@ -8,6 +8,7 @@
|
|||||||
<h2>Joyce Geometry Applet</h2>
|
<h2>Joyce Geometry Applet</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li> <a href="inscribed-equilateral.html">Before</a> </li>
|
<li> <a href="inscribed-equilateral.html">Before</a> </li>
|
||||||
|
<li> <a href="inscribed-revived.html">Revived</a> </li>
|
||||||
<li> <a href="inscribed-modified.html">After</a> </li>
|
<li> <a href="inscribed-modified.html">After</a> </li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2>WRL Files</h2>
|
<h2>WRL Files</h2>
|
||||||
|
135
public/inscribed-revived.html
Normal file
135
public/inscribed-revived.html
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- fix buggy IE8, especially for mathjax -->
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">
|
||||||
|
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>An equilateral triangle inscribed in a rectangle</title>
|
||||||
|
<link rel="stylesheet" type="text/css" media="screen" href="style.css">
|
||||||
|
|
||||||
|
<script type="text/javascript"
|
||||||
|
src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML,http://userpages.umbc.edu/~rostamia/mathjax-config.js">
|
||||||
|
MathJax.Hub.Queue( function() {document.body.style.visibility="visible"} );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body style="visibility:hidden">
|
||||||
|
|
||||||
|
<h1>An equilateral triangle inscribed in a rectangle</h1>
|
||||||
|
|
||||||
|
<table class="centered">
|
||||||
|
<tr><td align="center">
|
||||||
|
<applet code="Geometry" archive="Geometry.zip" width="410" height="370">
|
||||||
|
<param name="background" value="ffffff">
|
||||||
|
<param name="title" value="An equilateral triangle inscribed in a rectangle">
|
||||||
|
|
||||||
|
<!-- the moving mechanism -->
|
||||||
|
<param name="e[1]" value="O;point;fixed;290,320">
|
||||||
|
<param name="e[2]" value="U1;point;fixed;510,320">
|
||||||
|
<param name="e[3]" value="V1;point;perpendicular;O,U1">
|
||||||
|
<param name="e[4]" value="U;point;angleDivider;U1,O,V1,3">
|
||||||
|
<param name="e[5]" value="V;point;angleDivider;V1,O,U1,3">
|
||||||
|
<param name="e[6]" value="circ1;circle;radius;O,U">
|
||||||
|
<param name="e[7]" value="li1;line;parallel;U,O,U1">
|
||||||
|
<param name="e[8]" value="li2;line;parallel;V,O,V1">
|
||||||
|
<param name="e[9]" value="W;point;intersection;li1,li2">
|
||||||
|
<param name="e[10]" value="VW;line;connect;V,W;0;0;lightGray">
|
||||||
|
<param name="e[11]" value="@;point;lineSegmentSlider;V,W,0,220;red;red">
|
||||||
|
<param name="e[12]" value="li3;line;parallel;@,O,U1">
|
||||||
|
<param name="e[13]" value="li4;line;chord;circ1,li3">
|
||||||
|
<param name="e[14]" value="X1;point;first;li4">
|
||||||
|
|
||||||
|
<!-- the triangle -->
|
||||||
|
<param name="e[15]" value="A;point;fixed;50,320">
|
||||||
|
<param name="e[16]" value="V2;point;perpendicular;A,U1">
|
||||||
|
<param name="e[17]" value="li5;line;parallel;A,O,X1">
|
||||||
|
<param name="e[18]" value="X2;point;last;li5">
|
||||||
|
<param name="e[19]" value="X;point;extend;A,X2,A,X2">
|
||||||
|
|
||||||
|
<param name="e[20]" value="tri1;polygon;equilateralTriangle;X,A">
|
||||||
|
<param name="e[21]" value="Y;point;vertex;tri1,3">
|
||||||
|
<param name="e[22]" value="B;point;midpoint;X,Y">
|
||||||
|
<param name="e[23]" value="ABC;polygon;equilateralTriangle;A,B">
|
||||||
|
<param name="e[24]" value="C;point;vertex;ABC,3">
|
||||||
|
|
||||||
|
<!-- the rectangle -->
|
||||||
|
<param name="e[25]" value="D;point;foot;B,A,U1">
|
||||||
|
<param name="e[26]" value="F;point;foot;C,A,V2">
|
||||||
|
<param name="e[27]" value="FE;line;parallel;F,A,D">
|
||||||
|
<param name="e[28]" value="E;point;last;FE">
|
||||||
|
<param name="e[29]" value="rect;polygon;quadrilateral;A,D,E,F;0;0;black;0">
|
||||||
|
<param name="e[30]" value="ADB;polygon;triangle;A,D,B;0;0;0;pink">
|
||||||
|
<param name="e[31]" value="ACF;polygon;triangle;A,C,F;0;0;0;pink">
|
||||||
|
<param name="e[32]" value="BCE;polygon;triangle;B,C,E;0;0;0;cyan">
|
||||||
|
|
||||||
|
</applet>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td>
|
||||||
|
<b>
|
||||||
|
Slide the “@” up and down to change the geometry.<br>
|
||||||
|
Press “r” to reset the diagram to its initial state.<br>
|
||||||
|
Proposition: The blue area equals the sum of the two pink areas.
|
||||||
|
</b>
|
||||||
|
</td></tr></table>
|
||||||
|
|
||||||
|
<h2>Problem statement</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The diagram above shows an equilateral triangle inscribed in a rectangle
|
||||||
|
in such a way that the two have a vertex in common. This subdivides the
|
||||||
|
rectangle into four disjoint triangles.
|
||||||
|
The original equilateral triangle is shown in white
|
||||||
|
in the diagram; the other three are shown in color.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Proposition</b>
|
||||||
|
<em>
|
||||||
|
The area of the blue triangle equals the sum
|
||||||
|
of the areas of the two pink triangles.
|
||||||
|
</em>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The trigonometric proof is quite straightforward. I don't
|
||||||
|
know of a classical proof <i>a la</i> <span class="name">Euclid</span>.
|
||||||
|
(Well, actually I haven't tried much.)
|
||||||
|
If you can think of a neat non-trigonometric proof, let me know. I will
|
||||||
|
put it here with due credit.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This problem appeared as a conjecture
|
||||||
|
<a href="http://mathforum.org/kb/thread.jspa?forumID=129&messageID=1083967">in an article</a>
|
||||||
|
in the <code>geometry.puzzles</code> newsgroup on March 15, 1997.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>Note added January 8, 2017:</b>
|
||||||
|
Here is a
|
||||||
|
<a href="inscribed-equilateral-solution.html">clever solution</a>
|
||||||
|
that <b>Peter Renz</b> sent me a in December 2016. Thanks, Peter!
|
||||||
|
|
||||||
|
<hr width="60%">
|
||||||
|
<p>
|
||||||
|
<em>This applet was created by
|
||||||
|
<a href="http://userpages.umbc.edu/~rostamia">Rouben Rostamian</a>
|
||||||
|
using
|
||||||
|
<a href="http://aleph0.clarku.edu/~djoyce/home.html">David Joyce</a>'s
|
||||||
|
<a href="http://aleph0.clarkU.edu/~djoyce/java/Geometry/Geometry.html">Geometry
|
||||||
|
Applet</a>
|
||||||
|
on July 2, 2010.
|
||||||
|
</em>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<td valign="top">Go to <a href="index.html">Geometry Problems and Puzzles</a></td>
|
||||||
|
<td align="right" style="width:200px;">
|
||||||
|
<a href="http://validator.w3.org/check?uri=referer">
|
||||||
|
<img src="/~rostamia/images/valid-html401.png" class="noborder" width="88" height="31" alt="Valid HTML"></a>
|
||||||
|
<a href="http://jigsaw.w3.org/css-validator/check/referer">
|
||||||
|
<img src="/~rostamia/images/valid-css.png" class="noborder" width="88" height="31" alt="Valid CSS"></a>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -16,6 +16,30 @@ $('applet[code="Geometry"]').before (i, html) ->
|
|||||||
height: parseInt(this.getAttribute('height') ?? '200') }
|
height: parseInt(this.getAttribute('height') ?? '200') }
|
||||||
`<div id="${id}"></div>`
|
`<div id="${id}"></div>`
|
||||||
|
|
||||||
|
type Split<S extends string>
|
||||||
|
S extends `${infer W} ${infer R}` ? (W | Split<R>) : S
|
||||||
|
|
||||||
|
classes := 'point line circle polygon sector plane sphere polyhedron'
|
||||||
|
type JoyceClass = Split<typeof classes>
|
||||||
|
function assertJoyceClass(s: string): asserts s is JoyceClass
|
||||||
|
unless classes.includes s then throw new Error `Oops ${s} slipped through`
|
||||||
|
|
||||||
|
type JoyceName = string // we use this to indicate where the names
|
||||||
|
// from the Joyce commands (which are used as captions in the GeoGebra
|
||||||
|
// applet) go.
|
||||||
|
type GeoName = string // and this to indicate where GeoGebra identifiers go
|
||||||
|
type AnyName = GeoName | JoyceName // and this for slots that can be either
|
||||||
|
|
||||||
|
type Description
|
||||||
|
otherName: AnyName
|
||||||
|
usesCaptions: JoyceName[]
|
||||||
|
klass: JoyceClass
|
||||||
|
ends?: [GeoName, GeoName]
|
||||||
|
|
||||||
|
// We put both JoyceNames and GeoNames in here, pointing to each other
|
||||||
|
// with the otherName property:
|
||||||
|
type JoyceElements = Record<AnyName, Description>
|
||||||
|
|
||||||
jQuery.getScript 'https://www.geogebra.org/apps/deployggb.js', =>
|
jQuery.getScript 'https://www.geogebra.org/apps/deployggb.js', =>
|
||||||
for each jApp of joyceApplets
|
for each jApp of joyceApplets
|
||||||
params := {
|
params := {
|
||||||
@ -23,100 +47,325 @@ jQuery.getScript 'https://www.geogebra.org/apps/deployggb.js', =>
|
|||||||
jApp.width,
|
jApp.width,
|
||||||
jApp.height,
|
jApp.height,
|
||||||
appletOnLoad: (api: AppletObject) =>
|
appletOnLoad: (api: AppletObject) =>
|
||||||
|
elements: JoyceElements := {}
|
||||||
for child of jApp.children
|
for child of jApp.children
|
||||||
dispatchJcommand api, child
|
dispatchJcommand api, child, elements
|
||||||
api.setCoordSystem(-10, 10 + jApp.width, -10, 10 + jApp.height)
|
api.setCoordSystem(-10, 10 + jApp.width, -10, 10 + jApp.height)
|
||||||
} as const
|
} as const
|
||||||
geoApp := new GGBApplet params
|
geoApp := new GGBApplet params
|
||||||
geoApp.inject jApp.id
|
geoApp.inject jApp.id
|
||||||
|
|
||||||
type GeogebraCallback = (api: AppletObject) => void
|
type DimParts = [string[], string[], string[]] // Gives GeoNames
|
||||||
|
// or expressions for 0-, 1-, and 2-dimensional parts for coloring
|
||||||
|
|
||||||
|
// need to pass the parts into the callbacks because sometimes the parts
|
||||||
|
// are not generated until callback time
|
||||||
|
type GeogebraCallback = (api: AppletObject, parts: DimParts) => void
|
||||||
type Commander
|
type Commander
|
||||||
command: string
|
commands: string[]
|
||||||
callbacks: GeogebraCallback[]
|
callbacks: GeogebraCallback[]
|
||||||
|
parts: DimParts
|
||||||
|
auxiliaries: GeoName[] // extra entities needed in GeoGebra
|
||||||
|
ends?: [GeoName, GeoName]
|
||||||
|
|
||||||
|
function freshCommander(): Commander
|
||||||
|
commands: []
|
||||||
|
callbacks: []
|
||||||
|
parts: [[], [], []]
|
||||||
|
auxiliaries: []
|
||||||
|
|
||||||
|
type JoyceArguments =
|
||||||
|
Partial<Record<JoyceClass|'subpoints', GeoName[]> & {scalar: number[]}>
|
||||||
|
|
||||||
type ClassHandler = (
|
type ClassHandler = (
|
||||||
name: string, m: string, data: string, colors: string[]) => Commander
|
name: GeoName, m: string, args: JoyceArguments, index: number) => Commander
|
||||||
|
|
||||||
function dispatchJcommand(api: AppletObject, param: Element): void
|
// Executes the command corresponding to param against the GeoGebra applet
|
||||||
|
// api, consulting and extending by side effect the elements that are
|
||||||
|
// present in that applet
|
||||||
|
function dispatchJcommand(
|
||||||
|
api: AppletObject, param: Element, elements: JoyceElements): void
|
||||||
val := param.getAttribute 'value'
|
val := param.getAttribute 'value'
|
||||||
unless val return
|
unless val return
|
||||||
switch param.getAttribute 'name'
|
attr := param.getAttribute 'name'
|
||||||
|
switch attr
|
||||||
'background'
|
'background'
|
||||||
api.setGraphicsOptions 1, bgColor: `#${val}`
|
api.setGraphicsOptions 1, bgColor: `#${val}`
|
||||||
'title'
|
'title'
|
||||||
api.evalCommand `TitlePoint = Corner(1,1)
|
api.evalCommand `TitlePoint = Corner(1,1)
|
||||||
Text("${val}", TitlePoint + (2,5))`
|
Text("${val}", TitlePoint + (2,5))`
|
||||||
/e\[\d+\]/
|
/e\[\d+\]/
|
||||||
{command, callbacks} := jToG val
|
num := parseInt(attr.slice(2))
|
||||||
if command
|
{commands, callbacks, parts} := jToG val, elements, num
|
||||||
if api.evalCommand command
|
if commands.length
|
||||||
for each cb of callbacks
|
lastTried .= 0
|
||||||
cb(api)
|
if commands.filter((&)).every (cmd) =>
|
||||||
else
|
api.evalCommand(cmd) and ++lastTried
|
||||||
console.log `Geogebra command '${command}' translated from`,
|
callbacks.forEach &(api, parts)
|
||||||
val, 'failed.'
|
else console.log
|
||||||
else
|
`Geogebra command '${commands[lastTried]}'
|
||||||
console.log `Could not parse command '${val}'`
|
(part of translation of '${val}')
|
||||||
else
|
failed.`
|
||||||
console.log `Unkown param ${param}`
|
else console.log `Could not parse command '${val}'`
|
||||||
|
else console.log `Unkown param ${param}`
|
||||||
|
|
||||||
function jToG(jCom: string): Commander
|
// function myListener(...args: unknown[]) {
|
||||||
[name, klass, method, data, ...colors] := jCom.split(';')
|
// console.log 'In my listener with', args
|
||||||
if klass in classHandler
|
// }
|
||||||
return classHandler[klass] name, method, data, colors
|
|
||||||
|
// window.myListener = myListener
|
||||||
|
|
||||||
|
// Parses a Joyce element-creating command, extending the elements
|
||||||
|
// by side effect:
|
||||||
|
function jToG(jCom: string, elements: JoyceElements, index: number): Commander
|
||||||
|
[jname, klass, method, data, ...colors] := jCom.split(';')
|
||||||
|
cmdr .= freshCommander()
|
||||||
|
unless klass in classHandler
|
||||||
console.log `Unknown entity class ${klass}`
|
console.log `Unknown entity class ${klass}`
|
||||||
command: '', callbacks: []
|
return cmdr
|
||||||
|
assertJoyceClass klass // shouldn't need to do that :-/
|
||||||
|
name := if /^\p{L}\w*$/u.test jname then jname else geoname jname, elements
|
||||||
|
args: JoyceArguments := {}
|
||||||
|
usesCaptions := []
|
||||||
|
for each jdep of data.split ','
|
||||||
|
scalar := parseFloat jdep
|
||||||
|
if scalar is scalar // not NaN
|
||||||
|
(args.scalar ?= []).push scalar
|
||||||
|
continue
|
||||||
|
unless jdep in elements
|
||||||
|
console.log `Reference to unknown geometric entity ${jdep} in $jCom}`
|
||||||
|
return cmdr
|
||||||
|
usesCaptions.push jdep
|
||||||
|
{klass: depKlass, otherName: depGeo, ends} := elements[jdep]
|
||||||
|
(args[depKlass] ?= []).push depGeo
|
||||||
|
if depKlass is 'point'
|
||||||
|
(args.subpoints ?= []).push depGeo
|
||||||
|
else if depKlass is 'line'
|
||||||
|
(args.subpoints ?= []).push ...ends ?? []
|
||||||
|
cmdr = classHandler[klass] name, method, args, index
|
||||||
|
unless name is jname then cmdr.callbacks.push (api: AppletObject) =>
|
||||||
|
api.setCaption(name, jname)
|
||||||
|
api.setLabelStyle(name, 3) // style CAPTION = 3
|
||||||
|
if cmdr.auxiliaries.length
|
||||||
|
cmdr.callbacks.push (api: AppletObject) =>
|
||||||
|
for each aux of cmdr.auxiliaries
|
||||||
|
api.setAuxiliary(aux, true)
|
||||||
|
api.setVisible(aux,false)
|
||||||
|
// Create callback to assign colors
|
||||||
|
if colors.length is 4 and colors.every (color) =>
|
||||||
|
false and color is '0' or color is 'none'
|
||||||
|
cmdr.callbacks.push (api: AppletObject) =>
|
||||||
|
api.setVisible(name, false)
|
||||||
|
// window[hideListener] = (arg) =>
|
||||||
|
// console.log('Hello', arg, 'disappearing', name)
|
||||||
|
// api.setVisible(name, false)
|
||||||
|
api.registerObjectUpdateListener(name, hideListener)
|
||||||
|
if cmdr.ends or klass is 'line'
|
||||||
|
elements[jname] =
|
||||||
|
{otherName: name, usesCaptions, klass: 'line', cmdr.ends}
|
||||||
|
elements[name] =
|
||||||
|
{otherName: jname, usesCaptions, klass: 'line', cmdr.ends}
|
||||||
|
else // any other geometry
|
||||||
|
elements[jname] = {otherName: name, usesCaptions, klass}
|
||||||
|
elements[name] = {otherName: jname, usesCaptions, klass}
|
||||||
|
cmdr
|
||||||
|
|
||||||
classHandler: Record<string, ClassHandler> :=
|
function geoname(jname: JoyceName, elements: JoyceElements): GeoName
|
||||||
point: (name, method, data, colors) =>
|
numCode .= 0n
|
||||||
command .= ''
|
numCode = numCode*128n + BigInt ch.codePointAt(0) ?? 1 for each ch of jname
|
||||||
callbacks: GeogebraCallback[] .= []
|
return .= 'Geo' + numCode.toString(36);
|
||||||
args := data.split(',')
|
return += '1' while return.value in elements
|
||||||
|
|
||||||
|
// All of the detailed semantics of each available command lies in this
|
||||||
|
// function.
|
||||||
|
classHandler: Record<JoyceClass, ClassHandler> :=
|
||||||
|
point: (name, method, args): Commander =>
|
||||||
|
return := freshCommander()
|
||||||
|
{commands, callbacks, parts, auxiliaries} := return.value
|
||||||
|
aux := name + 'aUx'
|
||||||
|
parts[0].push name
|
||||||
switch method
|
switch method
|
||||||
/free|fixed/
|
/free|fixed/
|
||||||
command += `${name} = (${data})`
|
commands.push `${name} = (${args.scalar?.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)
|
||||||
'perpendicular'
|
'perpendicular'
|
||||||
[center, direction] := args
|
// Note only the two-point option implemented so far
|
||||||
command += `${name} = Rotate(${direction}, 3*pi/2, ${center})`
|
unless args.subpoints return
|
||||||
|
[center, direction] := args.subpoints
|
||||||
|
// Note clockwise 90° rotation (3π/2) confirmed in Joyce source
|
||||||
|
commands.push `${name} = Rotate(${direction}, 3*pi/2, ${center})`
|
||||||
'angleDivider'
|
'angleDivider'
|
||||||
n .= -1
|
// Note doesn't yet handle plane argument
|
||||||
// use the fact that NaN doesn't equal itself:
|
unless args.subpoints return
|
||||||
nLoc := args.findIndex((arg) => (n = parseInt arg) is n)
|
[start, center, end] := args.subpoints
|
||||||
if n >= 0
|
// see if we need to make the destination segment from start to end
|
||||||
args.splice(nLoc)
|
destination .= ''
|
||||||
[center, start, end] := args
|
unless args.line?.length is 1 and args.point?[0] is center
|
||||||
command += `${name}aUx1 = Segment(${start}, ${end})
|
destination = aux + '1'
|
||||||
${name}aUx2 = Angle(${start}, ${center}, ${end})
|
auxiliaries.push destination
|
||||||
${name}aUx2a = If(${name}aUx2 > pi, ${name}aUx2 - 2*pi, ${name}aUx2)
|
commands.push `${destination} = Segment(${start}, ${end})`
|
||||||
${name}aUx3 = Rotate(${start}, ${name}aUx2a/${n}, ${center})
|
else destination = args.line[0]
|
||||||
${name}aUx4 = Ray(${center}, ${name}aUx3)
|
n := args.scalar?[0]
|
||||||
${name} = Intersect(${name}aUx1, ${name}aUx4)`
|
commands.push
|
||||||
|
`${aux}2 = Angle(${start}, ${center}, ${end})`
|
||||||
|
`${aux}3 = If(${aux}2 > pi, ${aux}2 - 2*pi, ${aux}2)`
|
||||||
|
`${aux}4 = Rotate(${start}, ${aux}3/${n}, ${center})`
|
||||||
|
`${name} = Intersect(${destination}, Ray(${center}, ${aux}4))`
|
||||||
|
auxiliaries.push ...[2..4].map (i) => `${aux}${i}`
|
||||||
'intersection'
|
'intersection'
|
||||||
command += `${name} = Intersect(${data})`
|
// Checking Joyce source, means intersection of lines, not
|
||||||
return {command, callbacks}
|
// intersection of line segments
|
||||||
|
unless args.subpoints then return
|
||||||
|
l1 := `Line(${args.subpoints[0]},${args.subpoints[1]})`
|
||||||
|
l2 := `Line(${args.subpoints[2]},${args.subpoints[3]})`
|
||||||
|
commands.push `${name} = Intersect(${l1},${l2})`
|
||||||
|
'lineSegmentSlider'
|
||||||
|
segment .= args.line?[0]
|
||||||
|
unless segment
|
||||||
|
unless args.point then return
|
||||||
|
commands.push `${aux} = Segment(${args.point.join ','})`
|
||||||
|
auxiliaries.push aux
|
||||||
|
segment = aux
|
||||||
|
commands.push `${name} = Point(${segment})`
|
||||||
|
if args.scalar and args.scalar.length
|
||||||
|
callbacks.push (api: AppletObject) =>
|
||||||
|
api.setCoords name,
|
||||||
|
...args.scalar as [number, number, number]
|
||||||
|
'first'
|
||||||
|
unless args.subpoints then return
|
||||||
|
commands.push `${name} = ${args.subpoints[0]}`
|
||||||
|
'last'
|
||||||
|
unless args.subpoints then return
|
||||||
|
commands.push `${name} = ${args.subpoints.at(-1)}`
|
||||||
|
'extend'
|
||||||
|
unless args.subpoints then return
|
||||||
|
sp := args.subpoints
|
||||||
|
direction .= `UnitVector(Vector(${sp[0]},${sp[1]}))`
|
||||||
|
if args.line and (
|
||||||
|
not args.point or args.point[0] !== args.subpoints[0])
|
||||||
|
direction = `UnitVector(${args.line[0]})`
|
||||||
|
displacement := `Distance(${sp[2]}, ${sp[3]})*${direction}`
|
||||||
|
commands.push `${name} = Translate(${sp[1]}, ${displacement})`
|
||||||
|
'vertex'
|
||||||
|
commands.push
|
||||||
|
`${name} = Vertex(${args.polygon?[0]},${args.scalar?[0]})`
|
||||||
|
'midpoint'
|
||||||
|
if args.line
|
||||||
|
commands.push `${name} = Midpoint(${args.line[0]})`
|
||||||
|
else
|
||||||
|
commands.push
|
||||||
|
`${name} = Midpoint(${args.point?[0]},${args.point?[1]})`
|
||||||
|
'foot'
|
||||||
|
pt := args.subpoints
|
||||||
|
unless pt then return
|
||||||
|
commands.push
|
||||||
|
`${name} = ClosestPoint(Line(${pt[1]},${pt[2]}), ${pt[0]})`
|
||||||
|
|
||||||
line: (name, method, data, colors) =>
|
line: (name, method, args) =>
|
||||||
command .= ''
|
return := freshCommander()
|
||||||
callbacks: GeogebraCallback[] .= []
|
return.value.ends = ['', '']
|
||||||
args := data.split(',')
|
{commands, callbacks, parts, auxiliaries, ends} := return.value
|
||||||
|
aux := name + 'aUx'
|
||||||
|
parts[1].push name
|
||||||
|
madeSegment .= false
|
||||||
switch method
|
switch method
|
||||||
'connect'
|
'connect'
|
||||||
command += `${name} = Segment(${data})`
|
unless args.subpoints and args.subpoints.length is 2 then return
|
||||||
|
ends[0] = args.subpoints[0]
|
||||||
|
ends[1] = args.subpoints[1]
|
||||||
'parallel'
|
'parallel'
|
||||||
[newStart, oldStart, oldEnd] := args
|
unless args.subpoints then return
|
||||||
command += `${name}aUx1 = Vector(${oldStart}, ${newStart})
|
[newStart, oldStart, oldEnd] := args.subpoints
|
||||||
${name}aUx2 = Translate(${oldEnd}, ${name}aUx1)
|
commands.push `${aux}1 = Vector(${oldStart}, ${newStart})`
|
||||||
${name} = Segment(${newStart}, ${name}aUx2)`
|
auxiliaries.push aux + 1, aux + 2
|
||||||
return {command, callbacks}
|
ends[0] = newStart
|
||||||
|
ends[1] = aux + 2
|
||||||
|
if args.line?.length is 1 and args.point?[0] is args.subpoints[0]
|
||||||
|
// In this case we are translating an existing segment
|
||||||
|
commands.push
|
||||||
|
`${name} = Translate(${args.line[0]}, ${aux}1)`
|
||||||
|
`${aux}2 = Vertex(${name}, 2)`
|
||||||
|
madeSegment = true
|
||||||
|
else
|
||||||
|
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
|
||||||
|
commands.push `${name} = Segment(${ends[0]},${ends[1]})`
|
||||||
|
callbacks.push (api: AppletObject) =>
|
||||||
|
api.setLabelVisible(name, true)
|
||||||
|
parts[0].push ...ends
|
||||||
|
|
||||||
circle: (name, method, data, colors) =>
|
circle: (name, method, args) =>
|
||||||
command .= ''
|
return := freshCommander()
|
||||||
callbacks: GeogebraCallback[] .= []
|
{commands, callbacks, parts, auxiliaries} := return.value
|
||||||
|
parts[2].push name
|
||||||
|
parts[1].push name
|
||||||
switch method
|
switch method
|
||||||
'radius'
|
'radius'
|
||||||
[center, point] := data.split(',')
|
unless args.subpoints then return
|
||||||
command += `${name} = Circle(${center}, ${point})`
|
[center, point] := args.subpoints
|
||||||
return {command, callbacks}
|
commands.push `${name} = Circle(${center}, ${point})`
|
||||||
|
callbacks.push (api: AppletObject) => api.setLabelVisible(name, true)
|
||||||
|
|
||||||
|
polygon: (name, method, args, index) =>
|
||||||
|
return := freshCommander()
|
||||||
|
{commands, callbacks, parts, auxiliaries} := return.value
|
||||||
|
parts[2].push name
|
||||||
|
// what to push for edges?
|
||||||
|
switch method
|
||||||
|
'equilateralTriangle'
|
||||||
|
pt := args.subpoints
|
||||||
|
unless pt then return
|
||||||
|
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)`
|
||||||
|
if not made return
|
||||||
|
for each obj of made.split ','
|
||||||
|
if obj is name continue
|
||||||
|
newObj := 'GeoAux' + index + obj
|
||||||
|
api.renameObject obj, newObj
|
||||||
|
switch api.getObjectType newObj
|
||||||
|
'segment'
|
||||||
|
parts[1].push newObj
|
||||||
|
'point'
|
||||||
|
parts[0].push newObj
|
||||||
|
api.setVisible(newObj, false)
|
||||||
|
/triangle|quadrilateral/
|
||||||
|
pt := args.subpoints
|
||||||
|
unless pt then return
|
||||||
|
commands.push ''
|
||||||
|
parts[0].push ...pt
|
||||||
|
callbacks.push (api: AppletObject, moreParts: DimParts) =>
|
||||||
|
made := api.evalCommandGetLabels
|
||||||
|
`${name} = Polygon(${pt.join ','})`
|
||||||
|
if not made return
|
||||||
|
for each obj of made.split ','
|
||||||
|
if obj is name continue
|
||||||
|
newObj := 'GeoAux' + index + obj
|
||||||
|
api.renameObject obj, newObj
|
||||||
|
parts[1].push newObj
|
||||||
|
|
||||||
|
sector: (name, method, args) => freshCommander()
|
||||||
|
plane: (name, method, args) => freshCommander()
|
||||||
|
sphere: (name, method, args) => freshCommander()
|
||||||
|
polyhedron: (name, method, args) => freshCommander()
|
||||||
|
@ -47,7 +47,7 @@ function makeBrowser(url: string)
|
|||||||
canvas
|
canvas
|
||||||
|
|
||||||
// Put eye icons after all of the eligible links
|
// Put eye icons after all of the eligible links
|
||||||
links := $('a').filter -> !!@.getAttribute('href')?.match knownExtensions
|
links := $('a').filter -> knownExtensions.test @.getAttribute('href') ?? ''
|
||||||
links.after ->
|
links.after ->
|
||||||
newSpan := $('<span>👁</span>')
|
newSpan := $('<span>👁</span>')
|
||||||
newSpan.hover
|
newSpan.hover
|
||||||
|
Loading…
Reference in New Issue
Block a user