feat: Improve construction element handling #32
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