Integrate engine into application prototype #15
@ -214,13 +214,13 @@ pub fn AddRemove() -> View {
|
||||
(subject_vec[0].clone(), subject_vec[1].clone())
|
||||
}
|
||||
);
|
||||
let rep = create_signal(0.0);
|
||||
let lorentz_prod = create_signal(0.0);
|
||||
let active = create_signal(true);
|
||||
state.assembly.insert_constraint(Constraint {
|
||||
subjects: subjects,
|
||||
rep: rep,
|
||||
rep_text: create_signal(String::new()),
|
||||
rep_valid: create_signal(false),
|
||||
lorentz_prod: lorentz_prod,
|
||||
lorentz_prod_text: create_signal(String::new()),
|
||||
lorentz_prod_valid: create_signal(false),
|
||||
active: active,
|
||||
});
|
||||
state.assembly.realize();
|
||||
@ -236,7 +236,7 @@ pub fn AddRemove() -> View {
|
||||
&JsValue::from(cst.subjects.0),
|
||||
&JsValue::from(cst.subjects.1),
|
||||
&JsValue::from(":"),
|
||||
&JsValue::from(cst.rep.get_untracked())
|
||||
&JsValue::from(cst.lorentz_prod.get_untracked())
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -244,10 +244,10 @@ pub fn AddRemove() -> View {
|
||||
// update the realization when the constraint activated, or
|
||||
// edited while active
|
||||
create_effect(move || {
|
||||
rep.track();
|
||||
lorentz_prod.track();
|
||||
console::log_2(
|
||||
&JsValue::from("Lorentz product updated to"),
|
||||
&JsValue::from(rep.get_untracked())
|
||||
&JsValue::from(lorentz_prod.get_untracked())
|
||||
);
|
||||
if active.get() {
|
||||
state.assembly.realize();
|
||||
|
@ -32,9 +32,9 @@ pub struct Element {
|
||||
#[derive(Clone)]
|
||||
pub struct Constraint {
|
||||
pub subjects: (ElementKey, ElementKey),
|
||||
pub rep: Signal<f64>,
|
||||
pub rep_text: Signal<String>,
|
||||
pub rep_valid: Signal<bool>,
|
||||
pub lorentz_prod: Signal<f64>,
|
||||
pub lorentz_prod_text: Signal<String>,
|
||||
glen marked this conversation as resolved
|
||||
pub lorentz_prod_valid: Signal<bool>,
|
||||
glen marked this conversation as resolved
glen
commented
This signal keeps track of whether The realization routine only enforces a constraint when Like > It's unclear what it means for a lorentz_product to be "valid"
This signal keeps track of whether `lorentz_prod_text` can be parsed to a floating point number. Maybe the name `lorentz_prod_specified` would be more descriptive?
The realization routine only enforces a constraint when `lorentz_prod_valid` is true. This helps ensure that the engine is always enforcing the constraints that the user is looking at. In pull request #16, the interface flags constraints that aren't being enforced because the Lorentz product can't be parsed.
Like `lorentz_prod_text`, this signal is closer to the views than to the model, so the discussion about separation of concerns from [this comment](https://code.studioinfinity.org/glen/dyna3/pulls/15#issuecomment-1851) applies to it too.
glen
commented
Yes, I think this will naturally go somewhere else and be called something clearer when the reactive strings that are part of the view go into the view. Yes, I think this will naturally go somewhere else and be called something clearer when the reactive strings that are part of the view go into the view.
|
||||
pub active: Signal<bool>
|
||||
}
|
||||
|
||||
@ -128,11 +128,11 @@ impl Assembly {
|
||||
let mut gram_to_be = PartialMatrix::new();
|
||||
self.constraints.with_untracked(|csts| {
|
||||
for (_, cst) in csts {
|
||||
if cst.active.get_untracked() && cst.rep_valid.get_untracked() {
|
||||
if cst.active.get_untracked() && cst.lorentz_prod_valid.get_untracked() {
|
||||
let subjects = cst.subjects;
|
||||
let row = elts[subjects.0].index;
|
||||
let col = elts[subjects.1].index;
|
||||
gram_to_be.push_sym(row, col, cst.rep.get_untracked());
|
||||
gram_to_be.push_sym(row, col, cst.lorentz_prod.get_untracked());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -17,15 +17,15 @@ fn LorentzProductInput(constraint: Constraint) -> View {
|
||||
view! {
|
||||
input(
|
||||
r#type="text",
|
||||
bind:value=constraint.rep_text,
|
||||
bind:value=constraint.lorentz_prod_text,
|
||||
on:change=move |event: Event| {
|
||||
let target: HtmlInputElement = event.target().unwrap().unchecked_into();
|
||||
match target.value().parse::<f64>() {
|
||||
Ok(rep) => batch(|| {
|
||||
constraint.rep.set(rep);
|
||||
constraint.rep_valid.set(true);
|
||||
Ok(lorentz_prod) => batch(|| {
|
||||
constraint.lorentz_prod.set(lorentz_prod);
|
||||
constraint.lorentz_prod_valid.set(true);
|
||||
}),
|
||||
Err(_) => constraint.rep_valid.set(false)
|
||||
Err(_) => constraint.lorentz_prod_valid.set(false)
|
||||
};
|
||||
}
|
||||
)
|
||||
|
But now I am confused; is this the string like 0.5 that represents the specified lorentz_prod of the constraint in the outline view? Why is that in the assembly? Or am I misunderstanding the import of this property?
If not, it seems like a violation of separation of concerns. The platonic reality of a constraint is just the number that the lorentz product has to be. The string is just an artifact of the outline view, so it should live there. To drive that point home more, we will surely at some point want to represent Constraints in the Display view. There, we might represent them as little translucent angle symbols, perhaps labeled with the angle in radians or radians/tau. It's unlikely that the raw lorentz product would appear in the graphical view. So an altogether different string would be involved.
Yes. More generally, it's the reactive string that represents the Lorentz product in every view where it's edited or displayed as a string. Right now, this just means the two inputs that appear in the outline view, but in the future it might also include inputs in a Gram matrix view.
I agree that the model and view aspects of the Lorentz product should be separated better. One way might be to write a wrapper around
Constraint
to hold reactive values which need to stay synchronized across several views, but aren't part of the Platonic ideal assembly. However, I'd expect this way—or any other way—to lead to an extra layer of organization over the whole assembly. For that reason, I think that any attempt to do this should be its own dedicated pull request. Right now, I don't see the less-than-ideal organization as a major obstacle to developing a minimum viable product.Especially right now where the only occurrences of the string are in the outline view, it seems absolutely clear that this string (and its validity) should be housed in the view, not in the assembly. It should then be "wired up" so that when a valid string has been entered, that updates the actual constraint in the assembly; and vice-versa, if the actual constraint in the assembly becomes changed (by some other control, say), then the representing view-string should be updated to the string of the new value, and the representing validity-flag should be updated to true.
Just like in Numberscope when you enter invalid input for a parameter, it should not disable the associated entity, it should simply not (yet at that point) update it (so the last valid value will continue to be used, until there's a new valid input in the box). There should of course be some visible indication that the input is in a bad state; if you think it is important enough, that indication could in addition display that previous value that's still being used.
With this design, suppose there is a Gram view in the future. It has its input boxes with their reactive strings. When one of them is updated, that action will update the actual constraint in the Assembly. And that event in turn should update the reactive string in the Outline view. So everything remains harmonized. This is no different from dragging a point onto an entity in the Display to mean that it should have a new constraint of lying on that entity vs selecting the point and entity in the Outline view and hitting the "coincident" tool, or whatever. It's the type of thing that will come up over and over again as we create multiple ways of doing things that amount to the same action in the Assembly -- there will not be any synchronized state kept across views. They should synchronize by virtue of two-way communication with the Assembly.
I think it's important to do it this way, because if you think about actual use cases, I think it will be quite rare that there would really ever be a "reactive string that needs to stay synchronized across views". For example, as dyna3 matures, the outline view is going to express this sort of a constraint as an angle, and it will likely have a degrees or radians or radians/tau selector which will affect what string is used, while the Gram matrix will likely express it as the cosine-value of the angle, which will entail a different string.
For all these reasons, please do change the location/architecture of the reactive values that are not properly part of the model (Assembly) for this PR. Let's keep the models and the view-controllers well separated from the start. Thanks so much for your understanding!
Note that if there is a concept in the Assembly of a "turned-off constraint", then the on/off state of a constraint should be represented as such in the model, and views that display or allow control of that bit should have their needed apparatus to do so.
That seems workable. I could probably implement it by moving the Lorentz product string into the
LorentzProductInput
component introduced in pull request #16.The likely costs of this will be:
Let me know whether you'd still like me to go ahead.
Please file an issue to do this immediately after #16.