Removing a Node on top of a Node

Having exported from FontLab to Glyphs I’ve found that many outlines have a duplicate node one on top of another (start/end?). Is there a script to remove these across the whole font?

Are they marked in red? Simply tidy up the paths. There is a function of the same name in the Paths menu.

Tried ‘Tidy up paths’ but that also changes various curved lines to straight lines so breaking the interpolation.
Is there a version of ‘Tidy up paths’, perhaps as ‘Tidy up nodes’ or ‘Tidy up red nodes’?

  1. Select the red (duplicate) nodes, as many as you like.
  2. Right click and choose Reconnect Nodes from the context menu.

OK. So glyph by glyph – can’t do this globally in one hit via Font View.
I’ll put the coffee on . . .

A little Python always helps. Put this in Macro Window:

Glyphs.clearLog()
print "Processing %i glyphs..." % len(Font.selectedLayers)
for thisLayer in Font.selectedLayers:
	duplicateCounter = 0
	for thisPath in thisLayer.paths:
		for i in range(len(thisPath.nodes),-1,-1):
			thisNode = thisPath.nodes[i]
			previousNode = thisNode.prevNode
			if thisNode.type == LINE and previousNode.position == thisNode.position:
				del thisPath.nodes[i]
				duplicateCounter += 1
	if duplicateCounter:
		print "%s: deleted %s duplicate nodes." % (thisLayer.parent.name, duplicateCounter)
print "Done."

Select glyphs in Font view, press the Run button of Macro Window. Report in Macro window.

2 Likes

Great :pray:
Thanks.
I’ll still brew coffee though :grinning:

1 Like

This goes through all layers of the selected glyphs.

Glyphs.clearLog()
print "Processing %i glyphs..." % len(Font.selectedLayers)
for layer in Font.selectedLayers:
	glyph = layer.parent
	for thisLayer in glyph.layers:
		print thisLayer
		duplicateCounter = 0
		for thisPath in thisLayer.paths:
			for i in range(len(thisPath.nodes),-1,-1):
				thisNode = thisPath.nodes[i]
				previousNode = thisNode.prevNode
				if thisNode.type == LINE and previousNode.position == thisNode.position:
					del thisPath.nodes[i]
					duplicateCounter += 1
		if duplicateCounter:
			print "%s: deleted %s duplicate nodes." % (thisLayer.parent.name, duplicateCounter)
print "Done."

Great Thom!

Hold on. This script removes all double points. But sometimes a double point is needed in one master, because in an other master these points are not on top of each other. So lets rewrite this code a bit more…

This should keep it all interpolatable.
Only remove double nodes if these nodes are double in all layers :slight_smile:

#MenuTitle: Remove Double Nodes from Export FL
"""
https://forum.glyphsapp.com/t/removing-a-node-on-top-of-a-node/11323/7
"""

Glyphs.clearLog()
from AppKit import NSPoint

def getPointIndex(layer,thisnode):
   for path in layer.paths:
   	for node in path.nodes:
   		if thisnode == node:
   			return (layer.paths.index(path), node.index)

def getPointPos(layer, pathIndex_nodeIndex):
   pathIndex, nodeIndex = pathIndex_nodeIndex
   p_i=-1
   for path in layer.paths:
   	p_i += 1
   	n_i = -1
   	for node in path.nodes:
   		n_i += 1
   		if p_i == pathIndex and n_i == nodeIndex:
   			#print "posNode:",node
   			return [node.x,node.y]

print "Processing %i glyphs..." % len(Font.selectedLayers)
for thisLayer in Font.selectedLayers:
   duplicateCounter = 0
   pathIndex = -1
   for thisPath in thisLayer.paths:
   	pathIndex+=1
   	nodeIndex = -1
   	for i in range(len(thisPath.nodes),-1,-1):
   		nodeIndex +=1
   		
   		thisNode = thisPath.nodes[i]
   		previousNode = thisNode.prevNode
   		if thisNode.type == LINE and previousNode.position == thisNode.position:
   			# check double in all layers
   			
   			checkPointIndex = getPointIndex(thisLayer,thisNode)
   			checkPrevPointIndex = getPointIndex(thisLayer,previousNode)
   			
   			glyph = thisLayer.parent
   			allDouble = True
   			for layer in glyph.layers:
   				if getPointPos(layer, checkPointIndex) == getPointPos(layer, checkPrevPointIndex):
   					continue
   				else:
   					allDouble = False
   			if allDouble:
   				print glyph.name
   				for layer in glyph.layers:
   					del layer.paths[checkPointIndex[0]].nodes[checkPointIndex[1]]
   					
print "Done."

Good point!

I dont know/understand how to do this!! please someone help