Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constraint solver syntax #21

Open
Irev-Dev opened this issue Nov 6, 2024 · 7 comments
Open

Constraint solver syntax #21

Irev-Dev opened this issue Nov 6, 2024 · 7 comments

Comments

@Irev-Dev
Copy link

Irev-Dev commented Nov 6, 2024

There's lots proposals for a constraint syntax, not at all centralised so I'm adding to the chaos with another proposal

There's my original non-solver constraints write up KittyCAD/modeling-app#111
Josh's https://github.com/KittyCAD/notes/blob/main/docs/personal/Gomez-Josh/kcl/kcl-refactor.md
Jon's #13
Nick's https://gist.github.com/nrc/efdec535bd961a0f7b470480676fa9eb

And here's my new write up for a solver syntax, I hope what I can contribute thinking through the code-mods and how individual bits of UI ties back to the code.

I have a handful of principles that should go into the design of the solver syntax. (I agree with lots that's been said already so you'll notice I'm repeating ideas from others below).

  1. We make a clean break from the chaining profile syntax
  2. A sketch is nothing but data
  3. A sketch should be in a special block sketch mySketch {...}
  4. Normal expressions are not allowed in said block, no variable declarations, no if statements, no loops. One exception would be basic math like 5+2 or myVar / 2 to be used as values.
  5. There is no difference between a "Constraint" and a "Dimension"
  6. Each constraint has a UI representation (when in sketch mode)
  7. 2D Solver should be implemented in KCL (as it must be compatible with partial/mock executions) 3D constraints are completely separate, and not related, I think we should use the solidwork's terminology "mates" to minimise confusion.
  8. Old syntax should stay, (but maybe not the UI for it).

Expanded on a little more

  1. The whole chaining syntax was added very much with the implicit constraints in mind, trying to merge the two together is fraught, if we're going with a different paradigm it needs syntax designed for it.
  2. Going from implict constraints to a solver syntax can be conceptualised as the constraints being solved in the code, now being solved at run time (by the solver). Then it's useful to conceptualise the syntax as just declaring data ready to be fed into the solver, relates strongly to 3 and 4.

3 & 4. Allowing dynamic language features smells like a huge foot gun that we should avoid in the first pass, and an easy way to do this is by giving it's own special block sketch mySketch {...} or similar to limit what can be used inside. Bit of an aside, because of the lack of chaining, we don't even have the concept of a profile tightly defined, so we should get "multi-profiles" for free

  1. Both Constraints and Dimensions are simply feeding info to solve, they should be grouped together in the syntax
  2. How the code get represented in the UI beyond the segments themselves has be to thought of ahead of time, and simple mental models are also important, So each constraint being something declared in the code and interactable in the UI is a great tie together, This will make more sense in the example code below.
  3. Sketch mode relies on the fact that we can run the 2d only part of the script locally, especially for respect constraints while animating edits (user dragging a handle) and the same forces are at place here. Possible performance issue here if the solver is slow, (which I doubt for anything but massive sketches) when using it for animating edits, if that's the case we can separate animating the currently segments the user is editing and letting the solver catch up with the rest of the sketch greyed out or similar.
  4. Considering it will be as simple as not deleting the old std lib functions they might as well stay, also relates to 4 since these are explicitly defining each segment, they are going to be much less of a foot gun when it comes to if statements, loops etc, so they'll still have a place for library authors or higher level abstractions.

To make the above concrete with how it will fit into the UI here a simple example:

In the below I didn't step through the most clicking to add each one of these segments because it's straight forward
First only the point1 { x: 0, y: 0.5 } is added to the block, and then on the second click another point is added point2 { x: 10, y: 1, } which allows a segment to be added between the two with line1 straight { start: point1, end: point2 } and so and so forth.

Other things to point out

  • The faint purple text is not intended to be part of the UI, instead hopefully will help with following along
  • You can see what I mean by the block is just data, I almost made it just JSON, but we can take some liberties to make it more ergonomic. I'm not tied to many of the specifics of this, beside that the special block itself is important, also having three explicit sections for points, segments and constraints is useful.

Image

The user selects the top and bottom segments and applies the horizontal constraint and the UI:

  • adds the horizontal constraints horizontal { segs: [line1, line3] }, it might be best to list these each explicitly, horizontal line1, horizontal line3 but either works.
  • Each of the constraints has a UI element added for it, it's the blue dashes next to the segments, we can make these clickable to remove or explainer tool-tips etc.
  • Information determined to be redundant by the solver is remove from the "points" section, In this case Points 2 and 4 no longer need y information, as that follows from the constraints.

The code mode for this is straight forward, it's basically adding horizontal { segs: [<User's selections>] }, will likely need to get the solver to inform a second mod as to what information is now redundant, in order to remove the y information from this example.

Image

To make sure it's not an after thought, let's consider what happens when a constraint is removed (maybe through the feature tree, or interacting with the symbol in the sketch), either way the user removes the horizontal constraint from the top segment. The obvious thing is that line3 is removed from the horizontal constraint, but from the previous step we removed y information from point2 that we need now. In the same manner that is done with the current constraints is we can pull segment information from the last execution to backfill this information to add y: 0.5, this basically the information needed so that we can remove the constraint without the segment moving. There's no need to try and restore the value before the constraint was applied in the first place.

Image

Let's also touch on editing the lines by dragging handles, we'll look at dragging two separate points, one partially constrained, the other not.
Below point2 is trying to be dragged down and to the right, but because it's constrained, the point only moves horizontally. The code mod here is really simple. From the mouse drag, we get updated XY coords, but when applying them to point2 { x: 12.3 } because there's no y component to change, only the x will update

Image

Where as the unconstrained point4 gets both x and y updated in the code mode.

Image

We'll continue on with the constraints, add the horizontal constraint back in

Image

And now two vertical constraints. Notice point4 no longer needs any XY info.

Image

User selects line4 and point3, adds a perpendicular distance constraint, this removes XY info from point2. This perpendicular distance is really a dimension, but as pointed out earlier, I don't think we should differentiate. This new constraint needs a UI element, and in this case it's obvious, it's the dimension overlay. giving a easy to way edit this after the fact as well.

The meta property is for us to dump a base64 string or similar for where to put the dimension overlay, because we can try and put it in an intelligent place and not let users move it around, but I think that would be a hard sell, and with code being the source of truth, we need to store it somewhere in the script, open to other ideas though.

Image

Another perpendicular distance constraint

Image

And finally we're just going to lock off point1 as fixed, (User selects this point and adds a fixed point constraint), again new constraint needs a UI element, the one I added is very goofy, not a suggestion for what this icon should actually be.

Image

Final comment here is that this sketch has been fully constrained, and the mental model is really simple when looking at the code, once all of the info is removed from the original points then it's constrained, there will be a few more nuances when it comes to non-straight segments, but I think we can extend this mental model.

I did not mention anything about tags here, which are not needed for use within sketches anymore, but are still needed for selecting faces and edges and etc once the sketch is extruded or other.

Relationship expressions like ||line2|| = 0.5 * ||line3|| from Jon's proposal I think is a cool concept, but not actually something ME really expect so not sure we should aim for this initially, when a equalLength {segs: [line2, line3]} would suffice (and the 0.5 * I doubt will get used much)

@adamchalmers
Copy link
Collaborator

adamchalmers commented Nov 6, 2024

And now two vertical constraints. Notice point4 no longer needs any XY info.

The XY info have been removed from point2 instead, right? Or am I misunderstanding. My impression is that one of those two points doesn't need any XY information anymore.

The meta property is for us to dump a base64 string or similar for where to put the dimension overlay, because we can try and put it in an intelligent place and not let users move it around, but I think that would be a hard sell, and with code being the source of truth, we need to store it somewhere in the script, open to other ideas though.

Why shouldn't the constraint's visible location in the 2D sketch view just be determined/locked onto one place? E.g. if you're constraining a distance, label that distance. If you're constraining that two lines are equal, put a little label on both lines, etc. If we do have to make it user-configurable, I'd prefer something meaningful to the user (e.g. "visuals: { anchorOn: point2 }" or "visuals: { centeredBetween: [line2, line4]} etc)". If it's base64-encoded binary data, there's no real way to resolve git conflicts and the program becomes a lot less readable.

Some other questions:

  • How can two sketch blocks interact? I think sketches from different blocks need the ability to reference each other's paths/points, so that we can make physically consistent dimensions across different planes. A sketch on the XY plane should be able to use the same height as a gap in a sketch on the XZ plane.
  • How does this become parametric? If I have a length defined somewhere else in the program, how do I use it to constrain a line in this block? Can blocks take parameters? If so, are they basically just functions that return a sketch? Because I like that idea.

@nrc
Copy link

nrc commented Nov 6, 2024

Minor note: #20 is an updated version of the gist at the start of the issue

@nrc
Copy link

nrc commented Nov 6, 2024

  1. Normal expressions are not allowed in said block, no variable declarations, no if statements, no loops. One exception would be basic math like 5+2 or myVar / 2 to be used as values.

Is this something you think is desirable long-term, or is it purely to make things easier to implement?

This seems like the most dramatic change proposed so far in that it is really a different paradigm for KCL. As you say in point 2, this is just data, not programming. I'm not sure how this idea of sketches as data/constraints works with variables or functions or other programming concepts we make heavy use of at the moment.

What do you envisage happening outside the sketch? E.g., what does extrude look like?

I guess bigger question, if KCL is just a data format (or mostly a data format with some programming around the edges) what is the key value proposition or distinguishing feature of KMA?

@nrc
Copy link

nrc commented Nov 6, 2024

  1. 2D Solver should be implemented in KCL

This seems fraught to me (not that I have a better solution, sketch mode is just awkward). How would it work? It seems like KCL would need knowledge of geometry which is currently only known in the engine. E.g., if there is a constrain that a line must be parallel to the tangent of a curve at a point, do we duplicate all the logic about every curve in the engine and in KCL? Does the engine have API to get this info? (But then, we are introducing possible latency issues by forcing round trips). And do we duplicate the constraint solver in KCL and the engine too? Or only send resolved constraints to the engine, but in that case the API of the engine does not seem like it would be fit for purpose as a CAD component for uses without KCL.

@adamchalmers
Copy link
Collaborator

adamchalmers commented Nov 6, 2024

  1. Normal expressions are not allowed in said block, no variable declarations, no if statements, no loops. One exception would be basic math like 5+2 or myVar / 2 to be used as values.

Is this something you think is desirable long-term, or is it purely to make things easier to implement?

This seems like the most dramatic change proposed so far in that it is really a different paradigm for KCL. As you say in point 2, this is just data, not programming. I'm not sure how this idea of sketches as data/constraints works with variables or functions or other programming concepts we make heavy use of at the moment.

What do you envisage happening outside the sketch? E.g., what does extrude look like?

I guess bigger question, if KCL is just a data format (or mostly a data format with some programming around the edges) what is the key value proposition or distinguishing feature of KMA?

Yeah, big concerns about this -- things like my procedural gear would not be possible without dynamic definitions of sketch lines.

Distinguishing features would still be the cloud streaming, the ability to break code into smaller functions with meaningful diffs and CRDT collaboration, copying and pasting, etc. But we'd lose a lot of the power.

@jtran
Copy link

jtran commented Nov 8, 2024

Some things I think are good:

  1. The idea that a sketch is data is nice because it doesn't matter what generates that data or how it gets generated.
  2. When the user constrains something (e.g. horizontal), its initial point is deleted from the code
  3. Nice UI integration

Some things I don't understand or I think could use work:

  1. Other than the point-and-click UI, the above leaves it unspecified as to how to generate the sketch. I.e. my first thought is that I will want code to generate the above JSON-ish data, with if-else, loops, functions, etc.
  2. "Normal expressions are not allowed in said block" so how do you build abstractions and compose them? This is a deal-breaker to me. The above seems less like a language and more like a data format. Is this what you intended? If so, I think using straight up JSON is preferable.
  3. "dump a base64 string or similar" This is a big detriment to humans reading the code. Why not make it transparent what the data is?

@nickmccleery
Copy link

Just to throw even more chaos into the mix (or cooks into the broth, or whatever)...

I read all I could find on this today, and thought it was all extremely considered and all round impressive. I threw together a quick gist with some of my thinking on it: https://gist.github.com/nickmccleery/ba3bf3078ff16addb0290df24408647c

The only bits that are in any way new really are some suggestions on preview vs. finalised state, and how we might indicate that in a code-first model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants