How to add off-curve nodes?

I’m struggling with adding nodes, especially off-curve (i.e. converting straight segment to curve). There seem to be multiple functions you can use for this purpose, but none of them seems to do the job. What’s the best way to do it, and if you have time, could you tell me what the differences are in the functions below?

divideCurve(P0, P1, P2, P3, t)
addPoints(P1, P2)
addNodeFast_()
addNode_()
insertNodeWithPathTime_()
insertNode_atIndex_()

What you want is the insertNode_atIndex_() function.
You create BCP1 and BCP2 and then you go:
myPath.insertNode_atIndex_(BCP1, 5)
myPath.insertNode_atIndex_(BCP2, 6)
5 and 6 being examples for the position in the path, 0 would be at the beginning, -1 at the end, etc.

divideCurve() will give you the coordinates of the two resulting segments if you add a node in the middle

addPoints() just sums up the coordinates

addNode_() and addNodeFast_() add a GSNode to the end of a GSPath

insertNodeWithPathTime_() inserts a node on the path, not changing the path’s shape.

here is the code that converts a line into a curve segment:

aNode has to be set

aPath = aNode.parent
Index = aPath.nodes._owner.pyobjc_instanceMethods.nodes().index(aNode)
PreviousNode = aPath.nodes[Index-1]

add the off curve nodes

newNode = GSNode()
newNode.type = GSOFFCURVE
newNode.position = NSMakePoint( PreviousNode.x + ((aNode.x-PreviousNode.x) / 3 * 2), PreviousNode.y + ((aNode.y-PreviousNode.y) / 3 * 2) )
aPath.insertNode_atIndex_(newNode, Index)

newNode = GSNode()
newNode.type = GSOFFCURVE
newNode.position = NSMakePoint( PreviousNode.x + ((aNode.x-PreviousNode.x) / 3), PreviousNode.y + ((aNode.y-PreviousNode.y) / 3) )
aPath.insertNode_atIndex_(newNode, Index)

change the type of the selected node

aNode.type = GSCURVE

1 Like

Thanks both. I’ve finally managed to do it after two days of effort before I saw the responses. Looks like the way I found is correct (especially it took a lot of time to figure out that I had to change the type of one of the on-curves).

By the way, is there a reason why the first node (e.g. somePath.nodes[0]) isn’t the starting node?

To avoid duplication when the last segment is a curve.

I don’t see why it’s a problem when it’s a curve, but I’m okay with that as long as there’s a good reason. Thanks.

Because when you do a curve segment, you must specify an oncurve as last node. And when the curve segment is the last segment in your path, that node must be the same as the moveto you had specified at the beginning of the path (otherwise it would not be the last segment). In other words, the moveto at the beginning is superfluous.

Example:
12 12 moveto
42 12 lineto
42 42 lineto
30 42 12 30 12 12 curveto

The 12 12 is specified twice. This is implicit in the Glyphs way of storing path data.

Another question about adding points:

I’m trying to build a rough filter myself, since there seems to be no way of accessing filters from Python.

The first step of Roughen filter places nodes by the distance specified in the field (with occasional skipping). How do you do that in Python? How can I place an on-curve node at, say, every 10 units on the curve? I can do it if I calculate the length of each segment and use insertNodeWithPathTime_(), which sounds very complicated, but do I have to do that?