# Delete points

Hello,

I am struggling with the use of the delete points node. When using patterns the delete points node is more useful compared to the compound path node - if you want to fill a shape with a pattern. Nevertheless, if you want punch out the shape from a pattern Nodebox reconnects the points (which has some logic to it). Any ideas for a work-around? I've attached an example of what I am trying to do. The circle on the left should be left empty.

Floris

- deletepoints.zip 1.22 KB
- Schermafbeelding_2020-06-20_om_17.11.03.png 123 KB

# Keyboard shortcuts

### Generic

? | Show this help |
---|---|

ESC | Blurs the current field |

### Comment Form

r | Focus the comment reply box |
---|---|

^ + ↩ | Submit the comment |

You can use `Command ⌘`

instead of `Control ^`

on Mac

Support Staff 1 Posted by

johnon 27 Jun, 2020 09:12 AMHi Floris,

I have attached a demo with two possible solutions.

The first one replaces each line with 100 tiny line segments, each its own path. When you copy that 20 times you have 2000 paths. I then set the Delete node to paths instead of points. This removes all the tiny segments inside the circle.

The second approach uses very thin rectangles instead of lines. Because rectangles are shapes instead of merely paths, you can use the compound node in difference mode to remove the circle.

Notice the subtle difference. In the first case each tiny segment is either included or not, resulting in clean breaks. In the second case the stripes include tiny curves along the edge of the circle for a more precise outline.

If you use a pattern other than horizontal lines you may need to make some adjustments to chop your pattern into tiny paths. But if your pattern consists of closed shapes (e.g. dots) you can just use a compound node.

Let me know is this solves your problem.

John

2 Posted by

florisdejongeon 27 Jun, 2020 09:41 AMThanks John for providing two solutions. Since I want to draw the results with a penplotter I'm more interested in the first one because it contains lines and not shapes. The second one might work if I would fit a path to the shape though.

With regard to plotting something with the first solution I run into a second problem. This results in a lot of small paths and therefore to a lot of up and down movements of the pen. So continuous lines are preferable. You've already provided something of a solution with your excellent node library :). With the Join-node I can combine all these paths. The result is pretty cool, but not particular what I was looking for. So is there a way to use the Join-node for a selection of paths/points? Slicing the list of paths could be a solution, but only if every 'row' contains the same number of paths.

Floris

Support Staff 3 Posted by

johnon 27 Jun, 2020 09:53 AMHmmm. I agree it would be painful to make your plotter lift its pen 2000 times.

There are several ways to solve this. If your pattern is always horizontal lines and your hole is always a circle (which I gather it is) one approach would be to actually calculate the intersections with the circle. A more general purpose solution would be to look for gaps between successive points in each line and use those to consolidate the segments.

I will take a quick run at this and get back to you.

John

Support Staff 4 Posted by

johnon 27 Jun, 2020 10:16 AMOK, here you go.

I used the circle intersection method. My "clean_lines" subnetwork returns a complete line if there are no collisions with the circle and two shorter lines (one before the circle and one after) if there are collisions. The subnetwork uses an intersct_circle node which I have not yet published; I will add it to the next release of my node library.

Give this one a try and see if your plotter likes it.

John

5 Posted by

florisdejongeon 28 Jun, 2020 06:40 AMHi John,

Thanks for exploring this solution. This would indeed work perfectly for plotting the drawing with the variables mentions: straight lines and with a circle. I liked the creative potential with different (multiple?) shapes and curved lines though (see examples provided). Therefore I followed your suggestion for a more general purpose solution. In the attached nodebox file I added a comparison which only selects the short line-segments. Unfortunately, I am subsequently stuck again at the earlier problem: connecting only points that are near eachother. I do not have a lot of experience with using booleans in Nodebox, but is it possible to only draw the line when the comparison is 'true'?

I would consider this general purpose solutions more feasible. This might be a topic for another discussion, but I think this would also be applicable to something I was working on earlier: translating the flying goat algorithm to Nodebox. I am currently making a CMYK version (example attached - file is a bit slow to load). For this I found a way to generate a csv-file of RGB-values of an input image. This also resulted in small line segments which I joined in Adobe Illustrator, put that doesn't work with larger amounts of segments and it produces unwanted connections in the lighter areas. And I can think of even more ways in which such a join/connect-solution is applicable. So if your are willing to contribute to such a solution, that'd be great.

Kind regards,

Floris

Support Staff 6 Posted by

johnon 28 Jun, 2020 08:39 PMFloris,

Attached is a general purpose node, bg_segments, that chops each background path you give it into a minimal set of clean subpaths not occluded by a foreground shape (or group of shapes),

To demonstrate it I use a large Times Roman "ab" as my foreground shape and three sets of background paths: straight lines at an angle, concentric circles, and sine waves. To show the separate identity of each segment path, I alternated colors between black and red (see screenshot). You can switch to data mode to verify that there are less than one hundred total subpaths for each case, not thousands as before.

The bg_segments node takes a value for minimum length, which I default to 10. If you lower this value you will get a sharper definition around your foreground shapes, but computation time will increase. Lowering the value to 5 for these 3 samples increased rendering time from about 3 seconds to 10 seconds - acceptable. But below 5 the rendering time increases exponentially.

bg_segments uses my subpath node which requires make_curve.py (included in the zip file).

Please give this node a try and see if makes your plotter happy with whatever background paths and foreground shapes you can throw at it. If I hear back from you that it works and is useful, I will add it the next release of my library.

I will leave the flying goat algorithm for another day. It has some similarities to this problem but will be challenging to do in an efficient way.

John

Support Staff 7 Posted by

johnon 29 Jun, 2020 01:50 AMFloris,

Followup. I finally looked at your vector raster network (which was missing the noise.clj file so I found a copy and replaced it). I'm not yet ready to tackle a general purpose flying goat, but I did find something else you might appreciate...

Some time ago I came across an algorithm for converting RGB values into CMYK dots. I converted this to NodeBox and made a demo which uses your CSV file as an input. The algorithm sizes the dots, slightly offsets them, and draws them in a certain z order.

In theory you could send the output directly to a CMYK printer. There are a lot of dots, but I suppose you could try plotting each dot as well. You may have to adjust the scaling value to match dot sizes to the original photo size; I guessed at a value of 640 which seemed to produce decent results. Once that is right you can group everything and scale that to final output size.

Zoom in to see the actual overlapping CMYK dots. Not perfect, but recognizable. And dots instead of flying goat line segments, but perhaps another step in that direction.

John

P.S. I'm not sure this flower photo, with its subtle gradations, is the best possible example to use for developing a flying goat line segment output. Maybe try something a little simpler with higher contrast for starters.

8 Posted by

florisdejongeon 29 Jun, 2020 07:17 PMJohn, This looks really good! Unfortunately, I do not have the time to test

it and look into it in more detail. But I will get back to you at the end

of the week. Kind regards, Floris

9 Posted by

florisdejongeon 04 Jul, 2020 06:20 AMJohn,

It works perfectly! Although I don’t quite understand how it works yet. Of course, I don’t only want to use your solution, but I want to understand how you got there. There are a lot of subnetworks though, with some unknown elements to me. Would you be so kind to elaborate on what you did? In a subnetwork you often start with a null-node. Can you explain what causes you to use this? What does the ‘indices’ node do exactly? And I am not familiar with nodes like split_curve or split_path. Are those new ones?

Either way, it is a great demonstration of what Nodebox with your extra node library is able to do. For example, it was clear to me what kind of things the individual nodes from the library would do (based on the examples provided), but not what they could do in combination with eachother.

I am going to try to apply this new background-node to some different things I was working on. I will post the results on my instagrampage (or is the show-your-work-section of this forum being used?) I’ll still have to look at your CMYK program. It looks like a more efficient way. Of course, I will try to learn from it with regard to using certain insights with the penplotter :)

Kind regards,

Floris

Support Staff 10 Posted by

johnon 04 Jul, 2020 08:13 AMFloris,

I would be delighted to elaborate on how the bg_segments node works. It's very deep if you follow it all the way down and there are some bits along the way that are not at all obvious.

Bg_segments (see screenshot) takes one background path at a time and tests it against a foreground shape. If there is no intersection it simply returns the background path unscathed. If there is an intersection it returns two or more distinct segments, each one a clean, continuous path. The min length controls the resolution of the testing.

The first thing it does is resample the background path using the min length parameter. Resample adds a steady series of points along the path, but still returns a single path, so you have to feed it into a points node to recover those individual points.

I feed that resampled path along with the foreground shape into a delete node and feed that into a points node as well. This gives me all the points in the original background path minus any points that happen to fall within the shape.

My goal now is to build up an internal table that tells me, for each point in the original path, whether or not it was occluded (deleted) and, if so, which segment the foreground shape broke it into. For example, if the background path is a line and the foreground shape is the letter 'a', the line might first hit the left edge of the a, shine through the bowl of the a, then emerge from the other side. This would produce three distinct segments: the part before the a, the part inside the a, and part after the a.

The first column of this table is an index I used to identify each original point - just the points numbered 0, 1, 2... My indices node does this simple task.

The second column has a 0 if the point survived and a 1 if it was deleted. This is calculated from a "deleted" node which compares the original point set with the occluded point set. To do this it uses my handy find_item node which takes each point in the occluded set, one at a time, and compares it to the full set of original points taken all at once. Find_item returns one or more indices if there is a match (meaning the point was found). So I count these indices and, if none are returned, I return a 1 (indicating that the point is missing).

The third column, the segment ID, is the ingenious part. To find it I simply take the running total of the 0s and 1s coming out of the deleted node. To see how this works recall our example from above of the letter a chopping a line into three pieces.

The first set of points before the line strikes the a are all 0s, so the running total is all 0s as well. Then 4 or 5 points are deleted as the line passes the first part of the a, producing a series of 1s. The running total now begins to climb: 1, 2, 3, 4, 5. In the center of the a we get a string of 0s and the running total stays unchanged: 5, 5, 5, 5, 5, 5, 5. As we cross the right side of the a the running total climbs again: 6, 7, 8, 9. After that it's all 0s so the running total stays the same: 9, 9, 9, 9, etc.

The table is now complete. Here comes the magic part: I simply filter the table to keep only the rows of points that were NOT deleted. If you look at the segment column you will see it now functions as a segment ID. All the points in the first segment have a segment value of 0. Points in the middle segment (inside the a) all have a segment value of 5. And all points in the final segment have a value of 9. The actual values (0, 5, and 9) don't matter; all that matters is there is now a unique identifier for each segment.

If I lookup the segment column and apply a distinct node I get those three segment IDs. The count tells me how many segments there are. I can use each segment ID to filter on the third column to return only the points that belong to that segment.

This is what the segments node does. Actually it does even more: it returns the actual subpath corresponding to the points included in each segment. To do this is uses my handy sub_path node.

I will not terrify you by trying to explain in detail how the sub_path node works. For now just accept that it returns a clean continuous subpath between j% and k% of an original path. So all I have to do is find values for j and k which define each segment.

I get these values by filtering for only the table rows matching a given segment ID and looking up the index value. This gives me a list of index values for each point in the segment. Suppose the segment inside the letter 'a' starts at index 35 and continues through index 47. In this case the first value on my list will be 35 and the last will be 47. To convert these into percentages all I need it the total number of points in the original background path (which I pass into the segments node).

So each time the segments node fires it returns the subpath of each segment. If there was no intersection with the foreground shape, all rows of my table will have a segment ID of 0, j will be 0%, k will be 100%, and segments will return the entire path.

That's it. The secret is my internal table and clever trick of using running totals to derive an ID for each segment. It takes some practice to come up with solutions like this. But I hope you see how useful these little internal tables are. By assigning each row a consecutive index and then filtering, sorting, and using the distinct node, you can do most anything.

You ask about my frequent use of null nodes inside my subnetworks. When making subnetworks it's tempting to simply select a subset of nodes and "group into network". This may work at first, but if you want to modify that subnetwork (and sooner or later you will) you will often need to route one of the input lines to a new node.

I have learned from experience to always make a separate node to receive each input to my subnetwork - and to rename that node so I can tell which input is which from inside the subnetwork. If the input value is a number I will use a number node or an integer node, if it's a string I will use a string node, if it's a boolean I will use a boolean node. And if its anything else - a path or a shape or a table row - I will use a null node. That's why you see so many of them in my code.

The indices node I have described above. Split_curve and split_path are sub-processes within my sub_path node. Ask me again and I will launch into a separate elaboration of how that node works!

Thanks for these excellent questions. Please keep them coming. And please do consider sharing some of your results in the show-your-work forum; that's what it's there for.

John