Compare commits

...

10 Commits
v0.1.0 ... main

12 changed files with 302 additions and 34 deletions

1
.gitignore vendored
View File

@ -4,4 +4,5 @@ docbuild
__tests__
coverage
dyna3.zip
tmpproj
*~

63
Makefile Normal file
View File

@ -0,0 +1,63 @@
GENEXT = src/helpers/pkglock_to_externals.litcoffee
GENDOC = doc/gendoc.litcoffee
DOCFILES = $(shell coffee $(GENDOC) -list)
COFFILES = $(wildcard src/*.litcoffee)
JSFILES = $(patsubst src/%.litcoffee,site/%.js,$(COFFILES))
HTMLSRC = $(wildcard src/*.html)
HTMLSITE = $(patsubst src/%,site/%,$(HTMLSRC))
BLDTARGS = $(JSFILES) site/externals.js site/doc/dyna3.html $(HTMLSITE) site/node_modules
TESTCOFF = $(wildcard coffeetest/*.coffee)
TESTJS = $(patsubst coffeetest/%.coffee,__tests__/%.js,$(TESTCOFF))
build: $(BLDTARGS)
site/externals.js: $(GENEXT) package.json package-lock.json
mkdir -p site
coffee $(GENEXT) > $@
site/%.js: src/%.litcoffee
coffee -o $@ -c -m $<
docbuild/extlist.md: $(GENEXT) package.json package-lock.json
mkdir -p docbuild
coffee $(GENEXT) --doc > $@
site/doc/dyna3.md: $(DOCFILES)
mkdir -p site/doc
coffee $(GENDOC) > $@
site/doc/dyna3.html: site/doc/dyna3.md
pandoc -s --metadata title='dyna3' --toc $< > $@
site/%.html: src/%.html
mkdir -p site
cp $< $@
site/node_modules: node_modules package.json package-lock.json
rm -rf tmpproj
mkdir tmpproj
mkdir -p site
cp package.json package-lock.json tmpproj
cd tmpproj && npm install --production
cp -r tmpproj/node_modules site
touch site/node_modules
dyna3.zip: $(BLDTARGS)
zip -r $@ site
__tests__/%.js: coffeetest/%.coffee
coffee -o $@ -c -m $<
$(BLDTARGS) $(TESTJS): Makefile
doc: site/doc/dyna3.md
dist: dyna3.zip
pretest: $(BLDTARGS) $(TESTJS)
test: $(BLDTARGS) $(TESTJS)
npm test
clean:
rm -rf site docbuild tmpproj __tests__ coverage dyna3.zip

View File

@ -9,3 +9,11 @@ Constraint-based three-dimensional dynamic geometry
From a thorough web search, there does not seem to be a dynamic geometry software package which (a) began its life handling three dimensions, rather than just two, and (b) allows you to express the desired geometric configuration in terms of constraints on the entities (e.g. l and k are parallel, a, b, and c a collinear, etc.) rather than as a construction (e.g. l is the perpendicular bisector of a and b). The goal of the dyna3 project is to close this gap.
Note that currently this is just the barest beginnings of the project, more of a framework for developing dyna3 rather than anything useful.
### Implementation goals
* Comfortable, intuitive UI
* Able to run in browser (so implemented in WASM-compatible language)
* Produce scalable graphics of 3D diagrams, and maybe STL files (or other fabricatable file format) as well.

View File

@ -2,6 +2,16 @@
```javascript
fs = require 'fs'
docsrcs = [
'README.md', 'src/dyna3.litcoffee',
'src/helpers/pkglock_to_externals.litcoffee', 'docbuild/extlist.md',
'doc/tech.md', 'doc/gendoc.litcoffee'
]
if process.argv.length > 2
process.stdout.write docsrcs.join ' '
process.stdout.write "\n"
process.exit 0
striplit = (text) -> String(text).replace(/^```javascript[^]*?```\n/mg,'')
incfile = (fn, filt = null) ->
if (filt is null)
@ -9,9 +19,5 @@
else filt = (x)->x
process.stdout.write filt fs.readFileSync fn
process.stdout.write "\n"
incfile f for f in [
'README.md', 'src/dyna3.litcoffee',
'src/helpers/pkglock_to_externals.litcoffee', 'docbuild/extlist.md',
'doc/tech.md', 'doc/gendoc.litcoffee' ]
incfile f for f in docsrcs
```

View File

@ -2,8 +2,9 @@
The intial modest outline for bootstrapping dyna3 is as follows:
- dyna3 will be an npm project, but as it runs entirely client-side, there's no initial concern for being able to run under Node.js. However, npm will be useful as a dependency manager and build tool. In addition, helper scripts for the build can be run with node (via coffee).
- The package will build into a top-level site/ directory, which will contain all and only the files that must be on a server to deliver dyna3 to a client browser. Everything in site/ will generated from the package files; nothing in site/ will be committed to the repository. Right now the planned contents a of site/ are just a handful of top-level files, the modules/ directory for in-package javascript, the /doc directory for generated documentation, and a copy of the node_modules/ directory as a backup for the external dependencies.
- dyna3 will be an npm project, but as it runs entirely client-side, there's no initial concern for being able to run under Node.js. However, npm will be useful as a dependency manager. In addition, helper scripts for the build can be run with node (via coffee).
- For a build tool, we are just going to go with the venerable "make." I tried using npm scripts, and they can handle the necessary operations, but they have no notion of a "target being up to date" like make does, so I was constantly re-running unnecessary/redundant pieces of the process, which was frustrating.
- The package will build into a top-level site/ directory, which will contain all and only the files that must be on a server to deliver dyna3 to a client browser. Everything in site/ will generated from the package files; nothing in site/ will be committed to the repository. Right now the planned contents a of site/ are just a handful of top-level files, the modules/ directory for in-package javascript, the /doc directory for generated documentation, and a copy of the production node_modules/ directory as a backup for the external dependencies.
- Literate coffeescript modules (in the src/modules directory except for the top-level application) will compile into individual js modules in the site/modules directory (again, except for the top-level application)
- Why coffeescript? The motivation for the javascript family of languages is wide deployability and availability of numerous libraries; the motivation for coffeescript is that it (still) reduces the amount of punctuation and extraneous characters from Javascript, resulting in better readability. And Literate coffeescript so that the manual and code are co-located.
- All modules will use ES Modules syntax/format for imports.

59
notes/design.md Normal file
View File

@ -0,0 +1,59 @@
#### Design and Trajectory
The development goal is to get some sort of end-to-end system doing something as quickly as possible.
To that end, we should likely start with only points, planes, and incidence constraints. That will let us make some simple polyhedra; not sure if there are any challenging problems with just those elements/constraints. If not, it's probably good, because we could get away with just rational numbers as our underlying mathematical space and a toy handwritten solver. It will also be a good start on gluing different components together to make a working system.
Speaking of components, here are some different pieces we need to consider:
##### Language
Top candidates include C++, likely with a syntactic sugar preprocessor to make coding in it bearable, compiled into WASM via Emscripten, and Lobster, a significant-whitespace language with an interesting type system that appears on the Awesome WASM Languages list (link should go here).
Other candidates include Civet (as it's already very comfortable to code in, but it mires us in the JavaScript-family morass), and Python (also very comfortable for coding, speed might not be an issue if we're mostly using other optimized components, but not sure how mature WASM compilation is).
##### Display and Interaction
Possibilities include three.js, D3.js, writing `<x3d>` elements to the DOM and then using either x3DOM or X_ITE, or direct WebGL programming. See also possibly ganja.js, mentioned in the coordinate representation as well.
##### User Interface
The goal is to take the best parts of the UIs of GeoGebra, Geometry Expressions, LibreCAD, and FreeCAD. Some lessons therefrom:
* Multi-selection should be effortless, probably click to toggle whether something is in the current selection -- but if so, need a really easy gesture to unselect everything.
* Modality: Should tools reset themselves after one use, or persist until the tool is canceled? One option is have the idiom that "select operands, then select tool" is a one-shot use of the tool, whereas selecting a tool when there are no operands selected "activates" that tool until it is "deactivated" by selecting another tool or deselecting that tool.
* Parallelism: Every operation should be equally invocable either by a menu item, toolbar button (if it is configured to be on one), keystroke (ditto), or textual "command" (e.g., in some functional notation). The menu layout should be fixed and comprehensive; the textual commands should be internationalized and comprehensive; and the program should offer a default toolbar layout and keyboard shortcuts that need not be comprehensive and which should be easily configurable.
##### Solver
SolveSpace could well provide a general numeric solver that would work for us. There is a new Julia-based computer algebra system OSCAR that could be of interest. Sage may have pieces we can use. There may be generic Gröbner basis implementations out there we can use.
A closely related question is the representation of numbers that appear in coordinates. We can start with exact rationals, which should be implemented or easy to implement in whatever language we choose. But as soon as we have any quadratic constraints (like equal distances) we will need at least quadratic algebraic numbers if not arbitrary algebraic numbers, and a package to represent/manipulate those will be needed.
##### Coordinate Representation
Besides just the representation of numbers, we have to decide how to coordinatize various geometric entities. The default starting place is just triples of numbers for points in $\mathbb{R}^3$, but we may quickly decide to use more elaborate coordinates that allow more operations or entities to be easily coordinatized. For example, many systems use homogeneous coordinates with one extra dimension so that all rigid motions and scalings can be represented as (in our case) 4×4 matrices. Systems that have been suggested are inversive coordinates (Alex Kontorovich, see [inversive.md](inversive.md)) and various Geometric Algebras (like Clifford Algebras and many variants). Related to this last operation, see ganja.js, which could also bear on the the display/interaction item above as well.
###### Representing lines
In general it seems in 3D it's more comfortable to represent planes and points than lines. There are appear to be numerous options, not clear if any are really perfect or canonical:
* [Equivalence classes of] pairs of points
* [Equivalence classes of] pairs of planes
* A bag of arbitrarily many collinear points [of course this is still equivalence classes, but the practical computational aspect is that when there's another point of interest that turns out to be on the line, you just throw it in the bag.]
* Plücker coordinates
* A plane through the origin and a point on it. The line is perpendicular to that plane and goes through that point. This representation at least seems to be 1-1. Easy to tell if lines are parallel. Maybe not as easy to tell if they intersect, but not particularly worse than pairs of points, for example.
* The unit normal vector of the plane from the last option, but projected to the xy-plane, and that point of intersection, but projected to the xy-plane, so that there are just four numbers corresponding to the four-dimensionality of the space of lines. This representation has some discontinuities: very close lines might be represented by faraway coordinates, and (partly as a result) it might be tricky to compute with in general.
###### Choice of coordinatization
Note that ultimately the choice of coordinates for entities serves solely to facilitate (a) display and manipulation of geometric objects, and (b) expressing and solving the constraints that are put on those entities. So we want to steer toward the choices that make those tasks the easiest. We won't necessarily have direct coordinates for every type of entity that Dyna3 allows to be created. Some may be "derived entities" that serve only as "front ends" for the actual items that Dyna3's internals are processing. For example, suppose we choose a system that provides direct parametrizations of spheres and planes (see e.g. [inversive.md](inversive.md)). Perhaps then when the user creates a circle, Dyna3 will actually create an auxiliary sphere and plane, and the auxiliary constraint that the plane contains the center of the circle. These auxiliaries would be marked so that only their intersection would be displayed, producing an arbitrary circle.
The sort of scheme outlined in the previous paragraph works particularly well when it is easy to express the sorts of constraints one wants on the derived entity in terms of the supported constraints on the auxiliary entities. So for example, we want to be able to constrain a circle to contain a given point -- that translates just to both the plane and the sphere contain that point, so that works well. We also want to be able to say that a line and circle are tangent. This translates to the line being in the associated plane (easily expressed) and then (as one possibility) that there is a second plane containing the line and perpendicular to the first plane and tangent to the sphere. So at least for those two examples, these underlying entities to represent circles seem to work fairly well.

42
notes/inversive.md Normal file
View File

@ -0,0 +1,42 @@
#### Inversive Coordinates
(proposed by Alex Kontorovich as a practical system for doing 3D geometric calculations)
These coordinates are of form $I=(c, r, x, y, z)$ where we think of $c$ as the co-radius, $r$ as the radius, and $x, y, z$ as the "Euclidean" part, which we abbreviate $E_I$. There is an underlying basic quadratic form $Q(I_1,I_2) = (c_1r_2+c_2r_1)/2 - x_1x_2 -y_1y_2-z_1z_2$ which aids in calculation/verification of coordinates in this representation. We have:
| Entity or Relationship | Representation | Comments/questions |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Sphere s with radius r>0 centered on P = (x,y,z) | $I_s = (1/c, 1/r, x/r, y/r, z/r)$ satisfying $Q(I_s,I_s) = -1$, i.e., $c = r/(\|P\|^2 - r^2)$. | Can also write $I_s = (\|P\|^2/r - r, 1/r, x/r. y/r, z/r)$ -- so there is no trouble if $\|E_{I_s}\| = r$, just get first coordinate to be 0. |
| Plane p with unit normal (x,y,z), a distance s from origin | $I_p = (2s, 0, x, y, z)$ | Note $Q(I_p, I_p)$ is still -1. Also, there are two representations for each plane through the origin, namely $(0,0,x,y,z)$ and $(0,0,-x,-y,-z)$ |
| Point P with Euclidean coordinates (x,y,z) | $I_P = (\|P\|^2, 1, x, y, z)$ | Note $Q(I_P,I_P) = 0$.  Because of this we might choose  some other scaling of the inversive coordinates, say $(\||P\||,1/\||P\||,x/\||P\||,y/\||P\||,z/\||P\||)$ instead, but that fails at the origin, and likely won't have some of the other nice properties listed below.  Note that scaling just the co-radius by $s$ and the radius by $1/s$ (which still preserves $Q=0$) dilates by a factor of $s$ about the origin, so that $(\|P\|, \|P\|, x, y, z)$, which might look symmetric, would actually have to represent the Euclidean point $(x/\||P\||, y/\||P\||, z/\||P\||)$ . |
| ∞, the "point at infinity" | $I_\infty = (1,0,0,0,0)$ | The only solution to $Q(I,I) = 0$ not covered by the above case. |
| P lies on sphere or plane given by I | $Q(I_P, I) = 0$ | |
| Sphere/planes represented by I and J are tangent | $Q(I,J) = 1$ (??, see note at right) | Seems as though this must be $Q(I,J) = \pm1$  ? For example, the $xy$ plane represented by (0,0,0,0,1)  is tangent to the unit circle centered at (0,0,1) rep'd by (0,1,0,0,1), but their Q-product is -1. And in general you can reflect any sphere tangent to any plane through the plane and it should flip the sign of $Q(I,J)$, if I am not mistaken. |
| Sphere/planes represented by I and J intersect (respectively, don't intersect) | $\|Q(I,J)\| < (\text{resp. }>)\; 1$ | Follows from the angle formula, at least conceptually. |
| P is center of sphere represented by I | Well, $Q(I_P, I)$ comes out to be $(\|P\|^2/r - r + \|P\|^2/r)/2 - \|P\|^2/r$ or just $-r/2$ . | Is it if and only if ?   No this probably doesn't work because center is not conformal quantity. |
| Distance between P and R is d | $Q(I_P, I_R) = d^2/2$ | |
| Distance between P and sphere/plane rep by I | | In the very simple case of a plane $I$ rep'd by $(2s, 0, x, y, z)$ and a point $P$ that lies on its perpendicular through the origin, rep'd by $(r^2, 1, rx, ry, rz)$ we get $Q(I, I_p) = s-r$, which is indeed the signed distance between $I$ and $P$. Not sure if this generalizes to other combinations? |
| Distance between sphere/planes rep by I and J | Note that for any two Euclidean-concentric spheres rep by $I$ and $J$ with radius $r$ and $s,$ $Q(I,J) = -\frac12\left(\frac rs  + \frac sr\right)$ depends only on the ratio of $r$ and $s$. So this can't give something that determines the Euclidean distance between the two spheres, which presumably grows as the two spheres are blown up proportionally. For another example, for any two parallel planes, $Q(I,J) = \pm1$. | Alex had said: Q(I,J)=cosh^2 (d/2) maybe where d is distance in usual hyperbolic metric. Or maybe cosh d. That may be right depending on what's meant by the hyperbolic metric there, but it seems like it won't determine a reasonable Euclidean distance between planes, which should differ between different pairs of parallel planes. |
| Sphere centered on P through R | | Probably just calculate distance etc. |
| Plane rep'd by I goes through center of sphere rep'd by J | I think this is equivalent to the plane being perpendicular to the sphere, i.e.$Q(I,J) = 0$. | |
| Dihedral angle between planes (or spheres?) rep by I and J | $\theta = \arccos(Q(I,J))$ | Aaron Fenyes points out: The angle between spheres in $S^3$ matches the angle between the planes they bound in $R^{(1,4)}$, which matches the angle between the spacelike vectors perpendicular to those planes. So we should have $Q(I,J) = \cos\theta$. Note that when the spheres do not intersect, we can interpret this as the "imaginary angle" between them, via $\cosh t = \cos it$. |
| R, P, S are collinear | Maybe just cross product of two differences is 0. Or, $R,P,S,\infty$ lie on a circle, or equivalently, $I_R,I_P,I_S,I_\infty$ span a plane (rather than a three-space). | Not a conformal property, but $R,P,S,\infty$ lying on a circle _is_. |
| Plane through noncollinear R, P, S | Should be, just solve Q(I, I_R) = 0 etc. | |
| circle | Maybe concentric sphere and the containing plane? Note it is easy to constrain the relationship between those two: they must be perpendicular. | Defn: circle is intersection of two spheres. That does cover lines. But you lose the canonicalness |
| line | Maybe two perpendicular containing planes? Maybe the plane perpendicular to the line and through origin, together with the point of the line on that plane? Or maybe just as a bag of collinear points? | The first is the limiting case of the possible circle rep, but it is not canonical. The second appears to be canonical, but I don't see a circle rep that corresponds to it. |
The unification of spheres/planes is indeed attractive for a project like Dyna3. The relationship between this representation and Geometric Algebras is a bit murky; likely it somehow fits under the Geometric Algebra umbrella.
##### Additional more disorganized notes
Discussed coordinates with Alex Kontorovich. He was suggesting "inversive coordinates" -- for a sphere, that's 1/coradius, 1/radius, center/radius (where coradius is radius of sphere inverted in the unit sphere.) The advantage is tangent to and perpendicular to are linear in these coordinates (in the sense that if one is known, the condition of being tangent to or perpendicular to that one are linear). Planes have 1/radius = 0, and in fact, you can take the coordinates to be (2s, 0, x, y, z) where s is the distance to the origin and x,y,z are the normal direction. (Note the normal direction is only determined up to a scalar multiple. So could always scale so that the first non-zero coordinate is 1, or if you like only allow x, y to vary and let z be determined as sqrt(1-x^2^-y^2^). ) Points can be given by (r^2,1,x,y,z) where x,y,z are the coordinates and r is the distance to the origin. Quadratic form that tells you if something is a sphere/plane, or in the boundary, or up in the hyperbolic plane above. There are some details, but not quite explicit for modeling R^3, at http://sites.math.rutgers.edu/~alexk/files/LetterToDuke.pdf -- all this emphasize need to be agnostic with respect to geometric model so that we can experiment. Not really sure exactly how this relates or not to conformal geometric algebra, and whether it can be combined with geometric algebra. As formulated, there are clear-ish reps for planes/spheres and for points, but not as clear for lines. Have to see how to compute distance and/or specify a given distance. To combine inversive coordinates and geometric algebra, maybe think dually; there should be a lift from a normal vector and distance from origin to the five-vector; bivectors would rep circles/lines; trivectors would rep point pairs/points. What is the signature of this algebra, i.e. how many coordinates square to +1, -1, or 0? But it doesn't seem worth it for three dimensions, because there is a natural representation of points, as follows:
The signature of Q will be (1,4), and in fact Q(I1,I2) = 1/2(ab+ba) - E1\dot E2, where a is the "first" or "coradius" coordinate, "b" is the "second" or "radius" coordinate, and E is the Euclidean part (x,y,z). Then the inversive coordinates of a sphere with center (x,y,z) and radius r will be I = (1/\hat{r},1/r,x/r,y/r,z/r) where \hat{r} = r/(|E|^2 -r^2). These coordinates satisfy Q(I,I) = -1. For this to make sense, of course r > 0, but we get planes by letting the radius of a tangent sphere to the plane go to infinity, and we get I = (2s, 0, x0, y0, z0) where (x0,y0,z0) is the unit normal to the plane and s is the perpendicular distance from the plane to the origin. Still Q(I,I) = -1.
Since r>0, we can't represent individual points this way. Instead we will use some coordinates J for which Q(J,J) = 0. In particular, if you take for the Euclidean point E = (u,v,w) the coordinates J = (`|E|`^2,1,u,v,w) then Q(J,J) = 0 and moreover it comes out that Q(I,J) = 0
whenever E lies on the sphere or plane described by some I with Q(I,I) = -1.
The condition that two spheres I1 and I2 are tangent seems to be that Q(I1,I2) = 1. So given a fixed sphere, the condition that another sphere be tangent to it is linear in the coordinates of that other sphere.
This system does seem promising for encoding points, spheres, and planes, and doing basic computations with them. I guess I would just encode a circle as the intersection of the concentric sphere and the containing plane, and a line as either a pair of points or a pair of planes (modulo some equivalence relation, since I can't see any canonical choice of either two planes or two points). Or actually as described below, there is a more canonical choice.
I will have to work out formulas for the Euclidean distance between two entities, and the angle between them, and especially the intersection of two lines and the condition that three points are collinear.
In this vein, it seems as though if J1 and J2 are the reps of two points, then Q(J1,J2) = d^2/2. So then the sphere centered at J1 through J2 is (J1-(2Q(J1,J2),0,0,0,0))/sqrt(2Q(J1,J2)). Ugh has a sqrt in it. Similarly for sphere centered at J3 through J2, (J3-(2Q(J3,J2),0000))/sqrt(2Q(J3,J2)). J1,J2,J3 are collinear if these spheres are tangent, i.e. if those vectors have Q-inner-product 1, which is to say Q(J1,J3) - Q(J1,J2) - Q(J3,J2) = 2sqrt(Q(J1,J2)Q(J2,J3)). But maybe that's not the simplest way of putting it. After all, we can just say that the cross-product of the two differences is 0; that has no square roots in it.
One conceivable way to canonicalize lines is to use the *perpendicular* plane that goes through the origin, that's uniquely defined, and anyway just amounts to I = (0,0,d) where d is the ordinary direction vector of the line; and a point J in that plane that the line goes through, which just amounts to J=(r^2,1,E) with Q(I,J) = 0, i.e. E\dot d = 0. It's also the point on the line closest to the origin. The reason that we don't usually use that point as the companion to the direction vector is that the resulting set of six coordinates is not homogeneous. But here that's not an issue, since we have our standard point coordinates and plane coordinates; and for a plane through the origin, only two of the direction coordinates are really free, and then we have the one dot-product relation, so only two of the point coordinates are really free, giving us the correct dimensionality of 4 for the set of lines. So in some sense this says that we could take naively as coordinates for a line the projection of the unit direction vector to the xy plane and the projection of the line's closest point to the origin to the xy plane. That doesn't seem to have any weird gimbal locks or discontinuities or anything. And with these coordinates, you can test if the point E=x,y,z is on the line (dx,dy,cx,cy) by extending (dx,dy) to d via dz = sqrt(1-dx^2 - dy^2), extending (cx,cy) to c by determining cz via d\dot c = 0, and then checking if d\cross(E-c) = 0. And you can see if two lines are parallel just by checking if they have the same direction vector, and if not, you can see if they are coplanar by projecting both of their closest points perpendicularly onto the line in the direction of the cross product of their directions, and if the projections match they are coplanar.

46
notes/theory.md Normal file
View File

@ -0,0 +1,46 @@
#### Theory
###### Jürgen Richter-Gebert
We begin with the writing, and personal remarks/advice of the author of Cinderella, probably the theoretically most sound 2D dynamic geometry program.
His monograph on the topic is _Realization Spaces of Polytopes_. The main result is the Universality Theorem for 4-Polytopes, which basically says that the realization space of a 4-polytope can be an arbitrary algebraic set (as constrasted with the Steinitz Theorem that says that every polyhedron can be realized with rational coordinates). The proof proceeds with a flavor similar to the proof of similar properties of the configuration spaces of linkages: he constructs polytopes which require that some measurement be the sum or product of some other measurements, etc.
He brings this result down a dimension to 3-diagrams, which are basically complexes of polyhedra -- in essence, Schlegel diagrams of polytopes. He has Corollary 10.4.1 on p.95, which says that, among other things, all algebraic numbers are needed to coordinatize all 3-diagrams, and that the realizability problem for 3-diagrams is NP-hard, being polynomial-time equivalent to the Existential Theory of the Reals. Other issues are that there are 3-diagrams for which the shape of some 2-face cannot be arbitrarily prescribed (again by contrast to the polyhedron case); the combinatorial types of realizable 3-diagrams cannot be characterized by a finite set of forbidden minors; and the maximum size of a 3 diagram with $n$ vertices under the constraint that coordinates be integers is bounded below by a doubly exponential function in $n$.
As a specific test case for a 3D dynamic geometry program, Jürgen suggests:
"a kinematic circle of six hinges. For almost all arrangements of of lengths and angles this will be a rigid body. But for some arrangements this will exhibit nice movements.
Here is a talk in German of me talking about those matters; a related Mechanism is at 7:00 [BR in der ARD Mediathek | ARD Mediathek](https://www.br.de/mediathek/video/campus-talks-prof-juergen-richter-gebert-av:5b4e7ccb00332600121858c5)"
Unfortunately, the link seems to be dead. However, I believe Jurgen was referring to mechanisms like kaleidocycles; see e.g. https://www.pnas.org/doi/10.1073/pnas.1809796115.
He also says the basic lesson of Cinderella was that to do the solving properly, it was critical to allow complex ambient spaces and allow solutions with complex coordinates (such as the imaginary intersection points of two non-intersecting circles). This technique allowed, for example, intersection points (say of lines and circles) to vary continuously around configurations where they disappeared, by tracing a path among the complex solutions to link the real solutions, avoiding the singularity.
Jürgen also emphasized the need for an intuitive user interface. Notes on that will be in a separate file.
His final mathematical advice was reasonably encouraging, however:
"But still I would consider it all more or less doable. One should very precisely think about a doable scope.
I think three things are essential for the math no matter what you exactly plan.
1. Think projectively,
Use Projective Geometry, Homogeneous Coordinates (or to a certain extent Quaternions, and Clifford Algebras, which are more or less an elegant way to merge Complex numbers with projective concepts.)
2. Consider ambient complex spaces.
The true nature of the objects can only be understood if embedded into a complex ambient space.
More or less this is the lesson we learned from the Cinderella project.
3. Use clever and adequate mathematical representations
So for 3D Geometry I would consider Plücker Coordinates as a good starting point,
Some parts of which are covered by Clifford algebras. Clifford Algebras might make it difficult to embed everything in a proper complex ambient space, since they are already complex in nature by themselves."
###### Looking at CindyJS
It would be nice to see how Jürgen handled some of these issues in a 2D system that he designed. Unfortunately, Cinderella was and remains closed-source; it was distributed for profit for some stretch of time. However, (a part of?) it was reimplemented in JavaScript as CindyJS, which is open source. I took a relatively quick look at that source code at one point, and these were my observations:
CindyJS uses very concrete basic objects: 2D points are represented via projective geometry as a list of three floating-point numbers, and everything is done numerically. There are no symbolic representations or exact algebraic numbers. (Not sure how a point on a circle or line is handled, that would take further investigation.)
Lines are given by explicit coordinates as well (not sure of the internal details/exact coordinatization, or of how a "LineThrough" is represented).
Was unclear to me how the complex parametrization for preserving continuity was handled in the code, even though Jürgen harps on complex ambient spaces; where are the complex numbers? Perhaps that part of Cinderella was never re-implemented?

8
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "dyna3",
"version": "0.1.0",
"version": "0.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -3535,9 +3535,9 @@
}
},
"three": {
"version": "0.111.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.111.0.tgz",
"integrity": "sha512-AcTqyGJ3H75AIusOuADy0LsP958QKQwm/YhABHFMzd6RFXPNVay1rlSbbG6WYv7KM72G2THMMwqbjQd4dVHMkQ=="
"version": "0.112.1",
"resolved": "https://registry.npmjs.org/three/-/three-0.112.1.tgz",
"integrity": "sha512-8I0O74hiYtKl3LgDNcPJbBGOlpekbcJ6fJnImmW3mFdeUFJ2H9Y3/UuUSW2sBdjrIlCM0gvOkaTEFlofO900TQ=="
},
"time-zone": {
"version": "1.0.0",

View File

@ -1,22 +1,11 @@
{
"name": "dyna3",
"version": "0.1.0",
"version": "0.1.1",
"description": "Constraint-based three-dimensional dynamic geometry",
"private": "true",
"main": "dyna3.js",
"scripts": {
"clean": "rm -rf site docbuild dyna3.zip",
"doc:extra": "mkdir -p docbuild && coffee src/helpers/pkglock_to_externals.litcoffee --doc > docbuild/extlist.md",
"doc:collate": "mkdir -p site/doc && coffee doc/gendoc.litcoffee > site/doc/dyna3.md",
"doc": "npm run doc:extra && npm run doc:collate",
"prebuild": "npm run clean",
"build:coffee": "coffee -o site -c -m src/*.litcoffee",
"build:ext": "coffee src/helpers/pkglock_to_externals.litcoffee > site/externals.js",
"build:doc": "npm run doc && pandoc -s --toc site/doc/dyna3.md > site/doc/dyna3.html",
"build:copy": "cp src/*.html site && cp -r node_modules site",
"build": "npm run build:coffee && npm run build:ext && npm run build:doc && npm run build:copy",
"dist": "npm run build && zip -r dyna3.zip site",
"pretest": "npm run build && coffee -o __tests__ -c -m coffeetest/*.coffee",
"pretest": "make pretest",
"test": "ava"
},
"repository": {

View File

@ -3,25 +3,63 @@
When you load dyna3, you should initially see a three-dimensional coordinate system with labeled axes.
```javascript
import {threeLoaded, three as J3} from './externals.js'
import {threeLoaded,
three as J3,
TrackballControls as J3x} from './externals.js'
LTx = { dashed: true, plain: false }
buildAxis = (to, color, tx) ->
geom = new J3.Geometry()
if tx then mat = new J3.LineDashedMaterial(
{linewidth: 3, color, dashSize: 0.5, gapSize: 0.5})
else mat = new J3.LineBasicMaterial {linewidth:3, color}
geom.vertices.push(new J3.Vector3(0,0,0))
geom.vertices.push to.clone()
axis = new J3.Line geom, mat, J3.LineSegments
axis.computeLineDistances()
axis
axes = (length) ->
ax = new J3.Object3D()
dirs = [ new J3.Vector3(length, 0, 0),
new J3.Vector3(0, length, 0),
new J3.Vector3(0, 0, length) ]
cols = [ 0xFF0000, 0x00FF00, 0x0000FF ]
for i in [0, 1, 2]
ax.add buildAxis dirs[i], cols[i], LTx.plain
dirs[i].multiplyScalar(-1)
ax.add buildAxis dirs[i], cols[i], LTx.dashed
ax
main = ->
renderer = new J3.WebGLRenderer()
rwd = window.innerWidth
rht = window.innerHeight
renderer.setSize rwd, rht
renderer.setClearColor 0xdddddd, 1.0
document.body.appendChild renderer.domElement
scene = new J3.Scene()
geometry = new J3.SphereBufferGeometry 1,5,5
material = new J3.MeshBasicMaterial {color: 0xff00ff}
geometry = new J3.IcosahedronBufferGeometry 1
material = new J3.MeshPhongMaterial {color: 0xff00ff}
ball = new J3.Mesh geometry, material
scene.add ball
scene.add axes 10
ambient = new J3.HemisphereLight 0xffffff, 0xaaaaaa, 1
ambient.position.set 0.1, 0.1, 1
scene.add ambient
camera = new J3.PerspectiveCamera 75, rwd/rht, 0.1, 1000
camera.position.z = 5
camera.up.set 0, 0, 1
camera.position.set 4, -1, 3
renderer.render scene, camera
controls = new J3x.TrackballControls camera, renderer.domElement
renderer.setAnimationLoop ->
controls.update()
renderer.render scene,camera
threeLoaded.then main
```

View File

@ -4,7 +4,7 @@
The dyna3 program depends on some externally-maintained JavaScript libraries/modules. The package uses npm to track these external dependencies. A module externals.js is automatically generated from the package.json and package_lock.json files created by npm to load the necessary modules at runtime.
The generation is performed by pkglock_to_externals.litcoffee, which also records the main importable file within the library, as there does not seem to be a systematic way to generate that filename from the module name.
The generation is performed by pkglock_to_externals.litcoffee, which also records all of the needed importable files within the library, as there's no way to generate that from the module name or the information in package-lock.json.
Specific packages/implementation approaches for components of dyna3 include the following items. All packages are obtained from npm unless otherwise noted.
@ -15,7 +15,10 @@ Specific packages/implementation approaches for components of dyna3 include the
```javascript
importable = { three: 'build/three.module.js' }
importable =
three:
three: 'build/three.module.js',
TrackballControls: 'examples/jsm/controls/TrackballControls.js'
```
And here is the current complete list of external libraries on which operation of dyna3 depends:
@ -38,19 +41,31 @@ And here is the current complete list of external libraries on which operation o
if process.argv.length > 2
process.stdout.write "- #{dep}\n"
else
exports = (
"export var #{mod};" for mod in Object.keys importable[dep]
).join "\n"
mainload = (
"#{mod} = await import('https://cdn.jsdelivr.net/npm/#{dep}@#{pldata.dependencies[dep].version}/#{importable[dep][mod]}');" for mod in Object.keys importable[dep]
).join "\n "
fallbackload = (
"#{mod} = await import('./node_modules/#{dep}/#{importable[dep][mod]}');" for mod in Object.keys importable[dep]
).join "\n "
block =
"""
export var #{dep};
#{exports}
export var #{dep}Loaded = new Promise(async function(resolve, reject) {
var success = false;
try {
#{dep} = await import('https://cdn.jsdelivr.net/npm/#{dep}@#{pldata.dependencies[dep].version}/#{importable[dep]}');
#{mainload}
console.log('CDN import of #{dep} module OK');
success = true;
} catch(err) {
console.log('CDN import of #{dep} failed: ' + JSON.stringify(err));
try {
#{dep} = await import('./node_modules/#{dep}/#{importable[dep]}');
#{fallbackload}
success = true;
} catch(err) {
}