PythonCAD/Generic/tools.py: Add new tool classes for changing Style attributes.
[pythoncad.git] / PythonCAD / Generic / tools.py
blobdaafb160d1c837cee4cb60a3d2e1b0df50aa9830
2 # Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Art Haas
4 # This file is part of PythonCAD.
6 # PythonCAD is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # PythonCAD is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with PythonCAD; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 # tool stuff
24 import math
25 import types
26 import array
28 from PythonCAD.Generic import util
29 from PythonCAD.Generic import color
30 from PythonCAD.Generic import linetype
31 from PythonCAD.Generic import style
32 from PythonCAD.Generic.point import Point
33 from PythonCAD.Generic.segment import Segment
34 from PythonCAD.Generic.circle import Circle
35 from PythonCAD.Generic.arc import Arc
36 from PythonCAD.Generic.leader import Leader
37 from PythonCAD.Generic.polyline import Polyline
38 from PythonCAD.Generic.hcline import HCLine
39 from PythonCAD.Generic.vcline import VCLine
40 from PythonCAD.Generic.acline import ACLine
41 from PythonCAD.Generic.cline import CLine
42 from PythonCAD.Generic.ccircle import CCircle
43 from PythonCAD.Generic.text import TextStyle, TextBlock
44 from PythonCAD.Generic import dimension
45 from PythonCAD.Generic.layer import Layer
46 from PythonCAD.Generic import tangent
48 class Tool(object):
49 """A generic tool object.
51 This class is meant to be a base class for tools. A Tool
52 instance the following attributes:
54 list: A list the tool can use to store objects
55 handlers: A dictionary used to store functions
57 A Tool object has the following methods:
59 {set/get/del/has}Handler(): Store/retrive/delete/test a handler for an event.
60 clearHandlers(): Unset all the handlers in the tool.
61 reset(): Restore the tool to a a default state.
62 initialize(): Retore the tool to its original state.
63 {set/get}Filter(): Set/Get a filtering procedure for object testing.
64 {set/get}Location(): Store/retrieve an image-based coordinate pair.
65 {set/get}CurrentPoint(): Store/retrieve a screen-based coordinate pair.
66 clearCurrentPoint(): Set the screen-based coordinate to None.
67 create(): Instantiate the object the tool is designed to create.
68 """
69 def __init__(self):
70 """Instantiate a Tool.
72 t = Tool()
73 """
74 super(Tool, self).__init__()
75 self.__objlist = []
76 self.__objtype = None
77 self.__fproc = None
78 self.__handlers = {}
79 self.__location = None
80 self.__curpoint = None
81 self.__points = []
82 self.__xpts = array.array('d')
83 self.__ypts = array.array('d')
84 self.__shift = None
86 def __len__(self):
87 """Return the number of objects in the list via len().
88 """
89 return len(self.__objlist)
91 def __iter__(self):
92 """Make the Tool iterable.
93 """
94 return iter(self.__objlist)
96 def getList(self):
97 """Return the Tool's object list.
99 getList()
101 return self.__objlist
103 list = property(getList, None, None, "Tool object list.")
105 def reset(self):
106 """Restore the Tool to its initial state.
108 reset()
110 This function purges the Tool's object list and handler dictionary.
112 del self.__objlist[:]
113 self.__handlers.clear()
114 self.__location = None
115 self.__curpoint = None
116 del self.__points[:]
117 del self.__xpts[:]
118 del self.__ypts[:]
119 self.__shift = None
121 def initialize(self):
122 self.reset()
124 def setHandler(self, key, func):
125 """Set a handler for the Tool.
127 setHandler(key, func)
129 There are two arguments for this function:
131 key: A string used to identify a particular action
132 func: A function object
134 There are no restrictions on what the function 'func' does,
135 the argument count, etc. Any call to setHandler() with
136 a key that is already stored replaces the old 'func' argument
137 with the new one. The function argument may be None, and
138 the key argument must be a string.
140 if key is None:
141 raise ValueError, "Key value cannot be None."
142 if not isinstance(key, str):
143 raise TypeError, "Invalid key type: " + `type(key)`
144 if func and not isinstance(func, types.FunctionType):
145 raise TypeError, "Invalid function type: " + `type(func)`
146 self.__handlers[key] = func
148 def getHandler(self, key):
149 """Return the function for a particular key.
151 getHandler(key)
153 Given argument 'key', the function associated with it is
154 returned. A KeyError is raised if the argument 'key' had
155 not be used to store a function.
157 if not isinstance(key, str):
158 raise TypeError, "Invalid key type: " + `type(key)`
159 if not self.__handlers.has_key(key):
160 raise KeyError, "Invalid key '%s'" % key
161 return self.__handlers[key]
163 def delHandler(self, key):
164 """Delete the handler associated with a particular key
166 delHandler(key)
168 The argument 'key' should be a string.
170 if self.__handlers.has_key(key):
171 del self.__handlers[key]
173 def hasHandler(self, key):
174 """Check if there is a handler stored for a given key.
176 hasHandler(key)
178 The 'key' argument must be a string. The function returns 1
179 if there is a handler for that key, 0 otherwise.
181 _k = key
182 if not isinstance(_k, str):
183 raise TypeError, "Invalid key type: " + `type(key)`
184 return self.__handlers.has_key(_k)
186 def clearHandlers(self):
187 """Unset all the handlers for the tool.
189 clearHandlers()
191 This function does not alter the Tool's object list.
193 self.__handlers.clear()
195 def setObjtype(self, objtype):
196 """Store the type of objects on which the tool operates.
198 setObjtype(objtype)
200 Argument 'objtype' can be a single type, a tuple of types, or 'None'.
202 if not isinstance(objtype, (tuple, types.NoneType, types.TypeType)):
203 raise TypeError, "Invalid objtype: " + `type(objtype)`
204 if objtype is not None:
205 if isinstance(objtype, tuple):
206 for _obj in objtype:
207 if not isinstance(_obj, types.TypeType):
208 raise TypeError, "Invalid objtype: " + `type(_obj)`
209 self.__objtype = objtype
211 def getObjtype(self):
212 """Return the type of object on which the tool operates.
214 getObjtype()
216 This method returns the value set in a setObjtype(), or None if
217 no object types have been specified.
219 return self.__objtype
221 def setFilter(self, proc):
222 """Store a procedure used to examine selected objects.
224 setFilter(proc)
226 Argument 'proc' must be a callable procedure.
228 if not callable(proc):
229 raise TypeError, "Invalid filter procedure: " + `type(proc)`
230 self.__fproc = proc
232 def getFilter(self):
233 """Return a stored procedure.
235 getFilter()
237 This method returns the procedure stored vai setFilter() or None.
239 return self.__fproc
241 def pushObject(self, obj):
242 """Add an object to the Tool's object list.
244 pushObject(obj)
246 self.__objlist.append(obj)
248 def popObject(self):
249 """Remove the last object on the Tool's object list.
251 popObject()
253 If the object list is empty, this function returns None.
255 _obj = None
256 if len(self.__objlist):
257 _obj = self.__objlist.pop()
258 return _obj
260 def delObjects(self):
261 """Remove all objects from the Tool's object list.
263 delObjects()
265 This function does not alter the Tool's handlers.
267 del self.__objlist[:]
269 def getObject(self, idx):
270 """Access an object in the tool.
272 getObject(idx)
274 The argument 'idx' is the index into the list of
275 stored objects.
277 return self.__objlist[idx]
279 def setLocation(self, x, y):
280 """Store an x/y location in the tool.
282 setLocation(x,y)
284 Store an x-y coordinate in the tool. Both arguments
285 should be floats
287 _x = util.get_float(x)
288 _y = util.get_float(y)
289 self.__location = (_x,_y)
291 def getLocation(self):
292 """Return the stored location in the tool
294 getLocation()
296 return self.__location
298 def clearLocation(self):
299 """Reset the location to an empty value.
301 clearLocation()
303 self.__location = None
305 def setCurrentPoint(self, x, y):
306 """Set the tool's current point.
308 setCurrentPoint(x,y)
310 Store an x-y coordinate in the tool. Both arguments
311 should be int.
313 _x = x
314 if not isinstance(_x, int):
315 _x = int(x)
316 _y = y
317 if not isinstance(_y, int):
318 _y = int(y)
319 self.__curpoint = (_x, _y)
321 def getCurrentPoint(self):
322 """Return the tool's current point value.
324 getCurrentPoint()
326 return self.__curpoint
328 def clearCurrentPoint(self):
329 """Reset the current point to an empty value
331 clearCurrentPoint()
333 self.__curpoint = None
335 def create(self, image):
336 """Create an object the tool is designed to construct.
338 create(image)
340 The argument 'image' is an image in which the newly created object
341 will be added. In the Tool class, this method does nothing. It is
342 meant to be overriden in classed using the Tool class as a base
343 class.
345 pass # override
348 # The ZoomTool, PasteTool, SelectTool, and DeselectTool classes are
349 # subclasses of the Tool class but with no additional functionality (yet)
351 class ZoomTool(Tool):
352 pass
354 class PasteTool(Tool):
355 pass
357 class SelectTool(Tool):
358 pass
360 class DeselectTool(Tool):
361 pass
363 class PointTool(Tool):
364 """A specialized tool for drawing Point objects.
366 The PointTool class is derived from the Tool class, so it
367 shares the methods and attributes of that class. The PointTool
368 class has the following additional methods:
370 {get/set}Point(): Get/Set a x/y coordinate in the tool.
372 def __init__(self):
373 super(PointTool, self).__init__()
374 self.__point = None
376 def setPoint(self, x, y):
377 """Store an x/y coordinate in the tool
379 setPoint(x, y)
381 Arguments 'x' and 'y' should be floats.
383 _x = util.get_float(x)
384 _y = util.get_float(y)
385 self.__point = (_x, _y)
387 def getPoint(self):
388 """Get the stored x/y coordinates from the tool.
390 getPoint()
392 This method returns a tuple containing the values passed in
393 with the setPoint() method, or None if that method has not
394 been invoked.
396 return self.__point
398 def reset(self):
399 """Restore the tool to its initial state.
401 reset()
403 This method extends Tool::reset().
405 super(PointTool, self).reset()
406 self.__point = None
408 def create(self, image):
409 """Create a new Point and add it to the image.
411 create(image)
413 This method overrides the Tool::create() method.
415 if self.__point is not None:
416 _active_layer = image.getActiveLayer()
417 _x, _y = self.__point
418 _p = Point(_x, _y)
419 _active_layer.addObject(_p)
420 self.reset()
422 class SegmentTool(Tool):
423 """A Specialized tool for drawing Segment objects.
425 The SegmentTool class is derived from the Tool class, so
426 it shares the attributes and methods of that class. The
427 SegmentTool class has the following additional methods:
429 {get/set}FirstPoint(): Get/Set the first point of the Segment.
430 {get/set}SecondPoint(): Get/Set the second point of the Segment.
432 def __init__(self):
433 super(SegmentTool, self).__init__()
434 self.__first_point = None
435 self.__second_point = None
437 def setFirstPoint(self, x, y):
438 """Store the first point of the Segment.
440 setFirstPoint(x, y)
442 Arguments 'x' and 'y' should be floats.
444 _x = util.get_float(x)
445 _y = util.get_float(y)
446 self.__first_point = (_x, _y)
448 def getFirstPoint(self):
449 """Get the first point of the Segment.
451 getFirstPoint()
453 This method returns a tuple holding the coordinates stored
454 by invoking the setFirstPoint() method, or None if that method
455 has not been invoked.
457 return self.__first_point
459 def setSecondPoint(self, x, y):
460 """Store the second point of the Segment.
462 setSecondPoint(x, y)
464 Arguments 'x' and 'y' should be floats. If the
465 tool has not had the first point set with setFirstPoint(),
466 a ValueError exception is raised.
468 if self.__first_point is None:
469 raise ValueError, "SegmentTool first point is not set."
470 _x = util.get_float(x)
471 _y = util.get_float(y)
472 self.__second_point = (_x, _y)
474 def getSecondPoint(self):
475 """Get the second point of the Segment.
477 getSecondPoint()
479 This method returns a tuple holding the coordinates stored
480 by invoking the setSecondPoint() method, or None if that method
481 has not been invoked.
483 return self.__second_point
485 def reset(self):
486 """Restore the tool to its initial state.
488 reset()
490 This method extends Tool::reset().
492 super(SegmentTool, self).reset()
493 self.__first_point = None
494 self.__second_point = None
496 def create(self, image):
497 """Create a new Segment and add it to the image.
499 create(image)
501 This method overrides the Tool::create() method.
503 if (self.__first_point is not None and
504 self.__second_point is not None):
505 _active_layer = image.getActiveLayer()
506 _x1, _y1 = self.__first_point
507 _x2, _y2 = self.__second_point
508 _pts = _active_layer.find('point', _x1, _y1)
509 if len(_pts) == 0:
510 _p1 = Point(_x1, _y1)
511 _active_layer.addObject(_p1)
512 else:
513 _p1 = _pts[0]
514 _pts = _active_layer.find('point', _x2, _y2)
515 if len(_pts) == 0:
516 _p2 = Point(_x2, _y2)
517 _active_layer.addObject(_p2)
518 else:
519 _p2 = _pts[0]
520 _s = image.getOption('LINE_STYLE')
521 _seg = Segment(_p1, _p2, _s)
522 _l = image.getOption('LINE_TYPE')
523 if _l != _s.getLinetype():
524 _seg.setLinetype(_l)
525 _c = image.getOption('LINE_COLOR')
526 if _c != _s.getColor():
527 _seg.setColor(_c)
528 _t = image.getOption('LINE_THICKNESS')
529 if abs(_t - _s.getThickness()) > 1e-10:
530 _seg.setThickness(_t)
531 _active_layer.addObject(_seg)
532 self.reset()
534 class RectangleTool(SegmentTool):
535 """A Specialized tool for drawing rectangles.
537 The RectangleTool is derived from the SegmentTool, so it
538 shares all the methods and attributes of that class. A
539 RectangleTool creates four Segments in the shape of
540 a rectangle in the image.
542 def __init__(self):
543 super(RectangleTool, self).__init__()
545 def create(self, image):
546 """Create Segments and add them to the image.
548 create(image)
550 This method overrides the SegmentTool::create() method.
552 _p1 = self.getFirstPoint()
553 _p2 = self.getSecondPoint()
554 if _p1 is not None and _p2 is not None:
555 _x1, _y1 = _p1
556 _x2, _y2 = _p2
557 _active_layer = image.getActiveLayer()
558 _pts = _active_layer.find('point', _x1, _y1)
559 if len(_pts) == 0:
560 _p1 = Point(_x1, _y1)
561 _active_layer.addObject(_p1)
562 else:
563 _p1 = _pts[0]
564 _pts = _active_layer.find('point', _x1, _y2)
565 if len(_pts) == 0:
566 _p2 = Point(_x1, _y2)
567 _active_layer.addObject(_p2)
568 else:
569 _p2 = _pts[0]
570 _pts = _active_layer.find('point', _x2, _y2)
571 if len(_pts) == 0:
572 _p3 = Point(_x2, _y2)
573 _active_layer.addObject(_p3)
574 else:
575 _p3 = _pts[0]
576 _pts = _active_layer.find('point', _x2, _y1)
577 if len(_pts) == 0:
578 _p4 = Point(_x2, _y1)
579 _active_layer.addObject(_p4)
580 else:
581 _p4 = _pts[0]
582 _s = image.getOption('LINE_STYLE')
583 _l = image.getOption('LINE_TYPE')
584 if _l == _s.getLinetype():
585 _l = None
586 _c = image.getOption('LINE_COLOR')
587 if _c == _s.getColor():
588 _c = None
589 _t = image.getOption('LINE_THICKNESS')
590 if abs(_t - _s.getThickness()) < 1e-10:
591 _t = None
592 _seg = Segment(_p1, _p2, _s, linetype=_l, color=_c, thickness=_t)
593 _active_layer.addObject(_seg)
594 _seg = Segment(_p2, _p3, _s, linetype=_l, color=_c, thickness=_t)
595 _active_layer.addObject(_seg)
596 _seg = Segment(_p3, _p4, _s, linetype=_l, color=_c, thickness=_t)
597 _active_layer.addObject(_seg)
598 _seg = Segment(_p4, _p1, _s, linetype=_l, color=_c, thickness=_t)
599 _active_layer.addObject(_seg)
600 self.reset()
602 class CircleTool(Tool):
603 """A Specialized tool for drawing Circle objects.
605 The CircleTool is derived from the Tool class, so it shares
606 all the methods and attributes of that class. The CircleTool
607 class has the following addtional methods:
609 {set/get}Center(): Set/Get the center point location of the circle.
610 {set/get}Radius(): Set/Get the radius of the circle.
612 def __init__(self):
613 super(CircleTool, self).__init__()
614 self.__center = None
615 self.__radius = None
617 def setCenter(self, x, y):
618 """Set the center point location of the circle.
620 setCenter(x, y)
622 The arguments 'x' and 'y' give the location for the center
623 of the circle.
625 _x = util.get_float(x)
626 _y = util.get_float(y)
627 self.__center = (_x, _y)
629 def getCenter(self):
630 """Get the center point location of the circle.
632 getCenter()
634 This method returns the coordinates stored with the setCenter()
635 method, or None if that method has not been called.
637 return self.__center
639 def setRadius(self, radius):
640 """Set the radius of the circle.
642 setRadius(radius)
644 The argument 'radius' must be a float value greater than 0.0
646 _r = util.get_float(radius)
647 if not _r > 0.0:
648 raise ValueError, "Invalid radius: %g" % _r
649 self.__radius = _r
651 def getRadius(self):
652 """Get the radius of the circle.
654 getRadius()
656 This method returns the value specified from the setRadius()
657 call, or None if that method has not been invoked.
659 return self.__radius
661 def reset(self):
662 """Restore the tool to its initial state.
664 reset()
666 This method extends Tool::reset().
668 super(CircleTool, self).reset()
669 self.__center = None
670 self.__radius = None
672 def create(self, image):
673 """Create a new Circle and add it to the image.
675 create(image)
677 This method overrides the Tool::create() method.
679 if (self.__center is not None and
680 self.__radius is not None):
681 _active_layer = image.getActiveLayer()
682 _x, _y = self.__center
683 _r = self.__radius
684 _pts = _active_layer.find('point', _x, _y)
685 if len(_pts) == 0:
686 _cp = Point(_x, _y)
687 _active_layer.addObject(_cp)
688 else:
689 _cp = _pts[0]
690 _s = image.getOption('LINE_STYLE')
691 _circle = Circle(_cp, _r, _s)
692 _l = image.getOption('LINE_TYPE')
693 if _l != _s.getLinetype():
694 _circle.setLinetype(_l)
695 _c = image.getOption('LINE_COLOR')
696 if _c != _s.getColor():
697 _circle.setColor(_c)
698 _t = image.getOption('LINE_THICKNESS')
699 if abs(_t - _s.getThickness()) > 1e-10:
700 _circle.setThickness(_t)
701 _active_layer.addObject(_circle)
702 self.reset()
704 class TwoPointCircleTool(CircleTool):
705 """A specialized class for drawing Circles between two points.
707 The TwoPointCircleTool class is derived from the CircleTool
708 class, so it shares all the methods and attributes of that
709 class. The TwoPointCircleTool class has the following addtional
710 methods:
712 {set/get}FirstPoint(): Set/Get the first point used to define the circle.
713 {set/get}SecondPoint(): Set/Get the second point used to define the circle.
715 def __init__(self):
716 super(TwoPointCircleTool, self).__init__()
717 self.__first_point = None
718 self.__second_point = None
720 def setFirstPoint(self, x, y):
721 """Set the first point used to define the location of the circle.
723 setFirstPoint(x, y)
725 Arguments 'x' and 'y' give the location of a point.
727 _x = util.get_float(x)
728 _y = util.get_float(y)
729 self.__first_point = (_x, _y)
731 def getFirstPoint(self):
732 """Get the first point used to define the location of the circle.
734 getFirstPoint()
736 This method returns a tuple holding the values used when the
737 setFirstPoint() method was called, or None if that method has
738 not yet been used.
740 return self.__first_point
742 def setSecondPoint(self, x, y):
743 """Set the second point used to define the location of the circle.
745 setSecondPoint(x, y)
747 Arguments 'x' and 'y' give the location of a point. Invoking
748 this method before the setFirstPoint() method will raise a
749 ValueError.
751 if self.__first_point is None:
752 raise ValueError, "First point is not set"
753 _x = util.get_float(x)
754 _y = util.get_float(y)
755 _x1, _y1 = self.__first_point
756 _xc = (_x + _x1)/2.0
757 _yc = (_y + _y1)/2.0
758 _radius = math.hypot((_x - _x1), (_y - _y1))/2.0
759 self.setCenter(_xc, _yc)
760 self.setRadius(_radius)
761 self.__second_point = (_x, _y)
763 def getSecondPoint(self):
764 """Get the second point used to define the location of the circle.
766 getSecondPoint()
768 This method returns a tuple holding the values used when the
769 setSecondPoint() method was called, or None if that method has
770 not yet been used.
772 return self.__second_point
774 def reset(self):
775 """Restore the tool to its initial state.
777 reset()
779 This method extends CircleTool::reset().
781 super(TwoPointCircleTool, self).reset()
782 self.__first_point = None
783 self.__second_point = None
785 class ArcTool(CircleTool):
786 """A specialized tool for drawing Arc objects.
788 The ArcTool is Derived from the CircleTool class, so it shares
789 all the attributes and methods of that class. The ArcTool class
790 has the following addtional methods:
792 {set/get}StartAngle(): Set/Get the start angle of the arc
793 {set/get}EndAngle(): Set/Get the end angle of the arc.
795 def __init__(self):
796 super(ArcTool, self).__init__()
797 self.__start_angle = None
798 self.__end_angle = None
800 def setStartAngle(self, angle):
801 """Set the start angle of the arc.
803 setStartAngle(angle)
805 The argument 'angle' should be a float value between 0.0 and 360.0
807 _angle = util.make_c_angle(angle)
808 self.__start_angle = _angle
810 def getStartAngle(self):
811 """Return the start angle of the arc.
813 getStartAngle()
815 This method returns the value defined in the previous setStartAngle()
816 call, or None if that method has not been called.
818 return self.__start_angle
820 def setEndAngle(self, angle):
821 """Set the start angle of the arc.
823 setStartAngle(angle)
825 The argument 'angle' should be a float value between 0.0 and 360.0
827 _angle = util.make_c_angle(angle)
828 self.__end_angle = _angle
830 def getEndAngle(self):
831 """Return the end angle of the arc.
833 getEndAngle()
835 This method returns the value defined in the previous setEndAngle()
836 call, or None if that method has not been called.
838 return self.__end_angle
840 def reset(self):
841 """Restore the tool to its initial state.
843 reset()
845 This method extends CircleTool::reset().
847 super(ArcTool, self).reset()
848 self.__start_angle = None
849 self.__end_angle = None
851 def create(self, image):
852 """Create a new Arc and add it to the image.
854 create(image)
856 This method overrides the CircleTool::create() method.
858 _center = self.getCenter()
859 _radius = self.getRadius()
860 _sa = self.__start_angle
861 _ea = self.__end_angle
862 if (_center is not None and
863 _radius is not None and
864 _sa is not None and
865 _ea is not None):
866 _active_layer = image.getActiveLayer()
867 _x, _y = _center
868 _pts = _active_layer.find('point', _x, _y)
869 if len(_pts) == 0:
870 _cp = Point(_x, _y)
871 _active_layer.addObject(_cp)
872 else:
873 _cp = _pts[0]
874 _s = image.getOption('LINE_STYLE')
875 _arc = Arc(_cp, _radius, _sa, _ea, _s)
876 _l = image.getOption('LINE_TYPE')
877 if _l != _s.getLinetype():
878 _arc.setLinetype(_l)
879 _c = image.getOption('LINE_COLOR')
880 if _c != _s.getColor():
881 _arc.setColor(_c)
882 _t = image.getOption('LINE_THICKNESS')
883 if abs(_t - _s.getThickness()) > 1e-10:
884 _arc.setThickness(_t)
885 for _ep in _arc.getEndpoints():
886 _ex, _ey = _ep
887 _pts = _active_layer.find('point', _ex, _ey)
888 if len(_pts) == 0:
889 _lp = Point(_ex, _ey)
890 _active_layer.addObject(_lp)
891 _active_layer.addObject(_arc)
892 self.reset()
895 # The ChamferTool and FilletTool class are subclasses of
896 # the Tool class but have no additional functionality (yet)
899 class ChamferTool(Tool):
900 pass
902 class FilletTool(Tool):
903 pass
905 class LeaderTool(Tool):
906 """A specialized tool for drawing Leader objects.
908 The LeaderTool class is derived from the Tool class, so it
909 shares the methods and attributes of that class. The LeaderTool
910 class has the following addtional methods:
912 {set/get}FirstPoint(): Set/Get the first point of the Leader.
913 {set/get}MidPoint(): Set/Get the second point of the Leader.
914 {set/get}FinalPoint(): Set/Get the final point of the Leader.
916 def __init__(self):
917 super(LeaderTool, self).__init__()
918 self.__start_point = None
919 self.__mid_point = None
920 self.__end_point = None
922 def setFirstPoint(self, x, y):
923 """Set the first point used to define the Leader.
925 setFirstPoint(x, y)
927 Arguments 'x' and 'y' give the location of a point.
929 _x = util.get_float(x)
930 _y = util.get_float(y)
931 self.__start_point = (_x, _y)
933 def getFirstPoint(self):
934 """Get the first point used to define the Leader.
936 getFirstPoint()
938 This method returns a tuple holding the values used when the
939 setFirstPoint() method was called, or None if that method has
940 not yet been used.
942 return self.__start_point
944 def setMidPoint(self, x, y):
945 """Set the second point used to define the Leader.
947 setMidPoint(x, y)
949 Arguments 'x' and 'y' give the location of a point. If the
950 first point has not been set this method raises a ValueError.
952 if self.__start_point is None:
953 raise ValueError, "First point not set in LeaderTool."
954 _x = util.get_float(x)
955 _y = util.get_float(y)
956 self.__mid_point = (_x, _y)
958 def getMidPoint(self):
959 """Get the second point used to define the Leader.
961 getMidPoint()
963 This method returns a tuple holding the values used when the
964 setMidPoint() method was called, or None if that method has
965 not yet been used.
967 return self.__mid_point
969 def setFinalPoint(self, x, y):
970 """Set the first point used to final point of the Leader.
972 setFinalPoint(x, y)
974 Arguments 'x' and 'y' give the location of a point. This method
975 raises an error if the first point or second point have not been
976 set.
978 if self.__start_point is None:
979 raise ValueError, "First point not set in LeaderTool."
980 if self.__mid_point is None:
981 raise ValueError, "Second point not set in LeaderTool."
982 _x = util.get_float(x)
983 _y = util.get_float(y)
984 self.__end_point = (_x, _y)
986 def getFinalPoint(self):
987 """Get the third point used to define the Leader.
989 getFinalPoint()
991 This method returns a tuple holding the values used when the
992 setFinalPoint() method was called, or None if that method has
993 not yet been used.
995 return self.__end_point
997 def reset(self):
998 """Restore the tool to its initial state.
1000 reset()
1002 This method extends Tool::reset().
1004 super(LeaderTool, self).reset()
1005 self.__start_point = None
1006 self.__mid_point = None
1007 self.__end_point = None
1009 def create(self, image):
1010 """Create a new Leader and add it to the image.
1012 create(image)
1014 This method overrides the Tool::create() method.
1016 if (self.__start_point is not None and
1017 self.__mid_point is not None and
1018 self.__end_point is not None):
1019 _active_layer = image.getActiveLayer()
1020 _x1, _y1 = self.__start_point
1021 _x2, _y2 = self.__mid_point
1022 _x3, _y3 = self.__end_point
1023 _pts = _active_layer.find('point', _x1, _y1)
1024 if len(_pts) == 0:
1025 _p1 = Point(_x1, _y1)
1026 _active_layer.addObject(_p1)
1027 else:
1028 _p1 = _pts[0]
1029 _pts = _active_layer.find('point', _x2, _y2)
1030 if len(_pts) == 0:
1031 _p2 = Point(_x2, _y2)
1032 _active_layer.addObject(_p2)
1033 else:
1034 _p2 = _pts[0]
1035 _pts = _active_layer.find('point', _x3, _y3)
1036 if len(_pts) == 0:
1037 _p3 = Point(_x3, _y3)
1038 _active_layer.addObject(_p3)
1039 else:
1040 _p3 = _pts[0]
1041 _size = image.getOption('LEADER_ARROW_SIZE')
1042 _s = image.getOption('LINE_STYLE')
1043 _leader = Leader(_p1, _p2, _p3, _size, _s)
1044 _l = image.getOption('LINE_TYPE')
1045 if _l != _s.getLinetype():
1046 _leader.setLinetype(_l)
1047 _c = image.getOption('LINE_COLOR')
1048 if _c != _s.getColor():
1049 _leader.setColor(_c)
1050 _t = image.getOption('LINE_THICKNESS')
1051 if abs(_t - _s.getThickness()) > 1e-10:
1052 _leader.setThickness(_t)
1053 _active_layer.addObject(_leader)
1054 self.reset()
1056 class PolylineTool(Tool):
1057 """A specialized tool for drawing Polyline objects.
1059 The PolylineTool class is derived from the Tool class, so it
1060 shares all the attributes and methods of that class. The PolylineTool
1061 class has the following addtional methods:
1063 storePoint(): Store a point used to define the Polyline.
1064 getPoint(): Retrieve a point used to define the Polyline.
1065 getLastPoint(): Retrieve the last point used to define the Polyline.
1066 getPoints(): Get the list of points that define the Polyline.
1068 def __init__(self):
1069 super(PolylineTool, self).__init__()
1070 self.__points = []
1072 def __len__(self):
1073 return len(self.__points)
1075 def storePoint(self, x, y):
1076 """Store a point that will define a Polyline.
1078 storePoint(x, y)
1080 The arguments 'x' and 'y' should be float values. There is
1081 no limit as to how long a Polyline should be, so each invocation
1082 of this method appends the values to the list of stored points.
1084 _x = util.get_float(x)
1085 _y = util.get_float(y)
1086 self.__points.append((_x, _y))
1088 def getPoint(self, i):
1089 """Retrieve a point used to define a Polyline.
1091 getPoint(i)
1093 Argument 'i' represents the index in the list of points that
1094 defines the polyline. Negative indicies will get points from
1095 last-to-first. Using an invalid index will raise an error.
1097 This method returns a tuple holding the x/y coordinates.
1099 return self.__points[i]
1101 def getPoints(self):
1102 """Get all the points that define the Polyline.
1104 getPoints()
1106 This method returns a list of tuples holding the x/y coordinates
1107 of all the points that define the Polyline.
1109 return self.__points[:]
1111 def reset(self):
1112 """Restore the tool to its initial state.
1114 reset()
1116 This method extends Tool::reset().
1118 super(PolylineTool, self).reset()
1119 del self.__points[:]
1121 def create(self, image):
1122 """Create a new Polyline and add it to the image.
1124 create(image)
1126 This method overrides the Tool::create() method.
1128 if len(self.__points):
1129 _pts = []
1130 _active_layer = image.getActiveLayer()
1131 for _pt in self.__points:
1132 _x, _y = _pt
1133 _lpts = _active_layer.find('point', _x, _y)
1134 if len(_lpts) == 0:
1135 _p = Point(_x, _y)
1136 _active_layer.addObject(_p)
1137 _pts.append(_p)
1138 else:
1139 _pts.append(_lpts[0])
1140 _s = image.getOption('LINE_STYLE')
1141 _pline = Polyline(_pts, _s)
1142 _l = image.getOption('LINE_TYPE')
1143 if _l != _s.getLinetype():
1144 _pline.setLinetype(_l)
1145 _c = image.getOption('LINE_COLOR')
1146 if _c != _s.getColor():
1147 _pline.setColor(_c)
1148 _t = image.getOption('LINE_THICKNESS')
1149 if abs(_t - _s.getThickness()) > 1e-10:
1150 _pline.setThickness(_t)
1151 _active_layer.addObject(_pline)
1152 self.reset()
1154 class PolygonTool(Tool):
1155 """A specialized to for creating Polygons from Segments.
1157 The PolygonTool will create an uniformly sized polygon from Segment
1158 entities. The minimum number of sides is three, creating an equilateral
1159 triangle. There is no maximum number of sides, though realistically any
1160 polygon with more than 20 or so sides is unlikely to be drawn. As
1161 the PolygonTool is derived from the Tool class, it shares all the attributes
1162 and method of that class. The PolygonTool has the following additional
1163 methods:
1165 {get/set}SideCount(): Get/Set the number of sides in the polygon.
1166 {get/set}External() Get/Set if the polygon is drawn inside or outside a circle.
1167 {get/set}Center(): Get/Set the center location of the polygon.
1168 getCoords(): Get the coordinates of the polygon corners.
1170 def __init__(self):
1171 super(PolygonTool, self).__init__()
1172 self.__nsides = None
1173 self.__increment = None
1174 self.__external = False
1175 self.__center = None
1176 self.__xpts = array.array("d")
1177 self.__ypts = array.array("d")
1179 def setSideCount(self, count):
1180 """Set the number of sides of the polygon to create.
1182 setSideCount(count)
1184 Argument "count" should be an integer value greater than 2.
1186 _count = count
1187 if not isinstance(_count, int):
1188 _count = int(count)
1189 if _count < 3:
1190 raise ValueError, "Invalid count: %d" % _count
1191 self.__nsides = _count
1192 self.__increment = (360.0/float(_count)) * (math.pi/180.0)
1193 for _i in range(_count):
1194 self.__xpts.insert(_i, 0.0)
1195 self.__ypts.insert(_i, 0.0)
1197 def getSideCount(self):
1198 """Get the number of sides of the polygon to be created.
1200 getSideCount()
1202 A ValueError exception is raised if the side count has not been
1203 set with setSideCount()
1205 if self.__nsides is None:
1206 raise ValueError, "No side count defined."
1207 return self.__nsides
1209 def setExternal(self):
1210 """Create the polygon on the outside of a reference circle.
1212 setExternal()
1214 By default the polygon is drawing completely contained within a
1215 circle. Invoking this method will created the polygon so that all
1216 sides are outside the circle.
1218 self.__external = True
1220 def getExternal(self):
1221 """Test if the polygon will be created outside a circle.
1223 getExternal()
1225 If the setExternal() method has been called, this method will
1226 return True. By default this method will return False.
1228 return self.__external
1230 def setCenter(self, x, y):
1231 """Define the center of the polygon.
1233 setCenter(x, y)
1235 Arguments 'x' and 'y' should be float values.
1237 _x = util.get_float(x)
1238 _y = util.get_float(y)
1239 self.__center = (_x, _y)
1241 def getCenter(self):
1242 """Retrieve the center of the polygon to be created.
1244 getCenter()
1246 This method returns a tuple holding two float values containing
1247 the 'x' and 'y' coordinates of the polygon center. A ValueError
1248 is raised if the center has not been set with a prior call to setCenter().
1250 if self.__center is None:
1251 raise ValueError, "Center is undefined."
1252 return self.__center
1254 def getCoord(self, i):
1255 """Get one of the coordinates of the polygon corners.
1257 getCoord(i)
1259 Argument "i" should be an integer value such that:
1261 0 <= i <= number of polygon sides
1263 _x = self.__xpts[i]
1264 _y = self.__ypts[i]
1265 return _x, _y
1267 def setLocation(self, x, y):
1268 """Set the tool location.
1270 setLocation(x, y)
1272 This method extends Tool::setLocation() and calculates the polygon
1273 points.
1275 super(PolygonTool, self).setLocation(x, y)
1276 _x, _y = self.getLocation()
1277 _count = self.__nsides
1278 _inc = self.__increment
1279 if self.__external:
1280 _offset = _inc/2.0
1281 else:
1282 _offset = 0.0
1283 _cx, _cy = self.__center
1284 _xsep = _x - _cx
1285 _ysep = _y - _cy
1286 _angle = math.atan2(_ysep, _xsep) + _offset
1287 _rad = math.hypot(_xsep, _ysep)/math.cos(_offset)
1288 _xp = self.__xpts
1289 _yp = self.__ypts
1290 for _i in range(_count):
1291 _xp[_i] = _cx + (_rad * math.cos(_angle))
1292 _yp[_i] = _cy + (_rad * math.sin(_angle))
1293 _angle = _angle + _inc
1295 def create(self, image):
1296 """Create a Polygon from Segments and add it to the image.
1298 create(image)
1300 This method overrides the Tool::create() method.
1302 if len(self.__xpts):
1303 _active_layer = image.getActiveLayer()
1304 _s = image.getOption('LINE_STYLE')
1305 _l = image.getOption('LINE_TYPE')
1306 if _l == _s.getLinetype():
1307 _l = None
1308 _c = image.getOption('LINE_COLOR')
1309 if _c == _s.getColor():
1310 _c = None
1311 _t = image.getOption('LINE_THICKNESS')
1312 if abs(_t - _s.getThickness()) < 1e-10:
1313 _t = None
1314 _count = self.__nsides
1315 _xp = self.__xpts
1316 _yp = self.__ypts
1317 _x = _xp[0]
1318 _y = _yp[0]
1320 # find starting point ...
1322 _pts = _active_layer.find('point', _x, _y)
1323 if len(_pts) == 0:
1324 _p0 = Point(_x, _y)
1325 _active_layer.addObject(_p0)
1326 else:
1327 _p0 = _pts[0]
1329 # make segments for all the points ...
1331 _p1 = _p0
1332 for _i in range(1, _count):
1333 _x = _xp[_i]
1334 _y = _yp[_i]
1335 _pts = _active_layer.find('point', _x, _y)
1336 if len(_pts) == 0:
1337 _pi = Point(_x, _y)
1338 _active_layer.addObject(_pi)
1339 else:
1340 _pi = _pts[0]
1341 _seg = Segment(_p1, _pi, _s, linetype=_l, color=_c, thickness=_t)
1342 _active_layer.addObject(_seg)
1343 _p1 = _pi
1345 # now add closing segment ...
1347 _seg = Segment(_p1, _p0, _s, linetype=_l, color=_c, thickness=_t)
1348 _active_layer.addObject(_seg)
1349 self.reset()
1351 def reset(self):
1352 """Restore the PolygonTool to its original state.
1354 reset()
1356 This method extends Tool::reset()
1358 super(PolygonTool, self).reset()
1359 # self.__nsides = None
1360 # self.__increment = None
1361 # self.__external = False # make this adjustable?
1362 self.__center = None
1363 for _i in range(self.__nsides):
1364 self.__xpts[_i] = 0.0
1365 self.__ypts[_i] = 0.0
1367 class HCLineTool(PointTool):
1368 """A specialized tool for drawing HCLine objects.
1370 The HCLineTool class is derived from the PointTool class
1371 so it shares all the attributes and methods of that class.
1373 There are no additional methods for this class.
1375 def __init__(self):
1376 super(HCLineTool, self).__init__()
1378 def create(self, image):
1379 """Create a new HCLine and add it to the image.
1381 create(image)
1383 This method overrides the Tool::create() method.
1385 _p = self.getPoint()
1386 if _p is not None:
1387 _active_layer = image.getActiveLayer()
1388 _x, _y = _p
1389 _pts = _active_layer.find('point', _x, _y)
1390 if len(_pts) == 0:
1391 _pt = Point(_x, _y)
1392 _active_layer.addObject(_pt)
1393 else:
1394 _pt = _pts[0]
1395 _hcl = HCLine(_pt)
1396 _active_layer.addObject(_hcl)
1397 self.reset()
1399 class VCLineTool(PointTool):
1400 """A specialized tool for drawing VCLine objects.
1402 The VCLineTool class is derived from the PointTool class
1403 so it shares all the attributes and methods of that class.
1405 There are no additional methods for this class.
1407 def __init__(self):
1408 super(VCLineTool, self).__init__()
1410 def create(self, image):
1411 """Create a new VCLine and add it to the image.
1413 create(image)
1415 This method overrides the Tool::create() method.
1417 _p = self.getPoint()
1418 if _p is not None:
1419 _active_layer = image.getActiveLayer()
1420 _x, _y = _p
1421 _pts = _active_layer.find('point', _x, _y)
1422 if len(_pts) == 0:
1423 _pt = Point(_x, _y)
1424 _active_layer.addObject(_pt)
1425 else:
1426 _pt = _pts[0]
1427 _vcl = VCLine(_pt)
1428 _active_layer.addObject(_vcl)
1429 self.reset()
1431 class ACLineTool(PointTool):
1432 """A specialized tool for drawing ACLine objects.
1434 The ACLineTool class is derived from the PointTool class
1435 so it shares all the attributes and methods of that class.
1436 The ACLineTool class has the following addtional methods:
1438 {set/get}Angle(): Set/Get the angle of the ACLine.
1440 def __init__(self):
1441 super(ACLineTool, self).__init__()
1442 self.__angle = None
1444 def setLocation(self, x, y):
1445 """Set the location of the Tool.
1447 setLocation(x, y)
1449 This method extends the Tool::setLocation() method.
1451 super(ACLineTool, self).setLocation(x, y)
1452 _loc = self.getLocation()
1453 if _loc is None:
1454 return
1455 _x, _y = _loc
1456 _x1, _y1 = self.getPoint()
1457 if abs(_y - _y1) < 1e-10: # horizontal
1458 self.__angle = 0.0
1459 elif abs(_x - _x1) < 1e-10: # vertical
1460 self.__angle = 90.0
1461 else:
1462 _slope = 180.0/math.pi * math.atan2((_y - _y1), (_x - _x1))
1463 self.__angle = util.make_angle(_slope)
1465 def setAngle(self, angle):
1466 """Set the angle for the ACLine.
1468 setAngle(angle)
1470 The argument 'angle' should be a float where -90.0 < angle < 90.0
1472 _angle = util.make_angle(angle)
1473 self.__angle = _angle
1475 def getAngle(self):
1476 """Get the angle for the ACLine.
1478 getAngle()
1480 This method returns a float.
1482 return self.__angle
1484 def reset(self):
1485 """Restore the tool to its initial state.
1487 reset()
1489 This method extends PointTool::reset().
1491 super(ACLineTool, self).reset()
1492 self.__angle = None
1494 def create(self, image):
1495 """Create a new ACLine and add it to the image.
1497 create(image)
1499 This method overrides the Tool::create() method.
1501 _p = self.getPoint()
1502 if (_p is not None and
1503 self.__angle is not None):
1504 _active_layer = image.getActiveLayer()
1505 _x, _y = _p
1506 _pts = _active_layer.find('point', _x, _y)
1507 if len(_pts) == 0:
1508 _pt = Point(_x, _y)
1509 _active_layer.addObject(_pt)
1510 else:
1511 _pt = _pts[0]
1512 _acl = ACLine(_pt, self.__angle)
1513 _active_layer.addObject(_acl)
1514 self.reset()
1516 class CLineTool(SegmentTool):
1517 """A specialized tool for drawing CLine objects.
1519 The CLineTool class is derived from the SegmentTool class,
1520 so it shares all the attributes and methods of that class.
1522 There are no extra methods for the CLineTool class.
1524 def __init__(self):
1525 super(CLineTool, self).__init__()
1527 def create(self, image):
1528 """Create a new CLine and add it to the image.
1530 create(image)
1532 This method overrides the Tool::create() method.
1534 _p1 = self.getFirstPoint()
1535 _p2 = self.getSecondPoint()
1536 if _p1 is not None and _p2 is not None:
1537 _active_layer = image.getActiveLayer()
1538 _x1, _y1 = _p1
1539 _x2, _y2 = _p2
1540 _pts = _active_layer.find('point', _x1, _y1)
1541 if len(_pts) == 0:
1542 _p1 = Point(_x1, _y1)
1543 _active_layer.addObject(_p1)
1544 else:
1545 _p1 = _pts[0]
1546 _pts = _active_layer.find('point', _x2, _y2)
1547 if len(_pts) == 0:
1548 _p2 = Point(_x2, _y2)
1549 _active_layer.addObject(_p2)
1550 else:
1551 _p2 = _pts[0]
1552 _cline = CLine(_p1, _p2)
1553 _active_layer.addObject(_cline)
1554 self.reset()
1556 class CCircleTool(CircleTool):
1557 """A specialized tool for drawing CCircle objects.
1559 The CCircleTool class is derived from the CircleTool class,
1560 so it shares all the attributes and methods of that class.
1562 There are no additional methods for the CCircleTool class.
1564 def __init__(self):
1565 super(CCircleTool, self).__init__()
1567 def create(self, image):
1568 """Create a new CCircle and add it to the image.
1570 create(image)
1572 This method overrides the Tool::create() method.
1574 _active_layer = image.getActiveLayer()
1575 _x, _y = self.getCenter()
1576 _radius = self.getRadius()
1577 _pts = _active_layer.find('point', _x, _y)
1578 if len(_pts) == 0:
1579 _cp = Point(_x, _y)
1580 _active_layer.addObject(_cp)
1581 else:
1582 _cp = _pts[0]
1583 _ccircle = CCircle(_cp, _radius)
1584 _active_layer.addObject(_ccircle)
1585 self.reset()
1587 class TwoPointCCircleTool(TwoPointCircleTool):
1588 """A specialized tool for drawing CCircle objects between two points.
1590 The TwoPointCCircleTool class is derived from the TwoPointCircleTool
1591 class, so it shares all the attributes and methods of that class.
1592 There are no additional methods for the TwoPointCCircleTool class.
1594 def __init__(self):
1595 super(TwoPointCCircleTool, self).__init__()
1597 def create(self, image):
1598 """Create a new CCircle and add it to the image.
1600 create(image)
1602 This method overrides the Tool::create() method.
1604 _center = self.getCenter()
1605 _radius = self.getRadius()
1606 if _center is not None and _radius is not None:
1607 _active_layer = image.getActiveLayer()
1608 _x, _y = _center
1609 _pts = _active_layer.find('point', _x, _y)
1610 if len(_pts) == 0:
1611 _cp = Point(_x, _y)
1612 _active_layer.addObject(_cp)
1613 else:
1614 _cp = _pts[0]
1615 _ccircle = CCircle(_cp, _radius)
1616 _active_layer.addObject(_ccircle)
1617 self.reset()
1620 # The PerpendicularCLineTool and TangentCLineTool classes are
1621 # subclasses of Tool without any additional functionality (yet)
1624 class PerpendicularCLineTool(Tool):
1625 pass
1627 class TangentCLineTool(Tool):
1628 pass
1630 class ParallelOffsetTool(Tool):
1631 """A specialized tool for creating parallel construction lines.
1633 The ParallelOffsetTool will create a construction line parallel
1634 to another construction line a fixed distance from the original
1635 construction line. The type of the new construction line will match
1636 that of the original.
1638 The ParallelOffsetTool is derived from the Tool class, so it shares
1639 all the attributes and methods of that class. The ParallelOffsetTool
1640 has the following addtional methods:
1642 {set/get}Offset(): Set/Get the distance between the construction lines.
1643 {set/get}ConstructionLine(): Set/Get the original construction line
1644 {set/get}ReferencePoint(): Set/Get the point to define where the new
1645 construction line will go.
1648 def __init__(self):
1649 super(ParallelOffsetTool, self).__init__()
1650 self.__refpt = None
1651 self.__offset = None
1652 self.__conline = None
1654 def setOffset(self, offset):
1655 """Store the displacement in the tool.
1657 setOffset(offset)
1659 Argument 'offset' must be a float.
1661 _offset = util.get_float(offset)
1662 self.__offset = _offset
1664 def getOffset(self):
1665 """Return the stored offset from the tool.
1667 getOffset()
1669 This method will raise a ValueError exception if the offset has
1670 not been set with setOffset()
1672 _offset = self.__offset
1673 if _offset is None:
1674 raise ValueError, "Offset is not defined."
1675 return _offset
1677 def setConstructionLine(self, conline):
1678 """Store the reference construction line in the tool.
1680 setConstructionLine(conline)
1682 Argument 'conline' must be a VCLine, HCLine, ACLine, or CLine object.
1684 if not isinstance(conline, (HCLine, VCLine, ACLine, CLine)):
1685 raise TypeError, "Invalid Construction line: " + `type(conline)`
1686 self.__conline = conline
1688 def getConstructionLine(self):
1689 """Retrieve the stored construction line from the tool.
1691 getConstructionLine()
1693 A ValueError exception is raised if the construction line has not been
1694 set with the setConstructionLine() method.
1696 _conline = self.__conline
1697 if _conline is None:
1698 raise ValueError, "Construction line is not defined."
1699 return _conline
1701 def setReferencePoint(self, x, y):
1702 """Store the reference point for positioning the new construction line.
1704 setReferencePoint(x, y)
1706 Arguments 'x' and 'y' give the coordinates of a reference point
1707 used to determine where the new construction line will be placed.
1708 Both arguments should be floats.
1710 _x = util.get_float(x)
1711 _y = util.get_float(y)
1712 self.__refpt = (_x, _y)
1714 def getReferencePoint(self):
1715 """Retreive the reference point from the tool.
1717 getReferencePoint()
1719 This method returns a tuple containing the values stored from
1720 the setReferencePoint() call. This method will raise a ValueError
1721 exception if the reference point has not been set.
1723 _refpt = self.__refpt
1724 if _refpt is None:
1725 raise ValueError, "No reference point defined."
1726 return _refpt
1728 def reset(self):
1729 """Restore the tool to its initial state.
1731 reset()
1733 This method extends Tool::reset().
1735 super(ParallelOffsetTool, self).reset()
1736 self.__refpt = None
1737 self.__offset = None
1738 self.__conline = None
1740 def create(self, image):
1741 """Create a parallel construction line in an image.
1743 create(image)
1745 This method overrides the Tool::create() method.
1747 _offset = self.__offset
1748 _conline = self.__conline
1749 _refpt = self.__refpt
1750 if (_offset is not None and
1751 _conline is not None and
1752 _refpt is not None):
1753 _active_layer = image.getActiveLayer()
1754 _rx, _ry = _refpt
1755 _lp1 = _lp2 = _ncl = None
1756 if isinstance(_conline, HCLine):
1757 _x, _y = _conline.getLocation().getCoords()
1758 if _ry > _y:
1759 _yn = _y + _offset
1760 else:
1761 _yn = _y - _offset
1762 if len(_active_layer.find('hcline', _yn)) == 0:
1763 _pts = _active_layer.find('point', _x, _yn)
1764 if len(_pts) == 0:
1765 _lp1 = Point(_x, _yn)
1766 else:
1767 _lp1 = _pts[0]
1768 _ncl = HCLine(_lp1)
1769 elif isinstance(_conline, VCLine):
1770 _x, _y = _conline.getLocation().getCoords()
1771 if _rx > _x:
1772 _xn = _x + _offset
1773 else:
1774 _xn = _x - _offset
1775 if len(_active_layer.find('vcline', _xn)) == 0:
1776 _pts = _active_layer.find('point', _xn, _y)
1777 if len(_pts) == 0:
1778 _lp1 = Point(_xn, _y)
1779 else:
1780 _lp1 = _pts[0]
1781 _ncl = VCLine(_lp1)
1782 elif isinstance(_conline, ACLine):
1783 _x, _y = _conline.getLocation().getCoords()
1784 _angle = _conline.getAngle()
1785 if abs(_angle) < 1e-10: # horizontal
1786 _dx = 0.0
1787 _dy = _offset
1788 elif abs(abs(_angle) - 90.0) < 1e-10: # vertical
1789 _dx = _offset
1790 _dy = 0.0
1791 else:
1792 _slope = math.tan(_angle * (math.pi/180.0))
1793 _yint = _y - (_slope * _x)
1795 # p1 => (_x, _y)
1796 # p2 => (0, _yint)
1798 # redefine angle from p1 to p2 ...
1799 _angle = math.atan2((_yint - _y), -_x)
1800 if _angle < 0.0:
1801 _angle = _angle + (2.0 * math.pi)
1802 _sqlen = math.hypot(_x, (_y - _yint))
1803 _sn = ((_y - _ry) * (0.0 - _x)) - ((_x - _rx) * (_yint - _y))
1804 _s = _sn/_sqlen
1805 if _s < 0.0:
1806 _perp = _angle + (math.pi/2.0)
1807 else:
1808 _perp = _angle - (math.pi/2.0)
1809 _dx = _offset * math.cos(_perp)
1810 _dy = _offset * math.sin(_perp)
1811 _angle = _conline.getAngle() # reset variable
1812 _xn = _x + _dx
1813 _yn = _y + _dy
1814 if len(_active_layer.find('acline', _xn, _yn, _angle)) == 0:
1815 _pts = _active_layer.find('point', _xn, _yn)
1816 if len(_pts) == 0:
1817 _lp1 = Point(_xn, _yn)
1818 else:
1819 _lp1 = _pts[0]
1820 _ncl = ACLine(_lp1, _angle)
1821 elif isinstance(_conline, CLine):
1822 _p1, _p2 = _conline.getKeypoints()
1823 _x1, _y1 = _p1.getCoords()
1824 _x2, _y2 = _p2.getCoords()
1825 if abs(_x2 - _x1) < 1e-10: # vertical
1826 _dx = _offset
1827 _dy = 0.0
1828 elif abs(_y2 - _y1) < 1e-10: # horizontal
1829 _dx = 0.0
1830 _dy = _offset
1831 else:
1832 _angle = math.atan2((_y2 - _y1), (_x2 - _x1))
1833 if _angle < 0.0:
1834 _angle = _angle + (2.0 * math.pi)
1835 _sqlen = math.hypot((_x2 - _x1), (_y2 - _y1))
1836 _sn = ((_y1 - _ry) * (_x2 - _x1)) - ((_x1 - _rx) * (_y2 - _y1))
1837 _s = _sn/_sqlen
1838 if _s < 0.0:
1839 _perp = _angle + (math.pi/2.0)
1840 else:
1841 _perp = _angle - (math.pi/2.0)
1842 _dx = math.cos(_perp) * _offset
1843 _dy = math.sin(_perp) * _offset
1844 _x1n = _x1 + _dx
1845 _y1n = _y1 + _dy
1846 _x2n = _x2 + _dx
1847 _y2n = _y2 + _dy
1848 if len(_active_layer.find('cline', _x1n, _y1n, _x2n, _y2n)) == 0:
1849 _pts = _active_layer.find('point', _x1n, _y1n)
1850 if len(_pts) == 0:
1851 _lp1 = Point(_x1n, _y1n)
1852 else:
1853 _lp1 = _pts[0]
1854 _pts = _active_layer.find('point', _x2n, _y2n)
1855 if len(_pts) == 0:
1856 _lp2 = Point(_x2n, _y2n)
1857 else:
1858 _lp2 = _pts[0]
1859 _ncl = CLine(_lp1, _lp2)
1860 else:
1861 raise TypeError, "Invalid Construction line type: " + `type(_conline)`
1862 if _ncl is not None:
1863 if _lp1 is not None and _lp1.getParent() is None:
1864 _active_layer.addObject(_lp1)
1865 if _lp2 is not None and _lp2.getParent() is None:
1866 _active_layer.addObject(_lp2)
1867 _active_layer.addObject(_ncl)
1868 self.reset()
1870 class TangentCircleTool(Tool):
1871 """A specialized class for creating tangent construction circles.
1873 This class is meant to be a base class for tools that create tangent
1874 construction circles. It is derived from the tool class so it shares
1875 all the attributes and methods of that class. This class has the
1876 following additional methods:
1878 {set/get}Center(): Set/Get the center of the tangent circle.
1879 {set/get}Radius(): Set/Get the radius of the tangent circle.
1880 {set/get}PixelRect(): Set/Get the screen rectangle for drawing the circle.
1882 def __init__(self):
1883 super(TangentCircleTool, self).__init__()
1884 self.__center = None
1885 self.__radius = None
1886 self.__rect = None
1888 def setCenter(self, x, y):
1889 """Store the tangent circle center point in the tool.
1891 setCenter(x, y)
1893 Arguments 'x' and 'y' should be floats.
1895 _x = util.get_float(x)
1896 _y = util.get_float(y)
1897 self.__center = (_x, _y)
1899 def getCenter(self):
1900 """Return the center of the tangent circle.
1902 getCenter()
1904 This method returns a tuple holding two floats, the first
1905 is the 'x' coordinate of the center, the second is the 'y'
1906 coordinate. If the tool has not yet been invoked with a
1907 setLocation() call, this method returns None.
1909 return self.__center
1911 def setRadius(self, radius):
1912 """Store the radius in the tool.
1914 setRadius(radius)
1916 Argument 'radius' should be a float.
1918 _radius = util.get_float(radius)
1919 self.__radius = _radius
1921 def getRadius(self):
1922 """Return the center of the tangent circle.
1924 getRadius()
1926 This method returns a float giving the radius of the tangent
1927 circle, or None if the radius is not set.
1929 return self.__radius
1931 def setPixelRect(self, xmin, ymin, width, height):
1932 """Store the screen coordinates used to draw the circle.
1934 setPixelRect(xmin, ymin, width, height)
1936 All the arguments should be integer values.
1938 _xmin = xmin
1939 if not isinstance(_xmin, int):
1940 _xmin = int(xmin)
1941 _ymin = ymin
1942 if not isinstance(_ymin, int):
1943 _ymin = int(ymin)
1944 _width = width
1945 if not isinstance(_width, int):
1946 _width = int(_width)
1947 _height = height
1948 if not isinstance(_height, int):
1949 _height = int(height)
1950 self.__rect = (_xmin, _ymin, _width, _height)
1952 def getPixelRect(self):
1953 """Return the screen boundary of the circle to draw
1955 getPixelRect(self)
1957 This method will return a tuple holding four integer values:
1959 xmin, ymin, width, height
1961 If the rectangle has not been set by calling setPixelRect(), then
1962 this method will return None.
1964 return self.__rect
1966 def reset(self):
1967 """Restore the tool to its initial state.
1969 reset()
1971 This method extends Tool::reset().
1973 super(TangentCircleTool, self).reset()
1974 self.__center = None
1975 self.__radius = None
1976 self.__rect = None
1978 def create(self, image):
1979 """Create a new CCircle and add it to the image.
1981 create(image)
1983 This method overrides the Tool::create() method.
1985 _active_layer = image.getActiveLayer()
1986 _x, _y = self.__center
1987 _radius = self.__radius
1988 _pts = _active_layer.find('point', _x, _y)
1989 if len(_pts) == 0:
1990 _cp = Point(_x, _y)
1991 _active_layer.addObject(_cp)
1992 else:
1993 _cp = _pts[0]
1994 _ccircle = CCircle(_cp, _radius)
1995 _active_layer.addObject(_ccircle)
1996 self.reset()
1998 class TangentCCircleTool(TangentCircleTool):
1999 """A specialized tool for creating tangent construction circles.
2001 The TangentCCircleTool will create a construction circle tangent
2002 to a construction line or a construction circle.
2004 The TangentCCircleTool is derived from the TangentCircleTool class, so
2005 it shares all the attributes and methods of that class. The TangentCCircleTool
2006 has the following addtional methods:
2008 {set/get}ConstructionObject(): Set/Get the reference construction object.
2010 def __init__(self):
2011 super(TangentCCircleTool, self).__init__()
2012 self.__conobj = None
2014 def setConstructionLine(self, conobj):
2015 """Store the reference construction object in the tool.
2017 setConstructionLine(conobj)
2019 Argument 'conobj' must be a VCLine, HCLine, ACLine, CLine,
2020 or CCircle object.
2022 if not isinstance(conobj, (HCLine, VCLine, ACLine, CLine, CCircle)):
2023 raise TypeError, "Invalid Construction entity type: " + `type(conobj)`
2024 self.__conobj = conobj
2026 def getConstructionLine(self):
2027 """Retrieve the stored construction line from the tool.
2029 getConstructionLine()
2031 A ValueError exception is raised if the construction line has not been
2032 set with the setConstructionLine() method.
2034 _conobj = self.__conobj
2035 if _conobj is None:
2036 raise ValueError, "Construction object is not defined."
2037 return _conobj
2039 def setLocation(self, x, y):
2040 """Store an x/y coordinate pair in the tool.
2042 setLocation(x, y)
2044 Arguments 'x' and 'y' should be floats. This method extends
2045 the TangentCircleTool::setLocation() methods.
2047 super(TangentCCircleTool, self).setLocation(x, y)
2048 _tx, _ty = self.getLocation()
2049 _conobj = self.__conobj
2050 _cx = _cy = _radius = None
2051 if isinstance(_conobj, HCLine):
2052 _x, _y = _conobj.getLocation().getCoords()
2053 _cx = _tx
2054 _cy = (_ty + _y)/2.0
2055 _radius = abs(_ty - _y)/2.0
2056 elif isinstance(_conobj, VCLine):
2057 _x, _y = _conobj.getLocation().getCoords()
2058 _cx = (_tx + _x)/2.0
2059 _cy = _ty
2060 _radius = abs(_tx - _x)/2.0
2061 elif isinstance(_conobj, (ACLine, CLine)):
2062 _px, _py = _conobj.getProjection(_tx, _ty)
2063 _cx = (_tx + _px)/2.0
2064 _cy = (_ty + _py)/2.0
2065 _radius = math.hypot((_tx - _px), (_ty - _py))/2.0
2066 elif isinstance(_conobj, CCircle):
2067 _ccx, _ccy = _conobj.getCenter().getCoords()
2068 _rad = _conobj.getRadius()
2069 _sep = math.hypot((_tx - _ccx), (_ty - _ccy))
2070 if _sep < 1e-10:
2071 return
2072 _angle = math.atan2((_ty - _ccy), (_tx - _ccx))
2073 _px = _ccx + (_rad * math.cos(_angle))
2074 _py = _ccy + (_rad * math.sin(_angle))
2075 _cx = (_tx + _px)/2.0
2076 _cy = (_ty + _py)/2.0
2077 _radius = math.hypot((_tx - _px), (_ty - _py))/2.0
2078 else:
2079 raise TypeError, "Invalid construction entity type: " + `type(_conobj)`
2080 self.setCenter(_cx, _cy)
2081 self.setRadius(_radius)
2083 class TwoPointTangentCCircleTool(TangentCircleTool):
2084 """A specialized tool for creating tangent construction circles.
2086 The TwoPointTangentCCircleTool will create a construction circle tangent
2087 to two construction lines or a construction line and a construction
2088 circle if such a tangent circle can be created.
2090 The TwoPointTangentCCircleTool is derived from the TangentCircleTool
2091 class, so it shares all the attributes and methods of that class. This
2092 class also has the following addtional methods:
2094 {set/get}FirstConObject(): Set/Get the first construction object.
2095 {set/get}SecondConObject(): Set/Get the second constuction object.
2097 def __init__(self):
2098 super(TwoPointTangentCCircleTool, self).__init__()
2099 self.__cobj1 = None
2100 self.__cobj2 = None
2102 def setFirstConObject(self, conobj):
2103 """Store the first reference construction object in the tool.
2105 setFirstConObject(conobj)
2107 Argument 'conobj' must be a VCLine, HCLine, ACLine, CLine, or CCircle object.
2109 if not isinstance(conobj, (HCLine, VCLine, ACLine, CLine, CCircle)):
2110 raise TypeError, "Invalid Construction entity type: " + `type(conobj)`
2111 self.__cobj1 = conobj
2113 def getFirstConObject(self):
2114 """Retreive the first construction object from the tool.
2116 getFirstConObject()
2118 return self.__cobj1
2120 def setSecondConObject(self, conobj):
2121 """Store the second reference construction object in the tool.
2123 setSecondConObject(conobj)
2125 Argument 'conobj' must be a VCLine, HCLine, ACLine, or a CLine object.
2126 Drawing a tangent circle against two CCircle objects is not yet supported.
2127 A ValueError exception will be raised if this method is called before the
2128 first construction object has been set.
2130 if self.__cobj1 is None:
2131 raise ValueError, "First construction object not set."
2132 if not isinstance(conobj, (HCLine, VCLine, ACLine, CLine)):
2133 raise TypeError, "Invalid Construction line type: " + `type(conobj)`
2134 self.__cobj2 = conobj
2136 def getSecondConObject(self):
2137 """Retreive the second construction object from the tool.
2139 getSecondConObject()
2141 return self.__cobj2
2143 def reset(self):
2144 """Restore the tool to its initial state.
2146 reset()
2148 This method extends the TangentCircleTool::reset() method.
2150 super(TwoPointTangentCCircleTool, self).reset()
2151 self.__cobj1 = None
2152 self.__cobj2 = None
2154 def setLocation(self, x, y):
2155 """Store an x/y coordinate pair in the tool.
2157 setLocation(x, y)
2159 Arguments 'x' and 'y' should be floats. This method extends
2160 the TangentCircleTool::setLocation() methods.
2162 super(TwoPointTangentCCircleTool, self).setLocation(x, y)
2163 _tx, _ty = self.getLocation()
2164 _obja = self.__cobj1
2165 _objb = self.__cobj2
2166 _tandata = tangent.calc_tangent_circle(_obja, _objb, _tx, _ty)
2167 if _tandata is not None:
2168 _cx, _cy, _radius = _tandata
2169 self.setCenter(_cx, _cy)
2170 self.setRadius(_radius)
2173 class CCircleTangentLineTool(Tool):
2174 """A specialized class for creating tangent lines to construction circles.
2176 This class is a specialized class that handles creating construction
2177 lines around circles. There can be at most four possible tangent lines.
2178 There are two tangent lines if the circles overlap, and no tangent
2179 lines if one circle is inside another. As this tool is derived from
2180 the Tool class it shares all the attributes and methods of that
2181 class. The CCircleTangentLineTool class has the following additional
2182 methods:
2184 {get/set}FirstCCircle(): Get/Set the first CCircle in the tool.
2185 {get/set}SecondCCircle(): Get/Set the second CCircle in the tool.
2187 def __init__(self):
2188 super(CCircleTangentLineTool, self).__init__()
2189 self.__circ1 = None
2190 self.__circ2 = None
2191 self.__tanpts = []
2193 def setFirstCCircle(self, ccircle):
2194 """Store the first construction circle in the tool.
2196 setFirstCCircle(ccircle)
2198 Argument 'ccircle' must be a CCircle object.
2200 if not isinstance(ccircle, CCircle):
2201 raise TypeError, "Invalid CCircle type: " + `type(ccircle)`
2202 self.__circ1 = ccircle
2204 def getFirstCCircle(self):
2205 """Retreive the first construction circle from the tool.
2207 getFirstCCircle()
2209 return self.__circ1
2211 def setSecondCCircle(self, ccircle):
2212 """Store the second construction circle in the tool.
2214 setSecondCCircle(ccircle)
2216 Argument 'ccircle' must be a CCircle object. A ValueError exception will
2217 be raised if this method is called before the first construction circle
2218 has been set.
2220 if self.__circ1 is None:
2221 raise ValueError, "First construction circle not set."
2222 if not isinstance(ccircle, CCircle):
2223 raise TypeError, "Invalid CCircle type: " + `type(ccircle)`
2224 self.__circ2 = ccircle
2226 # calculate the tangent points if they exist
2228 _cc1 = self.__circ1
2229 _cc2 = self.__circ2
2230 _cx1, _cy1 = _cc1.getCenter().getCoords()
2231 _r1 = _cc1.getRadius()
2232 _cx2, _cy2 = _cc2.getCenter().getCoords()
2233 _r2 = _cc2.getRadius()
2234 _sep = math.hypot((_cx2 - _cx1), (_cy2 - _cy1))
2235 _angle = math.atan2((_cy2 - _cy1), (_cx2 - _cx1))
2236 _sine = math.sin(_angle)
2237 _cosine = math.cos(_angle)
2239 # tangent points are calculated as if the first circle
2240 # center is (0, 0) and both circle centers on the x-axis,
2241 # so the points need to be transformed to the correct coordinates
2243 _tanpts = self.__tanpts
2244 del _tanpts[:] # make sure it is empty ...
2245 _tansets = tangent.calc_two_circle_tangents(_r1, _r2, _sep)
2246 for _set in _tansets:
2247 _x1, _y1, _x2, _y2 = _set
2248 _tx1 = ((_x1 * _cosine) - (_y1 * _sine)) + _cx1
2249 _ty1 = ((_x1 * _sine) + (_y1 * _cosine)) + _cy1
2250 _tx2 = ((_x2 * _cosine) - (_y2 * _sine)) + _cx1
2251 _ty2 = ((_x2 * _sine) + (_y2 * _cosine)) + _cy1
2252 _tanpts.append((_tx1, _ty1, _tx2, _ty2))
2255 def getSecondCCircle(self):
2256 """Retreive the second construction circle from the tool.
2258 getSecondCCircle()
2260 return self.__circ2
2262 def hasTangentPoints(self):
2263 """Test if tangent points were found for the two circles.
2265 hasTangentPoints()
2267 _val = False
2268 if len(self.__tanpts):
2269 _val = True
2270 return _val
2272 def getTangentPoints(self):
2273 """Return the tangent points calculated for two-circle tangency.
2275 getTangentPoints()
2277 This method returns a list of tuples holding four float values:
2279 (x1, y1, x2, y2)
2281 A tangent line can be drawn between the two circles from (x1, y1) to (x2, y2).
2283 return self.__tanpts[:]
2285 def create(self, image):
2286 """Create the tangent line for two circles.
2288 create(image)
2290 _x, _y = self.getLocation()
2291 _tanpts = self.__tanpts
2292 if not len(_tanpts):
2293 raise ValueError, "No tangent points defined."
2294 _minsep = _px1 = _py1 = _px2 = _py2 = None
2295 for _set in _tanpts:
2296 _x1, _y1, _x2, _y2 = _set
2297 _sqlen = pow((_x2 - _x1), 2) + pow((_y2 - _y1), 2)
2298 _rn = ((_x - _x1) * (_x2 - _x1)) + ((_y - _y1) * (_y2 - _y1))
2299 _r = _rn/_sqlen
2300 _px = _x1 + _r * (_x2 - _x1)
2301 _py = _y1 + _r * (_y2 - _y1)
2302 _sep = math.hypot((_px - _x), (_py - _y))
2303 if _minsep is None or _sep < _minsep:
2304 _minsep = _sep
2305 _px1 = _x1
2306 _py1 = _y1
2307 _px2 = _x2
2308 _py2 = _y2
2309 _active_layer = image.getActiveLayer()
2310 _pts = _active_layer.find('point', _px1, _py1)
2311 if len(_pts) == 0:
2312 _p1 = Point(_px1, _py1)
2313 _active_layer.addObject(_p1)
2314 else:
2315 _p1 = _pts[0]
2316 _pts = _active_layer.find('point', _px2, _py2)
2317 if len(_pts) == 0:
2318 _p2 = Point(_px2, _py2)
2319 _active_layer.addObject(_p2)
2320 else:
2321 _p2 = _pts[0]
2322 _active_layer.addObject(CLine(_p1, _p2))
2324 def reset(self):
2325 """Restore the tool to its initial state.
2327 reset()
2329 This method extends Tool::reset().
2331 super(CCircleTangentLineTool, self).reset()
2332 self.__circ1 = None
2333 self.__circ2 = None
2334 del self.__tanpts[:]
2336 class DimensionTool(Tool):
2337 """A base class for tools creating Dimension objects.
2339 The DimensionTool class is meant to be a base class for classes
2340 that will create Dimension objects. The DimensionTool class is
2341 derived from the Tool class, so it shares all the attributes and
2342 methods of that class. The DimensionTool class has the following
2343 additional methods:
2345 setDimension(): Store a dimension object in the tool
2346 getDimension(): Retrieve a stored dimension object.
2347 setDimPrefs(): Apply the current dimension preferences on a stored dimension.
2349 def __init__(self):
2350 super(DimensionTool, self).__init__()
2351 self.__dim = None
2353 def _setDimension(self, dim):
2354 """Store a dimension in the tool.
2356 setDimension(dim)
2358 The argument 'dim' must be a Dimension object.
2360 if not isinstance(dim, dimension.Dimension):
2361 raise TypeError, "Invalid Dimension type: " + `type(dim)`
2362 self.__dim = dim
2364 def getDimension(self):
2365 """Retrieve the stored dimension object from the tool.
2367 getDimension()
2369 This method returns the stored Dimension or None.
2371 return self.__dim
2373 def setDimPrefs(self, image):
2374 """Apply the current dimension options to the stored dimension.
2376 setDimPrefs(image)
2378 The argument 'image' is an image option in which the current dimension
2379 preferences are retrieved.
2381 _dim = self.__dim
2382 if _dim is None:
2383 raise ValueError, "No dimension stored in tool."
2384 _pds = _dim.getPrimaryDimstring()
2385 _pds.mute()
2386 _pds.setFamily(image.getOption('DIM_PRIMARY_FONT_FAMILY'))
2387 _pds.setWeight(image.getOption('DIM_PRIMARY_FONT_WEIGHT'))
2388 _pds.setStyle(image.getOption('DIM_PRIMARY_FONT_STYLE'))
2389 _pds.setColor(image.getOption('DIM_PRIMARY_FONT_COLOR'))
2390 _pds.setSize(image.getOption('DIM_PRIMARY_TEXT_SIZE'))
2391 _pds.setAlignment(image.getOption('DIM_PRIMARY_TEXT_ALIGNMENT'))
2392 _pds.setPrecision(image.getOption('DIM_PRIMARY_PRECISION'))
2393 _pds.setUnits(image.getOption('DIM_PRIMARY_UNITS'))
2394 _pds.setPrintZero(image.getOption('DIM_PRIMARY_LEADING_ZERO'))
2395 _pds.setPrintDecimal(image.getOption('DIM_PRIMARY_TRAILING_DECIMAL'))
2396 _pds.unmute()
2397 _sds = _dim.getSecondaryDimstring()
2398 _sds.mute()
2399 _sds.setFamily(image.getOption('DIM_SECONDARY_FONT_FAMILY'))
2400 _sds.setWeight(image.getOption('DIM_SECONDARY_FONT_WEIGHT'))
2401 _sds.setStyle(image.getOption('DIM_SECONDARY_FONT_STYLE'))
2402 _sds.setColor(image.getOption('DIM_SECONDARY_FONT_COLOR'))
2403 _sds.setSize(image.getOption('DIM_SECONDARY_TEXT_SIZE'))
2404 _sds.setAlignment(image.getOption('DIM_SECONDARY_TEXT_ALIGNMENT'))
2405 _sds.setPrecision(image.getOption('DIM_SECONDARY_PRECISION'))
2406 _sds.setUnits(image.getOption('DIM_SECONDARY_UNITS'))
2407 _sds.setPrintZero(image.getOption('DIM_SECONDARY_LEADING_ZERO'))
2408 _sds.setPrintDecimal(image.getOption('DIM_SECONDARY_TRAILING_DECIMAL'))
2409 _sds.unmute()
2410 _dim.setOffset(image.getOption('DIM_OFFSET'))
2411 _dim.setExtension(image.getOption('DIM_EXTENSION'))
2412 _dim.setColor(image.getOption('DIM_COLOR'))
2413 _dim.setPosition(image.getOption('DIM_POSITION'))
2414 _dim.setEndpointType(image.getOption('DIM_ENDPOINT'))
2415 _dim.setEndpointSize(image.getOption('DIM_ENDPOINT_SIZE'))
2416 _dim.setDualDimMode(image.getOption('DIM_DUAL_MODE'))
2418 def reset(self):
2419 """Restore the tool to its initial state.
2421 reset()
2423 This method sets the DimensionTool dimension to None.
2425 super(DimensionTool, self).reset()
2426 self.__dim = None
2428 class LinearDimensionTool(DimensionTool):
2429 """A specialized tool for drawing LinearDimension objects.
2431 The LinearDimensionTool is derived from the DimensionTool and
2432 Tool, class, so it shares all the attributes and methods of those classes.
2433 The LinearDimensionTool class has the following addtional methods:
2435 {set/get}FirstPoint(): Set/Get the first point in the LinearDimension
2436 {set/get}SecondPoint(): Set/Get the second point in the LinearDimension.
2437 {set/get}DimPosition(): Set/Get the location of the dimension text.
2439 def __init__(self):
2440 super(LinearDimensionTool, self).__init__()
2441 self.__p1 = None
2442 self.__p2 = None
2443 self.__position = None
2445 def setFirstPoint(self, p):
2446 """Store the first point for the LinearDimension.
2448 setFirstPoint(p):
2450 The argument 'p' must be a Point instance.
2452 if not isinstance (p, Point):
2453 raise TypeError, "Invalid Point: " + `type(p)`
2454 if p.getParent() is None:
2455 raise ValueError, "Point not found in a Layer."
2456 self.__p1 = p
2458 def getFirstPoint(self):
2459 """Return the first point for the LinearDimension.
2461 getFirstPoint()
2463 This method returns a tuple of two objects: the first object
2464 is a Layer, the second object is a Point.
2466 return self.__p1
2468 def setSecondPoint(self, p):
2469 """Store the second point for the LinearDimension.
2471 setSecondPoint(p):
2473 The argument 'p' must be a Point instance.
2475 if self.__p1 is None:
2476 raise ValueError, "First point not set for LinearDimension."
2477 if not isinstance (p, Point):
2478 raise TypeError, "Invalid Point: " + `type(p)`
2479 if p.getParent() is None:
2480 raise ValueError, "Point not found in a Layer."
2481 self.__p2 = p
2483 def getSecondPoint(self):
2484 """Return the second point for the LinearDimension.
2486 getSecondPoint()
2488 This method returns a tuple of two objects: the first object
2489 is a Layer, the second object is a Point.
2491 return self.__p2
2493 def setDimPosition(self, x, y):
2494 """Store the point where the dimension text will be located.
2496 setDimPosition(x, y)
2498 Arguments 'x' and 'y' should be float values.
2500 _x = util.get_float(x)
2501 _y = util.get_float(y)
2502 self.__position = (_x, _y)
2504 def getDimPosition(self):
2505 """Retrieve where the dimension text should be placed.
2507 getDimPosition()
2509 This method returns a tuple containing the x/y coodindates defined
2510 by the setDimPosition() call, or None if that method has not been invoked.
2512 return self.__position
2514 def reset(self):
2515 """Restore the tool to its initial state.
2517 reset()
2519 This method extends the reset() methods of its base classes.
2521 super(LinearDimensionTool, self).reset()
2522 self.__p1 = None
2523 self.__p2 = None
2524 self.__position = None
2526 def makeDimension(self, image):
2527 """Create a LinearDimension based on the stored tool values.
2529 makeDimension(image)
2531 The argument 'image' is an image object where the dimension will be used.
2533 _p1 = self.__p1
2534 _p2 = self.__p2
2535 _x, _y = self.__position
2536 if (_p1 is not None and
2537 _p2 is not None and
2538 _x is not None and
2539 _y is not None):
2540 _ds = image.getOption('DIM_STYLE')
2541 _ldim = dimension.LinearDimension(_p1, _p2, _x, _y, _ds)
2542 self._setDimension(_ldim)
2543 self.setDimPrefs(image)
2545 def create(self, image):
2546 """Create a new LinearDimension and add it to the image.
2548 create(image)
2550 This method overrides the Tool::create() method.
2552 _ldim = self.getDimension()
2553 if _ldim is not None:
2554 _pds = _ldim.getPrimaryDimstring()
2555 _pds.mute()
2556 try:
2557 _pds.setPrefix(image.getOption('DIM_PRIMARY_PREFIX'))
2558 _pds.setSuffix(image.getOption('DIM_PRIMARY_SUFFIX'))
2559 finally:
2560 _pds.unmute()
2561 _sds = _ldim.getSecondaryDimstring()
2562 _sds.mute()
2563 try:
2564 _sds.setPrefix(image.getOption('DIM_SECONDARY_PREFIX'))
2565 _sds.setSuffix(image.getOption('DIM_SECONDARY_SUFFIX'))
2566 finally:
2567 _sds.unmute()
2568 _ldim.calcDimValues()
2569 image.addObject(_ldim)
2570 self.reset()
2572 class HorizontalDimensionTool(LinearDimensionTool):
2573 """A specialized tool for drawing HorizontalDimension objects.
2575 The HorizontalDimensionTool is derived from the LinearDimensionTool
2576 and the Tool classes, so it shares all the attributes and methods of
2577 those class.
2579 There are no additional methods for the HorizontalDimension class.
2581 def __init__(self):
2582 super(HorizontalDimensionTool, self).__init__()
2584 def makeDimension(self, image):
2585 """Create a HorizontalDimension based on the stored tool values.
2587 makeDimension(image)
2589 The argument 'image' is an image object where the dimension willbe used.
2591 _p1 = self.getFirstPoint()
2592 _p2 = self.getSecondPoint()
2593 _x, _y = self.getDimPosition()
2594 if (_p1 is not None and
2595 _p2 is not None and
2596 _x is not None and
2597 _y is not None):
2598 _ds = image.getOption('DIM_STYLE')
2599 _hdim = dimension.HorizontalDimension(_p1, _p2, _x, _y, _ds)
2600 self._setDimension(_hdim)
2601 self.setDimPrefs(image)
2603 def create(self, image):
2604 """Create a new HorizontalDimension and add it to the image.
2606 create(image)
2608 This method overrides the LinearDimensionTool::create() method.
2610 _hdim = self.getDimension()
2611 if _hdim is not None:
2612 _pds = _hdim.getPrimaryDimstring()
2613 _pds.mute()
2614 try:
2615 _pds.setPrefix(image.getOption('DIM_PRIMARY_PREFIX'))
2616 _pds.setSuffix(image.getOption('DIM_PRIMARY_SUFFIX'))
2617 finally:
2618 _pds.unmute()
2619 _sds = _hdim.getSecondaryDimstring()
2620 _sds.mute()
2621 try:
2622 _sds.setPrefix(image.getOption('DIM_SECONDARY_PREFIX'))
2623 _sds.setSuffix(image.getOption('DIM_SECONDARY_SUFFIX'))
2624 finally:
2625 _sds.unmute()
2626 _hdim.calcDimValues()
2627 image.addObject(_hdim)
2628 self.reset()
2630 class VerticalDimensionTool(LinearDimensionTool):
2631 """A specialized tool for drawing VerticalDimension objects.
2633 The VerticalalDimensionTool is derived from the LinearDimensionTool
2634 and the Tool classes, so it shares all the attributes and methods of
2635 those class.
2637 There are no additional methods for the VerticalalDimension class.
2639 def __init__(self):
2640 super(VerticalDimensionTool, self).__init__()
2642 def makeDimension(self, image):
2643 """Create a VerticalDimension based on the stored tool values.
2645 makeDimension(image)
2647 The argument 'image' is an image object where the dimension will be used.
2649 _p1 = self.getFirstPoint()
2650 _p2 = self.getSecondPoint()
2651 _x, _y = self.getDimPosition()
2652 if (_p1 is not None and
2653 _p2 is not None and
2654 _x is not None and
2655 _y is not None):
2656 _ds = image.getOption('DIM_STYLE')
2657 _vdim = dimension.VerticalDimension(_p1, _p2, _x, _y, _ds)
2658 self._setDimension(_vdim)
2659 self.setDimPrefs(image)
2661 def create(self, image):
2662 """Create a new VerticalDimension and add it to the image.
2664 create(image)
2666 This method overrides the LinearDimensionTool::create() method.
2668 _vdim = self.getDimension()
2669 if _vdim is not None:
2670 _pds = _vdim.getPrimaryDimstring()
2671 _pds.mute()
2672 try:
2673 _pds.setPrefix(image.getOption('DIM_PRIMARY_PREFIX'))
2674 _pds.setSuffix(image.getOption('DIM_PRIMARY_SUFFIX'))
2675 finally:
2676 _pds.unmute()
2677 _sds = _vdim.getSecondaryDimstring()
2678 _sds.mute()
2679 try:
2680 _sds.setPrefix(image.getOption('DIM_SECONDARY_PREFIX'))
2681 _sds.setSuffix(image.getOption('DIM_SECONDARY_SUFFIX'))
2682 finally:
2683 _sds.unmute()
2684 _vdim.calcDimValues()
2685 image.addObject(_vdim)
2686 self.reset()
2688 class RadialDimensionTool(DimensionTool):
2689 """A specialized tool for drawing RadialDimension objects.
2691 The RadialDimensionTool class is derived from the DimensionTool class
2692 and Tool class, so it shares all the attributes and methods of those
2693 classes. The RadialDimensionTool class has the following additional
2694 methods:
2696 {set/get}DimObject(): Set/Get the circular object being dimensioned.
2697 {set/get}DimPosition(): Set/Get the location of the dimension text.
2699 def __init__(self):
2700 super(RadialDimensionTool, self).__init__()
2701 self.__cobj = None
2702 self.__position = None
2704 def setDimObject(self, cobj):
2705 """Store the Circle or Arc that the RadialDimension will describe.
2707 setDimObject(cobj):
2709 The argument 'cobj' must be either a Circle or Arc instance.
2711 if not isinstance (cobj, (Circle, Arc)):
2712 raise TypeError, "Invalid Circle or Arc: " + `type(cobj)`
2713 if cobj.getParent() is None:
2714 raise ValueError, "Circle/Arc not found in a Layer."
2715 self.__cobj = cobj
2717 def getDimObject(self):
2718 """Return the object the RadialDimension will define.
2720 getDimObject()
2722 This method returns a tuple of two objects: the first object
2723 is a Layer, the second object is either a Circle or an Arc
2725 return self.__cobj.getParent(), self.__cobj
2727 def setDimPosition(self, x, y):
2728 """Store the point where the dimension text will be located.
2730 setDimPosition(x, y)
2732 Arguments 'x' and 'y' should be float values.
2734 _x = util.get_float(x)
2735 _y = util.get_float(y)
2736 self.__position = (_x, _y)
2738 def getDimPosition(self):
2739 """Retrieve where the dimension text should be placed.
2741 getDimPosition()
2743 This method returns a tuple containing the x/y coodindates defined
2744 by the setDimPosition() call, or None if that method has not been
2745 invoked.
2747 return self.__position
2749 def reset(self):
2750 """Restore the tool to its initial state.
2752 reset()
2754 This method extends the reset() methods of its base classes.
2756 super(RadialDimensionTool, self).reset()
2757 self.__cobj = None
2758 self.__position = None
2760 def makeDimension(self, image):
2761 """Create a RadialDimension based on the stored tool values.
2763 makeDimension(image)
2765 The argument 'image' is an image object where the dimension will
2766 be used.
2768 _cobj = self.__cobj
2769 _x, _y = self.__position
2770 if (_cobj is not None and
2771 _x is not None and
2772 _y is not None):
2773 _ds = image.getOption('DIM_STYLE')
2774 _rdim = dimension.RadialDimension(_cobj, _x, _y, _ds)
2775 self._setDimension(_rdim)
2776 self.setDimPrefs(image)
2778 def create(self, image):
2779 """Create a new RadialDimension and add it to the image.
2781 create(image)
2783 This method overrides the Tool::create() method.
2785 _rdim = self.getDimension()
2786 if _rdim is not None:
2787 _pds = _rdim.getPrimaryDimstring()
2788 _pds.mute()
2789 try:
2790 _pds.setPrefix(image.getOption('RADIAL_DIM_PRIMARY_PREFIX'))
2791 _pds.setSuffix(image.getOption('RADIAL_DIM_PRIMARY_SUFFIX'))
2792 finally:
2793 _pds.unmute()
2794 _sds = _rdim.getSecondaryDimstring()
2795 _sds.mute()
2796 try:
2797 _sds.setPrefix(image.getOption('RADIAL_DIM_SECONDARY_PREFIX'))
2798 _sds.setSuffix(image.getOption('RADIAL_DIM_SECONDARY_SUFFIX'))
2799 finally:
2800 _sds.unmute()
2801 _rdim.setDiaMode(image.getOption('RADIAL_DIM_DIA_MODE'))
2802 _rdim.calcDimValues()
2803 image.addObject(_rdim)
2804 self.reset()
2806 class AngularDimensionTool(LinearDimensionTool):
2807 """A specialized tool for drawing AngularDimension objects.
2809 The AngularDimensionTool class is derived from the LinearDimensionTool
2810 class, so it shares all the attributes and methods of that class. The
2811 AngularDimensionTool class has the following additional methods:
2813 {set/get}VertexPoint(): Set/Get the vertex point used by the dimension
2815 def __init__(self):
2816 super(AngularDimensionTool, self).__init__()
2817 self.__vp = None
2819 def setVertexPoint(self, p):
2820 """Store the vertex point for the AngularDimension.
2822 setVertexPoint(p):
2824 The argument 'p' must be a Point instance.
2826 if not isinstance (p, Point):
2827 raise TypeError, "Invalid Point: " + `type(p)`
2828 if p.getParent() is None:
2829 raise ValueError, "Point not found in layer."
2830 self.__vp = p
2832 def getVertexPoint(self):
2833 """Return the vertex point for the AngularDimension.
2835 getVertexPoint()
2837 This method returns a tuple of two objects: the first object
2838 is a Layer, the second object is a Point.
2841 return self.__vp
2843 def reset(self):
2844 """Restore the tool to its initial state.
2846 reset()
2848 This method extends LinearDimensionTool::reset().
2850 super(AngularDimensionTool, self).reset()
2851 self.__vp = None
2853 def makeDimension(self, image):
2854 """Create an AngularDimension based on the stored tool values.
2856 makeDimension(image)
2858 The argument 'image' is an image object where the dimension will be used.
2860 _vp = self.__vp
2861 _p1 = self.getFirstPoint()
2862 _p2 = self.getSecondPoint()
2863 _x, _y = self.getDimPosition()
2864 if (_vp is not None and
2865 _p1 is not None and
2866 _p2 is not None and
2867 _x is not None and
2868 _y is not None):
2869 _ds = image.getOption('DIM_STYLE')
2870 _adim = dimension.AngularDimension(_vp, _p1, _p2, _x, _y, _ds)
2871 self._setDimension(_adim)
2872 self.setDimPrefs(image)
2874 def create(self, image):
2875 """Create a new AngularDimension and add it to the image.
2877 create(image)
2879 This method overrides the Tool::create() method.
2881 _adim = self.getDimension()
2882 if _adim is not None:
2883 _pds = _adim.getPrimaryDimstring()
2884 _pds.mute()
2885 try:
2886 _pds.setPrefix(image.getOption('ANGULAR_DIM_PRIMARY_PREFIX'))
2887 _pds.setSuffix(image.getOption('ANGULAR_DIM_PRIMARY_SUFFIX'))
2888 finally:
2889 _pds.unmute()
2890 _sds = _adim.getSecondaryDimstring()
2891 _sds.mute()
2892 try:
2893 _sds.setPrefix(image.getOption('ANGULAR_DIM_SECONDARY_PREFIX'))
2894 _sds.setSuffix(image.getOption('ANGULAR_DIM_SECONDARY_SUFFIX'))
2895 finally:
2896 _sds.unmute()
2897 _adim.calcDimValues()
2898 image.addObject(_adim)
2899 self.reset()
2902 # printing/plotting tools
2905 class PlotTool(Tool):
2906 """A tool for defining plot regions
2908 def __init__(self):
2909 super(PlotTool, self).__init__()
2910 self.__c1 = None
2911 self.__c2 = None
2913 def reset(self):
2914 """Restore the tool to its initial state.
2916 reset()
2918 This method extends Tool::reset().
2920 super(PlotTool, self).reset()
2921 self.__c1 = None
2922 self.__c2 = None
2924 def setFirstCorner(self, x, y):
2925 """Store the first corner of the plot region.
2927 setFirstCorner(x, y)
2929 Arguments 'x' and 'y' should be floats.
2931 _x = util.get_float(x)
2932 _y = util.get_float(y)
2933 self.__c1 = (_x, _y)
2935 def getFirstCorner(self):
2936 """Return the first corner of the plot region.
2938 getFirstCorner()
2940 if self.__c1 is None:
2941 raise ValueError, "First corner not defined"
2942 return self.__c1
2944 def setSecondCorner(self, x, y):
2945 """Store the second corner of the plot region.
2947 setSecondCorner(x, y)
2949 Arguments 'x' and 'y' should be floats.
2951 _x = util.get_float(x)
2952 _y = util.get_float(y)
2953 self.__c2 = (_x, _y)
2955 def getSecondCorner(self):
2956 """Return the second corner of the plot region.
2958 getSecondCorner()
2960 if self.__c2 is None:
2961 raise ValueError, "Second corner not defined"
2962 return self.__c2
2965 # entity modification tools
2968 class RegionTool(Tool):
2969 """A base class for a tool that can store a defined region.
2971 The RegionTool class is designed to be a base class for tools that
2972 need to store a rectangular region. The RegionTool class is derived
2973 from the Tool class, so it shares all the attributes and methods of
2974 that classs. The RegionTool class has the following additional methods:
2976 {set/get}Region(): Set/Get a stored rectangular region
2978 def __init__(self):
2979 super(RegionTool, self).__init__()
2980 self.__region = None
2982 def setRegion(self, xmin, ymin, xmax, ymax):
2983 """Store a rectangular region in the RegionTool.
2985 setRegion(xmin, ymin, xmax, ymax)
2987 xmin: The minimum x-coordinate value
2988 ymin: The minimum y-coordinate value
2989 xmax: The maximum x-coordinate value
2990 ymax: The maximum y-coordinate value
2992 All the arguments should be floats. If xmax < xmin or ymax < ymin
2993 a ValueError exception is raised.
2995 _xmin = util.get_float(xmin)
2996 _ymin = util.get_float(ymin)
2997 _xmax = util.get_float(xmax)
2998 if _xmax < _xmin:
2999 raise ValueError, "Invalid values: xmax < xmin"
3000 _ymax = util.get_float(ymax)
3001 if _ymax < _ymin:
3002 raise ValueError, "Invalid values: ymax < ymin"
3003 self.__region = (_xmin, _ymin, _xmax, _ymax)
3005 def getRegion(self):
3006 """Retrieve the stored rectangular region in the tool.
3008 getRegion()
3010 This method returns a tuple containing four float values:
3012 (xmin, ymin, xmax, ymax)
3014 return self.__region
3016 def reset(self):
3017 """Restore the tool to its initial state.
3019 reset()
3021 This method resets the RegionTool region to None.
3023 super(RegionTool, self).reset()
3024 self.__region = None
3026 class MoveTool(RegionTool):
3027 """A specialized class for moving objects.
3029 The MoveTool class is derived from the Tool and RegionTool classes,
3030 so it shares all the attributes and methods of those classes. The
3031 MoveTool class has the following additional methods:
3033 {set/get}Distance(): Set/Get the values to move objects.
3035 def __init__(self):
3036 super(MoveTool, self).__init__()
3037 self.__distance = None
3039 def setDistance(self, x, y):
3040 """Store the distance to move objects in the tool.
3042 setDistance(x, y)
3044 Arguments 'x' and 'y' should be floats, and represent the amount
3045 to move objects in the x-coordinate and y-coordinate axes.
3047 _x = util.get_float(x)
3048 _y = util.get_float(y)
3049 self.__distance = (_x, _y)
3051 def getDistance(self):
3052 """Get the displacement stored in the tool.
3054 getDistance()
3056 This method returns a tuple containing two float values.
3058 (xdisp, ydisp)
3060 If this method is called before the setDistance() method is
3061 used, a ValueError exception is raised.
3063 if self.__distance is None:
3064 raise ValueError, "No distance stored in tool."
3065 return self.__distance
3067 def reset(self):
3068 """Restore the tool to its initial state.
3070 reset()
3072 This method extends the reset() method of its base classes
3074 super(MoveTool, self).reset()
3075 self.__distance = None
3077 class HorizontalMoveTool(MoveTool):
3078 """A specialized class for moving objects horizontally.
3080 The HorizontalMoveTool is derived from the MoveTool class, so
3081 it shares all the attributes and methods of that class.
3083 There are no additional methods for this class.
3085 def __init__(self):
3086 super(HorizontalMoveTool, self).__init__()
3088 def setDistance(self, x, y):
3089 """Store the distance to move objects in the tool.
3091 setDistance(x, y)
3093 This method extends the MoveTool::setDistance() method by
3094 enforcing a y-axis displacement of 0.0
3096 _x = util.get_float(x)
3097 super(HorizontalMoveTool, self).setDistance(_x, 0.0)
3099 class VerticalMoveTool(MoveTool):
3100 """A specialized class for moving objects vertically.
3102 The VerticalMoveTool is derived from the MoveTool class, so
3103 it shares all the attributes and methods of that class.
3105 There are no additional methods for this class.
3108 def __init__(self):
3109 super(VerticalMoveTool, self).__init__()
3111 def setDistance(self, x, y):
3112 """Store the distance to move objects in the tool.
3114 setDistance(x, y)
3116 This method extends the MoveTool::setDistance() method by
3117 enforcing an x-axis displacement of 0.0
3119 _y = util.get_float(y)
3120 super(VerticalMoveTool, self).setDistance(0.0, _y)
3122 class StretchTool(MoveTool):
3123 """A specialized class for stretching objects
3125 The StretchTool class is derived from the MoveTool class, so
3126 it shares all the attributes and methods of that class.
3128 There are no additional methods for this class.
3130 def __init__(self):
3131 super(StretchTool, self).__init__()
3133 class HorizontalStretchTool(HorizontalMoveTool):
3134 """A specialized class for stretching objects horizontally.
3136 The HorizontalStretchTool class is derived from the HorizontalMoveTool
3137 class, so it shares all the attributes and methods of that class.
3139 There are no additional methods for this class.
3141 def __init__(self):
3142 super(HorizontalStretchTool, self).__init__()
3144 class VerticalStretchTool(VerticalMoveTool):
3145 """A specialized class for stretching objects horizontally.
3147 The VerticalStretchTool class is derived from the VerticalMoveTool
3148 class, so it shares all the attributes and methods of that class.
3150 There are no additional methods for this class.
3152 def __init__(self):
3153 super(VerticalStretchTool, self).__init__()
3155 class DeleteTool(RegionTool):
3156 """A specialized class for deleting objects.
3158 The DeleteTool class is derived from the Tool and RegionTool classes,
3159 so it shares all the attributes and methods of those classes.
3161 There are no additional methods for this class.
3163 def __init__(self):
3164 super(DeleteTool, self).__init__()
3166 class SplitTool(RegionTool):
3167 """A specialized class for splitting objects.
3169 The SplitTool class is derived from the Tool and RegionTool classes,
3170 so it shares all the attributes and methods of those classes.
3172 There are no additional methods for this class.
3174 def __init__(self):
3175 super(SplitTool, self).__init__()
3177 class MirrorTool(RegionTool):
3178 """A specialized class for mirroring objects.
3180 The MirrorTool class is derived from the Tool and RegionTool classes,
3181 so it shares all the attributes and methods of those classes. The
3182 MirrorTool class has the following additional methods:
3184 {set/get}MirrorLine(): Set/Get the construction line used for mirroring
3186 def __init__(self):
3187 super(MirrorTool, self).__init__()
3188 self.__mirrorline = None
3190 def setMirrorLine(self, mline):
3191 """Store the mirroring construction line in the tool.
3193 setMirrorLine(mline)
3195 The argument 'mline' must be a construction line, otherwise
3196 a TypeError exception is raised.
3198 if not isinstance(mline, (HCLine, VCLine, ACLine, CLine)):
3199 raise TypeError, "Invalid mirroring line type: " + `type(mline)`
3200 self.__mirrorline = mline
3202 def getMirrorLine(self):
3203 """Retrieve the mirroring construction line from the tool.
3205 getMirrorLine()
3207 If the mirroring construction line has not been set, a ValueError
3208 exception is raised.
3210 if self.__mirrorline is None:
3211 raise ValueError, "No mirror line set."
3212 return self.__mirrorline
3214 def reset(self):
3215 """Restore the tool to its initial state.
3217 reset()
3219 This method extends the RegionTool::reset() method.
3221 super(MirrorTool, self).reset()
3222 self.__mirrorline = None
3225 # The TransferTool class is a subclass of the RegionTool
3226 # with no additional functionality (yet)
3229 class TransferTool(RegionTool):
3230 pass
3232 class RotateTool(RegionTool):
3233 """A specialized class for rotating objects.
3235 The RotateTool class is derived from the Tool and RegionTool classes,
3236 so it shares all the attributes and methods of those classes. The
3237 Rotateool class has the following additional methods:
3239 {set/get}RotationPoint(): Set/Get the point objects are rotated around
3240 {set/get}Angle(): Set/Get the angle of rotation
3242 def __init__(self):
3243 super(RotateTool, self).__init__()
3244 self.__rp = None
3245 self.__angle = None
3247 def setRotationPoint(self, x, y):
3248 """Set the coordinates the entities will rotate around
3250 setRotationPoint(x, y)
3252 Arguments 'x' and 'y' should be floats
3254 _x = util.get_float(x)
3255 _y = util.get_float(y)
3256 self.__rp = (_x, _y)
3258 def getRotationPoint(self):
3259 """Get the point objects will rotate around
3261 getRotationPoint()
3263 This method returns a tuple of two floats or None if the rotation
3264 point has not be defined with setRotationPoint()
3266 return self.__rp
3268 def setAngle(self, angle):
3269 """Set the angle of rotation.
3271 setAngle(angle)
3273 Argument 'angle' should be a float value. The value is normalized
3274 so that abs(angle) < 360.0.
3276 _a = util.get_float(angle)
3277 self.__angle = math.fmod(_a, 360.0)
3279 def getAngle(self):
3280 """Get the angle of rotation.
3282 getAngle()
3284 This method returns a float or None if the angle has not been
3285 set with setAngle()
3287 return self.__angle
3289 def reset(self):
3290 """Restore the tool to its initial state.
3292 reset()
3294 This method extends Tool::reset().
3296 super(RotateTool, self).reset()
3297 self.__rp = None
3298 self.__angle = None
3300 class GraphicObjectTool(RegionTool):
3301 """A specialized class for changing attributes of GraphicObject instances.
3303 The GraphicObjectTool class is derived from the RegionTool class,
3304 so it shares all the attributes and methods of that class. The
3305 GraphicObjectTool class has the following additional methods:
3307 {set/get}Attribute(): Set/Get the desired attribute
3308 {set/get}Value(): Set/Get the new entity color.
3310 def __init__(self):
3311 super(RegionTool, self).__init__()
3312 self.__attr = None
3313 self.__value = None
3315 def setAttribute(self, attr):
3316 """Define which attribute the tool is modifying.
3318 setAttribute(attr)
3320 Argument 'attr' should be either 'setStyle', 'setLinetype', 'setColor',
3321 or 'setThickness'
3323 if not isinstance(attr, str):
3324 raise TypeError, "Invalid attribute type: " + `type(attr)`
3325 if attr not in ('setStyle', 'setLinetype', 'setColor', 'setThickness'):
3326 raise ValueError, "Invalid attribute: " + attr
3327 self.__attr = attr
3329 def getAttribute(self):
3330 """Return the specified attribute.
3332 getAttribute()
3334 If called before invoking setAttribute(), this method raises a ValueError.
3336 if self.__attr is None:
3337 raise ValueError, "Tool attribute not defined."
3338 return self.__attr
3340 def setValue(self, val):
3341 """Store the new value of the entity attribute.
3343 setValue(val)
3345 Argument 'val' depends on the type of attribute defined for the
3346 tool. If no attribute is defined this method raises a ValueError.
3347 Invoking this method with 'None' as an argument sets the tool
3348 to restore the default attribute value.
3350 if self.__attr is None:
3351 raise ValueError, "Tool attribute not defined."
3352 _a = self.__attr
3353 _val = None
3354 if val is not None:
3355 if _a == 'setStyle':
3356 if not isinstance(val, style.Style):
3357 raise TypeError, "Invalid Style: " + `type(val)`
3358 _val = val
3359 elif _a == 'setLinetype':
3360 if not isinstance(val, linetype.Linetype):
3361 raise TypeError, "Invalid Linetype: " + `type(val)`
3362 _val = val
3363 elif _a == 'setColor':
3364 if not isinstance(val, color.Color):
3365 raise TypeError, "Invalid Color: " + `type(val)`
3366 _val = val
3367 elif _a == 'setThickness':
3368 _val = util.get_float(val)
3369 if _val < 0.0:
3370 raise ValueError, "Invalid thickness: %g" % _val
3371 else:
3372 raise ValueError, "Unexpected attribute: " + _a
3373 self.__value = _val
3375 def getValue(self):
3376 """Get the stored attribute value.
3378 getValue()
3380 This method returns the value stored in setValue() or None.
3382 return self.__value
3388 class ChangeStyleTool(GraphicObjectTool):
3389 pass
3391 class ChangeLinetypeTool(GraphicObjectTool):
3392 pass
3394 class ChangeColorTool(GraphicObjectTool):
3395 pass
3397 class ChangeThicknessTool(GraphicObjectTool):
3398 pass
3400 class TextTool(RegionTool):
3401 """A specialized class for entering text.
3403 The TextTool class is derived from the Tool class, so it shares
3404 the attributes and methods of that class. The TextTool class also
3405 has the following additional methods:
3407 {set/get}Text(): Set/Get the text string in the tool.
3408 hasText(): Test if the tool has stored a text string
3409 {set/get}TextLocation(): Set/Get where the text is to be placed.
3410 {set/get}TextBlock(): Set/Get a TextBlock instance in the Tool
3411 {set/get}Bounds(): Set/Get the width and height of the text
3412 {set/get}PixelSize(): Set/Get the a rectangular region bounding the text.
3413 {set/get}Layout(): Set/Get the formatted text string display.
3414 {set/get/test}Attribute(): Set/Get/Test a TextBlock attribute
3415 {set/get}Value(): Set/Get the attribute value.
3417 def __init__(self):
3418 super(TextTool, self).__init__()
3419 self.__text = None
3420 self.__location = None
3421 self.__tblock = None
3422 self.__attr = None
3423 self.__value = None
3424 self.__bounds = None
3425 self.__pixel_size = None
3426 self.__layout = None
3428 def setText(self, text):
3429 """Store some text in the tool.
3431 setText(text)
3433 The argument 'text' should be a unicode object.
3435 _text = text
3436 if not isinstance(_text, unicode):
3437 _text = unicode(text)
3438 self.__text = _text
3440 def getText(self):
3441 """Retrieve the stored text from the TextTool.
3443 getText()
3445 If no text has been stored, this method raises a ValueError exception.
3447 if self.__text is None:
3448 raise ValueError, "No text stored in TextTool."
3449 return self.__text
3451 def hasText(self):
3452 """Test if the tool has stored a text string.
3454 hasText()
3456 return self.__text is not None
3458 def setTextLocation(self, x, y):
3459 """Store the location where the text will be placed.
3461 setTextLocation(x, y)
3463 The arguments 'x' and 'y' should be float values.
3465 _x, _y = util.make_coords(x, y)
3466 self.__location = (_x, _y)
3468 def getTextLocation(self):
3469 """Retrieve the location where the text will be placed.
3471 getTextLocation()
3473 This method returns a tuple holding two floats:
3475 (x, y)
3478 A ValueError exception is raised if this method is called prior to
3479 setting the text location with setTextLocation().
3481 if self.__location is None:
3482 raise ValueError, "No text location defined."
3483 return self.__location
3485 def testAttribute(self, attr):
3486 """Test that the given attribute is valid.
3488 testAttribute(attr)
3490 Argument 'attr' should be one of the following: 'setAngle',
3491 'setAlignment', 'setFamily', 'setStyle', 'setWeight', 'setColor',
3492 or 'setSize'
3494 if not isinstance(attr, str):
3495 raise TypeError, "Invalid attribute type: " + `type(attr)`
3496 return attr in ('setAngle', 'setAlignment', 'setFamily',
3497 'setStyle', 'setWeight', 'setColor', 'setSize')
3499 def setAttribute(self, attr):
3500 """Define which attribute the tool is modifying.
3502 setAttribute(attr)
3504 Argument 'attr' should be one of the following: 'setAngle',
3505 'setAlignment', 'setFamily', 'setStyle', 'setWeight', 'setColor',
3506 or 'setSize'
3508 if not self.testAttribute(attr):
3509 raise ValueError, "Invalid attribute: " + attr
3510 self.__attr = attr
3512 def getAttribute(self):
3513 """Return the specified attribute.
3515 getAttribute()
3517 If called before invoking setAttribute(), this method raises a ValueError.
3519 if self.__attr is None:
3520 raise ValueError, "Tool attribute not defined."
3521 return self.__attr
3523 def testValue(self, val):
3524 """Test that the given value is valid for the preset attribute.
3526 testValue(val)
3528 Argument 'val' depends on what attribute has been set with via setAttribute().
3530 _a = self.__attr
3531 if _a == 'setAngle':
3532 _val = util.getFloat(val)
3533 elif _a == 'setAlignment':
3534 if not isinstance(val, int):
3535 raise TypeError, "Invalid alignment type: " + `type(val)`
3536 if (val != TextStyle.ALIGN_LEFT and
3537 val != TextStyle.ALIGN_CENTER and
3538 val != TextStyle.ALIGN_RIGHT):
3539 raise ValueError, "Invalid alignment value: %d" % val
3540 _val = val
3541 elif _a == 'setColor':
3542 if not isinstance(val, color.Color):
3543 raise TypeError, "Invalid Color: " + `type(val)`
3544 _val = val
3545 elif _a == 'setFamily':
3546 if not isinstance(val, types.StringTypes):
3547 raise TypeError, "Invalid family type: " + `type(val)`
3548 _val = val
3549 elif _a == 'setStyle':
3550 if not isinstance(val, int):
3551 raise TypeError, "Invalid style type: " + `type(val)`
3552 if (val != TextStyle.FONT_NORMAL and
3553 val != TextStyle.FONT_OBLIQUE and
3554 val != TextStyle.FONT_ITALIC):
3555 raise ValueError, "Invalid style value: %d" % val
3556 _val = val
3557 elif _a == 'setWeight':
3558 if not isinstance(val, int):
3559 raise TypeError, "Invalid weight type: " + `type(val)`
3560 if (val != TextStyle.WEIGHT_NORMAL and
3561 val != TextStyle.WEIGHT_LIGHT and
3562 val != TextStyle.WEIGHT_BOLD and
3563 val != TextStyle.WEIGHT_HEAVY):
3564 raise ValueError, "Invalid weight value: %d" % val
3565 _val = val
3566 elif _a == 'setSize':
3567 _val = util.get_float(val)
3568 if _val < 0.0:
3569 raise ValueError, "Invalid size: %g" % _val
3570 else:
3571 raise ValueError, "Unexpected attribute: " + _a
3572 return _val
3574 def setValue(self, val):
3575 """Store the new value of the entity attribute.
3577 setValue(val)
3579 Argument 'val' depends on the type of attribute defined for the
3580 tool. If no attribute is defined this method raises a ValueError.
3581 Invoking this method with 'None' as an argument sets the tool
3582 to restore the default attribute value.
3584 if self.__attr is None:
3585 raise ValueError, "Tool attribute not defined."
3586 _val = None
3587 if val is not None:
3588 _val = self.testValue(val)
3589 self.__value = _val
3591 def getValue(self):
3592 """Get the stored attribute value.
3594 getValue()
3596 This method returns the value stored in setValue() or None.
3598 return self.__value
3600 def getBounds(self):
3601 """Return the width and height of the TextBlock.
3603 getBounds()
3605 if self.__bounds is None:
3606 raise ValueError, "TextBlock bounds not defined."
3607 return self.__bounds
3609 def setBounds(self, width, height):
3610 """Set the width and height of the TextBlock.
3612 setBounds(width, height):
3614 Arguments 'width' and 'height' should be positive float values.
3616 _w = util.get_float(width)
3617 if _w < 0.0:
3618 raise ValueError, "Invalid width: %g" % _w
3619 _h = util.get_float(height)
3620 if _h < 0.0:
3621 raise ValueError, "Invalid height: %g" % _h
3622 self.__bounds = (_w, _h)
3624 def setPixelSize(self, width, height):
3625 """Store a screen-size rectangular boundary for the text.
3627 setPixelSize(width, height)
3629 Arguments 'width' and 'height' should be positive integer values.
3631 This method is somewhat GTK specific ...
3633 _width = width
3634 if not isinstance(_width, int):
3635 _width = int(width)
3636 if _width < 0:
3637 raise ValueError, "Invalid width: %d" % _width
3638 _height = height
3639 if not isinstance(_height, int):
3640 _height = int(height)
3641 if _height < 0:
3642 raise ValueError, "Invalid height: %d" % _height
3643 self.__pixel_size = (_width, _height)
3645 def getPixelSize(self):
3646 """Retrieve the stored rectangular region of text.
3648 getPixelSize()
3650 A ValueError exception is raised if this method is called before
3651 the size has been set by setPixelSize()
3653 if self.__pixel_size is None:
3654 raise ValueError, "Pixel size is not defined."
3655 return self.__pixel_size
3657 def setLayout(self, layout):
3658 """Store a formatted layout string for the text.
3660 setLayout()
3662 This method is very GTK/Pango specific ...
3664 self.__layout = layout
3666 def getLayout(self):
3667 """Retrieve the formatted layout for the text string.
3669 getLayout()
3671 This method is very GTK/Pango specific ...
3673 return self.__layout
3675 def setTextBlock(self, tblock):
3676 """Store a TextBlock instance within the Tool.
3678 setTextBlock(tblock)
3680 Argument 'tblock' must be a TextBlock.
3682 if not isinstance(tblock, TextBlock):
3683 raise TypeError, "Invalid TextBlock: " + `type(tblock)`
3684 self.__tblock = tblock
3686 def getTextBlock(self):
3687 """Retrieve a stored TextBlock within the Tool.
3689 getTextBlock()
3691 This method may return None if no TextBlock has been stored
3692 via setTextBlock().
3694 return self.__tblock
3696 def create(self, image):
3697 """Create a new TextBlock and add it to the image.
3699 create(image)
3701 This method overrides the Tool::create() method.
3703 _tb = self.__tblock
3704 if _tb is None:
3705 _text = self.getText()
3706 _x, _y = self.getTextLocation()
3707 _ts = image.getOption('TEXT_STYLE')
3708 _tb = TextBlock(_x, _y, _text, _ts)
3709 _f = image.getOption('FONT_FAMILY')
3710 if _f != _ts.getFamily():
3711 _tb.setFamily(_f)
3712 _s = image.getOption('FONT_STYLE')
3713 if _s != _ts.getStyle():
3714 _tb.setStyle(_s)
3715 _w = image.getOption('FONT_WEIGHT')
3716 if _w != _ts.getWeight():
3717 _tb.setWeight(_w)
3718 _c = image.getOption('FONT_COLOR')
3719 if _c != _ts.getColor():
3720 _tb.setColor(_c)
3721 _sz = image.getOption('TEXT_SIZE')
3722 if abs(_sz - _ts.getSize()) > 1e-10:
3723 _tb.setSize(_sz)
3724 _a = image.getOption('TEXT_ANGLE')
3725 if abs(_a - _ts.getAngle()) > 1e-10:
3726 _tb.setAngle(_a)
3727 _al = image.getOption('TEXT_ALIGNMENT')
3728 if _al != _ts.getAlignment():
3729 _tb.setAlignment(_al)
3730 image.addObject(_tb)
3731 self.reset()
3733 def reset(self):
3734 """Restore the tool to its initial state.
3736 reset()
3738 This method extends Tool::reset().
3740 super(TextTool, self).reset()
3741 self.__text = None
3742 self.__location = None
3743 self.__tblock = None
3744 self.__bounds = None
3745 self.__pixel_size = None
3746 self.__layout = None
3748 class EditDimensionTool(RegionTool):
3749 """A specialized class for changing attributes of Dimension instances.
3751 The EditDimensionTool class is derived from the RegionTool class,
3752 so it shares all the attributes and methods of that class. The
3753 EditDimensionTool class has the following additional methods:
3755 {set/get}Attribute(): Set/Get the desired attribute
3756 {set/get}Value(): Set/Get the new entity color.
3758 def __init__(self):
3759 super(RegionTool, self).__init__()
3760 self.__attr = None
3761 self.__value = None
3763 def setAttribute(self, attr):
3764 """Define which attribute the tool is modifying.
3766 setAttribute(attr)
3768 Argument 'attr' should be one of the following:
3770 'setEndpointType', 'setEndpointSize', 'setDualDimMode', 'setDualModeOffset',
3771 'setOffset', 'setExtension', 'setColor', 'setThickness', 'setScale'
3773 if not isinstance(attr, str):
3774 raise TypeError, "Invalid attribute type: " + `type(attr)`
3775 if attr not in ('setEndpointType', 'setEndpointSize',
3776 'setDualDimMode', 'setDualModeOffset',
3777 'setOffset', 'setExtension', 'setColor',
3778 'setThickness', 'setScale'):
3779 raise ValueError, "Invalid attribute: " + attr
3780 self.__attr = attr
3782 def getAttribute(self):
3783 """Return the specified attribute.
3785 getAttribute()
3787 If called before invoking setAttribute(), this method raises a ValueError.
3789 if self.__attr is None:
3790 raise ValueError, "Tool attribute not defined."
3791 return self.__attr
3793 def setValue(self, val):
3794 """Store the new value of the entity attribute.
3796 setValue(val)
3798 Argument 'val' depends on the type of attribute defined for the
3799 tool. If no attribute is defined this method raises a ValueError.
3800 Invoking this method with 'None' as an argument sets the tool
3801 to restore the default attribute value.
3803 if self.__attr is None:
3804 raise ValueError, "Tool attribute not defined."
3805 _a = self.__attr
3806 _val = None
3807 if val is not None:
3808 if _a == 'setEndpointType':
3809 if (val != dimension.Dimension.DIM_ENDPT_NONE and
3810 val != dimension.Dimension.DIM_ENDPT_ARROW and
3811 val != dimension.Dimension.DIM_ENDPT_FILLED_ARROW and
3812 val != dimension.Dimension.DIM_ENDPT_SLASH and
3813 val != dimension.Dimension.DIM_ENDPT_CIRCLE):
3814 raise ValueError, "Invalid endpoint value: " + str(val)
3815 _val = val
3816 elif _a == 'setEndpointSize':
3817 _val = util.get_float(val)
3818 if _val < 0.0:
3819 raise ValueError, "Invalid endpoint size: %g" % _val
3820 _val = val
3821 elif _a == 'setDualDimMode':
3822 util.test_boolean(val)
3823 _val = val
3824 elif _a == 'setDualModeOffset':
3825 _val = util.get_float(val)
3826 if _val < 0.0:
3827 raise ValueError, "Invalid offset length: %g" % _val
3828 _val = val
3829 elif _a == 'setOffset':
3830 _val = util.get_float(val)
3831 if _val < 0.0:
3832 raise ValueError, "Invalid offset length: %g" % _val
3833 _val = val
3834 elif _a == 'setExtension':
3835 _val = util.get_float(val)
3836 if _val < 0.0:
3837 raise ValueError, "Invalid extension length: %g" % _val
3838 _val = val
3839 elif _a == 'setColor':
3840 if not isinstance(val, color.Color):
3841 raise TypeError, "Invalid Color: " + `type(val)`
3842 _val = val
3843 elif _a == 'setThickness':
3844 _val = util.get_float(val)
3845 if _val < 0.0:
3846 raise ValueError, "Invalid thickness: %g" % _val
3847 elif _a == 'setScale':
3848 _val = util.get_float(val)
3849 if _val < 0.0:
3850 raise ValueError, "Invalid scale: %g" % _val
3851 else:
3852 raise ValueError, "Unexpected attribute: " + _a
3853 self.__value = _val
3855 def getValue(self):
3856 """Get the stored attribute value.
3858 getValue()
3860 This method returns the value stored in setValue() or None.
3862 return self.__value
3864 class EditDimStringTool(TextTool):
3865 """A specialized class for modifying DimString instances in Dimensions.
3867 The EditDimStringTool class is derived from the TextTool class, so it
3868 shares the attributes and methods of that class. The TextTool class has
3869 the following additional methods:
3871 {set/get}Primary(): Set/Get the DimString on which the tool operates.
3873 def __init__(self):
3874 super(EditDimStringTool, self).__init__()
3875 self.__pds = True
3877 def setPrimary(self, flag=True):
3878 """Set the tool to operate on the primary DimString
3880 setPrimary([flag])
3882 Optional argument 'flag' should be a boolean. By default the
3883 tool will operate on the primary DimString of a Dimension. If
3884 argument 'flag' is False, the tool will operate on the secondary
3885 DimString.
3887 util.test_boolean(flag)
3888 self.__pds = flag
3890 def getPrimary(self):
3891 """Test if the tool operates on the primary DimString
3893 getPrimary()
3895 This method returns a boolean
3897 return self.__pds
3899 def testAttribute(self, attr):
3900 """Test that the attribute is valid for the DimString entity.
3902 testAttribute(attr)
3904 Argument 'attr' must be one of the following: 'setPrefix', 'setSuffix',
3905 'setUnits', 'setPrecision', 'setPrintZero', 'setPringDecimal', 'setFamily',
3906 'setStyle', 'setWeight', 'setSize', 'setAlignment', 'setColor', or
3907 'setAngle'.
3909 if not isinstance(attr, str):
3910 raise TypeError, "Invalid attribute type: " + `type(attr)`
3911 _res = attr in ('setPrefix', 'setSuffix', 'setUnits', 'setPrecision',
3912 'setPrintZero', 'setPrintDecimal')
3913 if _res:
3914 return _res
3915 return super(EditDimStringTool, self).testAttribute(attr)
3917 def testValue(self, val):
3918 """Test that the value is valid for a given DimString attribute.
3920 testValue(val)
3922 Argument 'val' depends on the attribute set for the EditDimString instance.
3924 _a = self.getAttribute()
3925 if _a == 'setPrefix' or _a == 'setSuffix':
3926 if not isinstance(val, types.StringTypes):
3927 raise TypeError, "Invalid %s type: %s " % (_a, `type(val)`)
3928 _val = val
3929 if not isinstance(_val, unicode):
3930 _val = unicode(val)
3931 elif _a == 'setPrecision':
3932 if not isinstance(val, int):
3933 raise TypeError, "Invalid precision type: " + `type(val)`
3934 _val = val
3935 elif _a == 'setPrintZero' or _a == 'setPrintDecimal':
3936 try:
3937 util.test_boolean(val)
3938 except TypeError:
3939 raise TypeError, "Invalid %s type: %s " % (_a, `type(val)`)
3940 _val = val
3941 elif _a == 'setUnits':
3942 _val = val # FIXME: needs error checking ...
3943 else:
3944 _val = super(EditDimStringTool, self).testValue(val)
3945 return _val
3947 def setText(self, txt):
3948 pass
3950 def getText(self):
3951 pass
3953 def hasText(self):
3954 pass
3956 def setTextLocation(self, x, y):
3957 pass
3959 def getTextLocation(self):
3960 pass
3962 def setBounds(self, width, height):
3963 pass
3965 def getBounds(self):
3966 pass
3968 def setPixelSize(self, width, height):
3969 pass
3971 def getPixelSize(self):
3972 pass
3974 def setLayout(self, layout):
3975 pass
3977 def getLayout(self):
3978 pass