Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add drawing path object creation, styling, and clip features. #27

Merged
merged 13 commits into from
Nov 9, 2023

Conversation

mpaperno
Copy link
Collaborator

@mpaperno mpaperno commented Oct 3, 2023

This adds a new way of creating image layers by specifying drawing paths which are independent of styling attributes (literally creating Path2D objects). Paths can then be fully styled using all available canvas styling attributes (vs. the subsets we've allowed so far), or can be used to create clipping masks. The concept should be familiar to anyone who's worked with vector graphics, though perhaps more advanced for a casual user.

I added 3 new actions for creating paths -- a basic rectangle with border radius, an ellipse (full or partial arcs), and the powerhouse "freeform" path which basically lets you draw anything you want (assuming you can fit it into the TP action data field :-). All the path actions have alignment and offset properties (like the current Text action) to help position them w/out necessarily requiring a transform action.

Path layers can also be combined together to form more complex shapes using boolean operations (union, difference, etc), or simply "added" together to form a group (for example to apply a transformation to the whole group).

There is a new action to apply styling (fill/stroke attributes) to paths, and another to create a clipping area/mask using preceding path layer(s). A style or clip mask can be applied to multiple path layers at once, eg. for "centralized" styling or to create more complex clip shapes.

The "freeform" path accepts lists (arrays) of X,Y coordinates for one or more paths, but most interestingly also SVG path strings. These are fairly simple but incredibly flexible, allowing arcs and Bezier curves, and ability to use relative coordinates. With some care for sizing, one can literally copy paths out of SVG files and use them. Then style them as desired, vs. using static images. As a more advanced/obscure feature, the freeform path action will also accept/evaluate a JS function which returns either point array(s) or an SVG path.

Another difference with paths is how they're transformed, because the transforms act on the paths themselves, not the overall canvas. This gives them proper origin points for things like scaling and rotation, default being the center of the overall shape, not the canvas. For example an arrow shaped path positioned at top right of the canvas and then rotated with a transform will spin around its own center point, as one might expect, vs. being spun around the center of the overall image if the whole canvas is being transformed.

I like somewhat over-the-top examples, so here's one.... keeping with the aviation theme :)
image

The only static image used here is the inside compass ticks with degrees/directions. Everything else is paths or text layers (except for a SRG making an appearance as the magenta "turn trend indicator" just under the heading readout). All the text outline boxes are individual paths and then styled all at once with one action. The outer short tick marks are drawn using a little JS function which returns arrays of lines to draw, embedded in the freeform path's data field. Now the number of actions used for this "icon" is getting beyond what TP can handle, taking several seconds for any update of the flow... but the plugin itself has no problem at all animating all these components in real time.

Partly that's because paths are quick/efficient to draw vs. images, but also because paths can be cached (as actual Path2D objects). As long as the actual path isn't modified, each future placement of it onto the canvas is nearly free, so to speak. Interpretation of the input only needs to happen once (especially relevant for more complex SVG paths or functions returning paths). Changes in styling or transformation of the path does not affect the cached version.

In addition I also refactored Transformations so they compute a matrix which can be cached and applied all at once vs. needing to be rebuilt/applied incrementally on each rendering round.

I added all the new actions in their own grouping with "Paths" prefix. The list now looks like so:
image

And the new actions, in same order as in the actions list:
image

For further updates (not necessarily to this PR), I want to add an "Update Color" action which would set a fill/stroke color on any layer element for which that makes sense. I also have an idea of how to use the current "Update Value" to set arbitrary properties on any layer element (by using property names). Another action I'd like is a way to enable/disable individual layers using an update-type action. And for ultimate flexibility I want to add a "run script file" action which will load a JS file and pass it a canvas context to draw on and any optional arguments the user specifies (that's the next plan for that gauge example above since it's too complex for TP's UI).

I could add an action to generate "tick marks" paths with various options.... maybe with text labels (or maybe separate one for that). Not sure if that's useful or just clutter. I remember us looking at some "gauge builder" app some time ago, talking about recreating that... I think that would complete those requirements. :) Can't think of what else may be generally useful.

I am aware/do have concern that this adds more complexity, even if it's just visually in the number of available actions (which of course no one is forced to use, but I don't know if that bothers or confuses people perhaps). So it could be time for an "advanced" version (we could call it "Dynamic Inkscape"... lol). I mean a more basic version could easily just hide some of the actions using a different entry.tp, if that's an issue.

Thanks for reading, sorry for the length!

-Max

…Rectangle types; Add Rectangle.center property; Annotate missing Rectangle property types.
…eates a cached `DOMMatrix` from the given parameters which is used to set the transforms on canvas or paths; Add new `TransformScope.Reset` for explicitly resetting `UntilReset` type transforms (which can now be nested); Reset functionality is moved to `DynamicIcon` (subsequent commit); Includes relevant gen_entry.js changes.
… drawing, and transforming Path2D layers; Adds support for nested `ResetUntil`/`Reset` type transforms.
… auto-scale line width; Add proper scaling for line dash and dash offset properties when rendering; Round scaled line width (and dashes) to 4 decimals.
… action (`IPathHandler` implementation); Allow setting `strokeOver` property from action data; Pass drawing area rectangle through to `StrokeStyle.render()`.
…ts: `ClippingMask`, `EllipsePath`, `FreeformPath`, `RectanglePath`.
… and update `LinearProgressBar` accordingly; Round size calculations to reasonable precision.
…; Rename "Rounded Shape" action to "Styled Rectangle".
@mpaperno mpaperno merged commit 39a0dc5 into main Nov 9, 2023
@mpaperno mpaperno deleted the mp/feat_paths branch November 9, 2023 05:50
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.

1 participant