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

feat: shape clipping #1411

Merged
merged 22 commits into from Jun 2, 2023
Merged

feat: shape clipping #1411

merged 22 commits into from Jun 2, 2023

Conversation

liangyiliang
Copy link
Contributor

@liangyiliang liangyiliang commented May 19, 2023

Description

Resolves #1405.

We implement shape-clipping and improve a couple of library computation and constraint functions to work with clipped shapes.

Group Shape clipPath Property

A Group shape now has an additional clipPath property that takes a ClipDataV value. A ClipDataV value has two variants: NoClip (which takes no additional data) and Clip (which takes in a Shape object). A ClipDataV can be constructed using one of two computation functions:

  • Function noClip takes no parameters and returns the NoClip variant.
  • Function clip takes in a shape and returns the Clip variant with the given shape.

The default value of the Group shape's clipPath property is the NoClip variant.

As an example, suppose we have shapes s1 and s2 in a group, and we want to clip the group by shape s3. We can write,

g = Group {
  shapes: [s1, s2]
  clipPath: clip(s3)
}

A shape involved in the clipPath field of a Group is considered a part of the group through all stages of the compilation, including the construction of the GroupGraph, so all the usual rules of shapes not belonging to multiple groups still apply. That is,

  • A shape used in clipPath of a group cannot appear in any other group, and cannot be used to clip another group; and
  • A shape in a group cannot be used to clip another group.

Corresponding SVG

Since a shape in clipPath is considered a group member, we render them with all other group members. For a group with clipPath, the generated SVG has structure,

<g>
  <clipPath id="some-id">
    ... (clipPath shape rendered here)
  </clipPath>

  <shape id="group-member-1" clip-path="url(#some-id)"> ... </shape>
  <shape id="group-member-2" clip-path="url(#some-id)"> ... </shape>
  <shape id="group-member-3" clip-path="url(#some-id)"> ... </shape>
  ... (other group members)
</g>

The clipPath SVG object id some-id is the name of the clipping shape, with suffix -clip. For example, if the clipping shape name is myShape, then the clipPath SVG object id is myShape-clip.

Bounding Box

Previously, the bounding box of a Group shape is simply the min-max-bbox. That is, take the bounding boxes of all group members, and then find the min-x, min-y, max-x, max-y, and use those to construct a new bounding box.

If a Group shape has a clipping shape, we compute the new bounding box as the previous bounding box, intersecting the bounding box of the clipping shape.

image

In the picture above, the bounding box of the yellow region (the larger ellipse clipped by the smaller ellipse) is computed as the bolded blue rectangle. One may observe that this bounding box is not optimum. Indeed, this box is not a "minimum bounding box" as it can overestimate the object. But this is relatively simple to compute.

Constraint contains

We add a branch to the contains constraint to deal with groups.

If a group does not have a clipping shape, checking whether a group contains a shape is equivalent to checking whether some group member contains the shape. We simply compute the contains constraint penalty between all of the group members and the shape, and take the minimum. If the minimum of the penalties is small, then one group member must contain the shape.

If a group has a clipping shape, then the contains constraint between the group and another shape just requires:

  • the group members (excluding the clipping shape) contains the other shape, and
  • the clipping shape contains the other shape.

We compute both constraints, and require that both constraints hold.

Examples with steps to reproduce them

https://clip.penrose-72l.pages.dev/try/?gist=11c14ac7a7e0cf45a13ec68066925b18

This example shows two sets, with their intersection between marked out using clipping. We also demonstrate the bounding box of the intersection (see the rectangle) and the contains constraint (the small, red circle is within the intersection).

Checklist

  • I have commented my code, particularly in hard-to-understand areas
  • I have reviewed any generated registry diagram changes

@cloudflare-pages
Copy link

cloudflare-pages bot commented May 19, 2023

Deploying with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0fc40c9
Status: ✅  Deploy successful!
Preview URL: https://26508e65.penrose-72l.pages.dev
Branch Preview URL: https://clip.penrose-72l.pages.dev

View logs

@github-actions
Copy link

github-actions bot commented May 19, 2023

± Registry diff

M	geometric-queries/closest-point/test-group.svg

📊 Performance

Key

Note that each bar component rounds up to the nearest 100ms, so each full bar is an overestimate by up to 400ms.

     0s   1s   2s   3s   4s   5s   6s   7s   8s
     |    |    |    |    |    |    |    |    |
name ▝▀▀▀▀▀▀▀▀▀▚▄▄▄▄▄▄▄▄▞▀▀▀▀▀▀▀▀▀▀▚▄▄▄▄▄▄▄▄▄▖
      compiling labeling optimizing rendering

If a row has only one bar instead of four, that means it's not a trio and the bar just shows the total time spent for that example, again rounded up to the nearest 100ms.

Data

                                                          0s   1s   2s   3s   4s   5s   6s
                                                          |    |    |    |    |    |    |
set-theory-domain/tree-venn                               ▝▀▞▖
set-theory-domain/tree-tree                               ▝▀▞▖
set-theory-domain/tree-venn-3d                            ▝▚▚
group-theory/quaternion-multiplication-table              ▝▀▀▄▞▖
group-theory/quaternion-cayley-graph                      ▝▀▞▖
atoms-and-bonds/wet-floor                                 ▝▚▀▖
atoms-and-bonds/one-water-molecule                        ▝▞▖
set-theory-domain/continuousmap                           ▝▞▖
linear-algebra-domain/two-vectors-perp                    ▝▚▚
molecules/nitricacid-lewis                                ▝▀▀▞▀▖
exterior-algebra/vector-wedge                             ▝▚▚
shape-spec/all-shapes                                     ▝▚▚▄
shape-spec/arrowheads                                     ▝▞▖
graph-domain/textbook/sec1/fig1                           ▝▀▀▚▀▚
graph-domain/textbook/sec1/fig2                           ▝▀▀▀▞▀▖
graph-domain/textbook/sec1/fig3                           ▝▀▀▀▞▀▖
graph-domain/textbook/sec1/fig4                           ▝▀▀▀▞▀▚
graph-domain/textbook/sec1/fig5                           ▝▀▀▀▀▚▀▀▀▚
graph-domain/textbook/sec1/fig6                           ▝▀▀▀▀▀▀▀▀▀▚▀▀▀▀▀▀▀▀▀▀▀▀▀▖
graph-domain/textbook/sec1/fig7                           ▝▀▚▀▖
graph-domain/textbook/sec1/fig8a                          ▝▀▀▀▀▚▀▀▀▀▖
graph-domain/textbook/sec1/fig8b                          ▝▀▀▚▀▀▖
graph-domain/textbook/sec1/fig9                           ▝▀▀▞▖
graph-domain/textbook/sec1/fig10                          ▝▀▀▞▖
graph-domain/textbook/sec1/fig11                          ▝▀▀▀▞▚
graph-domain/textbook/sec1/fig12                          ▝▀▀▀▀▀▞▀▀▀▀▖
graph-domain/textbook/sec1/fig13                          ▝▀▀▚▀▚
graph-domain/textbook/sec2/fig3                           ▝▀▀▀▀▀▚▀▀▀▀▖
graph-domain/textbook/sec2/fig4                           ▝▀▀▀▀▞▀▀▚
graph-domain/textbook/sec2/fig5                           ▝▀▀▀▀▀▀▀▞▀▀▀▀▖
graph-domain/textbook/sec2/fig6                           ▝▀▀▀▀▀▀▚▀▀▀▀▀▖
graph-domain/textbook/sec2/fig9                           ▝▀▀▀▀▀▀▀▀▀▚▀▀▀▀▀▀▖
graph-domain/textbook/sec2/fig10a                         ▝▀▀▀▞▀▚
graph-domain/textbook/sec2/fig10b                         ▝▀▀▞▀▖
graph-domain/textbook/sec2/fig11a                         ▝▀▞▖
graph-domain/textbook/sec2/fig11b                         ▝▀▞▖
graph-domain/textbook/sec2/fig11c                         ▝▀▞▖
graph-domain/textbook/sec2/fig12                          ▝▀▞▖
graph-domain/textbook/sec2/fig13                          ▝▀▀▀▀▀▀▀▀▀▀▀▚▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▚
graph-domain/textbook/sec2/fig14                          ▝▀▀▚▀▖
graph-domain/textbook/sec2/fig16b                         ▝▀▚▚
geometry-domain/textbook_problems/c05p13                  ▝▀▚▚
geometry-domain/textbook_problems/c01p01                  ▝▀▞▖
geometry-domain/textbook_problems/c03p01                  ▝▀▞▖
geometry-domain/textbook_problems/c05p01                  ▝▀▞▖
geometry-domain/textbook_problems/ex                      ▝▀▀▞▖
triangle-mesh-3d/two-triangles                            ▝▚▚
random-sampling/test                                      ▝▀▀▀▞▖
geometry-domain/textbook_problems/c11p12                  ▝▀▚▚
word-cloud/example                                        ▝▚▚
geometry-domain/siggraph-teaser                           ▝▀▚▚
minkowski-tests/maze/non-convex                           ▝▚▚
lagrange-bases/lagrange-bases                             ▝▚▚
hypergraph/hypergraph                                     ▝▀▀▚▀▀▀▀▀▀▀▀▀▖
persistent-homology/persistent-homology                   ▝▀▚▀▀▀▀▀▀▀▀▚
walk-on-spheres/laplace-estimator                         ▝▀▞▖
walk-on-spheres/poisson-estimator                         ▝▀▞▚
walk-on-spheres/nested-estimator                          ▝▀▚▀▀▚
walk-on-spheres/offcenter-estimator                       ▝▀▞▀▖
shape-distance/points-around-star                         ▝▀▀▀▚▚
shape-distance/points-around-polyline                     ▝▀▚▚
shape-distance/points-around-line                         ▝▀▞▖
shape-distance/lines-around-rect                          ▝▚▚
fake-3d-linear-algebra/projection                         ▝▞▖
animation/center-shrink-circle                            ▝▞▖
structural-formula/molecules/caffeine                     ▝▀▚▀▖
mobius/mobius                                             ▝▞▖
molecules/glutamine                                       ▝▀▞▖
matrix-ops/tests/matrix-matrix-addition                   ▝▚▚
matrix-ops/tests/matrix-matrix-division-elementwise       ▝▚▚
matrix-ops/tests/matrix-matrix-multiplication-elementwise ▝▚▚
matrix-ops/tests/matrix-matrix-multiplication             ▝▚▚
matrix-ops/tests/matrix-matrix-subtraction                ▝▚▚
matrix-ops/tests/matrix-transpose                         ▝▚▚
matrix-ops/tests/matrix-vector-left-multiplication        ▝▚▚
matrix-ops/tests/matrix-vector-right-multiplication       ▝▚▚
matrix-ops/tests/scalar-vector-division                   ▝▚▚
matrix-ops/tests/scalar-vector-left-multiplication        ▝▚▚
matrix-ops/tests/scalar-vector-right-multiplication       ▝▚▚
matrix-ops/tests/vector-vector-addition                   ▝▚▚
matrix-ops/tests/vector-vector-division-elementwise       ▝▚▚
matrix-ops/tests/vector-vector-multiplication-elementwise ▝▚▚
matrix-ops/tests/vector-vector-outerproduct               ▝▚▚
matrix-ops/tests/vector-vector-subtraction                ▝▚▚
logic-circuit-domain/half-adder                           ▝▚▚
curve-examples/cubic-bezier                               ▝▀▚▀▚
triangle-mesh-2d/diagrams/cotan-formula                   ▝▀▞▖
triangle-mesh-2d/diagrams/concyclic-pair                  ▝▀▞▖
triangle-mesh-2d/diagrams/halfedge-mesh                   ▝▀▞▖
triangle-mesh-2d/diagrams/relative-orientation            ▝▀▞▖
triangle-mesh-2d/diagrams/triangle-centers                ▝▚▚
triangle-mesh-2d/diagrams/angle-equivalence               ▝▀▚▀▀▖
timeline/penrose                                          ▝▀▀▞▖
graph-domain/textbook/sec5/ex32                           ▝▀▀▀▀▞▀▀▀▀▀▀▖
curve-examples/open-elastic-curve                         ▝▀▀▞▚
curve-examples/closed-elastic-curve                       ▝▀▚▀▚
graph-domain/other-examples/arpanet                       ▝▀▀▀▀▀▀▞▀▀▀▀▀▀▀▀▚
graph-domain/other-examples/nyc-subway                    ▝▀▀▀▀▚▀▀▀▀▚
curve-examples/blobs                                      ▝▀▀▀▀▀▀▀▞▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▚
curve-examples/space-curves                               ▝▀▀▀▀▚▀▀▀▚
solid/example                                             ▐
geometric-queries/ray-intersect/test-group                ▝▀▀▀▀▀▀▀▚▚
ray-tracing/path-trace                                    ▝▀▀▞▖
ray-tracing/bidirectional                                 ▝▀▚▚
ray-tracing/next-event-estimation                         ▝▀▀▞▖
geometric-queries/test                                    ▝▀▞▖
geometric-queries/closest-point/test-group                ▝▚▚
geometric-queries/closest-point/test                      ▝▚▚
geometric-queries/closest-silhouette-point/test           ▝▚▚
geometric-queries/ray-intersect/test                      ▝▀▀▀▀▀▀▀▚▚

@codecov
Copy link

codecov bot commented May 19, 2023

Codecov Report

Merging #1411 (c333ebf) into main (099fca1) will decrease coverage by 0.36%.
The diff coverage is 28.75%.

❗ Current head c333ebf differs from pull request most recent head 0fc40c9. Consider uploading reports for the commit 0fc40c9 to get more accurate results

@@            Coverage Diff             @@
##             main    #1411      +/-   ##
==========================================
- Coverage   64.77%   64.42%   -0.36%     
==========================================
  Files          64       64              
  Lines        7515     7586      +71     
  Branches     1760     1775      +15     
==========================================
+ Hits         4868     4887      +19     
- Misses       2521     2570      +49     
- Partials      126      129       +3     
Impacted Files Coverage Δ
packages/core/src/compiler/Style.ts 65.28% <0.00%> (-0.17%) ⬇️
packages/core/src/compiler/StyleTypeChecker.ts 73.23% <0.00%> (-2.13%) ⬇️
packages/core/src/contrib/Functions.ts 38.41% <0.00%> (-0.12%) ⬇️
packages/core/src/renderer/Renderer.ts 25.78% <0.00%> (-3.95%) ⬇️
packages/core/src/shapes/Group.ts 100.00% <ø> (ø)
packages/core/src/types/types.ts 100.00% <ø> (ø)
packages/core/src/engine/BBox.ts 76.04% <5.26%> (-7.78%) ⬇️
packages/core/src/engine/EngineUtils.ts 59.25% <55.55%> (-0.19%) ⬇️
packages/core/src/shapes/Shapes.ts 93.75% <66.66%> (-3.98%) ⬇️
packages/core/src/utils/GroupGraph.ts 94.64% <66.66%> (-3.44%) ⬇️
... and 3 more

@liangyiliang liangyiliang changed the title feat: shape clipping [WIP] feat: shape clipping May 22, 2023
@liangyiliang liangyiliang marked this pull request as ready for review May 22, 2023 21:14
@keenancrane
Copy link
Collaborator

This looks good from the language design end. It's a bit unfortunate we can't just do clipPath: shape, but I understand the reasons from an implementation perspective. Thank you for updating the documentation!

@wodeni
Copy link
Member

wodeni commented Jun 2, 2023

Merged main into this branch and tested clip paths with the diagram variation grid. Turns out that we need to namespace the ids of clip paths. I chose to use the namespace instead of makeIdsUnique because the latter doesn't behave well on a single SVG element. We use makeIdsUnique for opaque imports and generally favor namespaces for id-uniqueness.

image

@liangyiliang
Copy link
Contributor Author

Thanks for catching this!

Copy link
Member

@wodeni wodeni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thanks for adding this feature. As mentioned, I fixed one issue with id-uniqueness. I also added inline comments for makeIdsUnique to clarify its purpose (which is not used in this case so I explained why there) and for the implicit group membership of clipping shapes.

@wodeni wodeni merged commit 03873af into main Jun 2, 2023
10 checks passed
@wodeni wodeni deleted the clip branch June 2, 2023 18:54
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.

Expose clip paths in Style
3 participants