class BezierPenTool(object): """ The Pen Tool used to Draw Bezier Curves inside a class:`~editfonts.widgets.glyph_box`. Current Status ~~~~~~~~~~~~~~~~~ This tool is not complete and can only be used to draw contours with lines to activate the Bezier Pen Tool right click on the drawing area on the editor page and click any number of times to add a point to a new contour We can close those contours by clicking inside the halo around the starting point of the contour To be Implemented ~~~~~~~~~~~~~~~~~ Add the Functionality to make Bezier Curves This can be done by monitoring the **drag event** on the Drawing Area inside a **GlyphBox** to implement the above features the following open source code file(s) may be usefull + .. drawingarea.py : https://github.com/GNOME/pygobject/blob/master/demos/gtk-demo/demos/drawingarea.py """ def __init__(self, editorBox): self.editor = editorBox self.set_active(True) def set_active(self, state): """Activate/Deactivate the Bezier Pen Tool""" if state is True: self.is_active = True self.contour = Contour() self.contour.dirty = True self.editor.add_contour(self.contour) self.connect_editor() else: self.contour.dirty = False self.is_active = False self.disconnect_editor() globals.TOOL_ACTIVE['BezierPenTool'] = state def get_active(self): """ Get the state of the Bezier Pen Tool True ~ is active False ~ is inactive """ return self.is_active def disconnect_editor(self): """Disable Drawing with the Bezier Pen Tool""" self.editor.disconnect(self.handle_press) # self.editor.disconnect(self.handle_release) def connect_editor(self): """Enable Drawing with the Bezier Pen Tool""" # add all the events here which the pen tool needs to listen to self.handle_press = self.editor.connect("button-press-event", self._on_point_press) # self.handle_release = self.editor.connect("button-release-event", # self._on_point_release) def _on_point_press(self, widget, event): """Enable Drawing with the Bezier Pen Tool""" if event.type == Gdk.EventType.BUTTON_PRESS\ and event.button == 1: # print "Clicked on: (" + str(event.x) + ", " + str(event.y) + ")" # add point at the click locations # print self.get_active() if self.get_active(): self.add_point(event.x, event.y) else: # print "This shouldn't be happening" pass def _on_point_release(self, widget, event): if event.type == Gdk.EventType.BUTTON_RELEASE\ and event.button == 1: # print "Released on: (" + str(event.x) + ", " + str(event.y) + ")" # add point at the click locations """ if self.get_active(): self.add_point(event.x, event.y) else: print "This shouldn't be happening" """ def add_point(self, x, y): """Add a Point to the currently active contour""" # print "Yahoo" point = Point((globals.invX(x), globals.invY(y))) # print "{" + str(point.x) + "," + str(point.y) + "}" # point.x = globals.invX(x) # point.y = globals.invY(y) if len(self.contour[:]) == 0: # add the point to the contour point.segmentType = u'move' point.smooth = False self.contour.appendPoint(point) elif self._check_close_contour(point): # convert the first point to a line type # print "the contour should be closed now" # close the contour self.contour[0].segmentType = u'line' # deactivate the bezier pen tool self.set_active(False) # No need for this # self.contour[0].segmentType = u'line' else: # add the point to the contour point.segmentType = u'line' point.smooth = False self.contour.appendPoint(point) self.editor.update_control_points() # check if the click is inside a zone defined by the # ZONE_R of the first point of the contour def _check_close_contour(self, point): """ Check if the last click was within a certain distance **globals.ZONE_R** around the starting point of the contour """ if len(self.contour[:]) == 0: return False t = distance(self.contour[0].x, self.contour[0].y, point.x, point.y) if t <= globals.ZONE_R: return True return False
class ContourTest(unittest.TestCase): def __init__(self, methodName): unittest.TestCase.__init__(self, methodName) def setUp(self): self.font = Font() self.glyph = self.font.newGlyph("A") self.contour = Contour() def tearDown(self): del self.contour del self.glyph del self.font def test_getParent(self): self.assertIsNone(self.contour.getParent()) self.contour = Contour(self.glyph) self.assertEqual(self.contour.getParent(), self.glyph) def test_font(self): self.assertIsNone(self.contour.font) self.contour = Contour(self.glyph) self.assertEqual(self.contour.font, self.font) with self.assertRaises(AttributeError): self.contour.font = "foo" def test_layerSet(self): self.assertIsNone(self.contour.layerSet) self.contour = Contour(self.glyph) self.assertIsNotNone(self.contour.layerSet) self.assertEqual(self.contour.layerSet, self.glyph.layerSet) with self.assertRaises(AttributeError): self.contour.layerSet = "foo" def test_layer(self): self.assertIsNone(self.contour.layer) self.contour = Contour(self.glyph) self.assertIsNotNone(self.contour.layer) self.assertEqual(self.contour.layer, self.glyph.layer) with self.assertRaises(AttributeError): self.contour.layer = "foo" def test_glyph(self): self.assertIsNone(self.contour.glyph) self.contour = Contour(self.glyph) self.assertEqual(self.contour.glyph, self.glyph) glyph = Glyph() self.contour = Contour() self.contour.glyph = glyph self.assertEqual(self.contour.glyph, glyph) with self.assertRaises(AssertionError): self.contour.glyph = self.glyph def test_list_behavior(self): font = Font(getTestFontPath()) glyph = font["A"] contour = glyph[0] with self.assertRaises(IndexError): contour[len(contour) + 1] self.assertEqual(contour[len(contour):len(contour)], []) self.assertEqual(contour[len(contour) + 1:len(contour) + 2], []) self.assertNotEqual(contour[0:len(contour) + 1], []) self.assertEqual([(point.x, point.y) for point in contour[0:]], [(point.x, point.y) for point in contour[0:len(contour) + 1]]) self.assertEqual([(point.x, point.y) for point in contour[0:]], [(0, 0), (700, 0), (700, 700), (0, 700)]) self.assertEqual([(point.x, point.y) for point in contour], [(0, 0), (700, 0), (700, 700), (0, 700)]) def test_onCurvePoints(self): font = Font(getTestFontPath()) glyph = font["A"] contour = glyph[0] self.assertEqual(len(contour.onCurvePoints), 4) self.assertEqual([(point.x, point.y) for point in contour.onCurvePoints], [(0, 0), (700, 0), (700, 700), (0, 700)]) glyph = font["B"] contour = glyph[0] self.assertEqual(len(contour.onCurvePoints), 4) self.assertEqual([(point.x, point.y) for point in contour.onCurvePoints], [(0, 350), (350, 0), (700, 350), (350, 700)]) def test_appendPoint(self): pointA = Point((0, 5)) self.assertFalse(self.contour.dirty) self.contour.appendPoint(pointA) self.assertTrue(self.contour.dirty) self.assertEqual([(point.x, point.y) for point in self.contour], [(0, 5)]) pointB = Point((6, 7)) self.contour.appendPoint(pointB) self.assertEqual([(point.x, point.y) for point in self.contour], [(0, 5), (6, 7)]) def test_insertPoint(self): pointA = Point((0, 5)) pointB = Point((6, 7)) pointC = Point((8, 9)) self.assertFalse(self.contour.dirty) self.contour.insertPoint(0, pointA) self.assertTrue(self.contour.dirty) self.assertEqual([(point.x, point.y) for point in self.contour], [(0, 5)]) self.contour.insertPoint(0, pointB) self.assertEqual([(point.x, point.y) for point in self.contour], [(6, 7), (0, 5)]) self.contour.insertPoint(1, pointC) self.assertEqual([(point.x, point.y) for point in self.contour], [(6, 7), (8, 9), (0, 5)]) def test_removePoint(self): font = Font(getTestFontPath()) contour = font["A"][0] self.assertFalse(contour.dirty) contour.removePoint(contour[1]) self.assertTrue(contour.dirty) self.assertEqual([(point.x, point.y) for point in contour], [(0, 0), (700, 700), (0, 700)]) contour.removePoint(contour[0]) self.assertEqual([(point.x, point.y) for point in contour], [(700, 700), (0, 700)]) contour.removePoint(contour[1]) self.assertEqual([(point.x, point.y) for point in contour], [(700, 700)]) def test_setStartPoint(self): font = Font(getTestFontPath()) contour = font["B"][0] start = [(point.segmentType, point.x, point.y) for point in contour] contour.setStartPoint(6) self.assertTrue(contour.dirty) contour.setStartPoint(6) end = [(point.segmentType, point.x, point.y) for point in contour] self.assertEqual(start, end) contour = font["A"][0] start = [(point.segmentType, point.x, point.y) for point in contour] contour.setStartPoint(2) contour.setStartPoint(2) end = [(point.segmentType, point.x, point.y) for point in contour] self.assertEqual(start, end) contour = font["B"][0] start = [(point.segmentType, point.x, point.y) for point in contour] contour.setStartPoint(3) contour.setStartPoint(9) end = [(point.segmentType, point.x, point.y) for point in contour] self.assertEqual(start, end) def test_len(self): font = Font(getTestFontPath()) contour = font["A"][0] self.assertEqual(len(contour), 4) contour = font["B"][0] self.assertEqual(len(contour), 12) def test_iter(self): font = Font(getTestFontPath()) contour = font["A"][0] self.assertEqual([(point.x, point.y) for point in contour], [(0, 0), (700, 0), (700, 700), (0, 700)]) def test_index(self): font = Font(getTestFontPath()) contour = font["B"][0] self.assertEqual(contour.index(contour[2]), 2) def test_reverse(self): font = Font(getTestFontPath()) contour = font["A"][0] contour.reverse() self.assertEqual([(point.x, point.y) for point in contour._points], [(0, 0), (0, 700), (700, 700), (700, 0)]) contour.reverse() self.assertEqual([(point.x, point.y) for point in contour._points], [(0, 0), (700, 0), (700, 700), (0, 700)]) def test_segments(self): font = Font(getTestFontPath()) glyph = font["A"] contour = glyph[0] self.assertEqual( [simpleSegment(segment) for segment in contour.segments], [[(700, 0, "line")], [(700, 700, "line")], [(0, 700, "line")], [(0, 0, "line")]]) glyph = font["B"] contour = glyph[0] self.assertEqual( [simpleSegment(segment) for segment in contour.segments], [[(0, 157, None), (157, 0, None), (350, 0, "curve")], [(543, 0, None), (700, 157, None), (700, 350, "curve")], [(700, 543, None), (543, 700, None), (350, 700, "curve")], [(157, 700, None), (0, 543, None), (0, 350, "curve")]]) def test_removeSegment_lines(self): font = Font(getTestFontPath()) glyph = font["A"] contour = glyph[0] contour.removeSegment(len(contour.segments) - 2) self.assertEqual( [simpleSegment(segment) for segment in contour.segments], [[(700, 0, "line")], [(700, 700, "line")], [(0, 0, "line")]]) contour.removeSegment(len(contour.segments) - 1) self.assertEqual( [simpleSegment(segment) for segment in contour.segments], [[(700, 700, "line")], [(700, 0, "line")]]) def test_removeSegment_first_segment(self): font = Font(getTestFontPath()) glyph = font["A"] contour = glyph[0] contour.removeSegment(0) self.assertEqual( [simpleSegment(segment) for segment in contour.segments], [[(700, 700, "line")], [(0, 700, "line")], [(0, 0, "line")]]) def test_removeSegment_last_segment(self): font = Font(getTestFontPath()) glyph = font["A"] contour = glyph[0] contour.removeSegment(len(contour.segments) - 1) self.assertEqual( [simpleSegment(segment) for segment in contour.segments], [[(700, 700, "line")], [(0, 700, "line")], [(700, 0, "line")]]) def test_removeSegment_curves(self): font = Font(getTestFontPath()) glyph = font["B"] contour = glyph[0] contour.removeSegment(len(contour.segments) - 2) self.assertEqual( [simpleSegment(segment) for segment in contour.segments], [[(0, 157, None), (157, 0, None), (350, 0, "curve")], [(543, 0, None), (700, 157, None), (700, 350, "curve")], [(157, 700, None), (0, 543, None), (0, 350, "curve")]]) def test_removeSegment_curves_preserveCurve(self): font = Font(getTestFontPath()) glyph = font["B"] contour = glyph[0] contour.removeSegment(len(contour.segments) - 2, preserveCurve=True) self.assertEqual( [simpleSegment(segment) for segment in contour.segments], [[(0, 157, None), (157, 0, None), (350, 0, "curve")], [(543, 0, None), (700, 157, None), (700, 350, "curve")], [(700.0, 736.0, None), (0.0, 736.0, None), (0, 350, "curve")]]) def test_clockwise_get(self): font = Font(getTestFontPath()) contour = font["A"][0] self.assertFalse(contour.clockwise) contour = font["A"][1] self.assertTrue(contour.clockwise) contour._clockwiseCache = None contour.clockwise = False self.assertFalse(contour.clockwise) contour._clockwiseCache = None contour.clockwise = True self.assertTrue(contour.clockwise) def test_clockwise_set(self): font = Font(getTestFontPath()) contour = font["A"][0] contour.clockwise = False self.assertFalse(contour.clockwise) contour._clockwiseCache = None contour.clockwise = True self.assertTrue(contour.clockwise) def test_open(self): font = Font(getTestFontPath("TestOpenContour.ufo")) glyph = font["A"] self.assertTrue(glyph[0].open) self.assertFalse(glyph[1].open) self.assertTrue(glyph[2].open) self.assertFalse(glyph[3].open) contour = Contour() self.assertTrue(contour.open) def test_bounds(self): font = Font(getTestFontPath()) contour = font["A"][0] self.assertEqual(contour.bounds, (0, 0, 700, 700)) def test_controlPointBounds(self): font = Font(getTestFontPath()) contour = font["A"][0] self.assertEqual(contour.controlPointBounds, (0, 0, 700, 700)) def test_move(self): font = Font(getTestFontPath()) contour = font["A"][0] contour.move((100, 100)) self.assertEqual(contour.bounds, (100, 100, 800, 800)) self.assertTrue(contour.dirty) def test_pointInside(self): font = Font(getTestFontPath()) contour = font["A"][0] self.assertTrue(contour.pointInside((100, 100))) self.assertFalse(contour.pointInside((0, 0))) self.assertFalse(contour.pointInside((-100, -100))) def test_positionForProspectivePointInsertionAtSegmentAndT(self): font = Font(getTestFontPath()) contour = font["A"][0] self.assertEqual( contour.positionForProspectivePointInsertionAtSegmentAndT(0, .5), ((350.0, 0.0), False)) contour = font["B"][0] self.assertEqual( contour.positionForProspectivePointInsertionAtSegmentAndT(0, .5), ((102.625, 102.625), True)) contour = font["B"][1] self.assertEqual( contour.positionForProspectivePointInsertionAtSegmentAndT(0, .5), ((226.125, 473.5), True)) def test_splitAndInsertPointAtSegmentAndT(self): font = Font(getTestFontPath()) contour = font["A"][0] contour.splitAndInsertPointAtSegmentAndT(0, .5) self.assertEqual( [(point.x, point.y, point.segmentType) for point in contour], [(0, 0, "line"), (350.0, 0.0, "line"), (700, 0, "line"), (700, 700, "line"), (0, 700, "line")]) contour = font["B"][0] contour.splitAndInsertPointAtSegmentAndT(0, .5) self.assertEqual( [(point.x, point.y, point.segmentType) for point in contour], [(0, 350, "curve"), (0.0, 253.5, None), (39.25, 166.0, None), (102.625, 102.625, "curve"), (166.0, 39.25, None), (253.5, 0.0, None), (350, 0, "curve"), (543, 0, None), (700, 157, None), (700, 350, "curve"), (700, 543, None), (543, 700, None), (350, 700, "curve"), (157, 700, None), (0, 543, None)]) def test_identifier(self): glyph = Glyph() contour = Contour() glyph.appendContour(contour) contour.identifier = "contour 1" self.assertEqual(contour.identifier, "contour 1") self.assertEqual(sorted(glyph.identifiers), ["contour 1"]) contour = Contour() glyph.appendContour(contour) with self.assertRaises(AssertionError): contour.identifier = "contour 1" contour.identifier = "contour 2" self.assertEqual(sorted(glyph.identifiers), ["contour 1", "contour 2"]) contour.identifier = "not contour 2 anymore" self.assertEqual(contour.identifier, "contour 2") self.assertEqual(sorted(glyph.identifiers), ["contour 1", "contour 2"]) contour.identifier = None self.assertEqual(contour.identifier, "contour 2") self.assertEqual(sorted(glyph.identifiers), ["contour 1", "contour 2"]) def test_correct_direction_same_area(self): glyph = Glyph() pen = glyph.getPointPen() pen.beginPath() pen.addPoint((0, 0), segmentType="line") pen.addPoint((0, 50), segmentType="line") pen.addPoint((50, 50), segmentType="line") pen.endPath() pen.beginPath() pen.addPoint((50, 50), segmentType="line") pen.addPoint((50, 100), segmentType="line") pen.addPoint((100, 100), segmentType="line") pen.endPath() try: glyph.correctContourDirection() except Exception as e: self.fail("glyph.correctContourDirection() raised unexpected exception: " + str(e))
class BezierPenTool(object): """ The Pen Tool used to Draw Bezier Curves inside a class:`~editfonts.widgets.glyph_box`. Current Status ~~~~~~~~~~~~~~~~~ This tool is not complete and can only be used to draw contours with lines to activate the Bezier Pen Tool right click on the drawing area on the editor page and click any number of times to add a point to a new contour We can close those contours by clicking inside the halo around the starting point of the contour To be Implemented ~~~~~~~~~~~~~~~~~ Add the Functionality to make Bezier Curves This can be done by monitoring the **drag event** on the Drawing Area inside a **GlyphBox** to implement the above features the following open source code file(s) may be usefull + .. drawingarea.py : https://github.com/GNOME/pygobject/blob/master/demos/gtk-demo/demos/drawingarea.py """ def __init__(self, editorBox): self.editor = editorBox self.set_active(True) def set_active(self, state): """Activate/Deactivate the Bezier Pen Tool""" if state is True: self.is_active = True self.contour = Contour() self.contour.dirty = True self.editor.add_contour(self.contour) self.connect_editor() else: self.contour.dirty = False self.is_active = False self.disconnect_editor() globals.TOOL_ACTIVE['BezierPenTool'] = state def get_active(self): """ Get the state of the Bezier Pen Tool True ~ is active False ~ is inactive """ return self.is_active def disconnect_editor(self): """Disable Drawing with the Bezier Pen Tool""" self.editor.disconnect(self.handle_press) # self.editor.disconnect(self.handle_release) def connect_editor(self): """Enable Drawing with the Bezier Pen Tool""" # add all the events here which the pen tool needs to listen to self.handle_press = self.editor.connect("button-press-event", self._on_point_press) # self.handle_release = self.editor.connect("button-release-event", # self._on_point_release) def _on_point_press(self, widget, event): """Enable Drawing with the Bezier Pen Tool""" if event.type == Gdk.EventType.BUTTON_PRESS\ and event.button == 1: # print "Clicked on: (" + str(event.x) + ", " + str(event.y) + ")" # add point at the click locations # print self.get_active() if self.get_active(): self.add_point(event.x, event.y) else: # print "This shouldn't be happening" pass def _on_point_release(self, widget, event): if event.type == Gdk.EventType.BUTTON_RELEASE\ and event.button == 1: # print "Released on: (" + str(event.x) + ", " + str(event.y) + ")" # add point at the click locations """ if self.get_active(): self.add_point(event.x, event.y) else: print "This shouldn't be happening" """ def add_point(self, x, y): """Add a Point to the currently active contour""" # print "Yahoo" point = Point((globals.invX(x), globals.invY(y))) # print "{" + str(point.x) + "," + str(point.y) + "}" # point.x = globals.invX(x) # point.y = globals.invY(y) if len(self.contour[:]) == 0: # add the point to the contour point.segmentType = u'move' point.smooth = False self.contour.appendPoint(point) elif self._check_close_contour(point): # convert the first point to a line type # print "the contour should be closed now" # close the contour self.contour[0].segmentType = u'line' # deactivate the bezier pen tool self.set_active(False) # No need for this # self.contour[0].segmentType = u'line' else: # add the point to the contour point.segmentType = u'line' point.smooth = False self.contour.appendPoint(point) self.editor.update_control_points() # check if the click is inside a zone defined by the # ZONE_R of the first point of the contour def _check_close_contour(self, point): """ Check if the last click was within a certain distance **globals.ZONE_R** around the starting point of the contour """ if len(self.contour[:]) == 0: return False t = distance(self.contour[0].x, self.contour[0].y, point.x, point.y) if t <= globals.ZONE_R: return True return False
def create_contour(contour_path): contour = Contour() for point in map(create_point, contour_path): contour.appendPoint(point) return contour