2024-10-21 23:38:27 +00:00
|
|
|
[package]
|
2024-11-15 03:32:47 +00:00
|
|
|
name = "dyna3"
|
2024-10-21 23:38:27 +00:00
|
|
|
version = "0.1.0"
|
2024-11-15 03:32:47 +00:00
|
|
|
authors = ["Aaron Fenyes", "Glen Whitney"]
|
2024-10-21 23:38:27 +00:00
|
|
|
edition = "2021"
|
2025-05-06 19:17:30 +00:00
|
|
|
rust-version = "1.86"
|
2024-10-21 23:38:27 +00:00
|
|
|
|
|
|
|
[features]
|
|
|
|
default = ["console_error_panic_hook"]
|
2024-11-26 00:32:50 +00:00
|
|
|
dev = []
|
2024-10-21 23:38:27 +00:00
|
|
|
|
|
|
|
[dependencies]
|
feat: Point coordinate regulators
Implements regulators for the Euclidean coordinates of Point entities,
automatically creating all three of them for each added point entity. When
such a regulator is set, it freezes the corresponding representation
coordinate to the set point. In addition, if all three coordinates of a
given Point are set, the coradius coordinate (which holds the norm of the
point) is frozen as well.
Note that a PointCoordinateRegulator must be created with a Point as the
subject. This commit modifies HalfCurvatureRegulator analogously, so that
it can only be created with a Sphere.
A couple of prospective issues that should be filed in association with
this commit:
* The new coordinate regulators create redundant display information with
the raw representation coordinates of a point that are already shown in
the outline view.
* The optimization status of these regulators together with HalfCurvature
regulators (i.e., the ones implemented by freezing coordinates) is different
from InversiveDistance regulators when an Assembly is unrealizable: the
frozen-coordinate constraints will be "hard" in that they will be forced
to precisely equal their set point, whereas the distance regulators are
"soft" in that they can be relaxed from their set points in an effort to
minimize the loss function of the configuration as compared to the values
of the constraints. Perhaps at some point we should/will have a mechanism
to specify the softness/hardness of constraints, but in the meantime,
there should not be two different categories of constraints. Suppose we
decide that by default that all constraints are soft. Then the optimizer
should be able to search changing, for example, the radius of a
curvature-constrained sphere, so as to minimize the loss function (for a
loss that would therefore presumably have a term akin to the square of the
difference between the specified and actual half-curvature of the sphere).
For example, suppose you specify that the half-curvature of a sphere is 1
(so it has radius 1/2) but that its distance to a point is -1. These
constraints cannot be satisfied, so the optimization fails, presumably
with the point at the sphere center, and the sphere with radius 1/2.
So all of the loss is concentrated in the difference between the actual
point-sphere distance being -1/2, not -1. It would be more appropriate
(in the all-soft constraint regime) to end up at something like a sphere of
half-curvature 1/√2 with the point at the center, so that the loss is split
between both the half-curvature and the distance to the sphere being off by
1 - 1/√2. (At a guess, that would minimize the sum of the squares of the
two differences.)
2025-09-20 00:51:26 -07:00
|
|
|
enum-iterator = "2.3.0"
|
2024-10-21 23:38:27 +00:00
|
|
|
itertools = "0.13.0"
|
|
|
|
js-sys = "0.3.70"
|
Integrate engine into application prototype (#15)
Port the engine prototype to Rust, integrate it into the application prototype, and use it to enforce the constraints.
### Features
To see the engine in action:
1. Add a constraint by shift-clicking to select two spheres in the outline view and then hitting the 🔗 button
2. Click a summary arrow to see the outline item for the new constraint
2. Set the constraint's Lorentz product by entering a value in the text field at the right end of the outline item
* *The display should update as soon as you press* Enter *or focus away from the text field*
The checkbox at the left end of a constraint outline item controls whether the constraint is active. Activating a constraint triggers a solution update. (Deactivating a constraint doesn't, since the remaining active constraints are still satisfied.)
### Precision
The Julia prototype of the engine uses a generic scalar type, so you can pass in any type the linear algebra functions are implemented for. The examples use the [adjustable-precision](https://docs.julialang.org/en/v1/base/numbers/#Base.MPFR.setprecision) `BigFloat` type.
In the Rust port of the engine, the scalar type is currently fixed at `f64`. Switching to generic scalars shouldn't be too hard, but I haven't looked into [which other types](https://www.nalgebra.org/docs/user_guide/generic_programming) the linear algebra functions are implemented for.
### Testing
To confirm quantitatively that the Rust port of the engine is working, you can go to the `app-proto` folder and:
* Run some automated tests by calling `cargo test`.
* Inspect the optimization process in a few examples calling the `run-examples` script. The first example that prints is the same as the Irisawa hexlet example from the engine prototype. If you go into `engine-proto/gram-test`, launch Julia, and then
```
include("irisawa-hexlet.jl")
for (step, scaled_loss) in enumerate(history_alt.scaled_loss)
println(rpad(step-1, 4), " | ", scaled_loss)
end
```
you should see that it prints basically the same loss history until the last few steps, when the lower default precision of the Rust engine really starts to show.
### A small engine revision
The Rust port of the engine improves on the Julia prototype in one part of the constraint-solving routine: projecting the Hessian onto the subspace where the frozen entries stay constant. The Julia prototype does this by removing the rows and columns of the Hessian that correspond to the frozen entries, finding the Newton step from the resulting "compressed" Hessian, and then adding zero entries to the Newton step in the appropriate places. The Rust port instead replaces each frozen row and column with its corresponding standard unit vector, avoiding the finicky compressing and decompressing steps.
To confirm that this version of the constraint-solving routine works the same as the original, I implemented it in Julia as `realize_gram_alt_proj`. The solutions we get from this routine match the ones we get from the original `realize_gram` to very high precision, and in the simplest examples (`sphere-in-tetrahedron.jl` and `tetrahedron-radius-ratio.jl`), the descent paths also match to very high precision. In a more complicated example (`irisawa-hexlet.jl`), the descent paths diverge about a quarter of the way into the search, even though they end up in the same place.
Co-authored-by: Aaron Fenyes <aaron.fenyes@fareycircles.ooo>
Reviewed-on: https://code.studioinfinity.org/glen/dyna3/pulls/15
Co-authored-by: Vectornaut <vectornaut@nobody@nowhere.net>
Co-committed-by: Vectornaut <vectornaut@nobody@nowhere.net>
2024-11-12 00:46:16 +00:00
|
|
|
lazy_static = "1.5.0"
|
2024-10-21 23:38:27 +00:00
|
|
|
nalgebra = "0.33.0"
|
2025-03-10 23:43:24 +00:00
|
|
|
readonly = "0.2.12"
|
2025-06-26 22:11:02 +00:00
|
|
|
sycamore = "0.9.1"
|
2024-10-21 23:38:27 +00:00
|
|
|
|
2025-07-21 04:18:49 +00:00
|
|
|
# We use Charming to help display engine diagnostics
|
|
|
|
charming = { version = "0.5.1", features = ["wasm"] }
|
|
|
|
|
2024-10-21 23:38:27 +00:00
|
|
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
|
|
|
# logging them with `console.error`. This is great for development, but requires
|
|
|
|
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
|
|
|
# code size when deploying.
|
|
|
|
console_error_panic_hook = { version = "0.1.7", optional = true }
|
|
|
|
|
|
|
|
[dependencies.web-sys]
|
|
|
|
version = "0.3.69"
|
|
|
|
features = [
|
2024-11-27 05:02:06 +00:00
|
|
|
'DomRect',
|
2024-10-21 23:38:27 +00:00
|
|
|
'HtmlCanvasElement',
|
Integrate engine into application prototype (#15)
Port the engine prototype to Rust, integrate it into the application prototype, and use it to enforce the constraints.
### Features
To see the engine in action:
1. Add a constraint by shift-clicking to select two spheres in the outline view and then hitting the 🔗 button
2. Click a summary arrow to see the outline item for the new constraint
2. Set the constraint's Lorentz product by entering a value in the text field at the right end of the outline item
* *The display should update as soon as you press* Enter *or focus away from the text field*
The checkbox at the left end of a constraint outline item controls whether the constraint is active. Activating a constraint triggers a solution update. (Deactivating a constraint doesn't, since the remaining active constraints are still satisfied.)
### Precision
The Julia prototype of the engine uses a generic scalar type, so you can pass in any type the linear algebra functions are implemented for. The examples use the [adjustable-precision](https://docs.julialang.org/en/v1/base/numbers/#Base.MPFR.setprecision) `BigFloat` type.
In the Rust port of the engine, the scalar type is currently fixed at `f64`. Switching to generic scalars shouldn't be too hard, but I haven't looked into [which other types](https://www.nalgebra.org/docs/user_guide/generic_programming) the linear algebra functions are implemented for.
### Testing
To confirm quantitatively that the Rust port of the engine is working, you can go to the `app-proto` folder and:
* Run some automated tests by calling `cargo test`.
* Inspect the optimization process in a few examples calling the `run-examples` script. The first example that prints is the same as the Irisawa hexlet example from the engine prototype. If you go into `engine-proto/gram-test`, launch Julia, and then
```
include("irisawa-hexlet.jl")
for (step, scaled_loss) in enumerate(history_alt.scaled_loss)
println(rpad(step-1, 4), " | ", scaled_loss)
end
```
you should see that it prints basically the same loss history until the last few steps, when the lower default precision of the Rust engine really starts to show.
### A small engine revision
The Rust port of the engine improves on the Julia prototype in one part of the constraint-solving routine: projecting the Hessian onto the subspace where the frozen entries stay constant. The Julia prototype does this by removing the rows and columns of the Hessian that correspond to the frozen entries, finding the Newton step from the resulting "compressed" Hessian, and then adding zero entries to the Newton step in the appropriate places. The Rust port instead replaces each frozen row and column with its corresponding standard unit vector, avoiding the finicky compressing and decompressing steps.
To confirm that this version of the constraint-solving routine works the same as the original, I implemented it in Julia as `realize_gram_alt_proj`. The solutions we get from this routine match the ones we get from the original `realize_gram` to very high precision, and in the simplest examples (`sphere-in-tetrahedron.jl` and `tetrahedron-radius-ratio.jl`), the descent paths also match to very high precision. In a more complicated example (`irisawa-hexlet.jl`), the descent paths diverge about a quarter of the way into the search, even though they end up in the same place.
Co-authored-by: Aaron Fenyes <aaron.fenyes@fareycircles.ooo>
Reviewed-on: https://code.studioinfinity.org/glen/dyna3/pulls/15
Co-authored-by: Vectornaut <vectornaut@nobody@nowhere.net>
Co-committed-by: Vectornaut <vectornaut@nobody@nowhere.net>
2024-11-12 00:46:16 +00:00
|
|
|
'HtmlInputElement',
|
2024-10-21 23:38:27 +00:00
|
|
|
'Performance',
|
|
|
|
'WebGl2RenderingContext',
|
|
|
|
'WebGlBuffer',
|
|
|
|
'WebGlProgram',
|
|
|
|
'WebGlShader',
|
|
|
|
'WebGlUniformLocation',
|
|
|
|
'WebGlVertexArrayObject'
|
|
|
|
]
|
|
|
|
|
2024-11-26 00:32:50 +00:00
|
|
|
# the self-dependency specifies features to use for tests and examples
|
|
|
|
#
|
|
|
|
# https://github.com/rust-lang/cargo/issues/2911#issuecomment-1483256987
|
|
|
|
#
|
2024-10-21 23:38:27 +00:00
|
|
|
[dev-dependencies]
|
2024-11-26 00:32:50 +00:00
|
|
|
dyna3 = { path = ".", default-features = false, features = ["dev"] }
|
2024-10-21 23:38:27 +00:00
|
|
|
wasm-bindgen-test = "0.3.34"
|
|
|
|
|
2025-06-04 21:01:12 +00:00
|
|
|
# turn off spurious warnings about the custom config that Sycamore uses
|
|
|
|
#
|
|
|
|
# https://sycamore.dev/book/troubleshooting#unexpected-cfg-condition-name--sycamore-force-ssr
|
|
|
|
#
|
|
|
|
[lints.rust]
|
|
|
|
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(sycamore_force_ssr)"] }
|
|
|
|
|
2024-10-21 23:38:27 +00:00
|
|
|
[profile.release]
|
|
|
|
opt-level = "s" # optimize for small code size
|
|
|
|
debug = true # include debug symbols
|
2025-04-02 20:31:42 +00:00
|
|
|
|
|
|
|
[[example]]
|
|
|
|
name = "irisawa-hexlet"
|
|
|
|
test = true
|
|
|
|
harness = false
|
|
|
|
|
|
|
|
[[example]]
|
|
|
|
name = "kaleidocycle"
|
|
|
|
test = true
|
|
|
|
harness = false
|
|
|
|
|
|
|
|
[[example]]
|
|
|
|
name = "point-on-sphere"
|
|
|
|
test = true
|
|
|
|
harness = false
|
|
|
|
|
|
|
|
[[example]]
|
|
|
|
name = "three-spheres"
|
|
|
|
test = true
|
|
|
|
harness = false
|