penrose / penrose

Create beautiful diagrams just by typing notation in plain text.
https://penrose.cs.cmu.edu
MIT License
7.55k stars 348 forks source link

Running the optimizer asynchronously #1254

Open wodeni opened 1 year ago

wodeni commented 1 year ago

Background

1092 ported the optimizer to Rust and we can now run the optimizer as a WebAssembly module in the browser. One benefit is that the optimizer can run in Web Workers since WASM modules are cloneable. Since the TS port, we've been running the optimizer on the UI thread with various hacks to unblock the UI (e.g. #1017). This issue attempts to map out a new design for the optimization API for the multi-threaded usage. Much of this is learned through experimentation in #1207.

Make the optimizer interruptible

Running the optimizer synchronously is not ideal because it may take an unknown amount of time per step. For a sufficiently complex diagram, the default 10000 steps might take seconds. With autostep on, the UI loop will request optimizer steps at a much faster rate. Similar to how requestAnimationFrame can be cancelled, in a multi-threaded context, we'd also like to cancel an optimization request, no matter where the optimizer is in its on loop. A few implementation possibilities:

Maintain a single source of truth in the UI

Instead of including the optimization steps in the UI state machine, the UI should consider the optimizer as an asynchronous service that may or may not return results in time. This requires the UI to make and resolve requests to the optimizer asynchronously while maintaining a consistent state within the UI thread. For instance, consider running the optimizer continuously as the user is dragging a shape. Both the optimizer and the user alter the diagram, and the UI needs to decide which to render. Currently for dragging, we freeze the interacted shape during optimization (#1143), but this might not be possible in all circumstances. A couple of considerations:

wodeni commented 1 year ago

Related umbrella issue #1095