bluefishjs / bluefish-archive

A SolidJS diagramming framework
https://bluefishjs.org
MIT License
75 stars 4 forks source link

feat/fix: Reactivity #69

Closed joshpoll closed 6 months ago

joshpoll commented 7 months ago

This PR fixes the reactivity problems we've had with the Bluefish layout system. This PR does not affect most user-facing APIs except the low-level layout interface, which we expect to churn a little bit over time. This PR also slightly breaks useError, because errors no longer happen in an enclosing context. We will have to return to this since it wasn't working correctly before this PR anyway.

This makes two big changes.

BBox Linear System Solver

First we replace bbox propagation logic with a linear system solver. This makes the system much faster and more correct.

Previously we were using a bunch of handwritten rewrite rules to propagate bounding box values, but this was really slow (a bunch of conditionals) and error-prone b/c we had to implement each rule by hand and I forgot some of the rules.

The insight here is that e.g. for the x-axis a user has to specify at most two of left, centerX, right, and width to fully define all the others. We now maintain a set of linear equations that the user has specified. Once there are two of them, we solve these equations and use the results to compute the x-axis dimensions. The equations are in terms of centerX and width, so for example if a user specifies left = 50 that gets translated to 1 * centerX - 0.5 * width = 50, which we represent as [[1, -0.5], 50]. The resulting system is two equations in two unknowns, which is straightforward to solve.

Synchronous Layout System

Previously we were relying on createEffect, createRenderEffect, and reactivity to update the scenegraph, but this didn't actually work correctly. That lead to a bunch of problems with using Solid's reactivity for specifying diagrams. This PR introduces a synchronous approach. Layouts and Refs return an object { layout, jsx } that is the layout function and the JSX element to render. Parent layouts call their child layout synchronously. Every time a layout node changes somewhere in the scenegraph, we wipe the scenegraph and rebuild it. This turns out to be fast enough for now.

joshpoll commented 6 months ago

thanks @vezwork! I've incorporated your review. You're right that this is really several PRs in one. Since the project isn't very mature yet, I think it's better for dev velocity to just keep everything in one

joshpoll commented 6 months ago

Thanks! I made some minor updates to the comments and will merge now.