Dispatch normalization routines correctly #87
No reviewers
Labels
No labels
bug
design
duplicate
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_test
confirms 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_rep
do 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_rep
trait method, however, I've made it do normalization in both its implementation forSphere
and 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::log
withconsole_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 date
fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>>;
// normalize a representation vector for this kind of element
Do 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_mut
for spheres actually changes the equivalence class. That's because the only thing we usenormalize_rep_mut
for 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
rep
isn't working as intended in the namenormalize_rep_mut
anyway. 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_mut
methods 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_canonical
orproject_to_normal
orproject_to_normalized
orcanonical_projection
ornormal_projection
would 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 wordproj
would 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-cfg
lintThe 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?
54b34e0582
tof332f755e0
I 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_normalized
implementations was:project_to_normalized
a method of a newengine::EngineElement
trait.engine::EngineElement
a supertrait ofassembly::Element
.engine::EngineElement
for each element within theelement
module.We've already used this pattern with traits like
outline::OutlineItem
anddisplay::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 theelement
module depend on theassembly
module, which is left out of the standalone library target because it depends in turn on many parts of the interface.Possible workarounds
engine::EngineElement
into theassembly
module. However, this would leak engine-specific code out of theengine
module, which we've been trying to avoid.EngineElement
trait 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_normalized
implementations will best be done in a dedicated pull request that encapsulates element representation data types within theengine
module, so theassembly
module 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.
assembly
module #90Whoops, thanks for the reminder! This is now issue #90.