class OutlinePen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self,
                 glyphSet,
                 offset=10,
                 contrast=0,
                 contrastAngle=0,
                 connection="square",
                 cap="round",
                 miterLimit=None,
                 closeOpenPaths=True,
                 optimizeCurve=False,
                 preserveComponents=False,
                 filterDoubles=True):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths
        self.optimizeCurve = optimizeCurve

        self.connectionCallback = getattr(
            self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.filterDoubles = filterDoubles
        self.drawSettings()

    def _moveTo(self, pt):
        x, y = pt
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True

    def _lineTo(self, pt):
        x, y = pt
        if self.offset == 0:
            self.outerPen.lineTo((x, y))
            self.innerPen.lineTo((x, y))
            return
        self.originalPen.lineTo((x, y))

        currentPoint = self.pointClass(x, y)
        if currentPoint == self.prevPoint:
            return

        self.currentAngle = self.prevPoint.angle(currentPoint)
        thickness = self.getThickness(self.currentAngle)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerCurrentPoint

            self.firstAngle = self.currentAngle
        else:
            self.buildConnection()

        self.innerCurrentPoint = currentPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.innerPen.lineTo(self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        self.outerCurrentPoint = currentPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerPen.lineTo(self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = currentPoint
        self.prevAngle = self.currentAngle

    def _curveToOne(self, pt1, pt2, pt3):
        if self.optimizeCurve:
            curves = splitCubicAtT(self.prevPoint, pt1, pt2, pt3, .5)
        else:
            curves = [(self.prevPoint, pt1, pt2, pt3)]
        for curve in curves:
            p1, h1, h2, p2 = curve
            self._processCurveToOne(h1, h2, p2)

    def _processCurveToOne(self, pt1, pt2, pt3):
        if self.offset == 0:
            self.outerPen.curveTo(pt1, pt2, pt3)
            self.innerPen.curveTo(pt1, pt2, pt3)
            return
        self.originalPen.curveTo(pt1, pt2, pt3)

        p1 = self.pointClass(*pt1)
        p2 = self.pointClass(*pt2)
        p3 = self.pointClass(*pt3)

        if p1 == self.prevPoint:
            p1 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.01)
        if p2 == p3:
            p2 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.99)

        a1 = self.prevPoint.angle(p1)
        a2 = p2.angle(p3)

        self.currentAngle = a1
        tickness1 = self.getThickness(a1)
        tickness2 = self.getThickness(a2)

        a1bis = self.prevPoint.angle(p1, 0)
        a2bis = p3.angle(p2, 0)
        intersectPoint = interSect(
            (self.prevPoint,
             self.prevPoint + self.pointClass(cos(a1), sin(a1)) * 100),
            (p3, p3 + self.pointClass(cos(a2), sin(a2)) * 100))
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(a1), sin(a1)) * tickness1
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(a1), sin(a1)) * tickness1

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerPrevPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerPrevPoint = self.outerCurrentPoint

            self.firstAngle = a1
        else:
            self.buildConnection()

        h1 = None
        if intersectPoint is not None:
            h1 = interSect(
                (self.innerCurrentPoint, self.innerCurrentPoint +
                 self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),
                (intersectPoint, p1))
        if h1 is None:
            h1 = p1 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerCurrentPoint = p3 - self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect(
                (self.innerCurrentPoint, self.innerCurrentPoint +
                 self.pointClass(cos(a2bis), sin(a2bis)) * tickness2),
                (intersectPoint, p2))
        if h2 is None:
            h2 = p2 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerPen.curveTo(h1, h2, self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        ########
        h1 = None
        if intersectPoint is not None:
            h1 = interSect(
                (self.outerCurrentPoint, self.outerCurrentPoint +
                 self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),
                (intersectPoint, p1))
        if h1 is None:
            h1 = p1 + self.pointClass(cos(a1), sin(a1)) * tickness1

        self.outerCurrentPoint = p3 + self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect(
                (self.outerCurrentPoint, self.outerCurrentPoint +
                 self.pointClass(cos(a2bis), sin(a2bis)) * tickness2),
                (intersectPoint, p2))
        if h2 is None:
            h2 = p2 + self.pointClass(cos(a1), sin(a1)) * tickness1
        self.outerPen.curveTo(h1, h2, self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = p3
        self.currentAngle = a2
        self.prevAngle = a2

    def _closePath(self):
        if self.shouldHandleMove:
            return
        if self.offset == 0:
            self.outerPen.closePath()
            self.innerPen.closePath()
            return

        if not self.prevPoint == self.firstPoint:
            self._lineTo(self.firstPoint)

        self.originalPen.closePath()

        self.innerPrevPoint = self.innerCurrentPoint
        self.innerCurrentPoint = self.innerFirstPoint

        self.outerPrevPoint = self.outerCurrentPoint
        self.outerCurrentPoint = self.outerFirstPoint

        self.prevAngle = self.currentAngle
        self.currentAngle = self.firstAngle

        self.buildConnection(close=True)

        self.innerPen.closePath()
        self.outerPen.closePath()

    def _endPath(self):
        if self.shouldHandleMove:
            return

        self.originalPen.endPath()
        self.innerPen.endPath()
        self.outerPen.endPath()

        if self.closeOpenPaths:

            innerContour = self.innerGlyph[-1]
            outerContour = self.outerGlyph[-1]

            innerContour.reverse()

            innerContour[0].segmentType = "line"
            outerContour[0].segmentType = "line"

            self.buildCap(outerContour, innerContour)

            for point in innerContour:
                outerContour.addPoint((point.x, point.y),
                                      segmentType=point.segmentType,
                                      smooth=point.smooth)

            self.innerGlyph.removeContour(innerContour)

    def addComponent(self, glyphName, transform):
        if self.preserveComponents:
            self.components.append((glyphName, transform))
        else:
            BasePen.addComponent(self, glyphName, transform)

    # thickness

    def getThickness(self, angle):
        a2 = angle + pi * .5
        f = abs(sin(a2 + radians(self.contrastAngle)))
        f = f**5
        return self.offset + self.contrast * f

    # connections

    def buildConnection(self, close=False):
        if not checkSmooth(self.prevAngle, self.currentAngle):
            if checkInnerOuter(self.prevAngle, self.currentAngle):
                self.connectionCallback(self.outerPrevPoint,
                                        self.outerCurrentPoint, self.outerPen,
                                        close)
                self.connectionInnerCorner(self.innerPrevPoint,
                                           self.innerCurrentPoint,
                                           self.innerPen, close)
            else:
                self.connectionCallback(self.innerPrevPoint,
                                        self.innerCurrentPoint, self.innerPen,
                                        close)
                self.connectionInnerCorner(self.outerPrevPoint,
                                           self.outerCurrentPoint,
                                           self.outerPen, close)
        elif not self.filterDoubles:
            self.innerPen.lineTo(self.innerCurrentPoint)
            self.outerPen.lineTo(self.outerCurrentPoint)

    def connectionSquare(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(cos(angle_1),
                                            sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2),
                                          sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))

        if newPoint is not None:

            if self._inputmiterLimit is not None and roundFloat(
                    newPoint.distance(first)) > self._inputmiterLimit:
                pen.lineTo(tempFirst)
                pen.lineTo(tempLast)
            else:
                pen.lineTo(newPoint)

        if not close:
            pen.lineTo(last)

    def connectionRound(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(sin(angle_1), -cos(angle_1))
        tempLast = last + self.pointClass(sin(angle_2), -cos(angle_2))

        centerPoint = interSect((first, tempFirst), (last, tempLast))
        if centerPoint is None:
            # the lines are parallel, let's just take the middle
            centerPoint = (first + last) / 2

        angle_diff = (angle_1 - angle_2) % (2 * pi)
        if angle_diff > pi:
            angle_diff = 2 * pi - angle_diff
        angle_half = angle_diff / 2

        radius = centerPoint.distance(first)
        D = radius * (1 - cos(angle_half))
        handleLength = (4 * D / 3) / sin(angle_half)  # length of the bcp line

        bcp1 = first - self.pointClass(cos(angle_1),
                                       sin(angle_1)) * handleLength
        bcp2 = last + self.pointClass(cos(angle_2),
                                      sin(angle_2)) * handleLength
        pen.curveTo(bcp1, bcp2, last)

    def connectionButt(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    def connectionInnerCorner(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    # caps

    def buildCap(self, firstContour, lastContour):
        first = firstContour[-1]
        last = lastContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        self.capCallback(firstContour, lastContour, first, last,
                         self.prevAngle)

        first = lastContour[-1]
        last = firstContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        angle = radians(degrees(self.firstAngle) + 180)
        self.capCallback(lastContour, firstContour, first, last, angle)

    def capButt(self, firstContour, lastContour, first, last, angle):
        # not nothing
        pass

    def capRound(self, firstContour, lastContour, first, last, angle):
        hookedAngle = radians(degrees(angle) + 90)

        p1 = first - self.pointClass(cos(hookedAngle),
                                     sin(hookedAngle)) * self.offset

        p2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset

        oncurve = p1 + (p2 - p1) * .5

        roundness = .54  # should be self.magicCurve

        h1 = first - self.pointClass(
            cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness
        h2 = oncurve + self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness

        firstContour[-1].smooth = True

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))
        firstContour.addPoint((oncurve.x, oncurve.y),
                              smooth=True,
                              segmentType="curve")

        h1 = oncurve - self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness
        h2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset * roundness

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True

    def capSquare(self, firstContour, lastContour, first, last, angle):
        angle = radians(degrees(angle) + 90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p1.x, p1.y), smooth=False, segmentType="line")

        p2 = last - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p2.x, p2.y), smooth=False, segmentType="line")

    def drawSettings(self,
                     drawOriginal=False,
                     drawInner=False,
                     drawOuter=True):
        self.drawOriginal = drawOriginal
        self.drawInner = drawInner
        self.drawOuter = drawOuter

    def drawPoints(self, pointPen):
        if self.drawInner:
            reversePen = ReverseContourPointPen(pointPen)
            self.innerGlyph.drawPoints(CleanPointPen(reversePen))
        if self.drawOuter:
            self.outerGlyph.drawPoints(CleanPointPen(pointPen))

        if self.drawOriginal:
            if self.drawOuter:
                pointPen = ReverseContourPointPen(pointPen)
            self.originalGlyph.drawPoints(CleanPointPen(pointPen))

        for glyphName, transform in self.components:
            pointPen.addComponent(glyphName, transform)

    def draw(self, pen):
        pointPen = PointToSegmentPen(pen)
        self.drawPoints(pointPen)

    def getGlyph(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        self.drawPoints(pointPen)
        return glyph
Exemplo n.º 2
0
    def test_identifiers(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        pointPen.beginPath(identifier="contour 1")
        pointPen.addPoint((0, 0), identifier="point 1")
        pointPen.addPoint((0, 0), identifier="point 2")
        pointPen.endPath()
        pointPen.beginPath(identifier="contour 2")
        pointPen.endPath()
        pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                              identifier="component 1")
        pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                              identifier="component 2")
        guideline = Guideline()
        guideline.identifier = "guideline 1"
        glyph.appendGuideline(guideline)
        guideline = Guideline()
        guideline.identifier = "guideline 2"
        glyph.appendGuideline(guideline)

        self.assertEqual([contour.identifier for contour in glyph],
                         ["contour 1", "contour 2"])
        self.assertEqual([point.identifier for point in glyph[0]],
                         ["point 1", "point 2"])
        self.assertEqual(
            [component.identifier for component in glyph.components],
            ["component 1", "component 2"])
        with self.assertRaises(AssertionError):
            pointPen.beginPath(identifier="contour 1")
        pointPen.endPath()

        pointPen.beginPath()
        pointPen.addPoint((0, 0))
        with self.assertRaises(AssertionError):
            pointPen.addPoint((0, 0), identifier="point 1")
        pointPen.endPath()

        with self.assertRaises(AssertionError):
            pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                                  identifier="component 1")

        g = Guideline()
        g.identifier = "guideline 1"
        with self.assertRaises(AssertionError):
            glyph.appendGuideline(g)

        self.assertEqual(sorted(glyph.identifiers), [
            "component 1", "component 2", "contour 1", "contour 2",
            "guideline 1", "guideline 2", "point 1", "point 2"
        ])
        glyph.removeContour(glyph[0])
        self.assertEqual(sorted(glyph.identifiers), [
            "component 1", "component 2", "contour 2", "guideline 1",
            "guideline 2"
        ])
        glyph.removeComponent(glyph.components[0])
        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 2", "contour 2", "guideline 1", "guideline 2"])
        glyph.removeGuideline(glyph.guidelines[0])
        self.assertEqual(sorted(glyph.identifiers),
                         ["component 2", "contour 2", "guideline 2"])
Exemplo n.º 3
0
    def test_identifiers(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        pointPen.beginPath(identifier="contour 1")
        pointPen.addPoint((0, 0), identifier="point 1")
        pointPen.addPoint((0, 0), identifier="point 2")
        pointPen.endPath()
        pointPen.beginPath(identifier="contour 2")
        pointPen.endPath()
        pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                              identifier="component 1")
        pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                              identifier="component 2")
        guideline = Guideline()
        guideline.identifier = "guideline 1"
        glyph.appendGuideline(guideline)
        guideline = Guideline()
        guideline.identifier = "guideline 2"
        glyph.appendGuideline(guideline)

        self.assertEqual([contour.identifier for contour in glyph],
                         ["contour 1", "contour 2"])
        self.assertEqual([point.identifier for point in glyph[0]],
                         ["point 1", "point 2"])
        self.assertEqual(
            [component.identifier for component in glyph.components],
            ["component 1", "component 2"])
        with self.assertRaises(AssertionError):
            pointPen.beginPath(identifier="contour 1")
        pointPen.endPath()

        pointPen.beginPath()
        pointPen.addPoint((0, 0))
        with self.assertRaises(AssertionError):
            pointPen.addPoint((0, 0), identifier="point 1")
        pointPen.endPath()

        with self.assertRaises(AssertionError):
            pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                                  identifier="component 1")

        g = Guideline()
        g.identifier = "guideline 1"
        with self.assertRaises(AssertionError):
            glyph.appendGuideline(g)

        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 1", "component 2", "contour 1", "contour 2",
             "guideline 1", "guideline 2", "point 1", "point 2"])
        glyph.removeContour(glyph[0])
        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 1", "component 2", "contour 2",
             "guideline 1", "guideline 2"])
        glyph.removeComponent(glyph.components[0])
        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 2", "contour 2", "guideline 1", "guideline 2"])
        glyph.removeGuideline(glyph.guidelines[0])
        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 2", "contour 2", "guideline 2"])
Exemplo n.º 4
0
class OutlinePen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self,
                 glyphSet,
                 offset=10,
                 contrast=0,
                 contrastAngle=0,
                 connection="square",
                 cap="round",
                 miterLimit=None,
                 closeOpenPaths=True,
                 preserveComponents=False):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths

        self.connectionCallback = getattr(
            self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.drawSettings()

    def _moveTo(self, xyFix):
        (x, y) = xyFix
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True

    def _lineTo(self, xyFix):
        (x, y) = xyFix
        if self.offset == 0:
            self.outerPen.lineTo((x, y))
            self.innerPen.lineTo((x, y))
            return
        self.originalPen.lineTo((x, y))

        currentPoint = self.pointClass(x, y)
        if currentPoint == self.prevPoint:
            return

        self.currentAngle = self.prevPoint.angle(currentPoint)
        thickness = self.getThickness(self.currentAngle)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerCurrentPoint

            self.firstAngle = self.currentAngle
        else:
            self.buildConnection()

        self.innerCurrentPoint = currentPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.innerPen.lineTo(self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        self.outerCurrentPoint = currentPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerPen.lineTo(self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = currentPoint
        self.prevAngle = self.currentAngle

    def _curveToOne(self, xyFix1, xyFix2, xyFix3):
        (x1, y1) = xyFix1
        (x2, y2) = xyFix2
        (x3, y3) = xyFix3
        if self.offset == 0:
            self.outerPen.curveTo((x1, y1), (x2, y2), (x3, y3))
            self.innerPen.curveTo((x1, y1), (x2, y2), (x3, y3))
            return
        self.originalPen.curveTo((x1, y1), (x2, y2), (x3, y3))

        p1 = self.pointClass(x1, y1)
        p2 = self.pointClass(x2, y2)
        p3 = self.pointClass(x3, y3)

        if p1 == self.prevPoint:
            p1 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.01)
        if p2 == p3:
            p2 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.99)

        a1 = self.prevPoint.angle(p1)
        a2 = p2.angle(p3)

        self.currentAngle = a1
        tickness1 = self.getThickness(a1)
        tickness2 = self.getThickness(a2)

        a1bis = self.prevPoint.angle(p1, 0)
        a2bis = p3.angle(p2, 0)
        intersectPoint = interSect(
            (self.prevPoint,
             self.prevPoint + self.pointClass(cos(a1), sin(a1)) * 100),
            (p3, p3 + self.pointClass(cos(a2), sin(a2)) * 100))
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(a1), sin(a1)) * tickness1
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(a1), sin(a1)) * tickness1

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerPrevPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerPrevPoint = self.outerCurrentPoint

            self.firstAngle = a1
        else:
            self.buildConnection()
        h1 = None
        if intersectPoint is not None:
            h1 = interSect(
                (self.innerCurrentPoint, self.innerCurrentPoint +
                 self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),
                (intersectPoint, p1))
        if h1 is None:
            h1 = p1 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerCurrentPoint = p3 - self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect(
                (self.innerCurrentPoint, self.innerCurrentPoint +
                 self.pointClass(cos(a2bis), sin(a2bis)) * tickness2),
                (intersectPoint, p2))
        if h2 is None:
            h2 = p2 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerPen.curveTo(h1, h2, self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        ########
        h1 = None
        if intersectPoint is not None:
            h1 = interSect(
                (self.outerCurrentPoint, self.outerCurrentPoint +
                 self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),
                (intersectPoint, p1))
        if h1 is None:
            h1 = p1 + self.pointClass(cos(a1), sin(a1)) * tickness1

        self.outerCurrentPoint = p3 + self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect(
                (self.outerCurrentPoint, self.outerCurrentPoint +
                 self.pointClass(cos(a2bis), sin(a2bis)) * tickness2),
                (intersectPoint, p2))
        if h2 is None:
            h2 = p2 + self.pointClass(cos(a1), sin(a1)) * tickness1
        self.outerPen.curveTo(h1, h2, self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = p3
        self.currentAngle = a2
        self.prevAngle = a2

    def _closePath(self):
        if self.shouldHandleMove:
            return
        if self.offset == 0:
            self.outerPen.closePath()
            self.innerPen.closePath()
            return

        if not self.prevPoint == self.firstPoint:
            self._lineTo(self.firstPoint)

        self.originalPen.closePath()

        self.innerPrevPoint = self.innerCurrentPoint
        self.innerCurrentPoint = self.innerFirstPoint

        self.outerPrevPoint = self.outerCurrentPoint
        self.outerCurrentPoint = self.outerFirstPoint

        self.prevAngle = self.currentAngle
        self.currentAngle = self.firstAngle

        self.buildConnection(close=True)

        self.innerPen.closePath()
        self.outerPen.closePath()

    def _endPath(self):
        if self.shouldHandleMove:
            return

        self.originalPen.endPath()
        self.innerPen.endPath()
        self.outerPen.endPath()

        if self.closeOpenPaths:

            innerContour = self.innerGlyph[-1]
            outerContour = self.outerGlyph[-1]

            innerContour.reverse()

            innerContour[0].segmentType = "line"
            outerContour[0].segmentType = "line"

            self.buildCap(outerContour, innerContour)

            for point in innerContour:
                outerContour.addPoint((point.x, point.y),
                                      segmentType=point.segmentType,
                                      smooth=point.smooth)

            self.innerGlyph.removeContour(innerContour)

    def addComponent(self, glyphName, transform):
        if self.preserveComponents:
            self.components.append((glyphName, transform))
        else:
            BasePen.addComponent(self, glyphName, transform)

    ## thickness

    def getThickness(self, angle):
        a2 = angle + pi * .5
        f = abs(sin(a2 + radians(self.contrastAngle)))
        f = f**5
        return self.offset + self.contrast * f

    ## connections

    def buildConnection(self, close=False):
        if not checkSmooth(self.prevAngle, self.currentAngle):
            if checkInnerOuter(self.prevAngle, self.currentAngle):
                self.connectionCallback(self.outerPrevPoint,
                                        self.outerCurrentPoint, self.outerPen,
                                        close)
                self.connectionInnerCorner(self.innerPrevPoint,
                                           self.innerCurrentPoint,
                                           self.innerPen, close)
            else:
                self.connectionCallback(self.innerPrevPoint,
                                        self.innerCurrentPoint, self.innerPen,
                                        close)
                self.connectionInnerCorner(self.outerPrevPoint,
                                           self.outerCurrentPoint,
                                           self.outerPen, close)

    def connectionSquare(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(cos(angle_1),
                                            sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2),
                                          sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))

        if newPoint is not None:

            if self._inputmiterLimit is not None and roundFloat(
                    newPoint.distance(first)) > self._inputmiterLimit:
                pen.lineTo(tempFirst)
                pen.lineTo(tempLast)
            else:
                pen.lineTo(newPoint)

        if not close:
            pen.lineTo(last)

    def connectionRound(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(cos(angle_1),
                                            sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2),
                                          sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))
        if newPoint is None:
            pen.lineTo(last)
            return
        #print "(%s, %s)," % (newPoint.x, newPoint.y)
        distance1 = newPoint.distance(first)
        distance2 = newPoint.distance(last)
        #print distance1, distance2
        if roundFloat(distance1) > self.miterLimit + self.contrast:
            distance1 = self.miterLimit + tempFirst.distance(tempLast) * .7
        if roundFloat(distance2) > self.miterLimit + self.contrast:
            distance2 = self.miterLimit + tempFirst.distance(tempLast) * .7

        distance1 *= self.magicCurve
        distance2 *= self.magicCurve

        bcp1 = first - self.pointClass(cos(angle_1), sin(angle_1)) * distance1
        bcp2 = last + self.pointClass(cos(angle_2), sin(angle_2)) * distance2
        pen.curveTo(bcp1, bcp2, last)

    def connectionButt(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    def connectionInnerCorner(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    ## caps

    def buildCap(self, firstContour, lastContour):
        first = firstContour[-1]
        last = lastContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        self.capCallback(firstContour, lastContour, first, last,
                         self.prevAngle)

        first = lastContour[-1]
        last = firstContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        angle = radians(degrees(self.firstAngle) + 180)
        self.capCallback(lastContour, firstContour, first, last, angle)

    def capButt(self, firstContour, lastContour, first, last, angle):
        ## not nothing
        pass

    def capRound(self, firstContour, lastContour, first, last, angle):
        hookedAngle = radians(degrees(angle) + 90)

        p1 = first - self.pointClass(cos(hookedAngle),
                                     sin(hookedAngle)) * self.offset

        p2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset

        oncurve = p1 + (p2 - p1) * .5

        roundness = .54

        h1 = first - self.pointClass(
            cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness
        h2 = oncurve + self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness

        firstContour[-1].smooth = True

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))
        firstContour.addPoint((oncurve.x, oncurve.y),
                              smooth=True,
                              segmentType="curve")

        h1 = oncurve - self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness
        h2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset * roundness

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True

    def capSquare(self, firstContour, lastContour, first, last, angle):
        angle = radians(degrees(angle) + 90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p1.x, p1.y), smooth=False, segmentType="line")

        p2 = last - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p2.x, p2.y), smooth=False, segmentType="line")

    def drawSettings(self,
                     drawOriginal=False,
                     drawInner=False,
                     drawOuter=True):
        self.drawOriginal = drawOriginal
        self.drawInner = drawInner
        self.drawOuter = drawOuter

    def drawPoints(self, pointPen):
        if self.drawInner:
            reversePen = ReverseContourPointPen(pointPen)
            self.innerGlyph.drawPoints(CleanPointPen(reversePen))
        if self.drawOuter:
            self.outerGlyph.drawPoints(CleanPointPen(pointPen))

        if self.drawOriginal:
            if self.drawOuter:
                pointPen = ReverseContourPointPen(pointPen)
            self.originalGlyph.drawPoints(CleanPointPen(pointPen))

        for glyphName, transform in self.components:
            pointPen.addComponent(glyphName, transform)

    def draw(self, pen):
        pointPen = PointToSegmentPen(pen)
        self.drawPoints(pointPen)

    def getGlyph(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        self.drawPoints(pointPen)
        return glyph
Exemplo n.º 5
0
class OutlineFitterPen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self,
                 glyphSet,
                 offset=10,
                 contrast=0,
                 contrastAngle=0,
                 connection="square",
                 cap="round",
                 miterLimit=None,
                 closeOpenPaths=True,
                 optimizeCurve=False,
                 preserveComponents=False,
                 filterDoubles=True,
                 alwaysConnect=False):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths
        self.optimizeCurve = optimizeCurve

        self.connectionCallback = getattr(
            self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.filterDoubles = filterDoubles
        self.alwaysConnect = alwaysConnect
        self.drawSettings()

    def _moveTo(self, pt):
        x, y = pt
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True

    def _lineTo(self, pt):
        x, y = pt
        if self.offset == 0:
            self.outerPen.lineTo((x, y))
            self.innerPen.lineTo((x, y))
            return
        self.originalPen.lineTo((x, y))

        currentPoint = self.pointClass(x, y)
        if currentPoint == self.prevPoint:
            return

        self.currentAngle = self.prevPoint.angle(currentPoint)
        thickness = self.getThickness(self.currentAngle)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerCurrentPoint

            self.firstAngle = self.currentAngle
        else:
            self.buildConnection()

        self.innerCurrentPoint = currentPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.innerPen.lineTo(self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        self.outerCurrentPoint = currentPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerPen.lineTo(self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = currentPoint
        self.prevAngle = self.currentAngle

    def _curveToOne(self, pt1, pt2, pt3):
        if self.optimizeCurve:
            curves = splitCubicAtT(self.prevPoint, pt1, pt2, pt3, .5)
        else:
            curves = [(self.prevPoint, pt1, pt2, pt3)]
        for curve in curves:
            p1, h1, h2, p2 = curve
            self._processCurveToOne(h1, h2, p2)

    def _processCurveToOne(self, pt1, pt2, pt3):
        if self.offset == 0:
            self.outerPen.curveTo(pt1, pt2, pt3)
            self.innerPen.curveTo(pt1, pt2, pt3)
            return
        self.originalPen.curveTo(pt1, pt2, pt3)

        p1 = self.pointClass(*pt1)
        p2 = self.pointClass(*pt2)
        p3 = self.pointClass(*pt3)

        if p1 == self.prevPoint:
            p1 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.01)
        if p2 == p3:
            p2 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.99)

        a1 = self.prevPoint.angle(p1)
        a2 = p2.angle(p3)

        self.currentAngle = a1
        tickness1 = self.getThickness(a1)
        tickness2 = self.getThickness(a2)

        a1bis = self.prevPoint.angle(p1, 0)
        a2bis = p3.angle(p2, 0)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(a1), sin(a1)) * tickness1
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(a1), sin(a1)) * tickness1

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerPrevPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerPrevPoint = self.outerCurrentPoint

            self.firstAngle = a1
        else:
            self.buildConnection()

        # Collect four original points
        originalPoints = [self.prevPoint, p1, p2, p3]

        # Collect four inner points
        h1 = p1 - self.pointClass(cos(a1), sin(a1)) * tickness1
        h2 = p2 - self.pointClass(cos(a2), sin(a2)) * tickness2
        innerPoints = [self.innerCurrentPoint, h1, h2]
        self.innerCurrentPoint = p3 - self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2
        innerPoints.append(self.innerCurrentPoint)

        # Collect four outer points
        h1 = p1 + self.pointClass(cos(a1), sin(a1)) * tickness1
        h2 = p2 + self.pointClass(cos(a2), sin(a2)) * tickness2
        outerPoints = [self.outerCurrentPoint, h1, h2]
        self.outerCurrentPoint = p3 + self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2
        outerPoints.append(self.outerCurrentPoint)
        """
        # @@@ NEW TECHNIQUE:
        
        # Determine if a curve in/out of a BCP is rotating clockwise or counterclockwise
        # Compare vectors of the point to the bcp and the point to a split location just after/before the point
        splits = splitCubicAtT(self.prevPoint, p1, p2, p3, 0.01, 0.99)
        splitPt0 = self.pointClass(splits[0][-1])
        splitPt1 = self.pointClass(splits[-1][0])
        # vectors
        vBcp0 = self.prevPoint - p1
        vNext0 = self.prevPoint - splitPt0
        vBcp1 = p3 - p2
        vNext1 = p3 - splitPt1
        # Find which way the vector is rotating by looking at the sign of the dot product
        dot0 = vBcp0[0] * vNext0[1] - vBcp0[1] * vNext0[0]
        dot1 = vBcp1[0] * vNext1[1] - vBcp1[1] * vNext1[0]

        dir0 = 1
        dir1 = 1
        if dot0 < 0:
            dir0 = -1
        if dot1 < 0:
            dir1 = -1
        """

        # @@@ OLD TECHNIQUE:

        # Determine if a curve in/out of a BCP is rotating clockwise or counterclockwise
        # Compare vectors of the point to the bcp and the point to the next point
        vBcp0 = self.prevPoint - p1
        vNext0 = self.prevPoint - p3
        vBcp1 = self.pointClass(pt3) - p2
        vNext1 = self.pointClass(pt3) - self.prevPoint
        # Find which way the vector is rotating by looking at the sign of the dot product
        dot0 = vBcp0[0] * vNext0[1] - vBcp0[1] * vNext0[0]
        dot1 = vBcp1[0] * vNext1[1] - vBcp1[1] * vNext1[0]
        # Find the angles
        # ang0 = self.prevPoint.angle(p1)
        # ang1 = self.pointClass(pt3).angle(p2)
        # print(ang0, ang1)
        dir0 = 1
        dir1 = 1
        if dot0 < 0:
            dir0 = -1
        if dot1 < 0:
            dir1 = -1

        # print(dot0, dot1)
        """
        # If the onCurves are closer to each other than 2x the thickness, reverse the direction of the inner curve
        angle = self.prevPoint.angle(p3)
        distance = self.prevPoint.distance(p3)
        thickness = self.getThickness(angle)
        reverseInner = False
        #print(distance, thickness)
        if distance < thickness * 2:
            reverseInner = True
        # @@@ Do this differently, it's not helping
            
            
        Two bugs (maybe related)
        
        - When the on-curves are closer to each other than 2x the offset, need to reverse the direction on the inner handles
        - When the BCPs are in the same direction and are on top of each other, the curve asymmetrically sets the direction (wrong)
        
        """

        # Flatten the original curve
        flatPoints = flattenCurve(originalPoints)

        # Move the BCPs to fit the curve
        innerPoints = self.fitCurve(innerPoints, flatPoints, dir0, dir1)
        outerPoints = self.fitCurve(outerPoints, flatPoints, -dir0, -dir1)

        # Draw the four points
        self.innerPen.curveTo(innerPoints[1], innerPoints[2], innerPoints[3])
        self.innerPrevPoint = self.innerCurrentPoint
        self.outerPen.curveTo(outerPoints[1], outerPoints[2], outerPoints[3])
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = p3
        self.currentAngle = a2
        self.prevAngle = a2

    def _closePath(self):
        if self.shouldHandleMove:
            return
        if self.offset == 0:
            self.outerPen.closePath()
            self.innerPen.closePath()
            return

        if not self.prevPoint == self.firstPoint:
            self._lineTo(self.firstPoint)

        self.originalPen.closePath()

        self.innerPrevPoint = self.innerCurrentPoint
        self.innerCurrentPoint = self.innerFirstPoint

        self.outerPrevPoint = self.outerCurrentPoint
        self.outerCurrentPoint = self.outerFirstPoint

        self.prevAngle = self.currentAngle
        self.currentAngle = self.firstAngle

        self.buildConnection(close=True)

        self.innerPen.closePath()
        self.outerPen.closePath()

    def _endPath(self):
        if self.shouldHandleMove:
            return

        self.originalPen.endPath()
        self.innerPen.endPath()
        self.outerPen.endPath()

        if self.closeOpenPaths:

            innerContour = self.innerGlyph[-1]
            outerContour = self.outerGlyph[-1]

            innerContour.reverse()

            innerContour[0].segmentType = "line"
            outerContour[0].segmentType = "line"

            self.buildCap(outerContour, innerContour)

            for point in innerContour:
                outerContour.addPoint((point.x, point.y),
                                      segmentType=point.segmentType,
                                      smooth=point.smooth)

            self.innerGlyph.removeContour(innerContour)

    def addComponent(self, glyphName, transform):
        if self.preserveComponents:
            self.components.append((glyphName, transform))
        else:
            BasePen.addComponent(self, glyphName, transform)

    # thickness

    def getThickness(self, angle):
        a2 = angle + pi * .5
        f = abs(sin(a2 + radians(self.contrastAngle)))
        f = f**5
        return self.offset + self.contrast * f

    # fit curve

    def _testCurve(self, pts, flatPts, f0, f1, inRange=False):
        # Normalize and scale the BCPs
        scaled0 = (pts[1] - pts[0]) * f0
        pts[1] = pts[1] + scaled0
        dist1 = abs(self.pointClass(pts[3]).distance(pts[2]))
        scaled1 = (pts[3] - pts[2]) * f1
        pts[2] = pts[2] + scaled1
        # Split at a few locations
        splitFactors = [0.25, 0.5, 0.75]
        newSplit = splitCubicAtT(pts[0], pts[1], pts[2], pts[3], *splitFactors)
        newSplitLocs = [self.pointClass(pts[-1]) for pts in newSplit]
        newSplitLocs = newSplitLocs[:-1]
        # Measure these splits back to the flattened original segment
        # Prepare chunks of the flatPts, if we should be testing inRange
        #   ...a factor of 0.25 should only be compared against 0.2-0.3, 0.5 should be 0.45-0.55, 0.75 should be 0.7-0.8
        totalFlats = len(flatPts)
        flatRanges = [(int(totalFlats * 0.2), int(totalFlats * 0.3)),
                      (int(totalFlats * 0.45), int(totalFlats * 0.55)),
                      (int(totalFlats * 0.7), int(totalFlats * 0.8))
                      ]  # These relate directly to the factors in splitFactors
        totalDiff = 0
        for i, loc in enumerate(newSplitLocs):
            if inRange:
                # If it should only check a smaller range of flat points
                # (experimental)
                flatPtChunk = flatPts[flatRanges[i][0]:flatRanges[i][1]]
            else:
                flatPtChunk = flatPts
            closestLoc, closestDist = self.findClosest(loc, flatPtChunk)
            angle = loc.angle(self.pointClass(closestLoc))
            expectedDistance = self.getThickness(angle)
            diff = expectedDistance - closestDist
            totalDiff += diff
        return pts, totalDiff

    def findClosest(self, loc, locs):
        closest = None
        closestDist = None
        for testLoc in locs:
            d = abs(self.pointClass(loc).distance(self.pointClass(testLoc)))
            if not closestDist:
                closestDist = d
                closest = testLoc
            else:
                if d < closestDist:
                    closestDist = d
                    closest = testLoc
        return closest, closestDist

    def fitCurve(self, pts, flatPts, dir0, dir1):
        # pts is a list of four points defining a curve
        # Use three sample points along the curve to move the BCPs in directions dir0 and dir1
        # until the sampled points average out to the correct distance from flatPoints

        # positive or negative factor for moving the BCPs, depending on the direction the curve is turning
        f = 0.05
        if dir0 > 0:
            f0 = f
        else:
            f0 = -f
        if dir1 > 0:
            f1 = f
        else:
            f1 = -f

        closestPts = pts.copy()
        totalPasses = 0
        # First pass, coarse
        while True:
            totalPasses += 1
            if totalPasses > 50:
                break
            closestPts, totalDiff = self._testCurve(closestPts, flatPts, f0,
                                                    f1)
            if totalDiff < 0:
                # The diff crossed zero
                break

        # Second pass, more fine and in the other direction
        f0 *= -0.1
        f1 *= -0.1
        totalPasses = 0
        while True:
            totalPasses += 1
            if totalPasses > 50:
                break
            closestPts, totalDiff = self._testCurve(closestPts, flatPts, f0,
                                                    f1)
            if totalDiff > 0:
                # The diff crossed zero in the other direction this time
                break

        # Third pass, more fine and in the other direction
        f0 *= -0.1
        f1 *= -0.1
        totalPasses = 0
        while True:
            totalPasses += 1
            if totalPasses > 50:
                break
            closestPts, totalDiff = self._testCurve(closestPts, flatPts, f0,
                                                    f1)
            if totalDiff < 0:
                # The diff crossed zero
                break

        return closestPts

    # connections

    def buildConnection(self, close=False):
        if self.alwaysConnect:
            # Always force a connection for compatibility
            self.connectionCallback(self.outerPrevPoint,
                                    self.outerCurrentPoint, self.outerPen,
                                    close)
            self.connectionCallback(self.innerPrevPoint,
                                    self.innerCurrentPoint, self.innerPen,
                                    close)
        else:
            if not checkSmooth(self.prevAngle, self.currentAngle):
                if checkInnerOuter(self.prevAngle, self.currentAngle):
                    self.connectionCallback(self.outerPrevPoint,
                                            self.outerCurrentPoint,
                                            self.outerPen, close)
                    self.connectionInnerCorner(self.innerPrevPoint,
                                               self.innerCurrentPoint,
                                               self.innerPen, close)
                else:
                    self.connectionCallback(self.innerPrevPoint,
                                            self.innerCurrentPoint,
                                            self.innerPen, close)
                    self.connectionInnerCorner(self.outerPrevPoint,
                                               self.outerCurrentPoint,
                                               self.outerPen, close)
            elif not self.filterDoubles:
                self.innerPen.lineTo(self.innerCurrentPoint)
                self.outerPen.lineTo(self.outerCurrentPoint)

    def connectionSquare(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(cos(angle_1),
                                            sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2),
                                          sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))

        if newPoint is not None:

            if self._inputmiterLimit is not None and roundFloat(
                    newPoint.distance(first)) > self._inputmiterLimit:
                pen.lineTo(tempFirst)
                pen.lineTo(tempLast)
            else:
                pen.lineTo(newPoint)

        if not close:
            pen.lineTo(last)

    def connectionRound(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(sin(angle_1), -cos(angle_1))
        tempLast = last + self.pointClass(sin(angle_2), -cos(angle_2))

        centerPoint = interSect((first, tempFirst), (last, tempLast))
        if centerPoint is None:
            # the lines are parallel, let's just take the middle
            centerPoint = (first + last) / 2

        angle_diff = (angle_1 - angle_2) % (2 * pi)
        if angle_diff > pi:
            angle_diff = 2 * pi - angle_diff
        angle_half = angle_diff / 2

        radius = centerPoint.distance(first)
        D = radius * (1 - cos(angle_half))
        try:
            handleLength = (4 * D / 3) / sin(
                angle_half)  # length of the bcp line
        except:
            handleLength = 0

        bcp1 = first - self.pointClass(cos(angle_1),
                                       sin(angle_1)) * handleLength
        bcp2 = last + self.pointClass(cos(angle_2),
                                      sin(angle_2)) * handleLength
        pen.curveTo(bcp1, bcp2, last)

    def connectionButt(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    def connectionInnerCorner(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    # caps

    def buildCap(self, firstContour, lastContour):
        first = firstContour[-1]
        last = lastContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        self.capCallback(firstContour, lastContour, first, last,
                         self.prevAngle)

        first = lastContour[-1]
        last = firstContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        angle = radians(degrees(self.firstAngle) + 180)
        self.capCallback(lastContour, firstContour, first, last, angle)

    def capButt(self, firstContour, lastContour, first, last, angle):
        # not nothing
        pass

    def capRound(self, firstContour, lastContour, first, last, angle):
        hookedAngle = radians(degrees(angle) + 90)

        p1 = first - self.pointClass(cos(hookedAngle),
                                     sin(hookedAngle)) * self.offset

        p2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset

        oncurve = p1 + (p2 - p1) * .5

        roundness = .54  # should be self.magicCurve

        h1 = first - self.pointClass(
            cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness
        h2 = oncurve + self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness

        firstContour[-1].smooth = True

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))
        firstContour.addPoint((oncurve.x, oncurve.y),
                              smooth=True,
                              segmentType="curve")

        h1 = oncurve - self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness
        h2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset * roundness

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True

    def capSquare(self, firstContour, lastContour, first, last, angle):
        angle = radians(degrees(angle) + 90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p1.x, p1.y), smooth=False, segmentType="line")

        p2 = last - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p2.x, p2.y), smooth=False, segmentType="line")

    def capRoundsimple(self, firstContour, lastContour, first, last, angle):

        angle = radians(degrees(angle) + 90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle),
                                     sin(angle)) * (self.offset * 1.5)
        firstContour.addPoint((p1.x, p1.y))

        p2 = last - self.pointClass(cos(angle),
                                    sin(angle)) * (self.offset * 1.5)
        firstContour.addPoint((p2.x, p2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True
        #firstContour.addPoint((last.x, last.y), smooth=True, segmentType="curve")

    def drawSettings(self,
                     drawOriginal=False,
                     drawInner=False,
                     drawOuter=True):
        self.drawOriginal = drawOriginal
        self.drawInner = drawInner
        self.drawOuter = drawOuter

    def drawPoints(self, pointPen):
        if self.drawInner:
            reversePen = ReverseContourPointPen(pointPen)
            self.innerGlyph.drawPoints(CleanPointPen(reversePen))
        if self.drawOuter:
            self.outerGlyph.drawPoints(CleanPointPen(pointPen))

        if self.drawOriginal:
            if self.drawOuter:
                pointPen = ReverseContourPointPen(pointPen)
            self.originalGlyph.drawPoints(CleanPointPen(pointPen))

        for glyphName, transform in self.components:
            pointPen.addComponent(glyphName, transform)

    def draw(self, pen):
        pointPen = PointToSegmentPen(pen)
        self.drawPoints(pointPen)

    def getGlyph(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        self.drawPoints(pointPen)
        return glyph
class OutlinePen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self, glyphSet, offset=10, contrast=0, contrastAngle=0, connection="square", cap="round", miterLimit=None, closeOpenPaths=True, preserveComponents=False):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths

        self.connectionCallback = getattr(self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.drawSettings()

    def _moveTo(self, xy):
        (x, y) = xy
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True

    def _lineTo(self, xy):
        (x, y) = xy
        if self.offset == 0:
            self.outerPen.lineTo((x, y))
            self.innerPen.lineTo((x, y))
            return
        self.originalPen.lineTo((x, y))

        currentPoint = self.pointClass(x, y)
        if currentPoint == self.prevPoint:
            return

        self.currentAngle = self.prevPoint.angle(currentPoint)
        thickness = self.getThickness(self.currentAngle)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerCurrentPoint = self.prevPoint + self.pointClass(cos(self.currentAngle), sin(self.currentAngle)) * thickness

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerCurrentPoint

            self.firstAngle = self.currentAngle
        else:
            self.buildConnection()

        self.innerCurrentPoint = currentPoint - self.pointClass(cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.innerPen.lineTo(self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        self.outerCurrentPoint = currentPoint + self.pointClass(cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerPen.lineTo(self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = currentPoint
        self.prevAngle = self.currentAngle

    def _curveToOne(self, xy1, xy2, xy3):
        (x1, y1), (x2, y2), (x3, y3) = xy1, xy2, xy3
        if self.offset == 0:
            self.outerPen.curveTo((x1, y1), (x2, y2), (x3, y3))
            self.innerPen.curveTo((x1, y1), (x2, y2), (x3, y3))
            return
        self.originalPen.curveTo((x1, y1), (x2, y2), (x3, y3))

        p1 = self.pointClass(x1, y1)
        p2 = self.pointClass(x2, y2)
        p3 = self.pointClass(x3, y3)

        if p1 == self.prevPoint:
            p1 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.01)
        if p2 == p3:
            p2 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.99)

        a1 = self.prevPoint.angle(p1)
        a2 = p2.angle(p3)

        self.currentAngle = a1
        tickness1 = self.getThickness(a1)
        tickness2 = self.getThickness(a2)

        a1bis = self.prevPoint.angle(p1, 0)
        a2bis = p3.angle(p2, 0)
        intersectPoint = interSect((self. prevPoint, self.prevPoint + self.pointClass(cos(a1), sin(a1)) * 100),
                                   (p3, p3 + self.pointClass(cos(a2), sin(a2)) * 100))
        self.innerCurrentPoint = self.prevPoint - self.pointClass(cos(a1), sin(a1)) * tickness1
        self.outerCurrentPoint = self.prevPoint + self.pointClass(cos(a1), sin(a1)) * tickness1

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerPrevPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerPrevPoint = self.outerCurrentPoint

            self.firstAngle = a1
        else:
            self.buildConnection()
        h1 = None
        if intersectPoint is not None:
            h1 = interSect((self.innerCurrentPoint, self.innerCurrentPoint + self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),  (intersectPoint, p1))
        if h1 is None:
            h1 = p1 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerCurrentPoint = p3 - self.pointClass(cos(a2), sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect((self.innerCurrentPoint, self.innerCurrentPoint + self.pointClass(cos(a2bis), sin(a2bis)) * tickness2), (intersectPoint, p2))
        if h2 is None:
            h2 = p2 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerPen.curveTo(h1, h2, self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        ########
        h1 = None
        if intersectPoint is not None:
            h1 = interSect((self.outerCurrentPoint, self.outerCurrentPoint + self.pointClass(cos(a1bis), sin(a1bis)) * tickness1), (intersectPoint, p1))
        if h1 is None:
            h1 = p1 + self.pointClass(cos(a1), sin(a1)) * tickness1

        self.outerCurrentPoint = p3 + self.pointClass(cos(a2), sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect((self.outerCurrentPoint, self.outerCurrentPoint + self.pointClass(cos(a2bis), sin(a2bis)) * tickness2), (intersectPoint, p2))
        if h2 is None:
            h2 = p2 + self.pointClass(cos(a1), sin(a1)) * tickness1
        self.outerPen.curveTo(h1, h2, self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = p3
        self.currentAngle = a2
        self.prevAngle = a2

    def _closePath(self):
        if self.shouldHandleMove:
            return
        if self.offset == 0:
            self.outerPen.closePath()
            self.innerPen.closePath()
            return

        if not self.prevPoint == self.firstPoint:
            self._lineTo(self.firstPoint)

        self.originalPen.closePath()

        self.innerPrevPoint = self.innerCurrentPoint
        self.innerCurrentPoint = self.innerFirstPoint

        self.outerPrevPoint = self.outerCurrentPoint
        self.outerCurrentPoint = self.outerFirstPoint

        self.prevAngle = self.currentAngle
        self.currentAngle = self.firstAngle

        self.buildConnection(close=True)

        self.innerPen.closePath()
        self.outerPen.closePath()

    def _endPath(self):
        if self.shouldHandleMove:
            return

        self.originalPen.endPath()
        self.innerPen.endPath()
        self.outerPen.endPath()

        if self.closeOpenPaths:

            innerContour = self.innerGlyph[-1]
            outerContour = self.outerGlyph[-1]

            innerContour.reverse()

            innerContour[0].segmentType = "line"
            outerContour[0].segmentType = "line"

            self.buildCap(outerContour, innerContour)

            for point in innerContour:
                outerContour.addPoint((point.x, point.y), segmentType=point.segmentType, smooth=point.smooth)

            self.innerGlyph.removeContour(innerContour)

    def addComponent(self, glyphName, transform):
        if self.preserveComponents:
            self.components.append((glyphName, transform))
        else:
            BasePen.addComponent(self, glyphName, transform)

    ## thickness

    def getThickness(self, angle):
        a2 = angle + pi * .5
        f = abs(sin(a2 + radians(self.contrastAngle)))
        f = f ** 5
        return self.offset + self.contrast * f

    ## connections

    def buildConnection(self, close=False):
        if not checkSmooth(self.prevAngle, self.currentAngle):
            if checkInnerOuter(self.prevAngle, self.currentAngle):
                self.connectionCallback(self.outerPrevPoint, self.outerCurrentPoint, self.outerPen, close)
                self.connectionInnerCorner(self.innerPrevPoint, self.innerCurrentPoint, self.innerPen, close)
            else:
                self.connectionCallback(self.innerPrevPoint, self.innerCurrentPoint, self.innerPen, close)
                self.connectionInnerCorner(self.outerPrevPoint, self.outerCurrentPoint, self.outerPen, close)


    def connectionSquare(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle)+90)
        angle_2 = radians(degrees(self.currentAngle)+90)

        tempFirst = first - self.pointClass(cos(angle_1), sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2), sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))

        if newPoint is not None:

            if self._inputmiterLimit is not None and roundFloat(newPoint.distance(first)) > self._inputmiterLimit:
                pen.lineTo(tempFirst)
                pen.lineTo(tempLast)
            else:
                pen.lineTo(newPoint)

        if not close:
            pen.lineTo(last)

    def connectionRound(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle)+90)
        angle_2 = radians(degrees(self.currentAngle)+90)

        tempFirst = first - self.pointClass(cos(angle_1), sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2), sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))
        if newPoint is None:
            pen.lineTo(last)
            return
        #print "(%s, %s)," % (newPoint.x, newPoint.y)
        distance1 = newPoint.distance(first)
        distance2 = newPoint.distance(last)
        #print distance1, distance2
        if roundFloat(distance1) > self.miterLimit + self.contrast:
            distance1 = self.miterLimit + tempFirst.distance(tempLast) * .7
        if roundFloat(distance2) > self.miterLimit + self.contrast:
            distance2 = self.miterLimit + tempFirst.distance(tempLast) * .7

        distance1 *= self.magicCurve
        distance2 *= self.magicCurve

        bcp1 = first - self.pointClass(cos(angle_1), sin(angle_1)) * distance1
        bcp2 = last + self.pointClass(cos(angle_2), sin(angle_2)) * distance2
        pen.curveTo(bcp1, bcp2, last)

    def connectionButt(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    def connectionInnerCorner(self,  first, last, pen, close):
        if not close:
            pen.lineTo(last)


    ## caps

    def buildCap(self, firstContour, lastContour):
        first = firstContour[-1]
        last = lastContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        self.capCallback(firstContour, lastContour, first, last, self.prevAngle)

        first = lastContour[-1]
        last = firstContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        angle = radians(degrees(self.firstAngle)+180)
        self.capCallback(lastContour, firstContour, first, last, angle)


    def capButt(self, firstContour, lastContour, first, last, angle):
        ## not nothing
        pass

    def capRound(self, firstContour, lastContour, first, last, angle):
        hookedAngle = radians(degrees(angle)+90)

        p1 = first - self.pointClass(cos(hookedAngle), sin(hookedAngle)) * self.offset

        p2 = last - self.pointClass(cos(hookedAngle), sin(hookedAngle)) * self.offset

        oncurve = p1 + (p2-p1)*.5

        roundness = .54

        h1 = first - self.pointClass(cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness
        h2 = oncurve + self.pointClass(cos(angle), sin(angle)) * self.offset * roundness

        firstContour[-1].smooth = True

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))
        firstContour.addPoint((oncurve.x, oncurve.y), smooth=True, segmentType="curve")

        h1 = oncurve - self.pointClass(cos(angle), sin(angle)) * self.offset * roundness
        h2 = last - self.pointClass(cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True

    def capSquare(self, firstContour, lastContour, first, last, angle):
        angle = radians(degrees(angle)+90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p1.x, p1.y), smooth=False, segmentType="line")

        p2 = last - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p2.x, p2.y), smooth=False, segmentType="line")


    def drawSettings(self, drawOriginal=False, drawInner=False, drawOuter=True):
        self.drawOriginal = drawOriginal
        self.drawInner = drawInner
        self.drawOuter = drawOuter

    def drawPoints(self, pointPen):
        if self.drawInner:
            reversePen = ReverseContourPointPen(pointPen)
            self.innerGlyph.drawPoints(CleanPointPen(reversePen))
        if self.drawOuter:
            self.outerGlyph.drawPoints(CleanPointPen(pointPen))

        if self.drawOriginal:
            if self.drawOuter:
                pointPen = ReverseContourPointPen(pointPen)
            self.originalGlyph.drawPoints(CleanPointPen(pointPen))

        for glyphName, transform in self.components:
            pointPen.addComponent(glyphName, transform)

    def draw(self, pen):
        pointPen = PointToSegmentPen(pen)
        self.drawPoints(pointPen)

    def getGlyph(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        self.drawPoints(pointPen)
        return glyph