GSLayer.cutBetweenPoints() not working

Hello,

I am working on a script that should cut a glyph into several parts. To summarize it, it should do a cut of a 10% from the left and make a second one 60% from the left as well.

To do so I am copying the paths into a new sublayer and doing each cut to each layer. However, .cutBetweenPoints() does not seem to work in layers other than the master one and it is cutting the paths on each layer in a quite random way.

Here’s a simplified version of the script as it is right now when iterating through Font.selection and retrieving g:

cLayer = g.layers[Font.masters[0].id]
layerW = cLayer.bounds.size.width

cuts = [layerW * 0.1, layerW * 0.6]

# Iterate through each cut
for i, c in enumerate(cuts):
		# Create a new layer for the resulting shape
		newLayer = GSLayer()
		newLayer.shapes = cLayer.shapes
		newLayer.name = 'Left cut %s' % i
		newLayer.associatedMasterId = Font.masters[0].id
    
		# Appending the new layer
		Font.glyphs[g.name].layers.append(newLayer)

		# Applying the cut
		cX = cLayer.LSB + c
		newLayer.cutBetweenPoints(NSPoint(cutX, layerTop), NSPoint(cutX, layerBottom))

One of the resultant layers looks like this (which is not ok):

It also makes cuts to the master layer which I am not manipulating at all and it shows the resultant paths without some of the path nodes.

I don’t know whether this is bug or I am doing something wrong but the main goal of this script is to two slices from one glyph in different layers. One that goes from to 10% of its width from the left and the other one to 60%.

Thank you so much for any help that anyone will provide.

Ricard.

You probably need to make a copy of shapes, otherwise you’re cutting the original paths. Instead of this

newLayer.shapes = cLayer.shapes

try

newLayer.shapes = [shape.copy() for shape in cLayer.shapes]

Thank you so much! That was it! Now I just need to know why in some very steep segments it is not working properly.

Is it a bug or is it somehow manageable?

@alexs @GeorgSeifert

Also, in a next step I need to use del(layer.shapes[i]) but for some reason it does not work either. It worked at some point but suddenly it stoped working. Using .pop(i) on the array of shapes works, though.

As far as I know, that method works the same way as the knife tool, so your screenshot suggests that it only cut through one segment, not the whole path top to bottom. Instead of layerTop/Bottom try large values like 10000 and -10000.

By the way, this method is buggy in some complex paths or if you cut right through nodes:

There’s been discussions on the forum, the workaround was to add some small value to the coordinate, like x+=.00001

1 Like

Try this:

# Iterate through each cut
for i, c in enumerate(cuts):
	# Create a new layer for the resulting shape
	newLayer = cLayer.copy()
	newLayer.name = 'Left cut %s' % i
	# Appending the new layer
	cLayer.parent.layers.append(newLayer)

	# Applying the cut
	cutX = cLayer.LSB + c
	print(NSPoint(cutX, layerTop), NSPoint(cutX, layerBottom))
	newLayer.cutBetweenPoints(NSPoint(cutX, layerTop), NSPoint(cutX, layerBottom))
1 Like

About the steep segments: could you send me a sample file?

Thank you Georg, this seems to work as well just like the option suggested by @alexs.

Regarding the file, find attached a file with 3 characters. While the script works for O, it does not seem to work with the A or even H. I’ve tried incrementing a bit the cutX value as Alexs said but it didn’t work and resulted with the same output.

CutPathSample.glyphs (5.3 KB)

the A works for me. In the H, the overlap is messing it up a bit. So you might like to remove the overlap before you run the script.

I am actually doing it before making any cut with cLayer.removeOverlap() but maybe I need to refresh the layer passed as an argument to the function that handles the cuts since, after removing the overlap I amb calling handleCuts(cLayer).

However, the first cut is done properly but the second one is not.


How does your script looks like @GeorgSeifert?

Again, thank you so much.

Ok, I’ve solved it. I was assigning the top NSPoint() coordinate right in the y value of the cap height so the first point was actually on top of a path and this was causing the problem. Something that @alexs was pointing at in one of the responses.

I didn’t notice this until I applied new local guides at the top and bottom values of the cuts.

I’ll keep testing it everything works fine in the rest of the characters.

Thank you so much for the help @alexs and @GeorgSeifert!

I usually use 10000 and -10000 as the end coordinates for the cutting line to avoid such surprises.

1 Like

Makes lots of sense, that’s what I’ll do with this script and similar ones.

Thanks again.

Hey, I am coming back to this post since I am working on script for a project in which I need to create horizontal cuts to create a stencil version from a solid design.

I have this script that gets the y property of 4 guides that I have named and cuts the glyph at these heights from x -10000 to x 10000. What I am doing there is to rely on the script to remove the in between paths that have been created by these cuts but, for some reason it does not work and it adds points to the paths but not actually cutting them properly.

I have noticed that when I remove layer.removeOverlap() and layer.correctPathDirection() (which I thought would be nice to run before making the cuts) it works better but not sure if its a bug.

Here’s the script:

from Foundation import NSPoint

master = Font.selectedFontMaster
guides = master.guides
cutHeight = 40

for layer in Font.selectedLayers:
	for guide in guides:
		if guide.name:
			layer.removeOverlap()
			layer.correctPathDirection()
			layer.cutBetweenPoints(NSPoint(-10000, int(guide.y)), NSPoint(10000, int(guide.y)))

            #These are arbitrary names I have in my guides
			if '1-1' in guide.name or '2-1' in guide.name:
				for currentShape in list(layer.shapes):
					shape_y = currentShape.bounds.origin.y
					shape_h = currentShape.bounds.size.height
 
					if shape_y == guide.y and shape_h == cutHeight:
						layer.shapes.remove(currentShape)
						layer.correctPathDirection()

And this is the result.

Any idea what the script is doing wrong under the hood? It is something related to Glyphs’ version?

Thanks in advance.

the Remove Overlap after you cut is merging the paths again.
try:

from Foundation import NSPoint

master = Font.selectedFontMaster
guides = master.guides
cutHeight = 40

for layer in Font.selectedLayers:
	layer.removeOverlap()
	for guide in guides:
		if guide.name:
			layer.cutBetweenPoints(NSPoint(-1000, int(guide.y)), NSPoint(2000, int(guide.y)))

			#These are arbitrary names I have in my guides
			if '1-1' in guide.name or '2-1' in guide.name:
				for currentShape in list(layer.shapes):
					shape_y = currentShape.bounds.origin.y
					shape_h = currentShape.bounds.size.height
 
					if shape_y == guide.y and shape_h == cutHeight:
						layer.shapes.remove(currentShape)

(I didn’t try the second part with the “1-1”, “2-1” guides.)

1 Like

Ah, I see. I was removing the overlap on each iteration over the guides.

However, in some characters, if I just don’t remove overlaps and correct path directions it still creates some connections between paths in the end instead of cut them in a clean way. Some vertex points (edges of a path) that are right at the same height of the cut, could that be the problem? Is there a way I could create a workaround to prevent this problem?

Try cutting 0.1 units higher. That might get you off the edges?

Good point. However, correcting these shifts might take more time than the one I am saving batch cutting all glyphs. Thanks, though, @GeorgSeifert

Also, out of curiosity, just like instances/exports filters, is there any way I would run a script like this one in an instance other than the ones supported by Glyphs?

What exactly do you mean? What do you mean by “instances supported by Glyphs” and what would other instances be?

Hi, @SCarewe. Maybe I didn’t explain the situation well. Just like these filters (i.e. Roughenizer) are actually running scripts under the hood and applying them to instances, I was wondering whether I could apply a script of my own instead of the default supported by glyphs (see screenshot).

So you want to run your script on instances?

Package your script in a filter plugin (FilterPluginWithoutDialog). Then you can add it as an argument to a Filter custom parameter by referencing its class name.