Integrate engine into application prototype #15

Merged
glen merged 24 commits from engine-integration into main 2024-11-12 00:46:16 +00:00
3 changed files with 17 additions and 17 deletions
Showing only changes of commit 5839882ed7 - Show all commits

View File

@ -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();

View File

@ -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
Review

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.

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.
Review

is this the string like 0.5 that represents the specified lorentz_prod of the constraint in the outline view?

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.

> is this the string like 0.5 that represents the specified lorentz_prod of the constraint in the outline view? 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.
Review

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.

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.
Review

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.

That seems workable. I could probably implement it by moving the Lorentz product string into the LorentzProductInput component introduced in pull request #16.

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.

The likely costs of this will be:

  • The work I do to get this feature into this PR will be partly or entirely scrapped and redone for PR #16.
  • Merging #16 will take longer because of the additional merge conflicts.

Let me know whether you'd still like me to go ahead.

> 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. That seems workable. I could probably implement it by moving the Lorentz product string into the `LorentzProductInput` component introduced in pull request #16. > 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. The likely costs of this will be: - The work I do to get this feature into this PR will be partly or entirely scrapped and redone for PR #16. - Merging #16 will take longer because of the additional merge conflicts. Let me know whether you'd still like me to go ahead.
Review

Please file an issue to do this immediately after #16.

Please file an issue to do this immediately after #16.
pub lorentz_prod_valid: Signal<bool>,
glen marked this conversation as resolved
Review

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 applies to it too.

> 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.
Review

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());
}
}
});

View File

@ -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)
};
}
)