- title
- Crosscut Extension Design Jam • Alex's Notes
- dated
- Feb 2023
Part of the Crosscut Extension Design Jam
A Model of “Well-Formed” Circuits in Crosscut
Crosscut (as described in the essay) allows users to construct circuits that are not well-behaved. As an example, following circuit has a “variable” that is wired into both of the inputs of an “add” node:
This is problematic because the backwards computation wants to change the left operand and leave the top operand alone — but in this case, they’re the same thing! And so the evaluator misbehaves when the user changes the output of the “add”.
I spent some time thinking about how we might formalize some rules that can be used to determine whether a Crosscut circuit is “well-formed”. If we run these rules every time the user makes (or attempts to make) a new connection w/ meta ink, we can prevent offending connections from being made. That way only well-behaved circuits will be constructed.
I made pretty good progress on these checks, and I’m confident that we could finish them in a reasonable amount of time (maybe 1 week’s time?) if we decide that they’re important. IMO this would definitely be worth it if we wanted to make a more usable (shippable?) version of Crosscut. But we also have lots of ideas that involve changing the evaluation model / semantics of the language, and the resulting language would need a different set of well-formedness checks.
-
For details, see my hand-written notes under here.
On Degrees of Freedom, Transforms, and Wadler’s “Views”
@James Lindenbaum noted that relaxation-based systems (like the experiments that we worked on as part of the Rectoverse project) have some problems that result in a poor UX. Namely,
- the user has to write a large number of constraints to get the behavior that they want, and
- the (poor) performance of the solver tends to get in the way.
Van Overveld’s take on relaxation has better performance, but it may not be better enough for us. It also makes implementing new types of constraints more difficult — you have to do some calculus, and sometimes it gets pretty tricky.
I spent some time thinking about how we might solve these problems, and I came up with an idea that I’m excited about.
TODO(@Alessandro Warth): write this down more coherently.
-
In the meantime, here are my hand-written notes.
Processing the Results of Spatial Queries
Right now Crosscut has very limited support for spatial queries. (Other than the “count” thing, I can’t think of anything.) But there are lots of things that would be useful to support:
- all points in some region
- all lines in some region
- all intersections w/ this line
- …
I’m sure we could new operators to do these kinds of things. But how do we specify what to do with each “match” in a spatial query? Can we make it feel nice to use, and not too programming-like?
As an example, consider the way concrete ink is used to write words in the Crosscut essay:
Suppose I want to apply a 3D-like style to everything that’s written in this way, in a certain region of the canvas:
- How would I do this in Crosscut?
- Can we wire up the result of the spatial query to a circuit that creates the new points and lines?
- If new points are being created, maybe we have to use the existing abstraction mechanism? (Otherwise how do we distinguish between wanting a new point for every match of the query vs. wanting all of them to get connected to the same point?)
- Would the new points that are created be 1st-class points, like the original ones? (Probably not, I think.)
- What happens when the user moves the new points? (I think that’s fine — back-propagation would naturally make the old points move.)
- Can we write the circuit that does this by first dealing with a single “match” of a spatial query that doesn’t even exist yet, then write the spatial query, and rewire the circuit to the result of the spatial query? (It’s nice to be able to abstract after the fact, b/c sometimes you don’t know that you’re doing something that you’d like to repeat a bunch of times — advance planning shouldn’t be required, etc.)
Hiding Parts of a Model in a Reusable Component
When I was working through some of my thoughts on the “3D styling” problem described above, I drew the following circuit:
The specifics of how I’m using the port, etc. are not quite right. But the main idea is that I’m taking a point as an argument, and I’m creating another point that’s offset by 1 unit to the right, and 1 unit to the top, and drawing a line between the two points.
While it’s OK to use property selectors for this purpose, it would have been nicer to simply add (1, 1) to the original point — this could be done with a single “add” operator, since Crosscut’s operators are polymorphic. However, had I added that point to my circuit, it would have shown up at each application of this “function” which is not what I want.
@Marcel Goethals said that he explored a hiding mechanism in Habitat, but it didn’t feel great to use. I’d like to understand that better, and see if what we can do about it.
Other Interesting Ideas to Explore
- @James Lindenbaum talked about visualizing “causal chains” when the user changes something, i.e., it would be helpful for the system to highlight the changes that result from that “perturbation” (both downstream and upstream, using different colors). We could even animate little arrows, etc.
- @Marcel Goethals demoed his “raster” prototype. Being able to compute with pixel-level data (e.g., wiring the area of the intersection between two complex shapes into a Crosscut operator) could be really useful, and save us from having to do lots of work implementing computational geometry algorithms.
- A good use-case for this is @James Lindenbaum’s tides / kite foil problem.
- It would be cheap (engineering-wise) and but useful to extend the original Crosscut w/ support for user-defined operators. Not much is needed:
- user writes forward expression (
right = left + top
) - user writes backward expression (
left = right - top
)- this one is optional: if the user only provides a forward expression, the system can use Newton’s method / relaxation locally to calculate backwards!
- user gives a name to the operator (
add
). - Note: we may want to generalize this so that there can be multiple “top” operands, none of which changes in the backwards direction. This would enable the user to add some interesting things, like an angle measurer that takes 3 points as inputs and outputs an angle. (Only the point fed into the
left
input would change when change flows into theright
connection.)
- user writes forward expression (
- Can we implement loops / iteration using a sine wave, a line (at y=0), and the “intersection” spatial query? (Similar to the clock that Bret created in “Stop Drawing Dead Fish”, to make the fish blow bubbles — starts at 40:05.)
Useful Examples
These are some examples that have been useful as a “proving ground” for new ideas:
- Stacking boxes