Constrain angle tool ↩
This example shows a custom EditingTool which uses the font’s italic angle to constrain point movements – a useful tool when working on slanted designs.
The tool subclasses EditingTool._mouseDragged and disables its Shift constraint, before adding its own Shift behaviour.
Off-curve points are constrained in relation to their on-curve point, not the last mouse down point.
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.'''
        _tan1 = tan(radians(angle + 90))
        _tan2 = tan(radians(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())