Constrain to angles other than 0, 90, 45.

• Hey there!

I'm drawing a weird slanted thing with angled extremes and it's getting annoying to align everything by hand to guidelines.

So I was wondering if there's already a tool to constrain handles to an arbitrary angle (other than horizontal, vertical and 45º).

Thought I'd ask before I dive into trying to write one myself.

• I dont know of such a tool... and the current EditingTool has 0°, 90° and optionally 45° backed in.

Maybe RF should add the italic angle as an optional constrain. I remember testing multiple constrains but it didnt work out good (I dont know why, probably to complex from the users point of view)

But a single tool with a single constrain is not that hard! :)

• hello @ro-hernandezz,

here’s a custom editing tool which uses the font’s italic angle to constrain point movements. it’s actually very useful!

``````from math import tan, radians
from mojo.events import EditingTool, installTool
from defcon import Point

class ConstrainAngleEditingTool(EditingTool):

def getToolbarTip(self):
return "Constrain Angle"

def modifyDeltaForAngle(self, delta, angle):
'''Constrain delta to a given angle.'''
if abs(_tan1) < abs(_tan2):
delta.x = -_tan1 * delta.y
else:
delta.y = _tan2 * delta.x

def modifyDraggingPoint(self, point, delta):
'''Constrain on-curve points to italic angle.'''

# get italic angle from font!
f = CurrentFont()
self.angle = f.info.italicAngle - 90

# calculate delta for current mouse down point
if self.mouseDownPoints:
fx, fy = self.mouseDownPoints[-1]
else:
fx, fy = point

delta.x = point.x - fx
delta.y = point.y - fy

# constrain delta to angle
if self.shiftDown:
self.modifyDeltaForAngle(delta, self.angle)

return point, delta

def _mouseDragged(self, point, delta):
'''Constrain off-curve points to italic angle.'''

# apply default _mouseDragged behavior first
shiftdown = self.shiftDown
self.shiftDown = False
super()._mouseDragged(point, delta)
self.shiftDown = shiftdown

# handle single off-curve point selection
if self.shiftDown and self.selection.containsSingleOffCurve():

# get selected off-curve point
offcurve = self.selection.selectedPoints[0]

# get related on-curve point
info = self.selection.selectionDataForPoint(offcurve)
anchor = info["anchor"]

# calculate delta for off-curve point
offcurveDelta = Point((offcurve.x - anchor.x, offcurve.y - anchor.y))

# constrain delta to angle
self.modifyDeltaForAngle(offcurveDelta, self.angle)

# update position of off-curve point
offcurve.x = anchor.x + offcurveDelta.x
offcurve.y = anchor.y + offcurveDelta.y

installTool(ConstrainAngleEditingTool())
``````

handling off-curve points was a bit harder because they are constrained in relation to their on-curve point, not the last mouse down point. thanks @frederik for showing how to do it.

have fun drawing weird slanted things!

• @gferreira @frederik Oh wow! Thanks so so much!

I was wondering how to write such a thing since I've never done an interactive tool, but my timeline seems to go much slower than Gustavo's!

It seems I wasn't totally lost, but would've got stuck in sooo many places.

Thanks again! I will def use this a lot.

• @gferreira why `_mouseDragged` instead of `mouseDragged`?

• hello @colinmford,

that part was done by @frederik. if I understand it correctly, `_mouseDragged` is the internal method provided by the `BaseEventTool` superclass, which is called externally by the `mouseDragged` method in the `EditingTool` class that inherits from it. in this case, we’re modifying the internal behavior.

it’s a bit like the `_moveTo` and `moveTo` methods in a BasePen

I hope this is correct and makes sense!

• Its indeed similar to a pen. This allows easy subclassing of internal tools by simply adding behaviour to a tool.

This tool is subclassing `_mouseDragged` as it needs the same behaviour but with the shift constrain disabled, as this tool will perform his own shift behaviour.