Improved Clip Node
Attached is the demo of a much improved, general purpose clip node.
NOTE: SEE BELOW FOR AN EVEN BETTER VERSION THAT ALLOWS CLIPPING OUTSIDE.
The clip node currently in my library doesn't work very well. My goals for a better node:
- Can clip almost any combination of shapes, paths, and groups
- Works with almost any clipping region, even regions with multiple contours
- Returns an output as close to the input as possible (groups remain grouped, paths intact, colors maintained, etc.)
- Returns unbroken paths whenever possible to be more plotter-friendly
- Work much faster, even with large sets
- No external code required
My improved node is not perfect, but does much better than the old node. PLEASE give it a try. Tell me the kind of things you use the clip node for and how well the new node works. And if fails in some way or is unreasonably slow, please share your NDBX file so I can see where it failed. With your help I can make it even better.
Special note to Floris: In situations where you want to draw straight lines inside a boundary, clip may now work better and faster than mask. Please give it a try. Also let me know if you still have situations where neither mask nor clip works well for you; I may be able to share some variant nodes for special situations.
The mask node is similar to the clip node but has the following differences:
- The mask node only clips open paths or the edges of closed paths; clip handles open paths, closed solid shapes, and lines.
- The mask node is reversible (can return edges inside or outside the boundary); clip only return the inside.
- The mask node requires some approximation (resampling size); the clip node does a near perfect job with no need for fudge factors
- The mask node is slow with larger sets; the clip node runs significantly faster
- The mask node never returns the boundary; the clip node tries never to return the boundary but in some cases may return parts of it (if this happens to you please share it with me)
There are certain cases (e.g. concentric circles) where mask may still be preferred over clip, but these may be relatively few and far between. I'm not 100% sure we need both. If anyone out there has cases where mask works well and clip does not, I'd love to hear about them.
THANKS IN ADVANCE FOR ANYONE RESPONDING TO THIS POST!
Clipping pixels is easy. Clipping vectors is hard. I made a video with my new clip node showing why this is:
Pixels are easy because you just keep the ones inside and drop the ones outside. But with splines (vector paths consisting of line segments and Bézier curves) you actually have to reform the splines whenever they cross a boundary. Even detecting those crossing points is hard. It gets even messier because each spline can be complex, sometimes curving over itself, and the clipping region can also be complex, sometimes containing holes (multiple contours).
So how does Nodebox clip complex drawings to the canvas when you export SVGs, PNGs, PDFs, or movies? It doesn't. Like many vector apps it just waits until the vectors are inevitably converted to pixels (e.g. when displayed on a computer screen) and then clips those pixels. If you look inside an SVG file you will see that your entire drawing is represented with a line at the top listing the canvas size; programs like Preview draw the whole thing as pixels THEN trim away everything outside the canvas boundary.
Nodebox provides only two tools to clip shapes with: compound and delete. Compound only works with solid shapes; open paths will be ignored or treated as solids. Delete has two modes, pixels and paths, but in both cases is all or nothing. It cannot return partial paths. Moreover, both can be flaky. Compound in particular often does strange things to the paths you feed it including changing direction, shifting starting points, causing performance issues only for certain shapes, and adding microscopic points and loops.
The clip node takes a mutifaceted approach. For straight lines it uses the delete node, converting each line to individual points, tossing some, and then recreating the remaining line segments. For solid shapes it uses the compound node plus some extra logic to regroup and recolor. For open paths it first uses the compound node with a slightly shrunken version of the boundary shape to strip away the boundary, then explodes what's left, removes extraneous connecting lines, and then tries to reform remaining segments into continuous paths. There are MANY strange corner cases which it tests for.
Clipping is a surprisingly useful function which can be used to make many strange and wonderful effects. I hope anyone using this node will consider sharing their work on this forum.
- clip_screenshot.png 401 KB
- clip_demo.ndbx.zip 17.6 KB
|?||Show this help|
|ESC||Blurs the current field|
|r||Focus the comment reply box|
|^ + ↩||Submit the comment|
You can use
Command ⌘ instead of
Control ^ on Mac
1 Posted by florisdejonge on 07 Dec, 2022 07:42 PM
Thanks for the post and the new node! I see I have some reading up to do, as well as testing all these new things (I'm also thinking of the updated library). I'll share my experiences both here and on my Instagram as soon as I get back into Nodeboxing and plotting.
Support Staff 2 Posted by john on 25 Jan, 2023 11:18 AM
Attached is an improved version of the clip node.
It now has an "Outside" checkbox. If you check it, it will return everything outside the clipping boundary,
I tested it on a wide variety of shape groups and boundaries, but I may have missed some combination that causes a failure. So PLEASE give this thing a try and report back if it works for you. If you do find a problem, please include a demo of what failed.
Support Staff 3 Posted by john on 26 Jan, 2023 12:48 AM
Attached is an EVEN MORE IMPROVED version of the clip node!
I think this is the final update. It is a very minor improvement over the previous version.
Before, if you fed clip a single path wrapped as a group, it would return one or more paths (whatever parts of it were in or out of the clipping boundary).
Now, the output looks exactly the same, but if you wrap a single path as a group, it will be returned as a group.
The attached demo looks just like the previous one, but has the improved node.
This is a very subtle distinction, but can be important if you want to make sure the output corresponds precisely to the input. So, for example, if you feed a single line through a multi-contoured boundary like, say, a lower case b textpath, you may get multiple segments back. If you need to keep track of the fact that those segments all are associated with the single original line, you can now group that line before feeding it into the clip node and those segments will come out all inside the same group.
If you don't follow that, don't worry. This is an edge case that few people will notice or care about. But I did just notice it and I do care so I fixed it.
4 Posted by florisdejonge on 06 Apr, 2023 08:00 PM
I've been meaning to try to catch up on the new nodes you've made. It's quite impressive. Especially this one: before I could check out a new one you post an improvement :). I finally got around to test this one with the geometry I used earlier. I can't seem to get it to work it correctly: some lines are removed in the oclusion/clipping, which might be cause by the more complex shapes, or the resolution of the clipping, but I can't seem to find a control like with the mask node. Is there any way to prepare the shapes better? This node is a lot faster though, that's really clear.
Support Staff 5 Posted by john on 06 Apr, 2023 09:17 PM
Thanks. This is just the sort of feedback and real world examples I'm looking for.
Unless I am missing something, the example you shared appears to use the occlude node, not the clip node. Is the problem just with the trunks of certain trees, or are you seeing other problems?
I know the clip node does fail in certain cases involving line segments sticking out of shapes where the mask node works correctly. This may be a similar situation
I will dig into this. Meanwhile It would be helpful if you can provide a simpler demo which clearly shows a specific failure.
Support Staff 6 Posted by john on 07 Apr, 2023 01:57 AM
I peered closely at your example and isolated a case where some trees were missing parts of their trunk. I then discovered an obscure bug in clip causing this (since clip is inside occlude).
This bug only affects shapes with short lines poking outside of a more complex shape when the length of those lines is fractional. I believe I have fixed this issue.
The attached demo shows a small stand of your trees. The old occlude node removed part of the trunk in two of these trees, including the one on the upper right. You can see this by rendering the OLD_occlude node. The occlude node in this demo is the repaired version which fixes this issue.
You can see the root cause in this demo by rendering the OLD_clip and clip nodes. Here the clip node is supposed to show the parts of the tree outside the circular foliage. The OLD_clip node removes part of the exposed trunk; the (newly repaired) clip node leaves that trunk intact.
I hope I have not introduced any new issues by fixing this bug. So I would appreciate it if you keep testing both occlude and clip. For these tests, use the updated versions from this demo. I have already updated both of these nodes in version 3.2 of my library, which I will be posting soon.
Thanks again. I can't swat these obscure bugs without the help of fellow Nodeboxers like you.
7 Posted by florisdejonge on 08 Apr, 2023 11:48 AM
Thanks for looking into this so quickly. I indeed used the occlude node from this thread which used the new clip node. May I shoul've posted it there.
I've attached a more simple example in this message. I applied the occlude node to the forest, which indeed lead to missing trunks. The houses or village went fine. The hexagon network had missing lines as well. I added an example (to the right) of how it's supposed to look using the older occlude node from this thread, but that one's way slower and bogs down in more complex compositions. Do you know what might be the cause of this? Should I arrange the geometry of the inputsubnetwork differently to get it to work with the new occlude/clip node?
Support Staff 8 Posted by john on 09 Apr, 2023 12:05 AM
Thanks again for finding this second bug. I was finally able to track it down and fix it (I think).
The bug stemmed from a very subtle bug in Nodebox. I have to rely heavily on the compound node in cases like this, and the compound node, one time out of a hundred, will sometimes do perverse things like add dust or reverse path directions or, in this case, change a type 3 point to a type 1 point.
I added an extra test to catch this rare case and as far as I can tell it seems to be working. See attached demo.
NOTE: in this demo, both the occlude node and the clip node inside it are named occludeN and clipN (N for new). I did this to keep them from getting mixed up from the previous versions. But I have already renamed them occlude and clip in my library. Feel free to use them and rename them yourself until my library is published.
Please give it a try and CONFIRM that all three shapes (trees, houses, and hex cylinders) are now working for you. And please keep testing and let me know if you find any other issues.
You are helping me make these two very complex nodes better and better!
9 Posted by florisdejonge on 09 Apr, 2023 09:57 AM
Thanks for improving the node. The trees and houses and hexagons in the example provided worked perfectly. Also when scaling up the grid.
I've add some variables to the hexagon grid: aligning them tight and adding differences in height. But then some horizontal lines disappear (see image on the left). Hopefully you can find out the cause of this.
By the way: can you expand on in what way the clip node differs from the mask node? Or are there different applications for both nodes? EDIT: reread the first post, which explains the difference. I'll try and see which one is beter for which cases.
Support Staff 10 Posted by john on 09 Apr, 2023 07:22 PM
Thanks yet again. I have another fix.
First, a note about the latest demo you sent me. I was initially confused because the occludeN node was actually sitting on top of the previous occlude node, and was connected to the group5 node which was sitting UNDER the old group3 node. SEE FIRST SCREENSHOT. As a result, occludeN was not actually hooked into the final display.
This happens to me from time to time as I option-drag copy nodes. I've become quite paranoid about it and double-check all the time. When nodes sit on top of each other like this, debugging can become VERY confusing. Nodebox really shouldn't allow this to happen, but it does. So we must all be aware and on guard.
Now onto the bug...
The previous bug affected open paths with straight segments entirely separate from shapes that might obscure it. This new bug occurs in some cases where open paths with straight segments are partially obscured. This was harder to fix.
But I did fix it. The second screenshot shows my demo, now with three outputs. On the left is the previous occludeN. In the middle is my fix, which I temporarily named occludeZ. On the right is your reference output. As you can see, occludeZ now matches the reference.
In order to fix this bug I had to do something which may sound bizarre. I introduced a microscopic curvature into the straight segments of open paths. This curvature is too small to be seen by the naked eye (a bend of .01 pixels), but allows my clip node to distinguish the parts I want to keep from the parts I want to toss.
I don't think this invisible curvature will do any harm. But I am curious if it will have any noticeable effect when plotted. I don't think it should, but please try plotting your compressed hexagon pattern and let me know if you spot anything different.
You can use occludeZ (and clipZ inside it) for the next round of testing. The nodes will be renamed occlude and clip when I release them in the next rev of my library. I think I am now ready to publish that rev, but will wait a tad longer to see if you can find any more bugs.
As to your question about clip vs. mask.
Clip and mask do very similar things but use different methods to do it. Clip is more general purpose and, in theory, should handle every possible situation including what mask does (and do it faster). Mask basically only handles open paths.
Because of this I considered dropping mask from the next rev of my library. But it turns out there are still some situations where clip fails and mask works. In particular, clip sometimes returns part of the boundary where shape and the masking region meet; I have so far been unable to find a way around this.
So for now, you should always try to use clip. But if clip fails, mask can be your fallback. I am hoping that with all these bugs you've helped me find, you won't have to use mask very often.
Please confirm that occludeZ is now working for you and plots OK. And if you have the time, please keep stress testing it.
11 Posted by florisdejonge on 11 Apr, 2023 03:53 PM
Thanks John! So far it works with a more complex network (see attached screenshot). I'll try to plot one of these soon. The plotter prefers straight lines, so I'll try to find out what these slight curves do. But I can always resample the result (which has become a habit to efficiently plot curves).