ITADN

V0.10.0: the "Shape" update

#63Pull RequestCoffeeStraw 创建于 2025-06-22
C
CoffeeStrawcommented
# Changelog ## Shapes are now iterable objects with a structured “elements” list - **`shape.elements` – new way to inspect or edit a drawing** - Every `Shape` now owns a list called `elements`; - Each item is a **`ShapeElement`** object holding **one drawing command** (`m`, `l`, `b`, …) plus its coordinates; - This replaces the old “raw string hacking”: you can iterate the list, index it, or modify it in-place while keeping everything in sync. - **`for elem in shape:` shape iteration** - `Shape.__iter__` returns an iterator over the elements in a shape, yielding the *individual segments* of the outline in the same order they appear in the ASS string; - Implicit continuations after an `l` or `b` are split automatically, so every segment is explicit and self-contained. - **`drawing_cmds` is still there – it’s just smarter** - It became a *computed* read-only property generated on-the-fly from the `elements` list. ### Examples ```python shape = Shape("m 0 0 l 10 0 10 10") for part in shape: print(part) # → ShapeElement('m', [Point(0, 0)]) # → ShapeElement('l', [Point(10, 0)]) # → ShapeElement('l', [Point(10, 10)]) ``` ## Native Shapely bridges – `to_multipolygon()` ↔ `from_multipolygon()` - `shape.to_multipolygon()` converts your ASS outline into a Shapely MultiPolygon (curves will be flattened); - `Shape.from_multipolygon(mp)` travels the other way, rebuilding a Shape from any MultiPolygon you give it. - This is a huge step for PyonFX, since Shapely is the de-facto Python toolkit for planar geometry. ## Counterparts for ASS tags now available for Shape objects - `align(an=5, anchor=None)` (contributed by @Funami580): mirrors \an; - `scale(fscx=100, fscy=100, origin=(0, 0))`: mirrors \fscx/\fscy; - `rotate(frx=0, fry=0, frz=0, origin=(0, 0))`: mirrors \frx, \fry, \frz; - `shear(fax=0, fay=0, origin=(0, 0))`: mirrors \fax/\fay; ## New geometry operations: `boolean()` and `buffer()` - `boolean(other, op, tolerance=1, min_point_spacing=0.5)`: combines this shape with another via the classic set operators you already know from vector tools: - *union* → merge both outlines; - *intersection* → keep only the overlap; - *difference* → subtract other from this; - *xor* → areas that belong to one or the other, but not both. - `buffer(dist_xy, dist_y=None, kind='border'|'fill', join='round'|'bevel'|'mitre')`: recreates \bord and “fake-fill” tricks at outline level: - grow (`dist_xy` > 0) or shrink (`dist_xy` < 0) the outline. - `kind='border'` (default) returns only the stroke ring, while `kind='fill'` returns the full, bloated outline. ## Smooth shape transitions with `morph()` - `shape.morph(target, t)`: returns a new Shape that is an intermediate form between shape (when t=0) and target (when t=1). Any t in the 0 – 1 range is allowed, so you can animate the morph over time by stepping t frame-by-frame. - Under the hood every outline is flattened, decomposed into compound loops (shells & holes) and then paired by similarity (distance + area + overlap heuristics). ![Morphingv6-ezgif com-crop](https://github.com/user-attachments/assets/eca31a1d-2a97-4df3-9e61-c3bb5690aa42) ## `Shape.polygon(edges, side_length)`: one shape to rule them all - A single factory covers every regular shape: pass the number of edges (≥ 3) and a side_length, get a perfectly centred, unit-thick outline; - The specialised helpers `triangle()` and `rectangle()`/`square()` have been retired. ## Other improvements / changes - `bounding(exact=True)` (contributed by @Funami580): passing the new *exact=True* can be used to obtain boxes that hugs the curve with sub-pixel accuracy. Not passing *exact* or passing *exact=False* still returns the VsFilter/Libass bounding results; - Curve flattening (`.flatten()`) and segmentation (`.split()`) were re-implemented for numerical stability and speed; - `.map()` was re-implemented to use the new iterator of `ShapeElement`;
合并状态:未合并 0 条评论