Dispatch normalization routines correctly #87
No reviewers
Labels
No labels
bug
design
duplicate
engine
enhancement
maintenance
prospective
question
regression
stub
todo
ui
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: StudioInfinity/dyna3#87
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "Vectornaut/dyna3:dispatch-normalization"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This pull request addresses issue #86 by correctly dispatching the routine used to normalize spheres during nudging, and it adds a test that would've detected the issue. It also makes some other changes in support of these.
This pull request should commute with #85.
Primary changes
Dispatching the normalization routine correctly
On the main branch, the routine that's supposed to normalize spheres during nudging never runs, because the test in the conditional
always evaluates to
false. On the branch to be merged, we dispatch the normalization routine in a way that actually works, and is also more idiomatic.Testing for curvature drift
The normalization routine helps preserve the curvature of a sphere while nudging its position. The newly added
assembly::curvature_drift_testconfirms that the curvature of a sphere doesn't drift too much during a series of position nudges, equivalent to holding shift+S for six or seven seconds in the user interface. The test fails, as expected, if you disable sphere normalization by makingSphere::normalize_mut_repdo nothing.Supporting changes
Dispatching
Right now, only spheres need to be normalized during nudging, because moving along a tangent vector doesn't affect the normalization of points. To simplify the semantics of the new
Element::normalize_mut_reptrait method, however, I've made it do normalization in both its implementation forSphereand its implementation forPoint. This shouldn't have much of a performance cost.Testing
Since the tests aren't built for WebAssembly, we have to replace
console::logwithconsole_log!in all of the functions used byassembly::curvature_drift_test. We'll eventually want to do this replacement everywhere.Just a couple of superficial items, otherwise looks good
@ -47,2 +47,4 @@wasm-bindgen-test = "0.3.34"[lints.rust]unexpected_cfgs = { level = "warn", check-cfg = ["cfg(sycamore_force_ssr)"] }Do Cargo.toml files allow comments? If so, please comment the meaning/purpose of this new block, it's a bit inscrutable. Something like "previously we used server-side rendering, but we switched to BLAH because BLEE, so warn if SSR is turned on". Of course I may totally have misunderstood what's going on here, that's just meant as an example comment.
Done in commit
e19792d(previously8732cb7)!@ -106,6 +107,9 @@ pub trait Element: Serial + ProblemPoser + DisplayItem {// element is responsible for keeping this set up to datefn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>>;// normalize a representation vector for this kind of elementDo you like the word "normalize" or "canonicalize"? I feel like the latter more explicitly means "choose the favored representative of an equivalence class" (which I think is mainly what's going on here) and the former can have broader meanings in which the entity is actually changed, like when you find a unit vector in the same direction -- I think that's sometimes called normalization. Only make a change if you feel like "canonicalize" would be a better/clearer choice.
In my mind, an element is represented by a line through the origin in
\mathbb{R}^{4,1}, so choosing a favored representative is equivalent to choosing a particular scaling. That's why I used the word "normalization." However, the implementation ofnormalize_rep_mutfor spheres actually changes the equivalence class. That's because the only thing we usenormalize_rep_mutfor right now is to keep elements normalized during nudging, and normalizing in a way that preserves the equivalence class makes nudging less stable. If both "normalize" and "canonicalize" imply staying within an equivalence class for you, then maybe we need another term entirely.Ah, hmm, I hadn't appreciated that the result of normalize_rep_mut might be in a different equivalence class. "Normalize" does not by itself connote "stay in the same equivalence class, but "normalize representation" does seem to imply strongly that we are just choosing a different representation of the same entity. So I would agree the name needs to change -- but just "normalize" without the "representation" would be OK, since we're not just picking a different representation of the same geometric entity. If you don't like that, perhaps "stabilize" or "standardize" or "choose_canonical" or "nearby_canonical" or something?
How about something like
proj_to_normalized, to communicate that we're projecting the given representation vector to the normalized configuration variety for the given element type?The
repisn't working as intended in the namenormalize_rep_mutanyway. I added it to emphasize that this method acts on the representation vector provided as an argument, not the vector representing the element the method is called from. This makes it different from thenormalize_mutmethods for nalgebra matrices.(Using an associated function instead of a method would be the best way to clarify this, but I don't think it's possible to call associated functions from trait objects. This complaint about the limitation and this unsuccessful attempt to lift the limitation seem to demonstrate that it exists, or at least used to exist. If you want, though, I can try again to use an associated function and look at exactly what compiler error it produced.)
Sure, any of
project_to_canonicalorproject_to_normalorproject_to_normalizedorcanonical_projectionornormal_projectionwould be fine, whatever seems simplest/clearest to you. I'd suggest that with a name that's already this long, there's no point in abbreviating withproj-- just use whatever full wordprojwould be an abbreviation for. Using a method is fine, don't worry about the associated function thing.I just pushed a commit with better names (
54b34e0582). My look-over before pushing was a bit cursory; I'll look at it harder later today.I've looked at the renaming commit again and I still think it's fine, so let me know what you think!
check-cfglintThe branch is out-of-date. I would just click "update branch by rebase" but I am not sure if you want me rebasing your branches, so please bring it up-to-date as you see fit (and let me know if you are OK with my hitting the "update branch by rebase" button in the future).
I am happy with the names but it seems cumbersome that there are methods project_XXX_to_normalized and then two boilerplate implementations of project_to_normalized that just forward to the project_XXX versions. Could this method be refactored so that the code for project_to_normalized for each element type is just in one place where it makes the most sense to be located, and avoid this verbose forwarding pattern?
54b34e0582tof332f755e0I think asking me before hitting the "update branch by rebase" button is a good policy. In this case, I appreciated the chance to look over and test a local rebase before force-pushing the updated branch to the server. I've now force-pushed, as shown above.
Yes, I think this can profitably be cleaned up now. For now, I'll try to do it with small-scale changes. We may want to do a deeper refactoring later, especially if we keep using this pattern.
My first attempt to avoid forwarding the
project_to_normalizedimplementations was:project_to_normalizeda method of a newengine::EngineElementtrait.engine::EngineElementa supertrait ofassembly::Element.engine::EngineElementfor each element within theelementmodule.We've already used this pattern with traits like
outline::OutlineItemanddisplay::DisplayItem, and I like the way it works in general. However, there's an obstacle to using it in this case: the standalone dyna3 library target we set up in pull request #24. The pattern we've been using would make theelementmodule depend on theassemblymodule, which is left out of the standalone library target because it depends in turn on many parts of the interface.Possible workarounds
engine::EngineElementinto theassemblymodule. However, this would leak engine-specific code out of theenginemodule, which we've been trying to avoid.EngineElementtrait and its implementations in a new intermediate module, called something likeengine-bindings, which is allowed to contain engine-specific code but is left out of the standalone library target.During our meeting, we decided that cleaning up the forwarded
project_to_normalizedimplementations will best be done in a dedicated pull request that encapsulates element representation data types within theenginemodule, so theassemblymodule no longer has to know that each element is represented by aDVector<f64>. I'll file an issue for that later today.Tests pass, nudging around by hand looks good, and we are deferring the refactoring (don't forget to file that issue), so merging.
assemblymodule #90Whoops, thanks for the reminder! This is now issue #90.