diff options
| author | Adrien Tétar | 2015-09-12 21:17:44 +0200 | 
|---|---|---|
| committer | Adrien Tétar | 2015-09-12 21:17:44 +0200 | 
| commit | 1af89417c160886f8f990cff459aa9d22d830aba (patch) | |
| tree | 2de30f071c98bba2cbba117fe2bc835be8e367e7 /Lib/defconQt/glyphView.py | |
| parent | 68ceae15fc7b94b5f0510279e2af3f09afc28cf8 (diff) | |
| download | trufont-1af89417c160886f8f990cff459aa9d22d830aba.tar.bz2 | |
glyphView: add a knife tool
Diffstat (limited to 'Lib/defconQt/glyphView.py')
| -rw-r--r-- | Lib/defconQt/glyphView.py | 125 | 
1 files changed, 121 insertions, 4 deletions
diff --git a/Lib/defconQt/glyphView.py b/Lib/defconQt/glyphView.py index ec9b57a..ce3ef2f 100644 --- a/Lib/defconQt/glyphView.py +++ b/Lib/defconQt/glyphView.py @@ -113,11 +113,16 @@ class MainGfxWindow(QMainWindow):          self.rulerTool = toolsMenu.addAction("&Ruler")          self.rulerTool.setCheckable(True)          self.rulerTool.toggled.connect(self.view.setSceneRuler) -         + +        self.knifeTool = toolsMenu.addAction("&Knife") +        self.knifeTool.setCheckable(True) +        self.knifeTool.toggled.connect(self.view.setSceneKnife) +          self.toolsGroup = QActionGroup(self)          self.toolsGroup.addAction(self.selectTool)          self.toolsGroup.addAction(self.drawingTool)          self.toolsGroup.addAction(self.rulerTool) +        self.toolsGroup.addAction(self.knifeTool)          self.selectTool.setChecked(True)          self.menuBar().addMenu(toolsMenu) @@ -216,6 +221,7 @@ class SceneTools(Enum):      SelectionTool = 0      DrawingTool = 1      RulerTool = 2 +    KnifeTool = 3  class HandleLineItem(QGraphicsLineItem):      def __init__(self, x1, y1, x2, y2, parent): @@ -479,7 +485,7 @@ class OnCurvePointItem(QGraphicsPathItem):      def mouseDoubleClickEvent(self, event):          view = self.scene().views()[0] # XXX: meh, maybe refactor doubleClick event into the scene? -        if view._currentTool == SceneTools.RulerTool: +        if view._currentTool == SceneTools.RulerTool or view._currentTool == SceneTools.KnifeTool:              return          self.setIsSmooth(not self._isSmooth) @@ -583,6 +589,9 @@ class GlyphScene(QGraphicsScene):          self._integerPlane = True          self._cachedRuler = None          self._rulerObject = None +        self._cachedIntersections = [] +        self._knifeDots = [] +        self._knifeLine = None          self._dataForUndo = []          self._dataForRedo = [] @@ -725,7 +734,10 @@ class GlyphScene(QGraphicsScene):              data = self._glyphObject.getDataToSerializeForUndo()              self._dataForUndo.append(data)              self._dataForRedo = [] -            if not view._currentTool == SceneTools.DrawingTool: +            if view._currentTool == SceneTools.KnifeTool: +                self.knifeMousePress(event) +                return +            elif view._currentTool == SceneTools.SelectionTool:                  super(GlyphScene, self).mousePressEvent(event)                  return          self._blocked = True @@ -882,6 +894,9 @@ class GlyphScene(QGraphicsScene):              if currentTool == SceneTools.RulerTool:                  self.rulerMouseMove(event)                  return +            elif currentTool == SceneTools.KnifeTool: +                self.knifeMouseMove(event) +                return              items = self.items(event.scenePos())              # XXX: we must cater w mouse tracking              # we dont need isSelected() once its rid @@ -985,6 +1000,104 @@ class GlyphScene(QGraphicsScene):          self._rulerObject = None          event.accept() +    def knifeMousePress(self, event): +        scenePos = event.scenePos() +        x, y = scenePos.x(), scenePos.y() +        self._knifeLine = self.addLine(x, y, x, y) +        event.accept() + +    """ +    Computes intersection between a cubic spline and a line segment. +    Adapted from: https://www.particleincell.com/2013/cubic-line-intersection/ + +    Takes four defcon points describing curve and four scalars describing line +    parameters. +    """ +    def computeIntersections(self, p1, p2, p3, p4, x1, y1, x2, y2): +        from fontTools.misc import bezierTools +        bx, by = x1 - x2, y2 - y1 +        m = x1*(y1-y2) + y1*(x2-x1) +        a, b, c, d = bezierTools.calcCubicParameters((p1.x, p1.y), (p2.x, p2.y), +            (p3.x, p3.y), (p4.x, p4.y)) + +        pc0 = by*a[0] + bx*a[1] +        pc1 = by*b[0] + bx*b[1] +        pc2 = by*c[0] + bx*c[1] +        pc3 = by*d[0] + bx*d[1] + m +        r = bezierTools.solveCubic(pc0, pc1, pc2, pc3) + +        sol = [] +        for t in r: +            s0 = a[0]*t**3 + b[0]*t**2 + c[0]*t + d[0] +            s1 = a[1]*t**3 + b[1]*t**2 + c[1]*t + d[1] +            if (x2-x1) != 0: +                s = (s0-x1) / (x2-x1) +            else: +                s = (s1-y1) / (y2-y1) +            if not (t < 0 or t > 1 or s < 0 or s > 1): +                sol.append((s0, s1, t)) +        return sol + +    """ +    G. Bach, http://stackoverflow.com/a/1968345 +    """ +    def lineIntersection(self, x1, y1, x2, y2, x3, y3, x4, y4): +        Bx_Ax = x2 - x1 +        By_Ay = y2 - y1 +        Dx_Cx = x4 - x3 +        Dy_Cy = y4 - y3 +        determinant = (-Dx_Cx * By_Ay + Bx_Ax * Dy_Cy) +        if abs(determinant) < 1e-20: return [] +        s = (-By_Ay * (x1 - x3) + Bx_Ax * (y1 - y3)) / determinant +        t = ( Dx_Cx * (y1 - y3) - Dy_Cy * (x1 - x3)) / determinant +        if s >= 0 and s <= 1 and t >= 0 and t <= 1: +            return [(x1 + (t * Bx_Ax), y1 + (t * By_Ay), t)] +        return [] + +    def knifeMouseMove(self, event): +        # XXX: shouldnt have to do this, it seems mouseTracking is wrongly activated +        if self._knifeLine is None: return +        for dot in self._knifeDots: +            self.removeItem(dot) +        self._knifeDots = [] +        scenePos = event.scenePos() +        x, y = scenePos.x(), scenePos.y() +        line = self._knifeLine.line() +        line.setP2(QPointF(x, y)) +        # XXX: not nice +        glyph = self.views()[0]._glyph +        self._cachedIntersections = [] +        for contour in glyph: +            segments = contour.segments +            for index, seg in enumerate(segments): +                prev = segments[index-1][-1] +                if len(seg) == 3: +                    i = self.computeIntersections(prev, seg[0], seg[1], seg[2], line.x1(), line.y1(), x, y) +                else: +                    i = self.lineIntersection(prev.x, prev.y, seg[0].x, seg[0].y, line.x1(), line.y1(), x, y) +                for pt in i: +                    scale = self.getViewScale() +                    item = self.addEllipse(-offHalf/scale, -offHalf/scale, offWidth/scale, offHeight/scale) +                    item.setPos(pt[0], pt[1]) +                    self._cachedIntersections.append((contour, index, pt[2])) +                    self._knifeDots.append(item) +        self._knifeLine.setLine(line) +        event.accept() + +    def knifeMouseRelease(self, event): +        self.removeItem(self._knifeLine) +        self._knifeLine = None +        for dot in self._knifeDots: +            self.removeItem(dot) +        self._knifeDots = [] +        # reverse so as to not invalidate our cached segment indexes +        self._cachedIntersections.reverse() +        if len(self._cachedIntersections): +            for intersect in self._cachedIntersections: +                contour, index, t = intersect +                contour.splitAndInsertPointAtSegmentAndT(index, t) +        event.accept() +  class GlyphView(QGraphicsView):      Native, OpenGL, Image = range(3) @@ -1311,7 +1424,11 @@ class GlyphView(QGraphicsView):      def setSceneSelection(self):          self._currentTool = SceneTools.SelectionTool          self.setDragMode(QGraphicsView.RubberBandDrag) -     + +    def setSceneKnife(self): +        self._currentTool = SceneTools.KnifeTool +        self.setDragMode(QGraphicsView.NoDrag) +      # Lock/release handdrag does not seem to work…      '''      def mouseReleaseEvent(self, event):  | 
