Let's look at rotation in a canvas UI. Rotation is hard. 💀
When we have a single shape selected, we can start dragging on a handle here and then rotate the shape. The shape rotates around its center point.
When more than one item is selected, we do something a little different. We're actually making two changes to these shapes as we rotate the group. I'll show each change separately.
We still get the angle of rotation based on the center of the bounding box, however we rotate each shape by this angle around its own center, just as we would with a single shape.
We also use this rotation to move each shape's center point around the bounding box's center point.
When we do both things at the same time, we get the correct result.
You'll notice that the bounding box isn't rotating. That's good: while shapes have individual rotations, bounding boxes are recalculated on each update.
Rotation adds some complexity to things like hit tests.
It also makes transformations a LOT more complex.
For rotated shapes in a group, we first transform the shape's original bounding box, then center and scale the shape so that it fits within that box.
Individual shapes are the hardest problem. First, we counter-rotate the drag vector (initial click point ➡️ current point) by the shape's rotation, and use that to compute a new bounding box as if it weren't rotated. This creates the correct size but the wrong position.
To fix the position, we then rotate the new bounding box's corners around its center and compare them against the shape's original rotated corners, offsetting the point so that the opposite corner/edge stays fixed.
Different shapes handle rotations differently. Circles are easy, ellipses are harder. It's mostly about finding the new bounding box.
Still some details to add too, like rotating the cursor so that it matches the angle of the dragging edge. 👀
• • •
Missing some Tweet in this thread? You can try to
force a refresh
Figma has a “wireframe view” that might help here as a fallback, if it means keeping images etc out of memory, though it would be up to the app to switch into that. (And actually I’m not even sure if that would work)
For tldraw we have a limit of shapes per page and pages per project but it’s still theoretically possible to crash it out via memory depending on your browser.
Here's the full interaction, complete with hover indicators.
|
Note that you can interact with text directly either: a) when editing text or b) when the text tool is selected. This will mean you can't create text on top of other text, but I'm guessing this is okay.
In @figma, holding shift while drawing a selection box over items will: 1. select deselected items 2. deselect selected items
Is this the right behavior? Have you ever accidentally selected / deselected items while shift-selecting?
@figma I remember working out some more complicated logic here with a rule like "if any new items are being added to the selection, don't deselect any other items"
Remind me next time to migrate the database before shipping runtime validation 💀
In tldraw’s beta db, there were lots of different versions of our data scattered around, including some from the wild times before we wrote client-side migrations, and some that just included broken data, x = NaN etc.
We’d written validation in order to catch this type of bad data when it came into the app. We didn’t write any recovery from the bad data, the app would just throw as soon as it ran into it.
The ChatGPT thing really made me appreciate how quickly AI goes from novelty to expectation. Airplane Wi-Fi phenomenon
The bar just keeps getting higher, meaning a lot of cool stuff is going to fall into the “neat? I guess?” range
Also any time that AI creates an aesthetic then the fashion demon is in the room. Those AI avatars are going to look extremely 2022, which is fine for now but not for 2023.
Another canvas battle: what happens if you start drawing a selection box and then scroll using your mouse wheel / trackpad?
Most apps only update the selection box when you move your cursor. The correct behavior (imo) is to update that selection box whenever your cursor's position in the document changes.
An app translates the cursor's screen position into document position based on where the camera is. If the camera changes (e.g. due to a scroll or zoom) then the cursor position should change too—and update that selection box!