Programmatically flip nodes and retain their handle's relative positions

I have a task where I need to select a lot of pairs of nodes and swap their positions but when I tried to do this programmatically, only the nodes moved, is there a function that lets me swap them including the handles – i.e. the way the ‘flip horizontal’ and ‘flip vertical’ works?

Can you make a mock-up please? If you want to change node positions, why do you not want them to move?

I don’t know of a specific existing function, but I would approach it like this:

from Foundation import NSPoint

def swap_oncurve_node_positions_keep_offcurves():
	# get the selected oncurve nodes
	selected_nodes = [selected for selected in Layer.selection if type(selected) == GSNode and selected.type != "offcurve"]
	# check that exactly two nodes are selected
	if len(selected_nodes) != 2:
		return
	node_1 = selected_nodes[0]
	node_2 = selected_nodes[1]
	
	# collect node positions
	node_1_old_position = node_1.position
	node_2_old_position = node_2.position
	
	# collect offcurve node positions
	node_1_offcurves = [attached_node for attached_node in [node_1.prevNode, node_1.nextNode] if attached_node.type == "offcurve"]
	node_2_offcurves = [attached_node for attached_node in [node_2.prevNode, node_2.nextNode] if attached_node.type == "offcurve"]

	# calculate difference in position
	x_diff = node_1.position.x - node_2.position.x
	y_diff = node_1.position.y - node_2.position.y	
	
	# swap node positions
	node_1.position = node_2_old_position
	node_2.position = node_1_old_position
	
	# move offcurves by change in position
	for node_1_offcurve in node_1_offcurves:
		node_1_offcurve.position = NSPoint(node_1_offcurve.position.x - x_diff, node_1_offcurve.position.y - y_diff)
	
	for node_2_offcurve in node_2_offcurves:
		node_2_offcurve.position = NSPoint(node_2_offcurve.position.x + x_diff, node_2_offcurve.position.y + y_diff)

swap_oncurve_node_positions_keep_offcurves()

No, I am not ashamed of loving generator expressions.

If you want a more confusing way of writing the above, here are even more beautiful generator expressions:

from Foundation import NSPoint

def swap_oncurve_node_positions_keep_offcurves():
	# get selected oncurve nodes
	selected_nodes = [selected for selected in Layer.selection if type(selected) == GSNode and selected.type != "offcurve"]
	
	# check that exactly two nodes are selected
	if len(selected_nodes) != 2:
		return
	
	# collect the offcurve nodes for each selected node
	nodes_offcurves = [[attached_node for attached_node in [node.prevNode, node.nextNode] if attached_node.type == "offcurve"] for node in selected_nodes]
	
	# swap the oncurve node positions
	for index, position in enumerate([node.position for node in selected_nodes]):		
		selected_nodes[index - 1].position = position
	
	# move the offcurve nodes by the change in oncurve node position
	for index, node_offcurves in enumerate(nodes_offcurves):
		x_diff = selected_nodes[index].x - selected_nodes[index - 1].x
		y_diff = selected_nodes[index].y - selected_nodes[index - 1].y
		for offcurve in node_offcurves:
			offcurve.position = NSPoint(offcurve.position.x + x_diff, offcurve.position.y + y_diff)

swap_oncurve_node_positions_keep_offcurves()

Now part of my scripts:

1 Like

Amazing thank you! I thought there would’ve been a way to do it with an existing function instead of manually calculating the difference and applying it to the offcurve points.