Pewarta Proxy

📋 Server Request & Proxy Logs

Loading active proxy settings...
Developed by Khairul Adha (adhexa.id)
Mengambil log server...

⚙️ Pengaturan Proxy Target

Skip to content

Variables - a.k.a. Named Parameters - #1799

Open
phkahler wants to merge 7 commits into
solvespace:masterfrom
phkahler:variables
Open

phkahler wants to merge 7 commits into
solvespace:masterfrom
phkahler:variables

Conversation

@phkahler

@phkahler phkahler commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

This series of commits combines the previous work of @Evil-Spirit @iscgar @dgramop @ruevs @phkahler and others who contributed to the design discussions in order to implement named parameters in solvespace. Some commits from old branches were partially borrowed and a lot of cleanup and corner cases were corrected. A couple new member variable were added but they are only written to files when the new features are used on a sketch (our standard compatibility guarantee). We shall not overload opA to store exprScale ;-)

The approach uses requests within a group to define variables (named parameters) that can be used in expressions on a sketch. These can be locked via the little lock icon or by typing a fixed value. When typing a value, expressions using previously defined parameters can be used, though this probably doesn't seem useful yet - it will. Relation constraints are also implemented so you can write "width * height = 400" on a sketch so a rectangle with those names as dimensions will maintain a constant area as you drag it.

Probably the most controversial decision was to allow relations with undefined names to dynamically create requests for those names. It's a compromise that allows you to skip the variable declarations prior to use - although you will get a popup in case you made a typo.

Please test this PR. I'm planning another big one that will build on top of this but that is for another day so I need to know if there are design issues here.

phkahler and others added 7 commits October 27, 2026 19:50
A named parameter is a request with a name and a single parameter, and no
entities. Parameters are listed in each group's page in the text window,
including the references group, where they can be added, renamed, given
a value, locked or freed, and deleted. Giving a parameter a value locks it.
A locked parameter is left out of the solver's system, so it is a constant;
a free one is solved for like any other parameter.

Each group has a table of the names it can use: those of the previous
group in order, plus its own, which replace any with the same name. The
table is rebuilt on every regeneration and is not saved.

A dimension can be given as an expression that uses these names. The
expression is kept as typed in Constraint::expression, and exprScale
converts its value to mm (the units it was typed in) and holds the sign
of a signed distance. valA is kept equal to the value of the expression,
so reference dimensions, export, labels and anything else that reads valA
see the actual dimension. Both fields are left out of the file when there
is no expression, so files that don't use named parameters are unchanged.

A dimension whose expression uses a name that isn't defined generates no
equation, and the group reports it with the new UNKNOWN_NAME solve result.
The constraint and its expression are kept.

The expression parser takes an optional table of names. The unimplemented
VARIABLE op is removed, so names may start with an uppercase letter, and
an unknown capitalized word is an error instead of an assertion.

Also:
- Toggling "show as radius" on a dimension with an expression rewrites
  the expression so the circle keeps its size.
- Pasted dimensions keep their expressions, scaled with the paste.
- Step dimension refuses a dimension that is set by an expression.
A named parameter's value can be an expression that uses the named
parameters of earlier groups. The expression is kept in the new
Request::expression field, saved only when it isn't empty, and the
parameter's value is set from it on every regeneration, before its group
is solved. A parameter with an expression is always locked. Each group
now also keeps inheritedParamNames, the names of earlier groups, which
these expressions are parsed against, so they can't use names from their
own group.

If a parameter's expression uses a name that isn't defined, the parameter
keeps its last value, its group fails with UNKNOWN_NAME, and the group's
page shows the expression in the error color, with the reason as a
tooltip. The failure page lists such parameters along with any dimensions
that use undefined names.

An unknown-name failure now takes the text window to the group's failure
page only when it first happens. While a group keeps failing for the same
reason, regenerating leaves the user on the page they are on, so they can
add the missing name back.

The parameter rows in a group's page are laid out in columns: a lock icon
that locks or frees the parameter, the name, the value (or expression),
and [change] and [del] links. Names longer than the column are shortened,
with the whole name as a tooltip. The lock icons are new glyphs in the
private use area, and the text window can now show a tooltip for any text,
with %T in Printf().

A new named parameter starts with a value of 5.
A relation is a new constraint type, placed and drawn like a comment: at
a selected point, or where the user clicks next. It is in the Constrain
menu, with the '/' key, and in the toolbar with a new icon. Its text can
be edited like a comment's; it doesn't generate any equations yet.
A relation's text, kept in Constraint::expression, is an equation such as
"w*h = 400". It is split at the '=', and each side is parsed as an
expression that can use the named parameters in scope in its group; the
relation generates the one equation left - right = 0. Free parameters of
the group are solved for; those of earlier groups are constants. A
relation's text needs no units scale, since named parameters are unitless.

When a relation is typed, it must have exactly one '=', an expression on
each side, and use at least one named parameter; the group's degrees of
freedom are checked again. A new relation has no text, shows
"NEW RELATION -- DOUBLE-CLICK TO EDIT", and generates no equation until
it is edited. A relation that uses a name that isn't defined is reported
with the UNKNOWN_NAME solve result, like a dimension.

A relation isn't a dimension: it can't be made a reference, it still
applies when all of a group's dimensions are reference, and it can't be
stepped. Relations are copied and pasted like comments, and shown as
RELATION when selected.

Co-authored-by: Dhruv Gramopadhye <dgramopadhye@gmail.com>
When a dimension or relation is typed with names that aren't defined in
its group, those names become new free named parameters in that group,
as if they had been added in the text window. The text is first checked
as though the names existed, so nothing is created if it isn't otherwise
valid. The parameters are created in the same undo step as the edit, and
a message lists them, so that a mistyped name is noticed.

A dimension that is just one new name starts it at the dimension's
current value, in the units it was typed in, so nothing moves; other new
parameters start at the default value.

Names are only created when a constraint is typed. A name that later
goes missing is still reported as unknown.

Expr::FindUndefinedNames() finds the names, reading the text the way the
parser does, and Constraint::CreateNamedParams() creates the parameters.
@phkahler

Copy link
Copy Markdown
Member Author

@hlorus I think you should have a look at this to see if it's going to impact your project, or maybe you'll be able to use the feature. I'd hate to merge something this big without your input. I'm really not familiar with using the library but I see that it's updated with the new solve results and such. Just looking for your feedback as the most prominent user of the library.

@hlorus

hlorus commented Oct 29, 2026

Copy link
Copy Markdown

Thanks for the ping. No impact on us: the new enum values are appended, slvs.h is untouched, and UNKNOWN_NAME is unreachable through the library since nothing there parses an expression. Safe to merge from our side.

For the feature itself, named parameters and expression dimensions we'd do with Blender's own custom properties and drivers (dimension values are already stored that way so they can be driven), which also makes a parameter usable by modifiers and geometry nodes.

Relations are the part we can't do: w * h = 400 holding while you drag needs the parameters to be unknowns in the same solve. If the library ever exposes a parameter no entity owns plus a constraint carrying an equation, we'd use it, preferably structured rather than a string.

Bigger wish while you're in there: a first-class area constraint. Area over a closed loop is an equation over the point coordinates, so parameter expressions can't reach it, and it's what people actually ask for.

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

Successfully merging this pull request may close these issues.

4 participants