2 # Copyright (c) 2002, 2003, 2004, 2005, 2006 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 # generic dimension classes
28 from PythonCAD
.Generic
import text
29 from PythonCAD
.Generic
import point
30 from PythonCAD
.Generic
import circle
31 from PythonCAD
.Generic
import arc
32 from PythonCAD
.Generic
import color
33 from PythonCAD
.Generic
import units
34 from PythonCAD
.Generic
import util
35 from PythonCAD
.Generic
import tolerance
36 from PythonCAD
.Generic
import baseobject
37 from PythonCAD
.Generic
import entity
42 class DimString(text
.TextBlock
):
43 """A class for the visual presentation of the dimensional value.
45 The DimString class is used to present the numerical display for
46 the dimension. A DimString object is derived from the text.TextBlock
47 class, so it shares all of that classes methods and attributes.
49 The DimString class has the following additional properties:
51 prefix: A prefix prepended to the dimension text
52 suffix: A suffix appended to the dimension text
53 units: The units the dimension text will display
54 precision: The displayed dimension precision
55 print_zero: Displayed dimensions will have a leading 0 if needed
56 print_decimal: Displayed dimensions will have a trailing decimal point
58 The DimString class has the following additional methods:
60 {get/set}Prefix(): Get/Set the text preceding the dimension value.
61 {get/set}Suffix(): Get/Set the text following the dimension value.
62 {get/set}Units(): Define what units the dimension will be in.
63 {get/set}Precision(): Get/Set how many fractional digits are displayed.
64 {get/set}PrintZero(): Get/Set whether or not a dimension less than one
65 unit long should have a leading 0 printed
66 {get/set}PrintDecimal(): Get/Set whether or not a dimension with 0
67 fractional digits prints out the decimal point.
68 {get/set}Dimension(): Get/Set the dimension using the DimString
70 The DimString class has the following classmethods:
72 {get/set}DefaultTextStyle(): Get/Set the default TextStyle for the class.
78 'prefix_changed' : True,
79 'suffix_changed' : True,
80 'units_changed' : True,
81 'precision_changed' : True,
82 'print_zero_changed' : True,
83 'print_decimal_changed' : True,
84 'dimension_changed' : True,
87 def __init__(self
, x
, y
, **kw
):
88 """Initialize a DimString object
90 ds = DimString(x, y, **kw)
92 Arguments 'x' and 'y' should be floats. Various keyword arguments can
96 textstyle: The default textstyle
103 align: Text alignment
104 prefix: Default prefix
105 suffix: Default suffix
106 units: Displayed units
107 precision: Displayed precision of units
108 print_zero: Boolean to print a leading '0'
109 print_decimal: Boolean to print a leading '.'
111 By default, a DimString object has the following values ...
116 precision: 3 decimal places
123 _tstyle
= self
.getDefaultTextStyle()
124 if 'textstyle' in kw
:
125 _tstyle
= kw
['textstyle']
129 _prefix
= kw
['prefix']
130 if not isinstance(_prefix
, types
.StringTypes
):
131 raise TypeError, "Invalid prefix type: " + `
type(_prefix
)`
134 _suffix
= kw
['suffix']
135 if not isinstance(_suffix
, types
.StringTypes
):
136 raise TypeError, "Invalid suffix type: " + `
type(_suffix
)`
137 _unit
= units
.MILLIMETERS
141 if 'precision' in kw
:
142 _prec
= kw
['precision']
143 if not isinstance(_prec
, int):
144 raise TypeError, "Invalid precision type: " + `
type(_prec
)`
145 if _prec
< 0 or _prec
> 15:
146 raise ValueError, "Invalid precision: %d" % _prec
148 if 'print_zero' in kw
:
149 _pz
= kw
['print_zero']
150 util
.test_boolean(_pz
)
152 if 'print_decimal' in kw
:
153 _pd
= kw
['print_decimal']
154 util
.test_boolean(_pd
)
155 super(DimString
, self
).__init
__(x
, y
, _text
, textstyle
=_tstyle
, **kw
)
156 self
.__prefix
= _prefix
157 self
.__suffix
= _suffix
158 self
.__unit
= units
.Unit(_unit
)
159 self
.__precision
= _prec
160 self
.__print
_zero
= _pz
161 self
.__print
_decimal
= _pd
164 def getDefaultTextStyle(cls
):
165 if cls
.__defstyle
is None:
166 _s
= text
.TextStyle(u
'Default Dimension Text Style',
167 color
=color
.Color(0xffffff),
168 align
=text
.TextStyle
.ALIGN_CENTER
)
170 return cls
.__defstyle
172 getDefaultTextStyle
= classmethod(getDefaultTextStyle
)
174 def setDefaultTextStyle(cls
, s
):
175 if not isinstance(s
, text
.TextStyle
):
176 raise TypeError, "Invalid TextStyle: " + `
type(s
)`
179 setDefaultTextStyle
= classmethod(setDefaultTextStyle
)
182 """Finalization for DimString instances.
186 if self
.__dim
is not None:
188 super(DimString
, self
).finish()
191 """Return values comprising the DimString.
195 This method extends the TextBlock::getValues() method.
197 _data
= super(DimString
, self
).getValues()
198 _data
.setValue('type', 'dimstring')
199 _data
.setValue('prefix', self
.__prefix
)
200 _data
.setValue('suffix', self
.__suffix
)
201 _data
.setValue('units', self
.__unit
.getStringUnit())
202 _data
.setValue('precision', self
.__precision
)
203 _data
.setValue('print_zero', self
.__print
_zero
)
204 _data
.setValue('print_decimal', self
.__print
_decimal
)
208 """Get the entity containing the DimString.
212 This method overrides the Entity::getParent() call.
215 if self
.__dim
is not None:
216 _parent
= self
.__dim
.getParent()
219 def setLocation(self
, x
, y
):
220 """Set the location of the DimString.
224 Arguments 'x' and 'y' should be floats. This method extends
225 the TextBlock::setLocation() method.
228 # the DimString location is defined in relation to
229 # the position defined by the Dimension::setLocation()
230 # call, so don't bother sending out 'moved' or 'modified'
235 super(DimString
, self
).setLocation(x
, y
)
240 """Return the prefix for the DimString object.
246 def setPrefix(self
, prefix
=None):
247 """Set the prefix for the DimString object.
251 Invoking this method without arguments sets the prefix
252 to an empty string, or to the DimStyle value in the associated
253 Dimension if one is set for the DimString. When an argument
254 is passed, the argument should be a Unicode string.
257 raise RuntimeError, "Setting prefix not allowed - object locked."
261 if self
.__dim
is not None:
262 _p
= self
.__dim
.getStyleValue(self
, 'prefix')
263 if not isinstance(_p
, unicode):
267 self
.startChange('prefix_changed')
269 self
.endChange('prefix_changed')
271 self
.sendMessage('prefix_changed', _op
)
274 prefix
= property(getPrefix
, setPrefix
, None, 'Dimension string prefix')
277 """Return the suffix for the DimString object.
283 def setSuffix(self
, suffix
=None):
284 """Set the suffix for the DimString object.
288 Invoking this method without arguments sets the suffix
289 to an empty string, or to the DimStyle value in the associated
290 Dimension if one is set for the DimString.. When an argument
291 is passed, the argument should be a Unicode string.
294 raise RuntimeError, "Setting suffix not allowed - object locked."
298 if self
.__dim
is not None:
299 _s
= self
.__dim
.getStyleValue(self
, 'suffix')
300 if not isinstance(_s
, unicode):
304 self
.startChange('suffix_changed')
306 self
.endChange('suffix_changed')
308 self
.sendMessage('suffix_changed', _os
)
311 suffix
= property(getSuffix
, setSuffix
, None, 'Dimension string suffix')
313 def getPrecision(self
):
314 """Return the number of decimal points used for the DimString.
318 return self
.__precision
320 def setPrecision(self
, precision
=None):
321 """Set the number of decimal points used for the DimString.
325 The valid range of p is 0 <= p <= 15. Invoking this method without
326 arguments sets the precision to 3, or to the DimStyle value in the
327 associated Dimension if one is set for the DimString..
330 raise RuntimeError, "Setting precision not allowed - object locked."
334 if self
.__dim
is not None:
335 _p
= self
.__dim
.getStyleValue(self
, 'precision')
336 if not isinstance(_p
, int):
337 raise TypeError, "Invalid precision type: " + `
type(_p
)`
338 if _p
< 0 or _p
> 15:
339 raise ValueError, "Invalid precision: %d" % _p
340 _op
= self
.__precision
342 self
.startChange('precision_changed')
343 self
.__precision
= _p
344 self
.endChange('precision_changed')
346 self
.sendMessage('precision_changed', _op
)
349 precision
= property(getPrecision
, setPrecision
, None,
350 'Dimension precision')
353 """Return the current units used in the DimString().
357 return self
.__unit
.getUnit()
359 def setUnits(self
, unit
=None):
360 """The the units for the DimString.
364 The value units are given in the units module. Invoking this
365 method without arguments sets the units to millimeters, or
366 to the DimStyle value of the associated Dimension if one
367 is set for the DimString.
371 _u
= units
.MILLIMETERS
372 if self
.__dim
is not None:
373 _u
= self
.__dim
.getStyleValue(self
, 'units')
374 _ou
= self
.__unit
.getUnit()
376 self
.startChange('units_changed')
377 self
.__unit
.setUnit(_u
)
378 self
.endChange('units_changed')
380 self
.sendMessage('units_changed', _ou
)
383 units
= property(getUnits
, setUnits
, None, 'Dimensional units.')
385 def getPrintZero(self
):
386 """Return whether or not a leading 0 is printed for the DimString.
390 return self
.__print
_zero
392 def setPrintZero(self
, print_zero
=None):
393 """Set whether or not a leading 0 is printed for the DimString.
397 Invoking this method without arguments sets the value to True,
398 or to the DimStyle value of the associated Dimension if one is
399 set for the DimString. If called with an argument, the argument
400 should be either True or False.
405 if self
.__dim
is not None:
406 _pz
= self
.__dim
.getStyleValue(self
, 'print_zero')
407 util
.test_boolean(_pz
)
408 _flag
= self
.__print
_zero
410 self
.startChange('print_zero_changed')
411 self
.__print
_zero
= _pz
412 self
.endChange('print_zero_changed')
414 self
.sendMessage('print_zero_changed', _flag
)
417 print_zero
= property(getPrintZero
, setPrintZero
, None,
418 'Print a leading 0 for decimal dimensions')
420 def getPrintDecimal(self
):
421 """Return whether or not the DimString will print a trailing decimal.
425 return self
.__print
_decimal
427 def setPrintDecimal(self
, print_decimal
=None):
428 """Set whether or not the DimString will print a trailing decimal.
430 setPrintDecimal([pd])
432 Invoking this method without arguments sets the value to True, or
433 to the DimStyle value of the associated Dimension if one is set
434 for the DimString. If called with an argument, the argument should
435 be either True or False.
440 if self
.__dim
is not None:
441 _pd
= self
.__dim
.getStyleValue(self
, 'print_decimal')
442 util
.test_boolean(_pd
)
443 _flag
= self
.__print
_decimal
445 self
.startChange('print_decimal_changed')
446 self
.__print
_decimal
= _pd
447 self
.endChange('print_decimal_changed')
449 self
.sendMessage('print_decimal_changed', _flag
)
452 print_decimal
= property(getPrintDecimal
, setPrintDecimal
, None,
453 'Print a decimal point after the dimension value')
455 def getDimension(self
):
456 """Return the dimension using the Dimstring.
460 This method can return None if there is not Dimension association set
465 def setDimension(self
, dim
, adjust
):
466 """Set the dimension using this DimString.
468 setDimension(dim, adjust)
470 Argument 'dim' must be a Dimension or None, and argument
471 'adjust' must be a Boolean. Argument 'adjust' is only used
472 if a Dimension is passed for the first argument.
475 if _dim
is not None and not isinstance(_dim
, Dimension
):
476 raise TypeError, "Invalid dimension: " + `
type(_dim
)`
477 util
.test_boolean(adjust
)
480 self
.startChange('dimension_changed')
482 self
.endChange('dimension_changed')
483 if _dim
is not None and adjust
:
489 self
.setPrintDecimal()
497 self
.sendMessage('dimension_changed', _d
)
499 if self
.__dim
is not None:
500 self
.setParent(self
.__dim
.getParent())
503 # extend the TextBlock set methods to use the values
504 # found in a DimStyle if one is available
507 def setFamily(self
, family
=None):
508 """Set the font family for the DimString.
512 Calling this method without an argument will set the
513 family to that given in the DimStyle of the associated
514 Dimension if one is set for this DimString.
517 if _family
is None and self
.__dim
is not None:
518 _family
= self
.__dim
.getStyleValue(self
, 'font_family')
519 super(DimString
, self
).setFamily(_family
)
521 def setStyle(self
, style
=None):
522 """Set the font style for the DimString.
526 Calling this method without an argument will set the
527 font style to that given in the DimStyle of the associated
528 Dimension if one is set for this DimString.
531 if _style
is None and self
.__dim
is not None:
532 _style
= self
.__dim
.getStyleValue(self
, 'font_style')
533 super(DimString
, self
).setStyle(_style
)
535 def setWeight(self
, weight
=None):
536 """Set the font weight for the DimString.
540 Calling this method without an argument will set the
541 font weight to that given in the DimStyle of the associated
542 Dimension if one is set for this DimString.
545 if _weight
is None and self
.__dim
is not None:
546 _weight
= self
.__dim
.getStyleValue(self
, 'font_weight')
547 super(DimString
, self
).setWeight(_weight
)
549 def setColor(self
, color
=None):
550 """Set the font color for the DimString.
554 Calling this method without an argument will set the
555 font color to that given in the DimStyle of the associated
556 Dimension if one is set for this DimString.
559 if _color
is None and self
.__dim
is not None:
560 _color
= self
.__dim
.getStyleValue(self
, 'color')
561 super(DimString
, self
).setColor(_color
)
563 def setSize(self
, size
=None):
564 """Set the text size for the DimString.
568 Calling this method without an argument will set the
569 text size to that given in the DimStyle of the associated
570 Dimension if one is set for this DimString.
573 if _size
is None and self
.__dim
is not None:
574 _size
= self
.__dim
.getStyleValue(self
, 'size')
575 super(DimString
, self
).setSize(_size
)
577 def setAngle(self
, angle
=None):
578 """Set the text angle for the DimString.
582 Calling this method without an argument will set the
583 text angle to that given in the DimStyle of the associated
584 Dimension if one is set for this DimString.
587 if _angle
is None and self
.__dim
is not None:
588 _angle
= self
.__dim
.getStyleValue(self
, 'angle')
589 super(DimString
, self
).setAngle(_angle
)
591 def setAlignment(self
, align
=None):
592 """Set the text alignment for the DimString.
594 setAlignment([align])
596 Calling this method without an argument will set the
597 text alignment to that given in the DimStyle of the associated
598 Dimension if one is set for this DimString.
601 if _align
is None and self
.__dim
is not None:
602 _align
= self
.__dim
.getStyleValue(self
, 'alignment')
603 super(DimString
, self
).setAlignment(_align
)
605 def setText(self
, text
):
606 """Set the text in the DimString.
608 This method overrides the setText method in the TextBlock.
612 def formatDimension(self
, dist
):
613 """Return a formatted numerical value for a dimension.
615 formatDimension(dist)
617 The argument 'dist' should be a float value representing the
618 distance in millimeters. The returned value will have the
619 prefix prepended and suffix appended to the numerical value
620 that has been formatted with the precision.
622 _d
= abs(util
.get_float(dist
))
623 _fmtstr
= u
"%%#.%df" % self
.__precision
624 _dstr
= _fmtstr
% self
.__unit
.fromMillimeters(_d
)
625 if _d
< 1.0 and self
.__print
_zero
is False:
627 if _dstr
.endswith('.') and self
.__print
_decimal
is False:
629 _text
= self
.__prefix
+ _dstr
+ self
.__suffix
631 # don't send out 'text_changed' or 'modified' messages
635 super(DimString
, self
).setText(_text
)
640 def sendsMessage(self
, m
):
641 if m
in DimString
.__messages
:
643 return super(DimString
, self
).sendsMessage(m
)
645 class DimBar(entity
.Entity
):
646 """The class for the dimension bar.
648 A dimension bar leads from the point the dimension references
649 out to, and possibly beyond, the point where the dimension
650 text bar the DimBar to another DimBar. Linear,
651 horizontal, vertical, and angular dimension will have two
652 dimension bars; radial dimensions have none.
654 The DimBar class has the following methods:
656 getEndpoints(): Get the x/y position of the DimBar start and end
657 {get/set}FirstEndpoint(): Get/Set the starting x/y position of the DimBar.
658 {get/set}SecondEndpoint(): Get/Set the ending x/y position of the DimBar.
659 getAngle(): Get the angle at which the DimBar slopes
660 getSinCosValues(): Get trig values used for transformation calculations.
664 'attribute_changed' : True,
667 def __init__(self
, x1
=0.0, y1
=0.0, x2
=0.0, y2
=0.0, **kw
):
668 """Initialize a DimBar.
670 db = DimBar([x1, y1, x2, y2])
672 By default all the arguments are 0.0. Any arguments passed to this
673 method should be float values.
675 _x1
= util
.get_float(x1
)
676 _y1
= util
.get_float(y1
)
677 _x2
= util
.get_float(x2
)
678 _y2
= util
.get_float(y2
)
679 super(DimBar
, self
).__init
__(**kw
)
685 def getEndpoints(self
):
686 """Return the coordinates of the DimBar endpoints.
690 This method returns two tuples, each containing two float values.
691 The first tuple gives the x/y coordinates of the DimBar start,
692 the second gives the coordinates of the DimBar end.
694 _ep1
= (self
.__ex
1, self
.__ey
1)
695 _ep2
= (self
.__ex
2, self
.__ey
2)
698 def setFirstEndpoint(self
, x
, y
):
699 """Set the starting coordinates for the DimBar
701 setFirstEndpoint(x, y)
703 Arguments x and y should be float values.
706 raise RuntimeError, "Setting endpoint not allowed - object locked."
707 _x
= util
.get_float(x
)
708 _y
= util
.get_float(y
)
711 if abs(_sx
- _x
) > 1e-10 or abs(_sy
- _y
) > 1e-10:
714 self
.sendMessage('attribute_changed', 'endpoint', _sx
, _sy
,
715 self
.__ex
2, self
.__ey
2)
718 def getFirstEndpoint(self
):
719 """Return the starting coordinates of the DimBar.
723 This method returns a tuple giving the x/y coordinates.
725 return self
.__ex
1, self
.__ey
1
727 def setSecondEndpoint(self
, x
, y
):
728 """Set the ending coordinates for the DimBar
730 setSecondEndpoint(x, y)
732 Arguments x and y should be float values.
735 raise RuntimeError, "Setting endpoint not allowed - object locked."
736 _x
= util
.get_float(x
)
737 _y
= util
.get_float(y
)
740 if abs(_sx
- _x
) > 1e-10 or abs(_sy
- _y
) > 1e-10:
743 self
.sendMessage('attribute_changed', 'endpoint',
744 self
.__ex
1, self
.__ey
1, _sx
, _sy
)
747 def getSecondEndpoint(self
):
748 """Return the ending coordinates of the DimBar.
752 This method returns a tuple giving the x/y coordinates.
754 return self
.__ex
2, self
.__ey
2
757 """Return the angle at which the DimBar lies.
761 This method returns a float value giving the angle of inclination
764 The value returned will be a positive value less than 360.0.
770 if abs(_x2
- _x1
) < 1e-10 and abs(_y2
- _y1
) < 1e-10:
771 raise ValueError, "Endpoints are equal"
772 if abs(_x2
- _x1
) < 1e-10: # vertical
777 elif abs(_y2
- _y1
) < 1e-10: # horizontal
783 _angle
= _rtd
* math
.atan2((_y2
- _y1
), (_x2
- _x1
))
785 _angle
= _angle
+ 360.0
788 def getSinCosValues(self
):
789 """Return sin()/cos() values based on the DimBar slope
793 This method returns a tuple of two floats. The first value is
794 the sin() value, the second is the cos() value.
800 if abs(_x2
- _x1
) < 1e-10: # vertical
806 elif abs(_y2
- _y1
) < 1e-10: # horizontal
813 _angle
= math
.atan2((_y2
- _y1
), (_x2
- _x1
))
814 _sine
= math
.sin(_angle
)
815 _cosine
= math
.cos(_angle
)
816 return _sine
, _cosine
818 def sendsMessage(self
, m
):
819 if m
in DimBar
.__messages
:
821 return super(DimBar
, self
).sendsMessage(m
)
823 class DimCrossbar(DimBar
):
824 """The class for the Dimension crossbar.
826 The DimCrossbar class is drawn between two DimBar objects for
827 horizontal, vertical, and generic linear dimensions. The dimension
828 text is place over the DimCrossbar object. Arrow heads, circles, or
829 slashes can be drawn at the intersection of the DimCrossbar and
830 the DimBar if desired. These objects are called markers.
832 The DimCrossbar class is derived from the DimBar class so it shares
833 all the methods of that class. In addition the DimCrossbar class has
834 the following methods:
836 {set/get}FirstCrossbarPoint(): Set/Get the initial location of the crossbar.
837 {set/get}SecondCrossbarPoint(): Set/Get the ending location of the crossbar.
838 getCrossbarPoints(): Get the location of the crossbar endpoints.
839 clearMarkerPoints(): Delete the stored coordintes of the dimension markers.
840 storeMarkerPoint(): Save a coordinate pair of the dimension marker.
841 getMarkerPoints(): Return the coordinates of the dimension marker.
845 def __init__(self
, **kw
):
846 """Initialize a DimCrossbar object.
850 This method takes no arguments.
852 super(DimCrossbar
, self
).__init
__(**kw
)
859 def setFirstCrossbarPoint(self
, x
, y
):
860 """Store the initial endpoint of the DimCrossbar.
862 setFirstCrossbarPoint(x, y)
864 Arguments x and y should be floats.
867 raise RuntimeError, "Setting crossbar point not allowed - object locked."
868 _x
= util
.get_float(x
)
869 _y
= util
.get_float(y
)
872 if abs(_sx
- _x
) > 1e-10 or abs(_sy
- _y
) > 1e-10:
875 self
.sendMessage('attribute_changed', 'barpoint', _sx
, _sy
,
876 self
.__mx
2, self
.__my
2)
879 def getFirstCrossbarPoint(self
):
880 """Return the initial coordinates of the DimCrossbar.
882 getFirstCrossbarPoint()
884 This method returns a tuple of two floats giving the x/y coordinates.
886 return self
.__mx
1, self
.__my
1
888 def setSecondCrossbarPoint(self
, x
, y
):
889 """Store the terminal endpoint of the DimCrossbar.
891 setSecondCrossbarPoint(x, y)
893 Arguments 'x' and 'y' should be floats.
896 raise RuntimeError, "Setting crossbar point not allowed - object locked"
897 _x
= util
.get_float(x
)
898 _y
= util
.get_float(y
)
901 if abs(_sx
- _x
) > 1e-10 or abs(_sy
- _y
) > 1e-10:
904 self
.sendMessage('attribute_changed', 'barpoint',
905 self
.__mx
1, self
.__my
1, _sx
, _sy
)
908 def getSecondCrossbarPoint(self
):
909 """Return the terminal coordinates of the DimCrossbar.
911 getSecondCrossbarPoint()
913 This method returns a tuple of two floats giving the x/y coordinates.
915 return self
.__mx
2, self
.__my
2
917 def getCrossbarPoints(self
):
918 """Return the endpoints of the DimCrossbar.
922 This method returns two tuples, each tuple containing two float
923 values giving the x/y coordinates.
925 _mp1
= (self
.__mx
1, self
.__my
1)
926 _mp2
= (self
.__mx
2, self
.__my
2)
929 def clearMarkerPoints(self
):
930 """Delete the stored location of any dimension markers.
936 def storeMarkerPoint(self
, x
, y
):
937 """Save a coordinate pair of the current dimension marker.
939 storeMarkerPoint(x, y)
941 Arguments 'x' and 'y' should be floats. Each time this method is invoked
942 the list of stored coordinates is appended with the values given as
945 _x
= util
.get_float(x
)
946 _y
= util
.get_float(y
)
947 self
.__mpts
.append((_x
, _y
))
949 def getMarkerPoints(self
):
950 """Return the stored marker coordinates.
954 This method returns a list of coordinates stored with storeMarkerPoint().
955 Each item in the list is a tuple holding two float values - the x/y
956 coordinate of the point.
958 return self
.__mpts
[:]
960 def sendsMessage(self
, m
):
961 if m
in DimCrossbar
.__messages
:
963 return super(DimCrossbar
, self
).sendsMessage(m
)
965 class DimCrossarc(DimCrossbar
):
966 """The class for specialized crossbars for angular dimensions.
968 The DimCrossarc class is meant to be used only with angular dimensions.
969 As an angular dimension has two DimBar objects that are connected
970 with an arc. The DimCrossarc class is derived from the DimCrossbar
971 class so it shares all the methods of that class. The DimCrossarc
972 class has the following additional methods:
974 {get/set}Radius(): Get/Set the radius of the arc.
975 {get/set}StartAngle(): Get/Set the arc starting angle.
976 {get/set}EndAngle(): Get/Set the arc finishing angle.
980 'arcpoint_changed' : True,
981 'radius_changed' : True,
982 'start_angle_changed' : True,
983 'end_angle_changed' : True,
986 def __init__(self
, radius
=0.0, start
=0.0, end
=0.0, **kw
):
987 """Initialize a DimCrossarc object.
989 dca = DimCrossarc([radius, start, end])
991 By default the arguments are all 0.0. Any arguments passed to
992 this method should be floats.
994 super(DimCrossarc
, self
).__init
__(**kw
)
995 _r
= util
.get_float(radius
)
997 raise ValueError, "Invalid radius: %g" % _r
998 _start
= util
.make_c_angle(start
)
999 _end
= util
.make_c_angle(end
)
1001 self
.__start
= _start
1004 def getRadius(self
):
1005 """Return the radius of the arc.
1009 This method returns a float value.
1011 return self
.__radius
1013 def setRadius(self
, radius
):
1014 """Set the radius of the arc.
1018 Argument 'radius' should be a float value greater than 0.0.
1021 raise RuntimeError, "Setting radius not allowed - object locked."
1022 _r
= util
.get_float(radius
)
1024 raise ValueError, "Invalid radius: %g" % _r
1026 if abs(_sr
- _r
) > 1e-10:
1027 self
.startChange('radius_changed')
1029 self
.endChange('radius_changed')
1030 self
.sendMessage('radius_changed', _sr
)
1033 def getStartAngle(self
):
1034 """Return the arc starting angle.
1038 This method returns a float.
1042 def setStartAngle(self
, angle
):
1043 """Set the starting angle of the arc.
1045 setStartAngle(angle)
1047 Argument angle should be a float value.
1050 raise RuntimeError, "Setting start angle not allowed - object locked."
1052 _angle
= util
.make_c_angle(angle
)
1053 if abs(_sa
- _angle
) > 1e-10:
1054 self
.startChange('start_angle_changed')
1055 self
.__start
= _angle
1056 self
.endChange('start_angle_changed')
1057 self
.sendMessage('start_angle_changed', _sa
)
1060 def getEndAngle(self
):
1061 """Return the arc ending angle.
1065 This method returns a float.
1069 def setEndAngle(self
, angle
):
1070 """Set the ending angle of the arc.
1074 Argument angle should be a float value.
1077 raise RuntimeError, "Setting end angle not allowed - object locked."
1079 _angle
= util
.make_c_angle(angle
)
1080 if abs(_ea
- _angle
) > 1e-10:
1081 self
.startChange('end_angle_changed')
1083 self
.endChange('end_angle_changed')
1084 self
.sendMessage('end_angle_changed', _ea
)
1088 pass # override the DimBar::getAngle() method
1090 def getSinCosValues(self
):
1091 pass # override the DimBar::getSinCosValues() method
1093 def sendsMessage(self
, m
):
1094 if m
in DimCrossarc
.__messages
:
1096 return super(DimCrossarc
, self
).sendsMessage(m
)
1098 class Dimension(entity
.Entity
):
1099 """The base class for Dimensions
1101 A Dimension object is meant to be a base class for specialized
1104 Every Dimension object holds two DimString objects, so any
1105 dimension can be displayed with two separate formatting options
1108 A Dimension has the following methods
1110 {get/set}DimStyle(): Get/Set the DimStyle used for this Dimension.
1111 getPrimaryDimstring(): Return the DimString used for formatting the
1113 getSecondaryDimstring(): Return the DimString used for formatting the
1114 Secondary dimension.
1115 {get/set}EndpointType(): Get/Set the type of endpoints used in the Dimension
1116 {get/set}EndpointSize(): Get/Set the size of the dimension endpoints
1117 {get/set}DualDimMode(): Get/Set whether or not to display both the Primary
1118 and Secondary DimString objects
1119 {get/set}Offset(): Get/Set how far from the dimension endpoints to draw
1120 dimension lines at the edges of the dimension.
1121 {get/set}Extension(): Get/Set how far past the dimension crossbar line
1123 {get/set}Position(): Get/Set where the dimensional values are placed on the
1124 dimension cross bar.
1125 {get/set}Color(): Get/Set the color used to draw the dimension lines.
1126 {get/set}Location(): Get/Set where to draw the dimensional values.
1127 {get/set}PositionOffset(): Get/Set the dimension text offset when the text is
1128 above or below the crossbar/crossarc
1129 {get/set}DualModeOffset(): Get/Set the text offset for spaceing the two
1130 dimension strings above and below the bar
1131 separating the two dimensions
1132 {get/set}Thickness(): Get/Set the Dimension thickness.
1133 {get/set}Scale(): Get/Set the Dimension scaling factor.
1134 getStyleValue(): Return the DimStyle value for some option
1135 getDimensions(): Return the formatted dimensional values in this Dimension.
1136 inRegion(): Return if the dimension is visible within some are.
1137 calcDimValues(): Calculate the dimension lines endpoints.
1138 mapCoords(): Return the coordinates on the dimension within some point.
1139 onDimension(): Test if an x/y coordinate pair hit the dimension lines.
1140 getBounds(): Return the minma and maximum locations of the dimension.
1142 The Dimension class has the following classmethods:
1144 {get/set}DefaultDimStyle(): Get/Set the default DimStyle for the class.
1145 getEndpointTypeAsString(): Return the endpoint type as a string for a value.
1146 getEndpointTypeFromString(): Return the endpoint type value given a string.
1147 getEndpointTypeStrings(): Get the endpoint types values as strings.
1148 getEndpointTypeValues(): Get the endpoint type values.
1149 getPositionAsString(): Return the text position as a string for a value.
1150 getPositionFromString(): Return the text postion value given a string.
1151 getPositionStrings(): Get the text position values as strings.
1152 getPositionValues(): Get the text position values.
1160 DIM_ENDPT_FILLED_ARROW
= 2
1162 DIM_ENDPT_CIRCLE
= 4
1165 # Dimension position on dimline
1167 DIM_TEXT_POS_SPLIT
= 0
1168 DIM_TEXT_POS_ABOVE
= 1
1169 DIM_TEXT_POS_BELOW
= 2
1174 'dimstyle_changed' : True,
1175 'endpoint_type_changed' : True,
1176 'endpoint_size_changed' : True,
1177 'dual_mode_changed' : True,
1178 'offset_changed' : True,
1179 'extension_changed' : True,
1180 'position_changed' : True,
1181 'position_offset_changed' : True,
1182 'dual_mode_offset_changed' : True,
1183 'color_changed' : True,
1184 'thickness_changed' : True,
1185 'scale_changed' : True,
1186 'location_changed' : True,
1187 'dimstring_changed' : True,
1191 def __init__(self
, x
, y
, dimstyle
=None, **kw
):
1192 """Initialize a Dimension object
1194 dim = Dimension(x, y[, ds])
1196 Arguments 'x' and 'y' should be float values. Optional argument
1197 'ds' should be a DimStyle instance. A default DimStyle is used
1198 of the optional argument is not used.
1200 _x
= util
.get_float(x
)
1201 _y
= util
.get_float(y
)
1204 _ds
= self
.getDefaultDimStyle()
1205 if not isinstance(_ds
, DimStyle
):
1206 raise TypeError, "Invalid DimStyle type: " + `
type(_ds
)`
1208 if 'dual-mode' in kw
:
1209 _ddm
= kw
['dual-mode']
1210 if _ddm
is not None:
1211 util
.test_boolean(_ddm
)
1212 if _ddm
is _ds
.getValue('DIM_DUAL_MODE'):
1216 _offset
= util
.get_float(kw
['offset'])
1217 if _offset
is not None:
1219 raise ValueError, "Invalid dimension offset: %g" % _offset
1220 if abs(_offset
- _ds
.getValue('DIM_OFFSET')) < 1e-10:
1224 _extlen
= util
.get_float(kw
['extlen'])
1226 raise ValueError, "Invalid dimension extension: %g" % _extlen
1227 if abs(_extlen
- _ds
.getValue('DIM_EXTENSION')) < 1e-10:
1231 _textpos
= kw
['textpos']
1232 if (_textpos
!= Dimension
.DIM_TEXT_POS_SPLIT
and
1233 _textpos
!= Dimension
.DIM_TEXT_POS_ABOVE
and
1234 _textpos
!= Dimension
.DIM_TEXT_POS_BELOW
):
1235 raise ValueError, "Invalid dimension text position: '%s'" % str(_textpos
)
1236 if _textpos
== _ds
.getValue('DIM_POSITION'):
1240 _poffset
= util
.get_float(kw
['poffset'])
1242 raise ValueError, "Invalid text offset length %g" % _poffset
1243 if abs(_poffset
- _ds
.getValue('DIM_POSITION_OFFSET')) < 1e-10:
1246 if 'dmoffset' in kw
:
1247 _dmoffset
= util
.get_float(kw
['dmoffset'])
1249 raise ValueError, "Invalid dual mode offset length %g" % _dmoffset
1250 if abs(_dmoffset
- _ds
.getValue('DIM_DUAL_MODE_OFFSET')) < 1e-10:
1254 _eptype
= kw
['eptype']
1255 if (_eptype
!= Dimension
.DIM_ENDPT_NONE
and
1256 _eptype
!= Dimension
.DIM_ENDPT_ARROW
and
1257 _eptype
!= Dimension
.DIM_ENDPT_FILLED_ARROW
and
1258 _eptype
!= Dimension
.DIM_ENDPT_SLASH
and
1259 _eptype
!= Dimension
.DIM_ENDPT_CIRCLE
):
1260 raise ValueError, "Invalid endpoint: '%s'" % str(_eptype
)
1261 if _eptype
== _ds
.getValue('DIM_ENDPOINT'):
1265 _epsize
= util
.get_float(kw
['epsize'])
1267 raise ValueError, "Invalid endpoint size %g" % _epsize
1268 if abs(_epsize
- _ds
.getValue('DIM_ENDPOINT_SIZE')) < 1e-10:
1272 _color
= kw
['color']
1273 if not isinstance(_color
, color
.Color
):
1274 raise TypeError, "Invalid color type: " + `
type(_color
)`
1275 if _color
== _ds
.getValue('DIM_COLOR'):
1278 if 'thickness' in kw
:
1279 _thickness
= util
.get_float(kw
['thickness'])
1280 if _thickness
< 0.0:
1281 raise ValueError, "Invalid thickness: %g" % _thickness
1282 if abs(_thickness
- _ds
.getValue('DIM_THICKNESS')) < 1e-10:
1286 _scale
= util
.get_float(kw
['scale'])
1287 if not _scale
> 0.0:
1288 raise ValueError, "Invalid scale: %g" % _scale
1292 # the setDimension() call will adjust the values in the
1293 # new DimString instances if they get created
1296 _ds1adj
= _ds2adj
= True
1299 if not isinstance(_ds1
, DimString
):
1300 raise TypeError, "Invalid DimString type: " + `
type(_ds1
)`
1303 _ds1
= DimString(_x
, _y
)
1307 if not isinstance(_ds2
, DimString
):
1308 raise TypeError, "Invalid DimString type: " + `
type(_ds2
)`
1311 _ds2
= DimString(_x
, _y
)
1315 super(Dimension
, self
).__init
__(**kw
)
1316 self
.__dimstyle
= _ds
1318 self
.__offset
= _offset
1319 self
.__extlen
= _extlen
1320 self
.__textpos
= _textpos
1321 self
.__poffset
= _poffset
1322 self
.__dmoffset
= _dmoffset
1323 self
.__eptype
= _eptype
1324 self
.__epsize
= _epsize
1325 self
.__color
= _color
1326 self
.__thickness
= _thickness
1327 self
.__scale
= _scale
1328 self
.__dimloc
= (_x
, _y
)
1331 self
.__ds
1.setDimension(self
, _ds1adj
)
1332 self
.__ds
2.setDimension(self
, _ds2adj
)
1333 _ds1
.connect('change_pending', self
.__dimstringChangePending
)
1334 _ds1
.connect('change_complete', self
.__dimstringChangeComplete
)
1335 _ds2
.connect('change_pending', self
.__dimstringChangePending
)
1336 _ds2
.connect('change_complete', self
.__dimstringChangeComplete
)
1338 def getDefaultDimStyle(cls
):
1339 if cls
.__defstyle
is None:
1340 cls
.__defstyle
= DimStyle(u
'Default DimStyle')
1341 return cls
.__defstyle
1343 getDefaultDimStyle
= classmethod(getDefaultDimStyle
)
1345 def setDefaultDimStyle(cls
, s
):
1346 if not isinstance(s
, DimStyle
):
1347 raise TypeError, "Invalid DimStyle: " + `
type(s
)`
1350 setDefaultDimStyle
= classmethod(setDefaultDimStyle
)
1353 self
.__ds
1.disconnect(self
)
1354 self
.__ds
2.disconnect(self
)
1357 self
.__ds
1 = self
.__ds
2 = None
1358 super(Dimension
, self
).finish()
1360 def getValues(self
):
1361 """Return values comprising the Dimension.
1365 This method extends the Entity::getValues() method.
1367 _data
= super(Dimension
, self
).getValues()
1368 _data
.setValue('location', self
.__dimloc
)
1369 if self
.__offset
is not None:
1370 _data
.setValue('offset', self
.__offset
)
1371 if self
.__extlen
is not None:
1372 _data
.setValue('extension', self
.__extlen
)
1373 if self
.__textpos
is not None:
1374 _data
.setValue('position', self
.__textpos
)
1375 if self
.__eptype
is not None:
1376 _data
.setValue('eptype', self
.__eptype
)
1377 if self
.__epsize
is not None:
1378 _data
.setValue('epsize', self
.__epsize
)
1379 if self
.__color
is not None:
1380 _data
.setValue('color', self
.__color
.getColors())
1381 if self
.__ddm
is not None:
1382 _data
.setValue('dualmode', self
.__ddm
)
1383 if self
.__poffset
is not None:
1384 _data
.setValue('poffset', self
.__poffset
)
1385 if self
.__dmoffset
is not None:
1386 _data
.setValue('dmoffset', self
.__dmoffset
)
1387 if self
.__thickness
is not None:
1388 _data
.setValue('thickness', self
.__thickness
)
1389 _data
.setValue('ds1', self
.__ds
1.getValues())
1390 _data
.setValue('ds2', self
.__ds
2.getValues())
1391 _data
.setValue('dimstyle', self
.__dimstyle
.getValues())
1394 def getDimStyle(self
):
1395 """Return the DimStyle used in this Dimension.
1399 return self
.__dimstyle
1401 def setDimStyle(self
, ds
):
1402 """Set the DimStyle used for this Dimension.
1406 After setting the DimStyle, the values stored in it
1407 are applied to the DimensionObject.
1410 raise RuntimeError, "Changing dimstyle not allowed - object locked."
1411 if not isinstance(ds
, DimStyle
):
1412 raise TypeError, "Invalid DimStyle type: " + `
type(ds
)`
1413 _sds
= self
.__dimstyle
1415 _opts
= self
.getValues()
1416 self
.startChange('dimstyle_changed')
1417 self
.__dimstyle
= ds
1418 self
.endChange('dimstyle_changed')
1420 # call the various methods without arguments
1421 # so the values given in the new DimStyle are used
1426 self
.setEndpointType()
1427 self
.setEndpointSize()
1430 self
.setDualDimMode()
1431 self
.setPositionOffset()
1432 self
.setDualModeOffset()
1434 # set the values in the two DimString instances
1437 _d
.setPrefix(ds
.getValue('DIM_PRIMARY_PREFIX'))
1438 _d
.setSuffix(ds
.getValue('DIM_PRIMARY_SUFFIX'))
1439 _d
.setPrecision(ds
.getValue('DIM_PRIMARY_PRECISION'))
1440 _d
.setUnits(ds
.getValue('DIM_PRIMARY_UNITS'))
1441 _d
.setPrintZero(ds
.getValue('DIM_PRIMARY_LEADING_ZERO'))
1442 _d
.setPrintDecimal(ds
.getValue('DIM_PRIMARY_TRAILING_DECIMAL'))
1443 _d
.setFamily(ds
.getValue('DIM_PRIMARY_FONT_FAMILY'))
1444 _d
.setWeight(ds
.getValue('DIM_PRIMARY_FONT_WEIGHT'))
1445 _d
.setStyle(ds
.getValue('DIM_PRIMARY_FONT_STYLE'))
1446 _d
.setColor(ds
.getValue('DIM_PRIMARY_FONT_COLOR'))
1447 _d
.setSize(ds
.getValue('DIM_PRIMARY_TEXT_SIZE'))
1448 _d
.setAngle(ds
.getValue('DIM_PRIMARY_TEXT_ANGLE'))
1449 _d
.setAlignment(ds
.getVaue('DIM_PRIMARY_TEXT_ALIGNMENT'))
1451 _d
.setPrefix(ds
.getValue('DIM_SECONDARY_PREFIX'))
1452 _d
.setSuffix(ds
.getValue('DIM_SECONDARY_SUFFIX'))
1453 _d
.setPrecision(ds
.getValue('DIM_SECONDARY_PRECISION'))
1454 _d
.setUnits(ds
.getValue('DIM_SECONDARY_UNITS'))
1455 _d
.setPrintZero(ds
.getValue('DIM_SECONDARY_LEADING_ZERO'))
1456 _d
.setPrintDecimal(ds
.getValue('DIM_SECONDARY_TRAILING_DECIMAL'))
1457 _d
.setFamily(ds
.getValue('DIM_SECONDARY_FONT_FAMILY'))
1458 _d
.setWeight(ds
.getValue('DIM_SECONDARY_FONT_WEIGHT'))
1459 _d
.setStyle(ds
.getValue('DIM_SECONDARY_FONT_STYLE'))
1460 _d
.setColor(ds
.getValue('DIM_SECONDARY_FONT_COLOR'))
1461 _d
.setSize(ds
.getValue('DIM_SECONDARY_TEXT_SIZE'))
1462 _d
.setAngle(ds
.getValue('DIM_SECONDARY_TEXT_ANGLE'))
1463 _d
.setAlignment(ds
.getVaue('DIM_SECONDARY_TEXT_ALIGNMENT'))
1464 self
.sendMessage('dimstyle_changed', _sds
, _opts
)
1467 dimstyle
= property(getDimStyle
, setDimStyle
, None,
1468 "Dimension DimStyle object.")
1470 def getEndpointTypeAsString(cls
, ep
):
1471 """Return a text string for the dimension endpoint type.
1473 getEndpointTypeAsString(ep)
1475 This classmethod returns 'none', 'arrow', or 'filled-arrow', 'slash',
1478 if not isinstance(ep
, int):
1479 raise TypeError, "Invalid argument type: " + `
type(ep
)`
1480 if ep
== Dimension
.DIM_ENDPT_NONE
:
1482 elif ep
== Dimension
.DIM_ENDPT_ARROW
:
1484 elif ep
== Dimension
.DIM_ENDPT_FILLED_ARROW
:
1485 _str
= 'filled-arrow'
1486 elif ep
== Dimension
.DIM_ENDPT_SLASH
:
1488 elif ep
== Dimension
.DIM_ENDPT_CIRCLE
:
1491 raise ValueError, "Unexpected endpoint type value: %d" % ep
1494 getEndpointTypeAsString
= classmethod(getEndpointTypeAsString
)
1496 def getEndpointTypeFromString(cls
, s
):
1497 """Return the dimension endpoint type given a string argument.
1499 getEndpointTypeFromString(ep)
1501 This classmethod returns a value based on the string argument:
1503 'none' -> Dimension.DIM_ENDPT_NONE
1504 'arrow' -> Dimension.DIM_ENDPT_ARROW
1505 'filled-arrow' -> Dimension.DIM_ENDPT_FILLED_ARROW
1506 'slash' -> Dimension.DIM_ENDPT_SLASH
1507 'circle' -> Dimension.DIM_ENDPT_CIRCLE
1509 If the string is not listed above a ValueError execption is raised.
1511 if not isinstance(s
, str):
1512 raise TypeError, "Invalid argument type: " + `
type(s
)`
1515 _v
= Dimension
.DIM_ENDPT_NONE
1516 elif _ls
== 'arrow':
1517 _v
= Dimension
.DIM_ENDPT_ARROW
1518 elif (_ls
== 'filled-arrow' or _ls
== 'filled_arrow'):
1519 _v
= Dimension
.DIM_ENDPT_FILLED_ARROW
1520 elif _ls
== 'slash':
1521 _v
= Dimension
.DIM_ENDPT_SLASH
1522 elif _ls
== 'circle':
1523 _v
= Dimension
.DIM_ENDPT_CIRCLE
1525 raise ValueError, "Unexpected endpoint type string: " + s
1528 getEndpointTypeFromString
= classmethod(getEndpointTypeFromString
)
1530 def getEndpointTypeStrings(cls
):
1531 """Return the endpoint types as strings.
1533 getEndpointTypeStrings()
1535 This classmethod returns a list of strings.
1544 getEndpointTypeStrings
= classmethod(getEndpointTypeStrings
)
1546 def getEndpointTypeValues(cls
):
1547 """Return the endpoint type values.
1549 getEndpointTypeValues()
1551 This classmethod returns a list of values.
1553 return [Dimension
.DIM_ENDPT_NONE
,
1554 Dimension
.DIM_ENDPT_ARROW
,
1555 Dimension
.DIM_ENDPT_FILLED_ARROW
,
1556 Dimension
.DIM_ENDPT_SLASH
,
1557 Dimension
.DIM_ENDPT_CIRCLE
1560 getEndpointTypeValues
= classmethod(getEndpointTypeValues
)
1562 def getEndpointType(self
):
1563 """Return what type of endpoints the Dimension uses.
1569 _et
= self
.__dimstyle
.getValue('DIM_ENDPOINT')
1572 def setEndpointType(self
, eptype
=None):
1573 """Set what type of endpoints the Dimension will use.
1575 setEndpointType([e])
1577 The argument 'e' should be one of the following
1579 dimension.NO_ENDPOINT => no special marking at the dimension crossbar ends
1580 dimension.ARROW => an arrowhead at the dimension crossbar ends
1581 dimension.FILLED_ARROW => a filled arrohead at the dimension crossbar ends
1582 dimension.SLASH => a slash mark at the dimension crossbar ends
1583 dimension.CIRCLE => a filled circle at the dimension crossbar ends
1585 If this method is called without an argument, the endpoint type is set
1586 to that given in the DimStyle.
1589 raise RuntimeError, "Changing endpoint type allowed - object locked."
1592 if (_ep
!= Dimension
.DIM_ENDPT_NONE
and
1593 _ep
!= Dimension
.DIM_ENDPT_ARROW
and
1594 _ep
!= Dimension
.DIM_ENDPT_FILLED_ARROW
and
1595 _ep
!= Dimension
.DIM_ENDPT_SLASH
and
1596 _ep
!= Dimension
.DIM_ENDPT_CIRCLE
):
1597 raise ValueError, "Invalid endpoint value: '%s'" % str(_ep
)
1598 _et
= self
.getEndpointType()
1599 if ((_ep
is None and self
.__eptype
is not None) or
1600 (_ep
is not None and _ep
!= _et
)):
1601 self
.startChange('endpoint_type_changed')
1603 self
.endChange('endpoint_type_changed')
1604 self
.calcDimValues()
1605 self
.sendMessage('endpoint_type_changed', _et
)
1608 endpoint
= property(getEndpointType
, setEndpointType
,
1609 None, "Dimension endpoint type.")
1611 def getEndpointSize(self
):
1612 """Return the size of the Dimension endpoints.
1618 _es
= self
.__dimstyle
.getValue('DIM_ENDPOINT_SIZE')
1621 def setEndpointSize(self
, size
=None):
1622 """Set the size of the Dimension endpoints.
1624 setEndpointSize([size])
1626 Optional argument 'size' should be a float greater than or equal to 0.0.
1627 Calling this method without an argument sets the endpoint size to that
1628 given in the DimStle.
1631 raise RuntimeError, "Changing endpoint type allowed - object locked."
1633 if _size
is not None:
1634 _size
= util
.get_float(_size
)
1636 raise ValueError, "Invalid endpoint size: %g" % _size
1637 _es
= self
.getEndpointSize()
1638 if ((_size
is None and self
.__epsize
is not None) or
1639 (_size
is not None and abs(_size
- _es
) > 1e-10)):
1640 self
.startChange('endpoint_size_changed')
1641 self
.__epsize
= _size
1642 self
.endChange('endpoint_size_changed')
1643 self
.calcDimValues()
1644 self
.sendMessage('endpoint_size_changed', _es
)
1647 def getDimstrings(self
):
1648 """Return both primary and secondry dimstrings.
1652 return self
.__ds
1, self
.__ds
2
1654 def getPrimaryDimstring(self
):
1655 """ Return the DimString used for formatting the primary dimension.
1657 getPrimaryDimstring()
1661 def getSecondaryDimstring(self
):
1662 """Return the DimString used for formatting the secondary dimension.
1664 getSecondaryDimstring()
1668 def getDualDimMode(self
):
1669 """Return if the Dimension is displaying primary and secondary values.
1671 getDualDimMode(self)
1675 _mode
= self
.__dimstyle
.getValue('DIM_DUAL_MODE')
1678 def setDualDimMode(self
, mode
=None):
1679 """Set the Dimension to display both primary and secondary values.
1681 setDualDimMode([mode])
1683 Optional argument 'mode' should be either True or False.
1684 Invoking this method without arguments will set the dual dimension
1685 value display mode to that given from the DimStyle
1688 raise RuntimeError, "Changing dual mode not allowed - object locked."
1690 if _mode
is not None:
1691 util
.test_boolean(_mode
)
1692 _ddm
= self
.getDualDimMode()
1693 if ((_mode
is None and self
.__ddm
is not None) or
1694 (_mode
is not None and _mode
is not _ddm
)):
1695 self
.startChange('dual_mode_changed')
1697 self
.endChange('dual_mode_changed')
1698 self
.__ds
1.setBounds()
1699 self
.__ds
2.setBounds()
1700 self
.calcDimValues()
1701 self
.sendMessage('dual_mode_changed', _ddm
)
1704 dual_mode
= property(getDualDimMode
, setDualDimMode
, None,
1705 "Display both primary and secondary dimensions")
1707 def getOffset(self
):
1708 """Return the current offset value for the Dimension.
1712 _offset
= self
.__offset
1714 _offset
= self
.__dimstyle
.getValue('DIM_OFFSET')
1717 def setOffset(self
, offset
=None):
1718 """Set the offset value for the Dimension.
1722 Optional argument 'offset' should be a positive float.
1723 Calling this method without arguments sets the value to that
1724 given in the DimStyle.
1727 raise RuntimeError, "Setting offset not allowed - object locked."
1730 _o
= util
.get_float(_o
)
1732 raise ValueError, "Invalid dimension offset length: %g" % _o
1733 _off
= self
.getOffset()
1734 if ((_o
is None and self
.__offset
is not None) or
1735 (_o
is not None and abs(_o
- _off
) > 1e-10)):
1736 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
1737 self
.startChange('offset_changed')
1739 self
.endChange('offset_changed')
1740 self
.calcDimValues()
1741 self
.sendMessage('offset_changed', _off
)
1742 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
1745 offset
= property(getOffset
, setOffset
, None, "Dimension offset.")
1747 def getExtension(self
):
1748 """Get the extension length of the Dimension.
1752 _ext
= self
.__extlen
1754 _ext
= self
.__dimstyle
.getValue('DIM_EXTENSION')
1757 def setExtension(self
, ext
=None):
1758 """Set the extension length of the Dimension.
1762 Optional argument 'ext' should be a positive float value.
1763 Calling this method without arguments set the extension length
1764 to that given in the DimStyle.
1767 raise RuntimeError, "Setting extension not allowed - object locked."
1770 _e
= util
.get_float(_e
)
1772 raise ValueError, "Invalid dimension extension length: %g" % _e
1773 _ext
= self
.getExtension()
1774 if ((_e
is None and self
.__extlen
is not None) or
1775 (_e
is not None and abs(_e
- _ext
) > 1e-10)):
1776 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
1777 self
.startChange('extension_changed')
1779 self
.endChange('extension_changed')
1780 self
.calcDimValues()
1781 self
.sendMessage('extension_changed', _ext
)
1782 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
1785 extension
= property(getExtension
, setExtension
, None,
1786 "Dimension extension length.")
1788 def getPositionAsString(cls
, p
):
1789 """Return a text string for the dimension text position.
1791 getPositionAsString(p)
1793 This classmethod returns 'split', 'above', or 'below'
1795 if not isinstance(p
, int):
1796 raise TypeError, "Invalid argument type: " + `
type(p
)`
1797 if p
== Dimension
.DIM_TEXT_POS_SPLIT
:
1799 elif p
== Dimension
.DIM_TEXT_POS_ABOVE
:
1801 elif p
== Dimension
.DIM_TEXT_POS_BELOW
:
1804 raise ValueError, "Unexpected position value: %d" % p
1807 getPositionAsString
= classmethod(getPositionAsString
)
1809 def getPositionFromString(cls
, s
):
1810 """Return the dimension text position given a string argument.
1812 getPositionFromString(s)
1814 This classmethod returns a value based on the string argument:
1816 'split' -> Dimension.DIM_TEXT_POS_SPLIT
1817 'above' -> Dimension.DIM_TEXT_POS_ABOVE
1818 'below' -> Dimension.DIM_TEXT_POS_BELOW
1820 If the string is not listed above a ValueError execption is raised.
1823 if not isinstance(s
, str):
1824 raise TypeError, "Invalid argument type: " + `
type(s
)`
1827 _v
= Dimension
.DIM_TEXT_POS_SPLIT
1828 elif _ls
== 'above':
1829 _v
= Dimension
.DIM_TEXT_POS_ABOVE
1830 elif _ls
== 'below':
1831 _v
= Dimension
.DIM_TEXT_POS_BELOW
1833 raise ValueError, "Unexpected position string: " + s
1836 getPositionFromString
= classmethod(getPositionFromString
)
1838 def getPositionStrings(cls
):
1839 """Return the position values as strings.
1841 getPositionStrings()
1843 This classmethod returns a list of strings.
1850 getPositionStrings
= classmethod(getPositionStrings
)
1852 def getPositionValues(cls
):
1853 """Return the position values.
1857 This classmethod reutrns a list of values.
1859 return [Dimension
.DIM_TEXT_POS_SPLIT
,
1860 Dimension
.DIM_TEXT_POS_ABOVE
,
1861 Dimension
.DIM_TEXT_POS_BELOW
1864 getPositionValues
= classmethod(getPositionValues
)
1866 def getPosition(self
):
1867 """Return how the dimension text intersects the crossbar.
1871 _pos
= self
.__textpos
1873 _pos
= self
.__dimstyle
.getValue('DIM_POSITION')
1876 def setPosition(self
, pos
=None):
1877 """Set where the dimension text should be placed at the crossbar.
1881 Choices for optional argument 'pos' are:
1883 dimension.SPLIT => In the middle of the crossbar.
1884 dimension.ABOVE => Beyond the crossbar from the dimensioned objects.
1885 dimension.BELOW => Between the crossbar and the dimensioned objects.
1887 Calling this method without arguments sets the position to that given
1891 raise RuntimeError, "Setting position not allowed - object locked."
1893 if (_pos
!= Dimension
.DIM_TEXT_POS_SPLIT
and
1894 _pos
!= Dimension
.DIM_TEXT_POS_ABOVE
and
1895 _pos
!= Dimension
.DIM_TEXT_POS_BELOW
):
1896 raise ValueError, "Invalid dimension text position: '%s'" % str(_pos
)
1897 _dp
= self
.getPosition()
1898 if ((_pos
is None and self
.__textpos
is not None) or
1899 (_pos
is not None and _pos
!= _dp
)):
1900 self
.startChange('position_changed')
1901 self
.__textpos
= _pos
1902 self
.endChange('position_changed')
1903 self
.__ds
1.setBounds()
1904 self
.__ds
2.setBounds()
1905 self
.sendMessage('position_changed', _dp
)
1908 position
= property(getPosition
, setPosition
, None,
1909 "Dimension text position")
1911 def getPositionOffset(self
):
1912 """Get the offset for the dimension text and the crossbar/crossarc.
1916 _po
= self
.__poffset
1918 _po
= self
.__dimstyle
.getValue('DIM_POSITION_OFFSET')
1921 def setPositionOffset(self
, offset
=None):
1922 """Set the separation between the dimension text and the crossbar.
1924 setPositionOffset([offset])
1926 If this method is called without arguments, the text offset
1927 length is set to the value given in the DimStyle.
1928 If the argument 'offset' is supplied, it should be a positive float value.
1931 raise RuntimeError, "Setting text offset length not allowed - object locked."
1934 _o
= util
.get_float(_o
)
1936 raise ValueError, "Invalid text offset length: %g" % _o
1937 _to
= self
.getPositionOffset()
1938 if ((_o
is None and self
.__poffset
is not None) or
1939 (_o
is not None and abs(_o
- _to
) > 1e-10)):
1940 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
1941 self
.startChange('position_offset_changed')
1943 self
.endChange('position_offset_changed')
1944 self
.__ds
1.setBounds()
1945 self
.__ds
2.setBounds()
1946 self
.calcDimValues()
1947 self
.sendMessage('position_offset_changed', _to
)
1948 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
1951 position_offset
= property(getPositionOffset
, setPositionOffset
, None,
1952 "Text offset from crossbar/crossarc distance.")
1954 def getDualModeOffset(self
):
1955 """Get the offset for the dimension text when displaying two dimensions.
1959 _dmo
= self
.__dmoffset
1961 _dmo
= self
.__dimstyle
.getValue('DIM_DUAL_MODE_OFFSET')
1964 def setDualModeOffset(self
, offset
=None):
1965 """Set the separation between the dimensions and the dual mode dimension divider.
1967 setDualModeOffset([offset])
1969 If this method is called without arguments, the dual mode offset
1970 length is set to the value given in the DimStyle.
1971 If the argument 'offset' is supplied, it should be a positive float value.
1974 raise RuntimeError, "Setting dual mode offset length not allowed - object locked."
1977 _o
= util
.get_float(_o
)
1979 raise ValueError, "Invalid dual mode offset length: %g" % _o
1980 _dmo
= self
.getDualModeOffset()
1981 if ((_o
is None and self
.__dmoffset
is not None) or
1982 (_o
is not None and abs(_o
- _dmo
) > 1e-10)):
1983 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
1984 self
.startChange('dual_mode_offset_changed')
1985 self
.__dmoffset
= _o
1986 self
.endChange('dual_mode_offset_changed')
1987 self
.__ds
1.setBounds()
1988 self
.__ds
2.setBounds()
1989 self
.calcDimValues()
1990 self
.sendMessage('dual_mode_offset_changed', _dmo
)
1991 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
1994 dual_mode_offset
= property(getDualModeOffset
, setDualModeOffset
, None,
1995 "Text offset from dimension splitting bar when displaying two dimensions.")
1998 """Return the color of the dimension lines.
2004 _col
= self
.__dimstyle
.getValue('DIM_COLOR')
2007 def setColor(self
, c
=None):
2008 """Set the color of the dimension lines.
2012 Optional argument 'c' should be a Color instance. Calling this
2013 method without an argument sets the color to the value given
2017 raise RuntimeError, "Setting object color not allowed - object locked."
2020 if not isinstance(_c
, color
.Color
):
2021 raise TypeError, "Invalid color type: " + `
type(_c
)`
2022 _oc
= self
.getColor()
2023 if ((_c
is None and self
.__color
is not None) or
2024 (_c
is not None and _c
!= _oc
)):
2025 self
.startChange('color_changed')
2027 self
.endChange('color_changed')
2028 self
.sendMessage('color_changed', _oc
)
2031 color
= property(getColor
, setColor
, None, "Dimension Color")
2033 def getThickness(self
):
2034 """Return the thickness of the dimension bars.
2038 This method returns a float.
2040 _t
= self
.__thickness
2042 _t
= self
.__dimstyle
.getValue('DIM_THICKNESS')
2045 def setThickness(self
, thickness
=None):
2046 """Set the thickness of the dimension bars.
2048 setThickness([thickness])
2050 Optional argument 'thickness' should be a float value. Setting the
2051 thickness to 0 will display and print the lines with the thinnest
2052 value possible. Calling this method without arguments resets the
2053 thickness to the value defined in the DimStyle.
2056 raise RuntimeError, "Setting thickness not allowed - object locked."
2059 _t
= util
.get_float(_t
)
2061 raise ValueError, "Invalid thickness: %g" % _t
2062 _ot
= self
.getThickness()
2063 if ((_t
is None and self
.__thickness
is not None) or
2064 (_t
is not None and abs(_t
- _ot
) > 1e-10)):
2065 self
.startChange('thickness_changed')
2066 self
.__thickness
= _t
2067 self
.endChange('thickness_changed')
2068 self
.sendMessage('thickness_changed', _ot
)
2071 thickness
= property(getThickness
, setThickness
, None,
2072 "Dimension bar thickness.")
2075 """Return the Dimension scale factor.
2081 def setScale(self
, scale
=None):
2082 """Set the Dimension scale factor.
2086 Optional argument 's' should be a float value greater than 0. If
2087 no argument is supplied the default scale factor of 1 is set.
2090 raise RuntimeError, "Setting scale not allowed - object locked."
2094 _s
= util
.get_float(_s
)
2096 raise ValueError, "Invalid scale factor: %g" % _s
2098 if abs(_os
- _s
) > 1e-10:
2099 self
.startChange('scale_changed')
2101 self
.endChange('scale_changed')
2102 self
.sendMessage('scale_changed', _os
)
2105 scale
= property(getScale
, setScale
, None, "Dimension scale factor.")
2107 def getLocation(self
):
2108 """Return the location of the dimensional text values.
2112 return self
.__dimloc
2114 def setLocation(self
, x
, y
):
2115 """Set the location of the dimensional text values.
2119 The 'x' and 'y' arguments should be float values. The text is
2120 centered around that point.
2123 raise RuntimeError, "Setting location not allowed - object locked."
2124 _x
= util
.get_float(x
)
2125 _y
= util
.get_float(y
)
2126 _ox
, _oy
= self
.__dimloc
2127 if abs(_ox
- _x
) > 1e-10 or abs(_oy
- _y
) > 1e-10:
2128 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
2129 self
.startChange('location_changed')
2130 self
.__dimloc
= (_x
, _y
)
2131 self
.endChange('location_changed')
2132 self
.__ds
1.setBounds()
2133 self
.__ds
2.setBounds()
2134 self
.calcDimValues()
2135 self
.sendMessage('location_changed', _ox
, _oy
)
2136 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
2139 location
= property(getLocation
, setLocation
, None,
2140 "Dimension location")
2142 def move(self
, dx
, dy
):
2143 """Move a Dimension.
2147 The first argument gives the x-coordinate displacement,
2148 and the second gives the y-coordinate displacement. Both
2149 values should be floats.
2152 raise RuntimeError, "Moving not allowed - object locked."
2153 _dx
= util
.get_float(dx
)
2154 _dy
= util
.get_float(dy
)
2155 if abs(_dx
) > 1e-10 or abs(_dy
) > 1e-10:
2156 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
2157 _x
, _y
= self
.__dimloc
2158 self
.startChange('location_changed')
2159 self
.__dimloc
= ((_x
+ _dx
), (_y
+ _dy
))
2160 self
.endChange('location_changed')
2161 self
.__ds
1.setBounds()
2162 self
.__ds
2.setBounds()
2163 self
.calcDimValues()
2164 self
.sendMessage('location_changed', _x
, _y
)
2165 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
2168 def getStyleValue(self
, ds
, opt
):
2169 """Get the value in the DimStyle for some option
2171 getStyleValue(ds, opt)
2173 Argument 'ds' should be one of the DimString objects in
2174 the Dimension, and argument 'opt' should be a string.
2175 Valid choices for 'opt' are 'prefix', 'suffix', 'precision',
2176 'units', 'print_zero', 'print_decimal', 'font_family',
2177 'font_style', 'font_weight', 'size', 'color', 'angle',
2180 if not isinstance(ds
, DimString
):
2181 raise TypeError, "Invalid DimString type: " + `
type(ds
)`
2182 if not isinstance(opt
, str):
2183 raise TypeError, "Invalid DimStyle option type: " + `
type(opt
)`
2185 if ds
is self
.__ds
1:
2187 _key
= 'DIM_PRIMARY_PREFIX'
2188 elif opt
== 'suffix':
2189 _key
= 'DIM_PRIMARY_SUFFIX'
2190 elif opt
== 'precision':
2191 _key
= 'DIM_PRIMARY_PRECISION'
2192 elif opt
== 'units':
2193 _key
= 'DIM_PRIMARY_UNITS'
2194 elif opt
== 'print_zero':
2195 _key
= 'DIM_PRIMARY_LEADING_ZERO'
2196 elif opt
== 'print_decimal':
2197 _key
= 'DIM_PRIMARY_TRAILING_DECIMAL'
2198 elif opt
== 'font_family':
2199 _key
= 'DIM_PRIMARY_FONT_FAMILY'
2200 elif opt
== 'font_weight':
2201 _key
= 'DIM_PRIMARY_FONT_WEIGHT'
2202 elif opt
== 'font_style':
2203 _key
= 'DIM_PRIMARY_FONT_STYLE'
2205 _key
= 'DIM_PRIMARY_TEXT_SIZE'
2206 elif opt
== 'color':
2207 _key
= 'DIM_PRIMARY_FONT_COLOR'
2208 elif opt
== 'angle':
2209 _key
= 'DIM_PRIMARY_TEXT_ANGLE'
2210 elif opt
== 'alignment':
2211 _key
= 'DIM_PRIMARY_TEXT_ALIGNMENT'
2213 raise ValueError, "Unexpected option: %s" % opt
2214 elif ds
is self
.__ds
2:
2216 _key
= 'DIM_SECONDARY_PREFIX'
2217 elif opt
== 'suffix':
2218 _key
= 'DIM_SECONDARY_SUFFIX'
2219 elif opt
== 'precision':
2220 _key
= 'DIM_SECONDARY_PRECISION'
2221 elif opt
== 'units':
2222 _key
= 'DIM_SECONDARY_UNITS'
2223 elif opt
== 'print_zero':
2224 _key
= 'DIM_SECONDARY_LEADING_ZERO'
2225 elif opt
== 'print_decimal':
2226 _key
= 'DIM_SECONDARY_TRAILING_DECIMAL'
2227 elif opt
== 'font_family':
2228 _key
= 'DIM_SECONDARY_FONT_FAMILY'
2229 elif opt
== 'font_weight':
2230 _key
= 'DIM_SECONDARY_FONT_WEIGHT'
2231 elif opt
== 'font_style':
2232 _key
= 'DIM_SECONDARY_FONT_STYLE'
2234 _key
= 'DIM_SECONDARY_TEXT_SIZE'
2235 elif opt
== 'color':
2236 _key
= 'DIM_SECONDARY_FONT_COLOR'
2237 elif opt
== 'angle':
2238 _key
= 'DIM_SECONDARY_TEXT_ANGLE'
2239 elif opt
== 'alignment':
2240 _key
= 'DIM_SECONDARY_TEXT_ALIGNMENT'
2242 raise ValueError, "Unexpected option: %s" % opt
2244 raise ValueError, "DimString not used in this Dimension: " + `ds`
2246 raise ValueError, "Unexpected option: %s" % opt
2247 return self
.__dimstyle
.getValue(_key
)
2249 def getDimensions(self
, dimlen
):
2250 """Return the formatted dimensional values.
2252 getDimensions(dimlen)
2254 The argument 'dimlen' should be the length in millimeters.
2256 This method returns a list of the primary and secondary
2259 _dl
= util
.get_float(dimlen
)
2261 dims
.append(self
.__ds
1.formatDimension(_dl
))
2262 dims
.append(self
.__ds
2.formatDimension(_dl
))
2265 def calcDimValues(self
, allpts
=True):
2266 """Recalculate the values for dimensional display
2268 calcDimValues([allpts])
2270 This method is meant to be overriden by subclasses.
2274 def inRegion(self
, xmin
, ymin
, xmax
, ymax
, fully
=False):
2275 """Return whether or not a Dimension exists with a region.
2277 isRegion(xmin, ymin, xmax, ymax[, fully])
2279 The first four arguments define the boundary. The optional
2280 fifth argument 'fully' indicates whether or not the Dimension
2281 must be completely contained within the region or just pass
2284 This method should be overriden in classes derived from Dimension.
2288 def getBounds(self
):
2289 """Return the minimal and maximal locations of the dimension
2293 This method returns a tuple of four values - xmin, ymin, xmax, ymax.
2294 These values give the mimimum and maximum coordinates of the dimension
2297 This method should be overriden in classes derived from Dimension.
2299 _xmin
= _ymin
= -float(sys
.maxint
)
2300 _xmax
= _ymax
= float(sys
.maxint
)
2301 return _xmin
, _ymin
, _xmax
, _ymax
2303 def copyDimValues(self
, dim
):
2304 """This method adjusts one Dimension to match another Dimension
2308 Argument 'dim' must be a Dimension instance
2310 if not isinstance(dim
, Dimension
):
2311 raise TypeError, "Invalid Dimension type: " + `
type(dim
)`
2312 self
.setDimStyle(dim
.getDimStyle())
2313 self
.setOffset(dim
.getOffset())
2314 self
.setExtension(dim
.getExtension())
2315 self
.setEndpointType(dim
.getEndpointType())
2316 self
.setEndpointSize(dim
.getEndpointSize())
2317 self
.setColor(dim
.getColor())
2318 self
.setThickness(dim
.getThickness())
2319 self
.setDualDimMode(dim
.getDualDimMode())
2320 self
.setPositionOffset(dim
.getPositionOffset())
2321 self
.setDualModeOffset(dim
.getDualModeOffset())
2323 _ds1
, _ds2
= dim
.getDimstrings()
2326 _ds
.setTextStyle(_ds1
.getTextStyle())
2327 _ds
.setPrefix(_ds1
.getPrefix())
2328 _ds
.setSuffix(_ds1
.getSuffix())
2329 _ds
.setPrecision(_ds1
.getPrecision())
2330 _ds
.setUnits(_ds1
.getUnits())
2331 _ds
.setPrintZero(_ds1
.getPrintZero())
2332 _ds
.setPrintDecimal(_ds1
.getPrintDecimal())
2333 _ds
.setFamily(_ds1
.getFamily())
2334 _ds
.setWeight(_ds1
.getWeight())
2335 _ds
.setStyle(_ds1
.getStyle())
2336 _ds
.setColor(_ds1
.getColor())
2337 _ds
.setSize(_ds1
.getSize())
2338 _ds
.setAngle(_ds1
.getAngle())
2339 _ds
.setAlignment(_ds1
.getAlignment())
2342 _ds
.setTextStyle(_ds2
.getTextStyle())
2343 _ds
.setPrefix(_ds2
.getPrefix())
2344 _ds
.setSuffix(_ds2
.getSuffix())
2345 _ds
.setPrecision(_ds2
.getPrecision())
2346 _ds
.setUnits(_ds2
.getUnits())
2347 _ds
.setPrintZero(_ds2
.getPrintZero())
2348 _ds
.setPrintDecimal(_ds2
.getPrintDecimal())
2349 _ds
.setFamily(_ds2
.getFamily())
2350 _ds
.setWeight(_ds2
.getWeight())
2351 _ds
.setStyle(_ds2
.getStyle())
2352 _ds
.setColor(_ds2
.getColor())
2353 _ds
.setSize(_ds2
.getSize())
2354 _ds
.setAngle(_ds2
.getAngle())
2355 _ds
.setAlignment(_ds2
.getAlignment())
2357 def __dimstringChangePending(self
, p
, *args
):
2360 raise ValueError, "Invalid argument count: %d" % _alen
2363 self
.startChange(_arg
)
2364 elif (_arg
== 'textstyle_changed' or
2365 _arg
== 'font_family_changed' or
2366 _arg
== 'font_style_changed' or
2367 _arg
== 'font_weight_changed' or
2368 _arg
== 'font_color_changed' or
2369 _arg
== 'text_size_changed' or
2370 _arg
== 'text_angle_changed' or
2371 _arg
== 'text_alignment_changed' or
2372 _arg
== 'prefix_changed' or
2373 _arg
== 'suffix_changed' or
2374 _arg
== 'units_changed' or
2375 _arg
== 'precision_changed' or
2376 _arg
== 'print_zero_changed' or
2377 _arg
== 'print_decimal_changed'):
2378 self
.startChange('dimstring_changed')
2382 def __dimstringChangeComplete(self
, p
, *args
):
2385 raise ValueError, "Invalid argument count: %d" % _alen
2388 self
.endChanged(_arg
)
2389 elif (_arg
== 'textstyle_changed' or
2390 _arg
== 'font_family_changed' or
2391 _arg
== 'font_style_changed' or
2392 _arg
== 'font_weight_changed' or
2393 _arg
== 'font_color_changed' or
2394 _arg
== 'text_size_changed' or
2395 _arg
== 'text_angle_changed' or
2396 _arg
== 'text_alignment_changed' or
2397 _arg
== 'prefix_changed' or
2398 _arg
== 'suffix_changed' or
2399 _arg
== 'units_changed' or
2400 _arg
== 'precision_changed' or
2401 _arg
== 'print_zero_changed' or
2402 _arg
== 'print_decimal_changed'):
2403 self
.endChange('dimstring_changed')
2407 def sendsMessage(self
, m
):
2408 if m
in Dimension
.__messages
:
2410 return super(Dimension
, self
).sendsMessage(m
)
2413 # class stuff for dimension styles
2416 class DimStyle(object):
2417 """A class storing preferences for Dimensions
2419 The DimStyle class stores a set of dimension parameters
2420 that will be used when creating dimensions when the
2421 particular style is active.
2423 A DimStyle object has the following methods:
2425 getName(): Return the name of the DimStyle.
2426 getOption(): Return a single value in the DimStyle.
2427 getOptions(): Return all the options in the DimStyle.
2428 getValue(): Return the value of one of the DimStyle options.
2430 getOption() and getValue() are synonymous.
2432 The DimStyle class has the following classmethods:
2434 getDimStyleOptions(): Return the options defining a DimStyle.
2435 getDimStyleDefaultValue(): Return the default value for a DimStyle option.
2439 # the default values for the DimStyle class
2441 __deftextcolor
= color
.Color('#ffffff')
2442 __defdimcolor
= color
.Color(255,165,0)
2444 'DIM_PRIMARY_FONT_FAMILY' : 'Sans',
2445 'DIM_PRIMARY_TEXT_SIZE' : 1.0,
2446 'DIM_PRIMARY_FONT_WEIGHT' : text
.TextStyle
.FONT_NORMAL
,
2447 'DIM_PRIMARY_FONT_STYLE' : text
.TextStyle
.FONT_NORMAL
,
2448 'DIM_PRIMARY_FONT_COLOR' : __deftextcolor
,
2449 'DIM_PRIMARY_TEXT_ANGLE' : 0.0,
2450 'DIM_PRIMARY_TEXT_ALIGNMENT' : text
.TextStyle
.ALIGN_CENTER
,
2451 'DIM_PRIMARY_PREFIX' : u
'',
2452 'DIM_PRIMARY_SUFFIX' : u
'',
2453 'DIM_PRIMARY_PRECISION' : 3,
2454 'DIM_PRIMARY_UNITS' : units
.MILLIMETERS
,
2455 'DIM_PRIMARY_LEADING_ZERO' : True,
2456 'DIM_PRIMARY_TRAILING_DECIMAL' : True,
2457 'DIM_SECONDARY_FONT_FAMILY' : 'Sans',
2458 'DIM_SECONDARY_TEXT_SIZE' : 1.0,
2459 'DIM_SECONDARY_FONT_WEIGHT' : text
.TextStyle
.FONT_NORMAL
,
2460 'DIM_SECONDARY_FONT_STYLE' : text
.TextStyle
.FONT_NORMAL
,
2461 'DIM_SECONDARY_FONT_COLOR' : __deftextcolor
,
2462 'DIM_SECONDARY_TEXT_ANGLE' : 0.0,
2463 'DIM_SECONDARY_TEXT_ALIGNMENT' : text
.TextStyle
.ALIGN_CENTER
,
2464 'DIM_SECONDARY_PREFIX' : u
'',
2465 'DIM_SECONDARY_SUFFIX' : u
'',
2466 'DIM_SECONDARY_PRECISION' : 3,
2467 'DIM_SECONDARY_UNITS' : units
.MILLIMETERS
,
2468 'DIM_SECONDARY_LEADING_ZERO' : True,
2469 'DIM_SECONDARY_TRAILING_DECIMAL' : True,
2471 'DIM_EXTENSION' : 1.0,
2472 'DIM_COLOR' : __defdimcolor
,
2473 'DIM_THICKNESS' : 0.0,
2474 'DIM_POSITION' : Dimension
.DIM_TEXT_POS_SPLIT
,
2475 'DIM_ENDPOINT' : Dimension
.DIM_ENDPT_NONE
,
2476 'DIM_ENDPOINT_SIZE' : 1.0,
2477 'DIM_DUAL_MODE' : False,
2478 'DIM_POSITION_OFFSET' : 0.0,
2479 'DIM_DUAL_MODE_OFFSET' : 1.0,
2480 'RADIAL_DIM_PRIMARY_PREFIX' : u
'',
2481 'RADIAL_DIM_PRIMARY_SUFFIX' : u
'',
2482 'RADIAL_DIM_SECONDARY_PREFIX' : u
'',
2483 'RADIAL_DIM_SECONDARY_SUFFIX' : u
'',
2484 'RADIAL_DIM_DIA_MODE' : False,
2485 'ANGULAR_DIM_PRIMARY_PREFIX' : u
'',
2486 'ANGULAR_DIM_PRIMARY_SUFFIX' : u
'',
2487 'ANGULAR_DIM_SECONDARY_PREFIX' : u
'',
2488 'ANGULAR_DIM_SECONDARY_SUFFIX' : u
'',
2491 def __init__(self
, name
, keywords
={}):
2492 """Instantiate a DimStyle object.
2494 ds = DimStyle(name, keywords)
2496 The argument 'name' should be a unicode name, and the
2497 'keyword' argument should be a dict. The keys should
2498 be the same keywords used to set option values, such
2499 as DIM_OFFSET, DIM_EXTENSION, etc, and the value corresponding
2500 to each key should be set appropriately.
2502 super(DimStyle
, self
).__init
__()
2504 if not isinstance(_n
, types
.StringTypes
):
2505 raise TypeError, "Invalid DimStyle name type: "+ `
type(_n
)`
2506 if isinstance(_n
, str):
2508 if not isinstance(keywords
, dict):
2509 raise TypeError, "Invalid keywords argument type: " + `
type(keywords
)`
2510 from PythonCAD
.Generic
.options
import test_option
2511 self
.__opts
= baseobject
.ConstDict(str)
2513 for _kw
in keywords
:
2514 if _kw
not in DimStyle
.__defaults
:
2515 raise KeyError, "Unknown DimStyle keyword: " + _kw
2516 _val
= keywords
[_kw
]
2517 _valid
= test_option(_kw
, _val
)
2518 self
.__opts
[_kw
] = _val
2520 def __eq__(self
, obj
):
2521 """Test a DimStyle object for equality with another DimStyle.
2523 if not isinstance(obj
, DimStyle
):
2527 if self
.__name
!= obj
.getName():
2530 for _key
in DimStyle
.__defaults
.keys():
2531 _sv
= self
.getOption(_key
)
2532 _ov
= obj
.getOption(_key
)
2533 if ((_key
== 'DIM_PRIMARY_TEXT_SIZE') or
2534 (_key
== 'DIM_PRIMARY_TEXT_ANGLE') or
2535 (_key
== 'DIM_SECONDARY_TEXT_SIZE') or
2536 (_key
== 'DIM_SECONDARY_TEXT_ANGLE') or
2537 (_key
== 'DIM_OFFSET') or
2538 (_key
== 'DIM_EXTENSION') or
2539 (_key
== 'DIM_THICKNESS') or
2540 (_key
== 'DIM_ENDPOINT_SIZE') or
2541 (_key
== 'DIM_POSITION_OFFSET') or
2542 (_key
== 'DIM_DUAL_MODE_OFFSET')):
2543 if abs(_sv
- _ov
) > 1e-10:
2552 def __ne__(self
, obj
):
2553 """Test a DimStyle object for inequality with another DimStyle.
2555 return not self
== obj
2557 def getDimStyleOptions(cls
):
2558 """Return the options used to define a DimStyle instance.
2560 getDimStyleOptions()
2562 This classmethod returns a list of strings.
2564 return cls
.__defaults
.keys()
2566 getDimStyleOptions
= classmethod(getDimStyleOptions
)
2568 def getDimStyleDefaultValue(cls
, key
):
2569 """Return the default value for a DimStyle option.
2571 getDimStyleValue(key)
2573 Argument 'key' must be one of the options given in getDimStyleOptions().
2575 return cls
.__defaults
[key
]
2577 getDimStyleDefaultValue
= classmethod(getDimStyleDefaultValue
)
2580 """Return the name of the DimStyle.
2586 name
= property(getName
, None, None, "DimStyle name.")
2589 """Return the non-default options within the DimStyle.
2593 return self
.__opts
.keys()
2595 def getOptions(self
):
2596 """Return all the options stored within the DimStyle.
2600 _keys
= self
.__opts
.keys()
2601 for _key
in DimStyle
.__defaults
:
2602 if _key
not in self
.__opts
:
2606 def getOption(self
, key
):
2607 """Return the value of a particular option in the DimStyle.
2611 The key should be one of the strings returned from getOptions. If
2612 there is no value found in the DimStyle for the key, the value None
2615 if key
in self
.__opts
:
2616 _val
= self
.__opts
[key
]
2617 elif key
in DimStyle
.__defaults
:
2618 _val
= DimStyle
.__defaults
[key
]
2620 raise KeyError, "Unexpected DimStyle keyword: '%s'" % key
2623 def getValue(self
, key
):
2624 """Return the value of a particular option in the DimStyle.
2628 The key should be one of the strings returned from getOptions. This
2629 method raises a KeyError exception if the key is not found.
2632 if key
in self
.__opts
:
2633 _val
= self
.__opts
[key
]
2634 elif key
in DimStyle
.__defaults
:
2635 _val
= DimStyle
.__defaults
[key
]
2637 raise KeyError, "Unexpected DimStyle keyword: '%s'" % key
2640 def getValues(self
):
2641 """Return values comprising the DimStyle.
2646 _vals
['name'] = self
.__name
2647 for _opt
in self
.__opts
:
2648 _val
= self
.__opts
[_opt
]
2649 if ((_opt
== 'DIM_PRIMARY_FONT_COLOR') or
2650 (_opt
== 'DIM_SECONDARY_FONT_COLOR') or
2651 (_opt
== 'DIM_COLOR')):
2652 _vals
[_opt
] = _val
.getColors()
2657 class LinearDimension(Dimension
):
2658 """A class for Linear dimensions.
2660 The LinearDimension class is derived from the Dimension
2661 class, so it shares all of those methods and attributes.
2662 A LinearDimension should be used to display the absolute
2663 distance between two Point objects.
2665 A LinearDimension object has the following methods:
2667 {get/set}P1(): Get/Set the first Point for the LinearDimension.
2668 {get/set}P2(): Get/Set the second Point for the LinearDimension.
2669 getDimPoints(): Return the two Points used in this dimension.
2670 getDimLayers(): Return the two Layers holding the Points.
2671 getDimXPoints(): Get the x-coordinates of the dimension bar positions.
2672 getDimYPoints(): Get the y-coordinates of the dimension bar positions.
2673 getDimMarkerPoints(): Get the locaiton of the dimension endpoint markers.
2674 calcMarkerPoints(): Calculate the coordinates of any dimension marker objects.
2678 'point_changed' : True,
2681 def __init__(self
, p1
, p2
, x
, y
, ds
=None, **kw
):
2682 """Instantiate a LinearDimension object.
2684 ldim = LinearDimension(p1, p2, x, y, ds)
2686 p1: A Point contained in a Layer
2687 p2: A Point contained in a Layer
2688 x: The x-coordinate of the dimensional text
2689 y: The y-coordinate of the dimensional text
2690 ds: The DimStyle used for this Dimension.
2692 if not isinstance(p1
, point
.Point
):
2693 raise TypeError, "Invalid point type: " + `
type(p1
)`
2694 if p1
.getParent() is None:
2695 raise ValueError, "Point P1 not stored in a Layer!"
2696 if not isinstance(p2
, point
.Point
):
2697 raise TypeError, "Invalid point type: " + `
type(p2
)`
2698 if p2
.getParent() is None:
2699 raise ValueError, "Point P2 not stored in a Layer!"
2700 super(LinearDimension
, self
).__init
__(x
, y
, ds
, **kw
)
2703 self
.__bar
1 = DimBar()
2704 self
.__bar
2 = DimBar()
2705 self
.__crossbar
= DimCrossbar()
2707 p1
.connect('moved', self
.__movePoint
)
2708 p1
.connect('change_pending', self
.__pointChangePending
)
2709 p1
.connect('change_complete', self
.__pointChangeComplete
)
2711 p2
.connect('moved', self
.__movePoint
)
2712 p2
.connect('change_pending', self
.__pointChangePending
)
2713 p2
.connect('change_complete', self
.__pointChangeComplete
)
2714 self
.calcDimValues()
2716 def __eq__(self
, ldim
):
2717 """Test two LinearDimension objects for equality.
2719 if not isinstance(ldim
, LinearDimension
):
2721 _lp1
= self
.__p
1.getParent()
2722 _lp2
= self
.__p
2.getParent()
2723 _p1
, _p2
= ldim
.getDimPoints()
2724 _l1
= _p1
.getParent()
2725 _l2
= _p2
.getParent()
2728 self
.__p
1 == _p1
and
2733 self
.__p
1 == _p2
and
2738 def __ne__(self
, ldim
):
2739 """Test two LinearDimension objects for equality.
2741 if not isinstance(ldim
, LinearDimension
):
2743 _lp1
= self
.__p
1.getParent()
2744 _lp2
= self
.__p
2.getParent()
2745 _p1
, _p2
= ldim
.getDimPoints()
2746 _l1
= _p1
.getParent()
2748 _l2
= _p2
.getParent()
2751 self
.__p
1 == _p1
and
2756 self
.__p
1 == _p2
and
2762 self
.__p
1.disconnect(self
)
2763 self
.__p
1.freeUser(self
)
2764 self
.__p
2.disconnect(self
)
2765 self
.__p
2.freeUser(self
)
2766 self
.__bar
1 = self
.__bar
2 = self
.__crossbar
= None
2767 self
.__p
1 = self
.__p
2 = None
2768 super(LinearDimension
, self
).finish()
2770 def getValues(self
):
2771 """Return values comprising the LinearDimension.
2775 This method extends the Dimension::getValues() method.
2777 _data
= super(LinearDimension
, self
).getValues()
2778 _data
.setValue('type', 'ldim')
2779 _data
.setValue('p1', self
.__p
1.getID())
2780 _layer
= self
.__p
1.getParent()
2781 _data
.setValue('l1', _layer
.getID())
2782 _data
.setValue('p2', self
.__p
2.getID())
2783 _layer
= self
.__p
2.getParent()
2784 _data
.setValue('l2', _layer
.getID())
2788 """Return the first Point of a LinearDimension.
2795 """Set the first Point of a LinearDimension.
2799 There is one required argument for this method:
2801 p: A Point contained in a Layer
2804 raise RuntimeError, "Setting point not allowed - object locked."
2805 if not isinstance(p
, point
.Point
):
2806 raise TypeError, "Invalid point type: " + `
type(p
)`
2807 if p
.getParent() is None:
2808 raise ValueError, "Point not stored in a Layer!"
2811 _pt
.disconnect(self
)
2813 self
.startChange('point_changed')
2815 self
.endChange('point_changed')
2816 self
.sendMessage('point_changed', _pt
, p
)
2818 p
.connect('moved', self
.__movePoint
)
2819 p
.connect('change_pending', self
.__pointChangePending
)
2820 p
.connect('change_complete', self
.__pointChangeComplete
)
2821 if abs(_pt
.x
- p
.x
) > 1e-10 or abs(_pt
.y
- p
.y
) > 1e-10:
2822 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
2823 self
.calcDimValues()
2824 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
2827 p1
= property(getP1
, None, None, "Dimension first point.")
2830 """Return the second point of a LinearDimension.
2837 """Set the second Point of a LinearDimension.
2841 There is one required argument for this method:
2843 p: A Point contained in a Layer
2846 raise RuntimeError, "Setting point not allowed - object locked."
2847 if not isinstance(p
, point
.Point
):
2848 raise TypeError, "Invalid point type: " + `
type(p
)`
2849 if p
.getParent() is None:
2850 raise ValueError, "Point not stored in a Layer!"
2853 _pt
.disconnect(self
)
2855 self
.startChange('point_changed')
2857 self
.endChange('point_changed')
2858 self
.sendMessage('point_changed', _pt
, p
)
2860 p
.connect('moved', self
.__movePoint
)
2861 p
.connect('change_pending', self
.__pointChangePending
)
2862 p
.connect('change_complete', self
.__pointChangeComplete
)
2863 if abs(_pt
.x
- p
.x
) > 1e-10 or abs(_pt
.y
- p
.y
) > 1e-10:
2864 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
2865 self
.calcDimValues()
2866 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
2869 p2
= property(getP2
, None, None, "Dimension second point.")
2871 def getDimPoints(self
):
2872 """Return both points used in the LinearDimension.
2876 The two points are returned in a tuple.
2878 return self
.__p
1, self
.__p
2
2880 def getDimBars(self
):
2881 """Return the dimension boundary bars.
2885 return self
.__bar
1, self
.__bar
2
2887 def getDimCrossbar(self
):
2888 """Return the dimension crossbar.
2892 return self
.__crossbar
2894 def getDimLayers(self
):
2895 """Return both layers used in the LinearDimension.
2899 The two layers are returned in a tuple.
2901 _l1
= self
.__p
1.getParent()
2902 _l2
= self
.__p
2.getParent()
2905 def calculate(self
):
2906 """Determine the length of this LinearDimension.
2910 return self
.__p
1 - self
.__p
2
2912 def inRegion(self
, xmin
, ymin
, xmax
, ymax
, fully
=False):
2913 """Return whether or not a LinearDimension exists within a region.
2915 isRegion(xmin, ymin, xmax, ymax[, fully])
2917 The four arguments define the boundary of an area, and the
2918 function returns True if the LinearDimension lies within that
2919 area. If the optional argument fully is used and is True,
2920 then the dimension points and the location of the dimension
2921 text must lie within the boundary. Otherwise, the function
2924 _xmin
= util
.get_float(xmin
)
2925 _ymin
= util
.get_float(ymin
)
2926 _xmax
= util
.get_float(xmax
)
2928 raise ValueError, "Illegal values: xmax < xmin"
2929 _ymax
= util
.get_float(ymax
)
2931 raise ValueError, "Illegal values: ymax < ymin"
2932 util
.test_boolean(fully
)
2933 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
2934 if ((_dxmin
> _xmax
) or
2940 if ((_dxmin
> _xmin
) and
2941 (_dymin
> _ymin
) and
2942 (_dxmax
< _xmax
) and
2946 _dx
, _dy
= self
.getLocation()
2947 if _xmin
< _dx
< _xmax
and _ymin
< _dy
< _ymax
: # dim text
2952 _ep1
, _ep2
= self
.__bar
1.getEndpoints()
2955 if util
.in_region(_x1
, _y1
, _x2
, _y2
, _xmin
, _ymin
, _xmax
, _ymax
):
2960 _ep1
, _ep2
= self
.__bar
2.getEndpoints()
2963 if util
.in_region(_x1
, _y1
, _x2
, _y2
, _xmin
, _ymin
, _xmax
, _ymax
):
2968 _ep1
, _ep2
= self
.__crossbar
.getEndpoints()
2971 return util
.in_region(_x1
, _y1
, _x2
, _y2
, _xmin
, _ymin
, _xmax
, _ymax
)
2973 def calcDimValues(self
, allpts
=True):
2974 """Recalculate the values for dimensional display.
2976 calcDimValues([allpts])
2978 This method calculates where the points for the dimension
2979 bars and crossbar are located. The argument 'allpts' is
2980 optional. By default it is True. If the argument is set to
2981 False, then the coordinates of the dimension marker points
2982 will not be calculated.
2985 util
.test_boolean(_allpts
)
2986 _p1
, _p2
= self
.getDimPoints()
2989 _crossbar
= self
.__crossbar
2990 _p1x
, _p1y
= _p1
.getCoords()
2991 _p2x
, _p2y
= _p2
.getCoords()
2992 _dx
, _dy
= self
.getLocation()
2993 _offset
= self
.getOffset()
2994 _ext
= self
.getExtension()
2996 # see comp.graphics.algorithms.faq section on calcuating
2997 # the distance between a point and line for info about
2998 # the following equations ...
3002 _rnum
= ((_dx
- _p1x
) * _dpx
) + ((_dy
- _p1y
) * _dpy
)
3003 _snum
= ((_p1y
- _dy
) * _dpx
) - ((_p1x
- _dx
) * _dpy
)
3004 _den
= pow(_dpx
, 2) + pow(_dpy
, 2)
3007 _sep
= abs(_s
) * math
.sqrt(_den
)
3008 if abs(_dpx
) < 1e-10: # vertical
3010 _slope
= math
.pi
/2.0
3012 _slope
= -math
.pi
/2.0
3013 elif abs(_dpy
) < 1e-10: # horizontal
3019 _slope
= math
.atan2(_dpy
, _dpx
)
3020 if _s
< 0.0: # dim point left of p1-p2 line
3021 _angle
= _slope
+ (math
.pi
/2.0)
3022 else: # dim point right of p1-p2 line (or on it)
3023 _angle
= _slope
- (math
.pi
/2.0)
3024 _sin_angle
= math
.sin(_angle
)
3025 _cos_angle
= math
.cos(_angle
)
3026 _x
= _p1x
+ (_offset
* _cos_angle
)
3027 _y
= _p1y
+ (_offset
* _sin_angle
)
3028 _bar1
.setFirstEndpoint(_x
, _y
)
3030 _px
= _p1x
+ (_r
* _dpx
)
3031 _py
= _p1y
+ (_r
* _dpy
)
3032 _x
= _px
+ (_sep
* _cos_angle
)
3033 _y
= _py
+ (_sep
* _sin_angle
)
3035 _x
= _p1x
+ (_sep
* _cos_angle
)
3036 _y
= _p1y
+ (_sep
* _sin_angle
)
3037 _crossbar
.setFirstEndpoint(_x
, _y
)
3038 _x
= _p1x
+ (_sep
* _cos_angle
)
3039 _y
= _p1y
+ (_sep
* _sin_angle
)
3040 _crossbar
.setFirstCrossbarPoint(_x
, _y
)
3041 _x
= _p1x
+ ((_sep
+ _ext
) * _cos_angle
)
3042 _y
= _p1y
+ ((_sep
+ _ext
) * _sin_angle
)
3043 _bar1
.setSecondEndpoint(_x
, _y
)
3044 _x
= _p2x
+ (_offset
* _cos_angle
)
3045 _y
= _p2y
+ (_offset
* _sin_angle
)
3046 _bar2
.setFirstEndpoint(_x
, _y
)
3048 _px
= _p1x
+ (_r
* _dpx
)
3049 _py
= _p1y
+ (_r
* _dpy
)
3050 _x
= _px
+ (_sep
* _cos_angle
)
3051 _y
= _py
+ (_sep
* _sin_angle
)
3053 _x
= _p2x
+ (_sep
* _cos_angle
)
3054 _y
= _p2y
+ (_sep
* _sin_angle
)
3055 _crossbar
.setSecondEndpoint(_x
, _y
)
3056 _x
= _p2x
+ (_sep
* _cos_angle
)
3057 _y
= _p2y
+ (_sep
* _sin_angle
)
3058 _crossbar
.setSecondCrossbarPoint(_x
, _y
)
3059 _x
= _p2x
+ ((_sep
+ _ext
) * _cos_angle
)
3060 _y
= _p2y
+ ((_sep
+ _ext
) * _sin_angle
)
3061 _bar2
.setSecondEndpoint(_x
, _y
)
3063 self
.calcMarkerPoints()
3065 def calcMarkerPoints(self
):
3066 """Calculate and store the dimension endpoint markers coordinates.
3070 _type
= self
.getEndpointType()
3071 _crossbar
= self
.__crossbar
3072 _crossbar
.clearMarkerPoints()
3073 if _type
== Dimension
.DIM_ENDPT_NONE
or _type
== Dimension
.DIM_ENDPT_CIRCLE
:
3075 _size
= self
.getEndpointSize()
3076 _p1
, _p2
= _crossbar
.getCrossbarPoints()
3079 # print "x1: %g" % _x1
3080 # print "y1: %g" % _y1
3081 # print "x2: %g" % _x2
3082 # print "y2: %g" % _y2
3083 _sine
, _cosine
= _crossbar
.getSinCosValues()
3084 if _type
== Dimension
.DIM_ENDPT_ARROW
or _type
== Dimension
.DIM_ENDPT_FILLED_ARROW
:
3086 # p1 -> (x,y) = (size, _height)
3087 _mx
= (_cosine
* _size
- _sine
* _height
) + _x1
3088 _my
= (_sine
* _size
+ _cosine
* _height
) + _y1
3089 _crossbar
.storeMarkerPoint(_mx
, _my
)
3090 # p2 -> (x,y) = (size, -_height)
3091 _mx
= (_cosine
* _size
- _sine
*(-_height
)) + _x1
3092 _my
= (_sine
* _size
+ _cosine
*(-_height
)) + _y1
3093 _crossbar
.storeMarkerPoint(_mx
, _my
)
3094 # p3 -> (x,y) = (-size, _height)
3095 _mx
= (_cosine
* (-_size
) - _sine
* _height
) + _x2
3096 _my
= (_sine
* (-_size
) + _cosine
* _height
) + _y2
3097 _crossbar
.storeMarkerPoint(_mx
, _my
)
3098 # p4 -> (x,y) = (-size, -_height)
3099 _mx
= (_cosine
* (-_size
) - _sine
*(-_height
)) + _x2
3100 _my
= (_sine
* (-_size
) + _cosine
*(-_height
)) + _y2
3101 _crossbar
.storeMarkerPoint(_mx
, _my
)
3102 elif _type
== Dimension
.DIM_ENDPT_SLASH
:
3103 _angle
= 30.0 * _dtr
# slope of slash
3104 _height
= 0.5 * _size
* math
.sin(_angle
)
3105 _length
= 0.5 * _size
* math
.cos(_angle
)
3106 # p1 -> (x,y) = (-_length, -_height)
3107 _sx1
= (_cosine
* (-_length
) - _sine
* (-_height
))
3108 _sy1
= (_sine
* (-_length
) + _cosine
* (-_height
))
3109 # p2 -> (x,y) = (_length, _height)
3110 _sx2
= (_cosine
* _length
- _sine
* _height
)
3111 _sy2
= (_sine
* _length
+ _cosine
* _height
)
3113 # shift the calculate based on the location of the
3118 _crossbar
.storeMarkerPoint(_mx
, _my
)
3121 _crossbar
.storeMarkerPoint(_mx
, _my
)
3124 _crossbar
.storeMarkerPoint(_mx
, _my
)
3127 _crossbar
.storeMarkerPoint(_mx
, _my
)
3129 raise ValueError, "Unexpected endpoint type: '%s'" % str(_type
)
3131 def mapCoords(self
, x
, y
, tol
=tolerance
.TOL
):
3132 """Test an x/y coordinate pair if it could lay on the dimension.
3134 mapCoords(x, y[, tol])
3136 This method has two required parameters:
3141 These should both be float values.
3143 There is an optional third parameter tol giving the maximum distance
3144 from the dimension bars that the x/y coordinates may lie.
3146 _x
= util
.get_float(x
)
3147 _y
= util
.get_float(y
)
3148 _t
= tolerance
.toltest(tol
)
3149 _ep1
, _ep2
= self
.__bar
1.getEndpoints()
3153 _mp
= util
.map_coords(_x
, _y
, _ep1
[0], _ep1
[1], _ep2
[0], _ep2
[1], _t
)
3159 _ep1
, _ep2
= self
.__bar
2.getEndpoints()
3160 _mp
= util
.map_coords(_x
, _y
, _ep1
[0], _ep1
[1], _ep2
[0], _ep2
[1], _t
)
3166 _ep1
, _ep2
= self
.__crossbar
.getEndpoints()
3167 return util
.map_coords(_x
, _y
, _ep1
[0], _ep1
[1], _ep2
[0], _ep2
[1], _t
)
3169 def onDimension(self
, x
, y
, tol
=tolerance
.TOL
):
3170 return self
.mapCoords(x
, y
, tol
) is not None
3172 def getBounds(self
):
3173 """Return the minimal and maximal locations of the dimension
3177 This method overrides the Dimension::getBounds() method
3179 _dx
, _dy
= self
.getLocation()
3182 _ep1
, _ep2
= self
.__bar
1.getEndpoints()
3183 _dxpts
.append(_ep1
[0])
3184 _dypts
.append(_ep1
[1])
3185 _dxpts
.append(_ep2
[0])
3186 _dypts
.append(_ep2
[1])
3187 _ep1
, _ep2
= self
.__bar
2.getEndpoints()
3188 _dxpts
.append(_ep1
[0])
3189 _dypts
.append(_ep1
[1])
3190 _dxpts
.append(_ep2
[0])
3191 _dypts
.append(_ep2
[1])
3192 _ep1
, _ep2
= self
.__crossbar
.getEndpoints()
3193 _dxpts
.append(_ep1
[0])
3194 _dypts
.append(_ep1
[1])
3195 _dxpts
.append(_ep2
[0])
3196 _dypts
.append(_ep2
[1])
3197 _xmin
= min(_dx
, min(_dxpts
))
3198 _ymin
= min(_dy
, min(_dypts
))
3199 _xmax
= max(_dx
, max(_dxpts
))
3200 _ymax
= max(_dy
, max(_dypts
))
3201 return _xmin
, _ymin
, _xmax
, _ymax
3206 _x
, _y
= self
.getLocation()
3207 _ds
= self
.getDimStyle()
3208 _ldim
= LinearDimension(_p1
, _p2
, _x
, _y
, _ds
)
3209 _ldim
.copyDimValues(self
)
3212 def __pointChangePending(self
, p
, *args
):
3215 raise ValueError, "Invalid argument count: %d" % _alen
3216 if args
[0] == 'moved':
3217 self
.startChange('moved')
3219 def __pointChangeComplete(self
, p
, *args
):
3222 raise ValueError, "Invalid argument count: %d" % _alen
3223 if args
[0] == 'moved':
3224 self
.endChange('moved')
3226 def __movePoint(self
, p
, *args
):
3229 raise ValueError, "Invalid argument count: %d" % _alen
3230 if p
is not self
.__p
1 and p
is not self
.__p
2:
3231 raise ValueError, "Unexpected dimension point: " + `p`
3232 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
3233 self
.calcDimValues(True)
3234 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
3236 def sendsMessage(self
, m
):
3237 if m
in LinearDimension
.__messages
:
3239 return super(LinearDimension
, self
).sendsMessage(m
)
3241 class HorizontalDimension(LinearDimension
):
3242 """A class representing Horizontal dimensions.
3244 This class is derived from the LinearDimension class, so
3245 it shares all those attributes and methods of its parent.
3247 def __init__(self
, p1
, p2
, x
, y
, ds
=None, **kw
):
3248 """Initialize a Horizontal Dimension.
3250 hdim = HorizontalDimension(p1, p2, x, y, ds)
3252 p1: A Point contained in a Layer
3253 p2: A Point contained in a Layer
3254 x: The x-coordinate of the dimensional text
3255 y: The y-coordinate of the dimensional text
3256 ds: The DimStyle used for this Dimension.
3258 super(HorizontalDimension
, self
).__init
__(p1
, p2
, x
, y
, ds
, **kw
)
3260 def getValues(self
):
3261 """Return values comprising the HorizontalDimension.
3265 This method extends the LinearDimension::getValues() method.
3267 _data
= super(HorizontalDimension
, self
).getValues()
3268 _data
.setValue('type', 'hdim')
3271 def calculate(self
):
3272 """Determine the length of this HorizontalDimension.
3276 _p1
, _p2
= self
.getDimPoints()
3277 return abs(_p1
.x
- _p2
.x
)
3279 def calcDimValues(self
, allpts
=True):
3280 """Recalculate the values for dimensional display.
3282 calcDimValues([allpts])
3284 This method overrides the LinearDimension::calcDimValues() method.
3287 util
.test_boolean(_allpts
)
3288 _p1
, _p2
= self
.getDimPoints()
3289 _bar1
, _bar2
= self
.getDimBars()
3290 _crossbar
= self
.getDimCrossbar()
3291 _p1x
, _p1y
= _p1
.getCoords()
3292 _p2x
, _p2y
= _p2
.getCoords()
3293 _dx
, _dy
= self
.getLocation()
3294 _offset
= self
.getOffset()
3295 _ext
= self
.getExtension()
3296 _crossbar
.setFirstEndpoint(_p1x
, _dy
)
3297 _crossbar
.setSecondEndpoint(_p2x
, _dy
)
3298 if _dx
< min(_p1x
, _p2x
) or _dx
> max(_p1x
, _p2x
):
3301 _crossbar
.setFirstEndpoint(_dx
, _dy
)
3303 _crossbar
.setSecondEndpoint(_dx
, _dy
)
3306 _crossbar
.setSecondEndpoint(_dx
, _dy
)
3308 _crossbar
.setFirstEndpoint(_dx
, _dy
)
3309 _crossbar
.setFirstCrossbarPoint(_p1x
, _dy
)
3310 _crossbar
.setSecondCrossbarPoint(_p2x
, _dy
)
3311 if _dy
< min(_p1y
, _p2y
):
3312 _bar1
.setFirstEndpoint(_p1x
, (_p1y
- _offset
))
3313 _bar1
.setSecondEndpoint(_p1x
, (_dy
- _ext
))
3314 _bar2
.setFirstEndpoint(_p2x
, (_p2y
- _offset
))
3315 _bar2
.setSecondEndpoint(_p2x
, (_dy
- _ext
))
3316 elif _dy
> max(_p1y
, _p2y
):
3317 _bar1
.setFirstEndpoint(_p1x
, (_p1y
+ _offset
))
3318 _bar1
.setSecondEndpoint(_p1x
, (_dy
+ _ext
))
3319 _bar2
.setFirstEndpoint(_p2x
, (_p2y
+ _offset
))
3320 _bar2
.setSecondEndpoint(_p2x
, (_dy
+ _ext
))
3323 _bar1
.setFirstEndpoint(_p1x
, (_p1y
+ _offset
))
3324 _bar1
.setSecondEndpoint(_p1x
, (_dy
+ _ext
))
3326 _bar1
.setFirstEndpoint(_p1x
, (_p1y
- _offset
))
3327 _bar1
.setSecondEndpoint(_p1x
, (_dy
- _ext
))
3329 _bar2
.setFirstEndpoint(_p2x
, (_p2y
+ _offset
))
3330 _bar2
.setSecondEndpoint(_p2x
, (_dy
+ _ext
))
3332 _bar2
.setFirstEndpoint(_p2x
, (_p2y
- _offset
))
3333 _bar2
.setSecondEndpoint(_p2x
, (_dy
- _ext
))
3335 self
.calcMarkerPoints()
3338 _p1
, _p2
= self
.getDimPoints()
3339 _x
, _y
= self
.getLocation()
3340 _ds
= self
.getDimStyle()
3341 _hdim
= HorizontalDimension(_p1
, _p2
, _x
, _y
, _ds
)
3342 _hdim
.copyDimValues(self
)
3345 class VerticalDimension(LinearDimension
):
3346 """A class representing Vertical dimensions.
3348 This class is derived from the LinearDimension class, so
3349 it shares all those attributes and methods of its parent.
3352 def __init__(self
, p1
, p2
, x
, y
, ds
=None, **kw
):
3353 """Initialize a Vertical Dimension.
3355 vdim = VerticalDimension(p1, p2, x, y, ds)
3357 p1: A Point contained in a Layer
3358 p2: A Point contained in a Layer
3359 x: The x-coordinate of the dimensional text
3360 y: The y-coordinate of the dimensional text
3361 ds: The DimStyle used for this Dimension.
3363 super(VerticalDimension
, self
).__init
__(p1
, p2
, x
, y
, ds
, **kw
)
3365 def getValues(self
):
3366 """Return values comprising the VerticalDimension.
3370 This method extends the LinearDimension::getValues() method.
3372 _data
= super(VerticalDimension
, self
).getValues()
3373 _data
.setValue('type', 'vdim')
3376 def calculate(self
):
3377 """Determine the length of this VerticalDimension.
3381 _p1
, _p2
= self
.getDimPoints()
3382 return abs(_p1
.y
- _p2
.y
)
3384 def calcDimValues(self
, allpts
=True):
3385 """Recalculate the values for dimensional display.
3387 calcDimValues([allpts])
3389 This method overrides the LinearDimension::calcDimValues() method.
3392 util
.test_boolean(_allpts
)
3393 _p1
, _p2
= self
.getDimPoints()
3394 _bar1
, _bar2
= self
.getDimBars()
3395 _crossbar
= self
.getDimCrossbar()
3396 _p1x
, _p1y
= _p1
.getCoords()
3397 _p2x
, _p2y
= _p2
.getCoords()
3398 _dx
, _dy
= self
.getLocation()
3399 _offset
= self
.getOffset()
3400 _ext
= self
.getExtension()
3401 _crossbar
.setFirstEndpoint(_dx
, _p1y
)
3402 _crossbar
.setSecondEndpoint(_dx
, _p2y
)
3403 if _dy
< min(_p1y
, _p2y
) or _dy
> max(_p1y
, _p2y
):
3406 _crossbar
.setFirstEndpoint(_dx
, _dy
)
3408 _crossbar
.setSecondEndpoint(_dx
, _dy
)
3411 _crossbar
.setSecondEndpoint(_dx
, _dy
)
3413 _crossbar
.setFirstEndpoint(_dx
, _dy
)
3414 _crossbar
.setFirstCrossbarPoint(_dx
, _p1y
)
3415 _crossbar
.setSecondCrossbarPoint(_dx
, _p2y
)
3416 if _dx
< min(_p1x
, _p2x
):
3417 _bar1
.setFirstEndpoint((_p1x
- _offset
), _p1y
)
3418 _bar1
.setSecondEndpoint((_dx
- _ext
), _p1y
)
3419 _bar2
.setFirstEndpoint((_p2x
- _offset
), _p2y
)
3420 _bar2
.setSecondEndpoint((_dx
- _ext
), _p2y
)
3421 elif _dx
> max(_p1x
, _p2x
):
3422 _bar1
.setFirstEndpoint((_p1x
+ _offset
), _p1y
)
3423 _bar1
.setSecondEndpoint((_dx
+ _ext
), _p1y
)
3424 _bar2
.setFirstEndpoint((_p2x
+ _offset
), _p2y
)
3425 _bar2
.setSecondEndpoint((_dx
+ _ext
), _p2y
)
3428 _bar1
.setFirstEndpoint((_p1x
+ _offset
), _p1y
)
3429 _bar1
.setSecondEndpoint((_dx
+ _ext
), _p1y
)
3431 _bar1
.setFirstEndpoint((_p1x
- _offset
), _p1y
)
3432 _bar1
.setSecondEndpoint((_dx
- _ext
), _p1y
)
3434 _bar2
.setFirstEndpoint((_p2x
+ _offset
), _p2y
)
3435 _bar2
.setSecondEndpoint((_dx
+ _ext
), _p2y
)
3437 _bar2
.setFirstEndpoint((_p2x
- _offset
), _p2y
)
3438 _bar2
.setSecondEndpoint((_dx
- _ext
), _p2y
)
3440 self
.calcMarkerPoints()
3443 _p1
, _p2
= self
.getDimPoints()
3444 _x
, _y
= self
.getLocation()
3445 _ds
= self
.getDimStyle()
3446 _vdim
= VerticalDimension(_p1
, _p2
, _x
, _y
, _ds
)
3447 _vdim
.copyDimValues(self
)
3450 class RadialDimension(Dimension
):
3451 """A class for Radial dimensions.
3453 The RadialDimension class is derived from the Dimension
3454 class, so it shares all of those methods and attributes.
3455 A RadialDimension should be used to display either the
3456 radius or diamter of a Circle object.
3458 A RadialDimension object has the following methods:
3460 {get/set}DimCircle(): Get/Set the measured circle object.
3461 getDimLayer(): Return the layer containing the measured circle.
3462 {get/set}DiaMode(): Get/Set if the RadialDimension should return diameters.
3463 getDimXPoints(): Get the x-coordinates of the dimension bar positions.
3464 getDimYPoints(): Get the y-coordinates of the dimension bar positions.
3465 getDimMarkerPoints(): Get the locaiton of the dimension endpoint markers.
3466 getDimCrossbar(): Get the DimCrossbar object of the RadialDimension.
3467 calcDimValues(): Calculate the endpoint of the dimension line.
3468 mapCoords(): Return coordinates on the dimension near some point.
3469 onDimension(): Test if an x/y coordinate pair fall on the dimension line.
3472 'dimobj_changed' : True,
3473 'dia_mode_changed' : True,
3476 def __init__(self
, cir
, x
, y
, ds
=None, **kw
):
3477 """Initialize a RadialDimension object.
3479 rdim = RadialDimension(cir, x, y, ds)
3481 cir: A Circle or Arc object
3482 x: The x-coordinate of the dimensional text
3483 y: The y-coordinate of the dimensional text
3484 ds: The DimStyle used for this Dimension.
3486 super(RadialDimension
, self
).__init
__(x
, y
, ds
, **kw
)
3487 if not isinstance(cir
, (circle
.Circle
, arc
.Arc
)):
3488 raise TypeError, "Invalid circle/arc type: " + `
type(cir
)`
3489 if cir
.getParent() is None:
3490 raise ValueError, "Circle/Arc not found in Layer!"
3492 self
.__crossbar
= DimCrossbar()
3493 self
.__dia
_mode
= False
3495 _ds
= self
.getDimStyle()
3496 _pds
, _sds
= self
.getDimstrings()
3499 _pds
.setPrefix(_ds
.getValue('RADIAL_DIM_PRIMARY_PREFIX'))
3500 _pds
.setSuffix(_ds
.getValue('RADIAL_DIM_PRIMARY_SUFFIX'))
3505 _sds
.setPrefix(_ds
.getValue('RADIAL_DIM_SECONDARY_PREFIX'))
3506 _sds
.setSuffix(_ds
.getValue('RADIAL_DIM_SECONDARY_SUFFIX'))
3509 self
.setDiaMode(_ds
.getValue('RADIAL_DIM_DIA_MODE'))
3510 cir
.connect('moved', self
.__moveCircle
)
3511 cir
.connect('radius_changed', self
.__radiusChanged
)
3512 cir
.connect('change_pending', self
.__circleChangePending
)
3513 cir
.connect('change_complete', self
.__circleChangeComplete
)
3514 self
.calcDimValues()
3516 def __eq__(self
, rdim
):
3517 """Compare two RadialDimensions for equality.
3519 if not isinstance(rdim
, RadialDimension
):
3522 _layer
= self
.__circle
.getParent()
3523 _rc
= rdim
.getDimCircle()
3524 _rl
= _rc
.getParent()
3525 if _layer
is _rl
and self
.__circle
== _rc
:
3529 def __ne__(self
, rdim
):
3530 """Compare two RadialDimensions for inequality.
3532 if not isinstance(rdim
, RadialDimension
):
3535 _layer
= self
.__circle
.getParent()
3536 _rc
= rdim
.getDimCircle()
3537 _rl
= _rc
.getParent()
3538 if _layer
is _rl
and self
.__circle
== _rc
:
3543 self
.__circle
.disconnect(self
)
3544 self
.__circle
.freeUser(self
)
3545 self
.__circle
= self
.__crossbar
= None
3546 super(RadialDimension
, self
).finish()
3548 def getValues(self
):
3549 """Return values comprising the RadialDimension.
3553 This method extends the Dimension::getValues() method.
3555 _data
= super(RadialDimension
, self
).getValues()
3556 _data
.setValue('type', 'rdim')
3557 _data
.setValue('circle', self
.__circle
.getID())
3558 _layer
= self
.__circle
.getParent()
3559 _data
.setValue('layer', _layer
.getID())
3560 _data
.setValue('dia_mode', self
.__dia
_mode
)
3563 def getDiaMode(self
):
3564 """Return if the RadialDimension will return diametrical values.
3568 This method returns True if the diameter value is returned,
3569 and False otherwise.
3571 return self
.__dia
_mode
3573 def setDiaMode(self
, mode
=False):
3574 """Set the RadialDimension to return diametrical values.
3578 Calling this method without an argument sets the RadialDimension
3579 to return radial measurements. If the argument "mode" is supplied,
3580 it should be either True or False.
3582 If the RadialDimension is measuring an arc, the returned value
3583 will always be set to return a radius.
3585 util
.test_boolean(mode
)
3586 if not isinstance(self
.__circle
, arc
.Arc
):
3587 _m
= self
.__dia
_mode
3589 self
.startChange('dia_mode_changed')
3590 self
.__dia
_mode
= mode
3591 self
.endChange('dia_mode_changed')
3592 self
.sendMessage('dia_mode_changed', _m
)
3593 self
.calcDimValues()
3596 dia_mode
= property(getDiaMode
, setDiaMode
, None,
3597 "Draw the Dimension as a diameter")
3599 def getDimLayer(self
):
3600 """Return the Layer object holding the Circle for this RadialDimension.
3604 return self
.__circle
.getParent()
3606 def getDimCircle(self
):
3607 """Return the Circle object this RadialDimension is measuring.
3611 return self
.__circle
3613 def setDimCircle(self
, c
):
3614 """Set the Circle object measured by this RadialDimension.
3618 The argument for this method is:
3620 c: A Circle/Arc contained in a Layer
3623 raise RuntimeError, "Setting circle/arc not allowed - object locked."
3624 if not isinstance(c
, (circle
.Circle
, arc
.Arc
)):
3625 raise TypeError, "Invalid circle/arc type: " + `
type(c
)`
3626 if c
.getParent() is None:
3627 raise ValueError, "Circle/Arc not found in a Layer!"
3628 _circ
= self
.__circle
3630 _circ
.disconnect(self
)
3631 _circ
.freeUser(self
)
3632 self
.startChange('dimobj_changed')
3634 self
.endChange('dimobj_changed')
3636 self
.sendMessage('dimobj_changed', _circ
, c
)
3637 c
.connect('moved', self
.__moveCircle
)
3638 c
.connect('radius_changed', self
.__radiusChanged
)
3639 c
.connect('change_pending', self
.__circleChangePending
)
3640 c
.connect('change_complete', self
.__circleChangeComplete
)
3641 self
.calcDimValues()
3644 circle
= property(getDimCircle
, None, None,
3645 "Radial dimension circle object.")
3647 def getDimCrossbar(self
):
3648 """Get the DimCrossbar object used by the RadialDimension.
3652 return self
.__crossbar
3654 def calcDimValues(self
, allpts
=True):
3655 """Recalculate the values for dimensional display.
3657 calcDimValues([allpts])
3659 The optional argument 'allpts' is by default True. Calling
3660 this method with the argument set to False will skip the
3661 calculation of any dimension endpoint marker points.
3664 util
.test_boolean(_allpts
)
3666 _dimbar
= self
.__crossbar
3667 _cx
, _cy
= _c
.getCenter().getCoords()
3668 _rad
= _c
.getRadius()
3669 _dx
, _dy
= self
.getLocation()
3670 _dia_mode
= self
.__dia
_mode
3671 _sep
= math
.hypot((_dx
- _cx
), (_dy
- _cy
))
3672 _angle
= math
.atan2((_dy
- _cy
), (_dx
- _cx
))
3673 _sx
= _rad
* math
.cos(_angle
)
3674 _sy
= _rad
* math
.sin(_angle
)
3675 if isinstance(_c
, arc
.Arc
):
3676 assert _dia_mode
is False, "dia_mode for arc radial dimension"
3677 _sa
= _c
.getStartAngle()
3678 _ea
= _c
.getEndAngle()
3679 _angle
= _rtd
* _angle
3681 _angle
= _angle
+ 360.0
3682 if not _c
.throughAngle(_angle
):
3683 _ep1
, _ep2
= _c
.getEndpoints()
3686 _sx
= _rad
* math
.cos(_sa
)
3687 _sy
= _rad
* math
.sin(_sa
)
3689 _dx
= _cx
+ (_sep
* math
.cos(_sa
))
3690 _dy
= _cy
+ (_sep
* math
.sin(_sa
))
3693 _sx
= _rad
* math
.cos(_ea
)
3694 _sy
= _rad
* math
.sin(_ea
)
3696 _dx
= _cx
+ (_sep
* math
.cos(_ea
))
3697 _dy
= _cy
+ (_sep
* math
.sin(_ea
))
3699 _dimbar
.setFirstEndpoint((_cx
- _sx
), (_cy
- _sy
))
3700 _dimbar
.setFirstCrossbarPoint((_cx
- _sx
), (_cy
- _sy
))
3702 _dimbar
.setFirstEndpoint(_cx
, _cy
)
3703 _dimbar
.setFirstCrossbarPoint(_cx
, _cy
)
3705 _dimbar
.setSecondEndpoint(_dx
, _dy
)
3707 _dimbar
.setSecondEndpoint((_cx
+ _sx
), (_cy
+ _sy
))
3708 _dimbar
.setSecondCrossbarPoint((_cx
+ _sx
), (_cy
+ _sy
))
3712 # calculate dimension endpoint marker coordinates
3714 _type
= self
.getEndpointType()
3715 _dimbar
.clearMarkerPoints()
3716 if _type
== Dimension
.DIM_ENDPT_NONE
or _type
== Dimension
.DIM_ENDPT_CIRCLE
:
3718 _size
= self
.getEndpointSize()
3719 _x1
, _y1
= _dimbar
.getFirstCrossbarPoint()
3720 _x2
, _y2
= _dimbar
.getSecondCrossbarPoint()
3721 _sine
, _cosine
= _dimbar
.getSinCosValues()
3722 if _type
== Dimension
.DIM_ENDPT_ARROW
or _type
== Dimension
.DIM_ENDPT_FILLED_ARROW
:
3724 # p1 -> (x,y) = (size, _height)
3725 _mx
= (_cosine
* _size
- _sine
* _height
) + _x1
3726 _my
= (_sine
* _size
+ _cosine
* _height
) + _y1
3727 _dimbar
.storeMarkerPoint(_mx
, _my
)
3728 # p2 -> (x,y) = (size, -_height)
3729 _mx
= (_cosine
* _size
- _sine
*(-_height
)) + _x1
3730 _my
= (_sine
* _size
+ _cosine
*(-_height
)) + _y1
3731 _dimbar
.storeMarkerPoint(_mx
, _my
)
3732 # p3 -> (x,y) = (-size, _height)
3733 _mx
= (_cosine
* (-_size
) - _sine
* _height
) + _x2
3734 _my
= (_sine
* (-_size
) + _cosine
* _height
) + _y2
3735 _dimbar
.storeMarkerPoint(_mx
, _my
)
3736 # p4 -> (x,y) = (-size, -_height)
3737 _mx
= (_cosine
* (-_size
) - _sine
*(-_height
)) + _x2
3738 _my
= (_sine
* (-_size
) + _cosine
*(-_height
)) + _y2
3739 _dimbar
.storeMarkerPoint(_mx
, _my
)
3740 elif _type
== Dimension
.DIM_ENDPT_SLASH
:
3741 _angle
= 30.0 * _dtr
# slope of slash
3742 _height
= 0.5 * _size
* math
.sin(_angle
)
3743 _length
= 0.5 * _size
* math
.cos(_angle
)
3744 # p1 -> (x,y) = (-_length, -_height)
3745 _sx1
= (_cosine
* (-_length
) - _sine
* (-_height
))
3746 _sy1
= (_sine
* (-_length
) + _cosine
* (-_height
))
3747 # p2 -> (x,y) = (_length, _height)
3748 _sx2
= (_cosine
* _length
- _sine
* _height
)
3749 _sy2
= (_sine
* _length
+ _cosine
* _height
)
3751 # shift the calculate based on the location of the
3756 _dimbar
.storeMarkerPoint(_mx
, _my
)
3759 _dimbar
.storeMarkerPoint(_mx
, _my
)
3762 _dimbar
.storeMarkerPoint(_mx
, _my
)
3765 _dimbar
.storeMarkerPoint(_mx
, _my
)
3767 raise ValueError, "Unexpected endpoint type: '%s'" % str(_type
)
3769 def calculate(self
):
3770 """Return the radius or diamter of this RadialDimension.
3774 By default, a RadialDimension will return the radius of the
3775 circle. The setDiaMode() method can be called to set the
3776 returned value to corresponed to a diameter.
3778 _val
= self
.__circle
.getRadius()
3779 if self
.__dia
_mode
is True:
3783 def inRegion(self
, xmin
, ymin
, xmax
, ymax
, fully
=False):
3784 """Return whether or not a RadialDimension exists within a region.
3786 isRegion(xmin, ymin, xmax, ymax[, fully])
3788 The four arguments define the boundary of an area, and the
3789 function returns True if the RadialDimension lies within that
3790 area. If the optional argument 'fully' is used and is True,
3791 then the dimensioned circle and the location of the dimension
3792 text must lie within the boundary. Otherwise, the function
3795 _xmin
= util
.get_float(xmin
)
3796 _ymin
= util
.get_float(ymin
)
3797 _xmax
= util
.get_float(xmax
)
3799 raise ValueError, "Illegal values: xmax < xmin"
3800 _ymax
= util
.get_float(ymax
)
3802 raise ValueError, "Illegal values: ymax < ymin"
3803 util
.test_boolean(fully
)
3804 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
3805 if ((_dxmin
> _xmax
) or
3811 if ((_dxmin
> _xmin
) and
3812 (_dymin
> _ymin
) and
3813 (_dxmax
< _xmax
) and
3817 _dx
, _dy
= self
.getLocation()
3818 if _xmin
< _dx
< _xmax
and _ymin
< _dy
< _ymax
: # dim text
3820 _p1
, _p2
= self
.__crossbar
.getEndpoints()
3823 return util
.in_region(_x1
, _y1
, _x2
, _y2
, _xmin
, _ymin
, _xmax
, _ymax
)
3825 def mapCoords(self
, x
, y
, tol
=tolerance
.TOL
):
3826 """Test an x/y coordinate pair if it could lay on the dimension.
3828 mapCoords(x, y[, tol])
3830 This method has two required parameters:
3835 These should both be float values.
3837 There is an optional third parameter, 'tol', giving
3838 the maximum distance from the dimension bars that the
3839 x/y coordinates may sit.
3841 _x
= util
.get_float(x
)
3842 _y
= util
.get_float(y
)
3843 _t
= tolerance
.toltest(tol
)
3844 _p1
, _p2
= self
.__crossbar
.getEndpoints()
3847 return util
.map_coords(_x
, _y
, _x1
, _y1
, _x2
, _y2
, _t
)
3849 def onDimension(self
, x
, y
, tol
=tolerance
.TOL
):
3850 return self
.mapCoords(x
, y
, tol
) is not None
3852 def getBounds(self
):
3853 """Return the minimal and maximal locations of the dimension
3857 This method overrides the Dimension::getBounds() method
3859 _p1
, _p2
= self
.__crossbar
.getEndpoints()
3862 _xmin
= min(_x1
, _x2
)
3863 _ymin
= min(_y1
, _y2
)
3864 _xmax
= max(_x1
, _x2
)
3865 _ymax
= max(_y1
, _y2
)
3866 return _xmin
, _ymin
, _xmax
, _ymax
3870 _x
, _y
= self
.getLocation()
3871 _ds
= self
.getDimStyle()
3872 _rdim
= RadialDimension(_c
, _x
, _y
, _ds
)
3873 _rdim
.copyDimValues(self
)
3874 _rdim
.setDiaMode(self
.getDiaMode())
3877 def __circleChangePending(self
, p
, *args
):
3880 raise ValueError, "Invalid argument count: %d" % _alen
3881 if args
[0] == 'moved' or args
[0] =='radius_changed':
3882 self
.startChange('moved')
3884 def __circleChangeComplete(self
, p
, *args
):
3887 raise ValueError, "Invalid argument count: %d" % _alen
3888 if args
[0] == 'moved' or args
[0] =='radius_changed':
3889 self
.endChange('moved')
3891 def __moveCircle(self
, circ
, *args
):
3894 raise ValueError, "Invalid argument count: %d" % _alen
3895 if circ
is not self
.__circle
:
3896 raise ValueError, "Unexpected sender: " + `circ`
3897 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
3898 self
.calcDimValues()
3899 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
3901 def __radiusChanged(self
, circ
, *args
):
3902 self
.calcDimValues()
3904 def sendsMessage(self
, m
):
3905 if m
in RadialDimension
.__messages
:
3907 return super(RadialDimension
, self
).sendsMessage(m
)
3909 class AngularDimension(Dimension
):
3910 """A class for Angular dimensions.
3912 The AngularDimension class is derived from the Dimension
3913 class, so it shares all of those methods and attributes.
3915 AngularDimension objects have the following methods:
3917 {get/set}VertexPoint(): Get/Set the vertex point for the AngularDimension.
3918 {get/set}P1(): Get/Set the first Point for the AngularDimension.
3919 {get/set}P2(): Get/Set the second Point for the AngularDimension.
3920 getDimPoints(): Return the two Points used in this dimension.
3921 getDimLayers(): Return the two Layers holding the Points.
3922 getDimXPoints(): Get the x-coordinates of the dimension bar positions.
3923 getDimYPoints(): Get the y-coordinates of the dimension bar positions.
3924 getDimAngles(): Get the angles at which the dimension bars should be drawn.
3925 getDimMarkerPoints(): Get the locaiton of the dimension endpoint markers.
3926 calcDimValues(): Calculate the endpoint of the dimension line.
3927 mapCoords(): Return coordinates on the dimension near some point.
3928 onDimension(): Test if an x/y coordinate pair fall on the dimension line.
3929 invert(): Switch the endpoints used to measure the dimension
3933 'point_changed' : True,
3937 def __init__(self
, vp
, p1
, p2
, x
, y
, ds
=None, **kw
):
3938 """Initialize an AngularDimension object.
3940 adim = AngularDimension(vp, p1, p2, x, y, ds)
3942 vp: A Point contained in a Layer
3943 p1: A Point contained in a Layer
3944 p2: A Point contained in a Layer
3945 x: The x-coordinate of the dimensional text
3946 y: The y-coordinate of the dimensional text
3947 ds: The DimStyle used for this Dimension.
3949 super(AngularDimension
, self
).__init
__(x
, y
, ds
, **kw
)
3950 if not isinstance(vp
, point
.Point
):
3951 raise TypeError, "Invalid point type: " + `
type(vp
)`
3952 if vp
.getParent() is None:
3953 raise ValueError, "Vertex Point not found in a Layer!"
3954 if not isinstance(p1
, point
.Point
):
3955 raise TypeError, "Invalid point type: " + `
type(p1
)`
3956 if p1
.getParent() is None:
3957 raise ValueError, "Point P1 not found in a Layer!"
3958 if not isinstance(p2
, point
.Point
):
3959 raise TypeError, "Invalid point type: " + `
type(p2
)`
3960 if p2
.getParent() is None:
3961 raise ValueError, "Point P2 not found in a Layer!"
3965 self
.__bar
1 = DimBar()
3966 self
.__bar
2 = DimBar()
3967 self
.__crossarc
= DimCrossarc()
3968 _ds
= self
.getDimStyle()
3969 _pds
, _sds
= self
.getDimstrings()
3972 _pds
.setPrefix(_ds
.getValue('ANGULAR_DIM_PRIMARY_PREFIX'))
3973 _pds
.setSuffix(_ds
.getValue('ANGULAR_DIM_PRIMARY_SUFFIX'))
3978 _sds
.setPrefix(_ds
.getValue('ANGULAR_DIM_SECONDARY_PREFIX'))
3979 _sds
.setSuffix(_ds
.getValue('ANGULAR_DIM_SECONDARY_SUFFIX'))
3983 vp
.connect('moved', self
.__movePoint
)
3984 vp
.connect('change_pending', self
.__pointChangePending
)
3985 vp
.connect('change_complete', self
.__pointChangeComplete
)
3987 p1
.connect('moved', self
.__movePoint
)
3988 p1
.connect('change_pending', self
.__pointChangePending
)
3989 p1
.connect('change_complete', self
.__pointChangeComplete
)
3991 p2
.connect('moved', self
.__movePoint
)
3992 p2
.connect('change_pending', self
.__pointChangePending
)
3993 p2
.connect('change_complete', self
.__pointChangeComplete
)
3994 self
.calcDimValues()
3996 def __eq__(self
, adim
):
3997 """Compare two AngularDimensions for equality.
3999 if not isinstance(adim
, AngularDimension
):
4002 _lvp
= self
.__vp
.getParent()
4003 _lp1
= self
.__p
1.getParent()
4004 _lp2
= self
.__p
2.getParent()
4005 _vl
, _l1
, _l2
= adim
.getDimLayers()
4006 _vp
, _p1
, _p2
= adim
.getDimPoints()
4008 self
.__vp
== _vp
and
4010 self
.__p
1 == _p1
and
4016 def __ne__(self
, adim
):
4017 """Compare two AngularDimensions for inequality.
4019 if not isinstance(adim
, AngularDimension
):
4022 _lvp
= self
.__vp
.getParent()
4023 _lp1
= self
.__p
1.getParent()
4024 _lp2
= self
.__p
2.getParent()
4025 _vl
, _l1
, _l2
= adim
.getDimLayers()
4026 _vp
, _p1
, _p2
= adim
.getDimPoints()
4028 self
.__vp
== _vp
and
4030 self
.__p
1 == _p1
and
4037 self
.__vp
.disconnect(self
)
4038 self
.__vp
.freeUser(self
)
4039 self
.__p
1.disconnect(self
)
4040 self
.__p
1.freeUser(self
)
4041 self
.__p
2.disconnect(self
)
4042 self
.__p
2.freeUser(self
)
4043 self
.__bar
1 = self
.__bar
2 = self
.__crossarc
= None
4044 self
.__vp
= self
.__p
1 = self
.__p
2 = None
4045 super(AngularDimension
, self
).finish()
4047 def getValues(self
):
4048 """Return values comprising the AngularDimension.
4052 This method extends the Dimension::getValues() method.
4054 _data
= super(AngularDimension
, self
).getValues()
4055 _data
.setValue('type', 'adim')
4056 _data
.setValue('vp', self
.__vp
.getID())
4057 _layer
= self
.__vp
.getParent()
4058 _data
.setValue('vl', _layer
.getID())
4059 _data
.setValue('p1', self
.__p
1.getID())
4060 _layer
= self
.__p
1.getParent()
4061 _data
.setValue('l1', _layer
.getID())
4062 _data
.setValue('p2', self
.__p
2.getID())
4063 _layer
= self
.__p
2.getParent()
4064 _data
.setValue('l2', _layer
.getID())
4067 def getDimLayers(self
):
4068 """Return the layers used in an AngularDimension.
4072 _vl
= self
.__vp
.getParent()
4073 _l1
= self
.__p
1.getParent()
4074 _l2
= self
.__p
2.getParent()
4075 return _vl
, _l1
, _l2
4077 def getDimPoints(self
):
4078 """Return the points used in an AngularDimension.
4082 return self
.__vp
, self
.__p
1, self
.__p
2
4084 def getVertexPoint(self
):
4085 """Return the vertex point used in an AngularDimension.
4091 def setVertexPoint(self
, p
):
4092 """Set the vertex point for an AngularDimension.
4096 There is one required argument for this method:
4098 p: A Point contained in Layer
4101 raise RuntimeError, "Setting vertex point allowed - object locked."
4102 if not isinstance(p
, point
.Point
):
4103 raise TypeError, "Invalid point type: " + `
type(p
)`
4104 if p
.getParent() is None:
4105 raise ValueError, "Point not found in a Layer!"
4108 _vp
.disconnect(self
)
4110 self
.startChange('point_changed')
4112 self
.endChange('point_changed')
4114 p
.connect('moved', self
.__movePoint
)
4115 p
.connect('change_pending', self
.__pointChangePending
)
4116 p
.connect('change_complete', self
.__pointChangeComplete
)
4117 self
.sendMessage('point_changed', _vp
, p
)
4118 self
.calcDimValues()
4119 if abs(_vp
.x
- p
.x
) > 1e-10 or abs(_vp
.y
- p
.y
) > 1e-10:
4120 _x1
, _y1
= self
.__p
1.getCoords()
4121 _x2
, _y2
= self
.__p
2.getCoords()
4122 _dx
, _dy
= self
.getLocation()
4123 self
.sendMessage('moved', _vp
.x
, _vp
.y
, _x1
, _y1
,
4127 vp
= property(getVertexPoint
, None, None,
4128 "Angular Dimension vertex point.")
4131 """Return the first angle point used in an AngularDimension.
4138 """Set the first Point for an AngularDimension.
4142 There is one required argument for this method:
4144 p: A Point contained in a Layer.
4147 raise RuntimeError, "Setting vertex point allowed - object locked."
4148 if not isinstance(p
, point
.Point
):
4149 raise TypeError, "Invalid point type: " + `
type(p
)`
4150 if p
.getParent() is None:
4151 raise ValueError, "Point not found in a Layer!"
4154 _p1
.disconnect(self
)
4156 self
.startChange('point_changed')
4158 self
.endChange('point_changed')
4160 p
.connect('moved', self
.__movePoint
)
4161 p
.connect('change_pending', self
.__pointChangePending
)
4162 p
.connect('change_complete', self
.__pointChangeComplete
)
4163 self
.sendMessage('point_changed', _p1
, p
)
4164 self
.calcDimValues()
4165 if abs(_p1
.x
- p
.x
) > 1e-10 or abs(_p1
.y
- p
.y
) > 1e-10:
4166 _vx
, _vy
= self
.__vp
.getCoords()
4167 _x2
, _y2
= self
.__p
2.getCoords()
4168 _dx
, _dy
= self
.getLocation()
4169 self
.sendMessage('moved', _vx
, _vy
, _p1
.x
, _p1
.y
,
4173 p1
= property(getP1
, None, None, "Dimension first point.")
4176 """Return the second angle point used in an AngularDimension.
4183 """Set the second Point for an AngularDimension.
4187 There is one required argument for this method:
4189 l: The layer holding the Point p
4190 p: A point in Layer l
4193 raise RuntimeError, "Setting vertex point allowed - object locked."
4194 if not isinstance(p
, point
.Point
):
4195 raise TypeError, "Invalid point type: " + `
type(p
)`
4196 if p
.getParent() is None:
4197 raise ValueError, "Point not found in a Layer!"
4200 _p2
.disconnect(self
)
4202 self
.startChange('point_changed')
4204 self
.endChange('point_changed')
4206 p
.connect('moved', self
.__movePoint
)
4207 p
.connect('change_pending', self
.__pointChangePending
)
4208 p
.connect('change_complete', self
.__pointChangeComplete
)
4209 self
.sendMessage('point_changed', _p2
, p
)
4210 self
.calcDimValues()
4211 if abs(_p2
.x
- p
.x
) > 1e-10 or abs(_p2
.y
- p
.y
) > 1e-10:
4212 _vx
, _vy
= self
.__vp
.getCoords()
4213 _x1
, _y1
= self
.__p
1.getCoords()
4214 _dx
, _dy
= self
.getLocation()
4215 self
.sendMessage('moved', _vx
, _vy
, _x1
, _y1
,
4216 _p2
.x
, _p2
.y
, _dx
, _dy
)
4220 p2
= property(getP2
, None, None, "Dimension second point.")
4222 def getDimAngles(self
):
4223 """Get the array of dimension bar angles.
4227 _angle1
= self
.__bar
1.getAngle()
4228 _angle2
= self
.__bar
2.getAngle()
4229 return _angle1
, _angle2
4231 def getDimRadius(self
):
4232 """Get the radius of the dimension crossarc.
4236 return self
.__crossarc
.getRadius()
4238 def getDimBars(self
):
4239 """Return the dimension boundary bars.
4243 return self
.__bar
1, self
.__bar
2
4245 def getDimCrossarc(self
):
4246 """Get the DimCrossarc object used by the AngularDimension.
4250 return self
.__crossarc
4253 """Switch the endpoints used in this object.
4257 Invoking this method on an AngularDimension will result in
4258 it measuring the opposite angle than what it currently measures.
4261 self
.startChange('inverted')
4262 self
.__p
1 = self
.__p
2
4264 self
.endChange('inverted')
4265 self
.sendMessage('inverted')
4266 self
.calcDimValues()
4269 def calculate(self
):
4270 """Find the value of the angle measured by this AngularDimension.
4274 _vx
, _vy
= self
.__vp
.getCoords()
4275 _p1x
, _p1y
= self
.__p
1.getCoords()
4276 _p2x
, _p2y
= self
.__p
2.getCoords()
4277 _a1
= _rtd
* math
.atan2((_p1y
- _vy
), (_p1x
- _vx
))
4280 _a2
= _rtd
* math
.atan2((_p2y
- _vy
), (_p2x
- _vx
))
4288 def inRegion(self
, xmin
, ymin
, xmax
, ymax
, fully
=False):
4289 """Return whether or not an AngularDimension exists within a region.
4291 isRegion(xmin, ymin, xmax, ymax[, fully])
4293 The four arguments define the boundary of an area, and the
4294 function returns True if the RadialDimension lies within that
4295 area. If the optional argument 'fully' is used and is True,
4296 then the dimensioned circle and the location of the dimension
4297 text must lie within the boundary. Otherwise, the function
4300 _xmin
= util
.get_float(xmin
)
4301 _ymin
= util
.get_float(ymin
)
4302 _xmax
= util
.get_float(xmax
)
4304 raise ValueError, "Illegal values: xmax < xmin"
4305 _ymax
= util
.get_float(ymax
)
4307 raise ValueError, "Illegal values: ymax < ymin"
4308 util
.test_boolean(fully
)
4309 _vx
, _vy
= self
.__vp
.getCoords()
4310 _dx
, _dy
= self
.getLocation()
4311 _pxmin
, _pymin
, _pxmax
, _pymax
= self
.getBounds()
4313 if ((_pxmin
> _xmax
) or
4318 if _xmin
< _dx
< _xmax
and _ymin
< _dy
< _ymax
:
4323 _ep1
, _ep2
= self
.__bar
1.getEndpoints()
4326 if util
.in_region(_ex1
, _ey1
, _ex2
, _ey2
, _xmin
, _ymin
, _xmax
, _ymax
):
4331 _ep1
, _ep2
= self
.__bar
2.getEndpoints()
4334 if util
.in_region(_ex1
, _ey1
, _ex2
, _ey2
, _xmin
, _ymin
, _xmax
, _ymax
):
4337 # dimension crossarc
4340 _r
= self
.__crossarc
.getRadius()
4341 _d1
= math
.hypot((_xmin
- _vx
), (_ymin
- _vy
))
4342 _d2
= math
.hypot((_xmin
- _vx
), (_ymax
- _vy
))
4343 _d3
= math
.hypot((_xmax
- _vx
), (_ymax
- _vy
))
4344 _d4
= math
.hypot((_xmax
- _vx
), (_ymin
- _vy
))
4345 _dmin
= min(_d1
, _d2
, _d3
, _d4
)
4346 _dmax
= max(_d1
, _d2
, _d3
, _d4
)
4347 if _xmin
< _vx
< _xmax
and _ymin
< _vy
< _ymax
:
4350 if _vx
> _xmax
and _ymin
< _vy
< _ymax
:
4352 elif _vx
< _xmin
and _ymin
< _vy
< _ymax
:
4354 elif _vy
> _ymax
and _xmin
< _vx
< _xmax
:
4356 elif _vy
< _ymin
and _xmin
< _vx
< _xmax
:
4358 if _dmin
< _r
< _dmax
:
4359 _da
= _rtd
* math
.atan2((_ymin
- _vy
), (_xmin
- _vx
))
4362 _val
= self
._throughAngle
(_da
)
4365 _da
= _rtd
* math
.atan2((_ymin
- _vy
), (_xmax
- _vx
))
4368 _val
= self
._throughAngle
(_da
)
4371 _da
= _rtd
* math
.atan2((_ymax
- _vy
), (_xmax
- _vx
))
4374 _val
= self
._throughAngle
(_da
)
4377 _da
= _rtd
* math
.atan2((_ymax
- _vy
), (_xmin
- _vx
))
4380 _val
= self
._throughAngle
(_da
)
4383 def _throughAngle(self
, angle
):
4384 """Test if the angular crossarc exists at a certain angle.
4388 This method is private to the AngularDimension class.
4390 _crossarc
= self
.__crossarc
4391 _sa
= _crossarc
.getStartAngle()
4392 _ea
= _crossarc
.getEndAngle()
4394 if abs(_sa
- _ea
) > 1e-10:
4396 if angle
> _ea
and angle
< _sa
:
4399 if angle
> _ea
or angle
< _sa
:
4403 def calcDimValues(self
, allpts
=True):
4404 """Recalculate the values for dimensional display.
4406 calcDimValues([allpts])
4408 The optional argument 'allpts' is by default True. Calling
4409 this method with the argument set to False will skip the
4410 calculation of any dimension endpoint marker points.
4413 util
.test_boolean(_allpts
)
4414 _vx
, _vy
= self
.__vp
.getCoords()
4415 _p1x
, _p1y
= self
.__p
1.getCoords()
4416 _p2x
, _p2y
= self
.__p
2.getCoords()
4417 _dx
, _dy
= self
.getLocation()
4418 _offset
= self
.getOffset()
4419 _ext
= self
.getExtension()
4422 _crossarc
= self
.__crossarc
4423 _dv1
= math
.hypot((_p1x
- _vx
), (_p1y
- _vy
))
4424 _dv2
= math
.hypot((_p2x
- _vx
), (_p2y
- _vy
))
4425 _ddp
= math
.hypot((_dx
- _vx
), (_dy
- _vy
))
4426 _crossarc
.setRadius(_ddp
)
4428 # first dimension bar
4430 _angle
= math
.atan2((_p1y
- _vy
), (_p1x
- _vx
))
4431 _sine
= math
.sin(_angle
)
4432 _cosine
= math
.cos(_angle
)
4433 _deg
= _angle
* _rtd
4436 _crossarc
.setStartAngle(_deg
)
4437 _ex
= _vx
+ (_ddp
* _cosine
)
4438 _ey
= _vy
+ (_ddp
* _sine
)
4439 _crossarc
.setFirstCrossbarPoint(_ex
, _ey
)
4440 _crossarc
.setFirstEndpoint(_ex
, _ey
)
4441 if _ddp
> _dv1
: # dim point is radially further to vp than p1
4442 _x1
= _p1x
+ (_offset
* _cosine
)
4443 _y1
= _p1y
+ (_offset
* _sine
)
4444 _x2
= _vx
+ ((_ddp
+ _ext
) * _cosine
)
4445 _y2
= _vy
+ ((_ddp
+ _ext
) * _sine
)
4446 else: # dim point is radially closer to vp than p1
4447 _x1
= _p1x
- (_offset
* _cosine
)
4448 _y1
= _p1y
- (_offset
* _sine
)
4449 _x2
= _vx
+ ((_ddp
- _ext
) * _cosine
)
4450 _y2
= _vy
+ ((_ddp
- _ext
) * _sine
)
4451 _bar1
.setFirstEndpoint(_x1
, _y1
)
4452 _bar1
.setSecondEndpoint(_x2
, _y2
)
4454 # second dimension bar
4456 _angle
= math
.atan2((_p2y
- _vy
), (_p2x
- _vx
))
4457 _sine
= math
.sin(_angle
)
4458 _cosine
= math
.cos(_angle
)
4459 _deg
= _angle
* _rtd
4462 _crossarc
.setEndAngle(_deg
)
4463 _ex
= _vx
+ (_ddp
* _cosine
)
4464 _ey
= _vy
+ (_ddp
* _sine
)
4465 _crossarc
.setSecondCrossbarPoint(_ex
, _ey
)
4466 _crossarc
.setSecondEndpoint(_ex
, _ey
)
4467 if _ddp
> _dv2
: # dim point is radially further to vp than p2
4468 _x1
= _p2x
+ (_offset
* _cosine
)
4469 _y1
= _p2y
+ (_offset
* _sine
)
4470 _x2
= _vx
+ ((_ddp
+ _ext
) * _cosine
)
4471 _y2
= _vy
+ ((_ddp
+ _ext
) * _sine
)
4472 else: # dim point is radially closers to vp than p2
4473 _x1
= _p2x
- (_offset
* _cosine
)
4474 _y1
= _p2y
- (_offset
* _sine
)
4475 _x2
= _vx
+ ((_ddp
- _ext
) * _cosine
)
4476 _y2
= _vy
+ ((_ddp
- _ext
) * _sine
)
4477 _bar2
.setFirstEndpoint(_x1
, _y1
)
4478 _bar2
.setSecondEndpoint(_x2
, _y2
)
4480 # test if the DimString lies outside the measured angle
4481 # and if it does adjust either the crossarc start or end angle
4483 _deg
= _rtd
* math
.atan2((_dy
- _vy
), (_dx
- _vx
))
4486 _csa
= _crossarc
.getStartAngle()
4487 _cea
= _crossarc
.getEndAngle()
4488 if ((_csa
> _cea
) and (_cea
< _deg
< _csa
)):
4489 if abs(_csa
- _deg
) < abs(_deg
- _cea
): # closer to start
4490 _crossarc
.setStartAngle(_deg
)
4492 _crossarc
.setEndAngle(_deg
)
4493 elif ((_cea
> _csa
) and ((_csa
> _deg
) or (_cea
< _deg
))):
4496 _a2
= 360.0 - _deg
+ _csa
4498 _a1
= 360.0 - _cea
+ _deg
4500 if abs(_a1
) > abs(_a2
): # closer to start
4501 _crossarc
.setStartAngle(_deg
)
4503 _crossarc
.setEndAngle(_deg
)
4509 # calculate dimension endpoint marker coordinates
4511 _type
= self
.getEndpointType()
4512 _crossarc
.clearMarkerPoints()
4513 if _type
== Dimension
.DIM_ENDPT_NONE
or _type
== Dimension
.DIM_ENDPT_CIRCLE
:
4515 _size
= self
.getEndpointSize()
4516 _a1
= _bar1
.getAngle() - 90.0
4517 _a2
= _bar2
.getAngle() - 90.0
4518 # print "a1: %g" % _a1
4519 # print "a2: %g" % _a2
4520 _mp1
, _mp2
= _crossarc
.getCrossbarPoints()
4523 # print "x1: %g" % _x1
4524 # print "y1: %g" % _y1
4525 # print "x2: %g" % _x2
4526 # print "y2: %g" % _y2
4527 _sin1
= math
.sin(_dtr
* _a1
)
4528 _cos1
= math
.cos(_dtr
* _a1
)
4529 _sin2
= math
.sin(_dtr
* _a2
)
4530 _cos2
= math
.cos(_dtr
* _a2
)
4531 if _type
== Dimension
.DIM_ENDPT_ARROW
or _type
== Dimension
.DIM_ENDPT_FILLED_ARROW
:
4533 # p1 -> (x,y) = (size, _height)
4534 _mx
= (_cos1
* (-_size
) - _sin1
* _height
) + _x1
4535 _my
= (_sin1
* (-_size
) + _cos1
* _height
) + _y1
4536 _crossarc
.storeMarkerPoint(_mx
, _my
)
4537 # p2 -> (x,y) = (size, -_height)
4538 _mx
= (_cos1
* (-_size
) - _sin1
*(-_height
)) + _x1
4539 _my
= (_sin1
* (-_size
) + _cos1
*(-_height
)) + _y1
4540 _crossarc
.storeMarkerPoint(_mx
, _my
)
4541 # p3 -> (x,y) = (size, _height)
4542 _mx
= (_cos2
* _size
- _sin2
* _height
) + _x2
4543 _my
= (_sin2
* _size
+ _cos2
* _height
) + _y2
4544 _crossarc
.storeMarkerPoint(_mx
, _my
)
4545 # p4 -> (x,y) = (size, -_height)
4546 _mx
= (_cos2
* _size
- _sin2
*(-_height
)) + _x2
4547 _my
= (_sin2
* _size
+ _cos2
*(-_height
)) + _y2
4548 _crossarc
.storeMarkerPoint(_mx
, _my
)
4549 elif _type
== Dimension
.DIM_ENDPT_SLASH
:
4550 _angle
= 30.0 * _dtr
# slope of slash
4551 _height
= 0.5 * _size
* math
.sin(_angle
)
4552 _length
= 0.5 * _size
* math
.cos(_angle
)
4553 # p1 -> (x,y) = (-_length, -_height)
4554 _mx
= (_cos1
* (-_length
) - _sin1
* (-_height
)) + _x1
4555 _my
= (_sin1
* (-_length
) + _cos1
* (-_height
)) + _y1
4556 _crossarc
.storeMarkerPoint(_mx
, _my
)
4557 # p2 -> (x,y) = (_length, _height)
4558 _mx
= (_cos1
* _length
- _sin1
* _height
) + _x1
4559 _my
= (_sin1
* _length
+ _cos1
* _height
) + _y1
4560 _crossarc
.storeMarkerPoint(_mx
, _my
)
4561 # p3 -> (x,y) = (-_length, -_height)
4562 _mx
= (_cos2
* (-_length
) - _sin2
* (-_height
)) + _x2
4563 _my
= (_sin2
* (-_length
) + _cos2
* (-_height
)) + _y2
4564 _crossarc
.storeMarkerPoint(_mx
, _my
)
4565 # p4 -> (x,y) = (_length, _height)
4566 _mx
= (_cos2
* _length
- _sin2
* _height
) + _x2
4567 _my
= (_sin2
* _length
+ _cos2
* _height
) + _y2
4568 _crossarc
.storeMarkerPoint(_mx
, _my
)
4570 raise ValueError, "Unexpected endpoint type: '%s'" % str(_type
)
4572 def mapCoords(self
, x
, y
, tol
=tolerance
.TOL
):
4573 """Test an x/y coordinate pair hit the dimension lines and arc.
4575 mapCoords(x, y[, tol])
4577 This method has two required parameters:
4582 These should both be float values.
4584 There is an optional third parameter, 'tol', giving
4585 the maximum distance from the dimension bars that the
4586 x/y coordinates may sit.
4588 _x
= util
.get_float(x
)
4589 _y
= util
.get_float(y
)
4590 _t
= tolerance
.toltest(tol
)
4594 _ep1
, _ep2
= self
.__bar
1.getEndpoints()
4597 _mp
= util
.map_coords(_x
, _y
, _ex1
, _ey1
, _ex2
, _ey2
, _t
)
4603 _ep1
, _ep2
= self
.__bar
2.getEndpoints()
4604 _mp
= util
.map_coords(_x
, _y
, _ex1
, _ey1
, _ex2
, _ey2
, _t
)
4610 _vx
, _vy
= self
.__vp
.getCoords()
4611 _psep
= math
.hypot((_vx
- _x
), (_vy
- y
))
4612 _dx
, _dy
= self
.getLocation()
4613 _dsep
= math
.hypot((_vx
- _dx
), (_vy
- _dy
))
4614 if abs(_psep
- _dsep
) < _t
:
4615 _crossarc
= self
.__crossarc
4616 _sa
= _crossarc
.getStartAngle()
4617 _ea
= _crossarc
.getEndAngle()
4618 _angle
= _rtd
* math
.atan2((_y
- _vy
), (_x
- _vx
))
4620 if abs(_sa
- _ea
) > 1e-10:
4622 if _angle
< _sa
or _angle
> _ea
:
4625 if _angle
> _ea
or _angle
< _sa
:
4628 _xoff
= _dsep
* math
.cos(_angle
)
4629 _yoff
= _dsep
* math
.sin(_angle
)
4630 return (_vx
+ _xoff
), (_vy
+ _yoff
)
4633 def onDimension(self
, x
, y
, tol
=tolerance
.TOL
):
4634 return self
.mapCoords(x
, y
, tol
) is not None
4636 def getBounds(self
):
4637 """Return the minimal and maximal locations of the dimension
4641 This method overrides the Dimension::getBounds() method
4643 _vx
, _vy
= self
.__vp
.getCoords()
4644 _dx
, _dy
= self
.getLocation()
4647 _ep1
, _ep2
= self
.__bar
1.getEndpoints()
4648 _dxpts
.append(_ep1
[0])
4649 _dypts
.append(_ep1
[1])
4650 _dxpts
.append(_ep2
[0])
4651 _dypts
.append(_ep2
[1])
4652 _ep1
, _ep2
= self
.__bar
2.getEndpoints()
4653 _dxpts
.append(_ep1
[0])
4654 _dypts
.append(_ep1
[1])
4655 _dxpts
.append(_ep2
[0])
4656 _dypts
.append(_ep2
[1])
4657 _rad
= self
.__crossarc
.getRadius()
4658 if self
._throughAngle
(0.0):
4659 _dxpts
.append((_vx
+ _rad
))
4660 if self
._throughAngle
(90.0):
4661 _dypts
.append((_vy
+ _rad
))
4662 if self
._throughAngle
(180.0):
4663 _dxpts
.append((_vx
- _rad
))
4664 if self
._throughAngle
(270.0):
4665 _dypts
.append((_vy
- _rad
))
4666 _xmin
= min(_dx
, min(_dxpts
))
4667 _ymin
= min(_dy
, min(_dypts
))
4668 _xmax
= max(_dx
, max(_dxpts
))
4669 _ymax
= max(_dy
, max(_dypts
))
4670 return _xmin
, _ymin
, _xmax
, _ymax
4676 _x
, _y
= self
.getLocation()
4677 _ds
= self
.getDimStyle()
4678 _adim
= AngularDimension(_vp
, _p1
, _p2
, _x
, _y
, _ds
)
4679 _adim
.copyDimValues(self
)
4682 def __movePoint(self
, p
, *args
):
4685 raise ValueError, "Invalid argument count: %d" % _alen
4686 if ((p
is not self
.__vp
) and
4687 (p
is not self
.__p
1) and
4688 (p
is not self
.__p
2)):
4689 raise ValueError, "Unexpected dimension point: " + `p`
4690 _dxmin
, _dymin
, _dxmax
, _dymax
= self
.getBounds()
4691 self
.calcDimValues()
4692 self
.sendMessage('moved', _dxmin
, _dymin
, _dxmax
, _dymax
)
4694 def __pointChangePending(self
, p
, *args
):
4697 raise ValueError, "Invalid argument count: %d" % _alen
4698 if args
[0] == 'moved':
4699 self
.startChange('moved')
4701 def __pointChangeComplete(self
, p
, *args
):
4704 raise ValueError, "Invalid argument count: %d" % _alen
4705 if args
[0] == 'moved':
4706 self
.endChange('moved')
4708 def sendsMessage(self
, m
):
4709 if m
in AngularDimension
.__messages
:
4711 return super(AngularDimension
, self
).sendsMessage(m
)
4714 # DimString history class
4717 class DimStringLog(text
.TextBlockLog
):
4719 'prefix_changed' : DimString
.setPrefix
,
4720 'suffix_changed' : DimString
.setSuffix
,
4721 'units_changed' : DimString
.setUnits
,
4722 'precision_changed' : DimString
.setPrecision
,
4723 'print_zero_changed' : DimString
.setPrintZero
,
4724 'print_decimal_changed' : DimString
.setPrintDecimal
,
4725 'dimension_changed' : DimString
.setDimension
,
4729 'prefix_changed' : DimString
.getPrefix
,
4730 'suffix_changed' : DimString
.getSuffix
,
4731 'units_changed' : DimString
.getUnits
,
4732 'precision_changed' : DimString
.getPrecision
,
4733 'print_zero_changed' : DimString
.getPrintZero
,
4734 'print_decimal_changed' : DimString
.getPrintDecimal
,
4735 'dimension_changed' : DimString
.getDimension
,
4738 def __init__(self
, obj
):
4739 if not isinstance(obj
, DimString
):
4740 raise TypeError, "Invalid DimString type: " + `
type(obj
)`
4741 super(DimStringLog
, self
).__init
__(obj
)
4742 _ds
= self
.getObject()
4743 _ds
.connect('prefix_changed', self
._prefixChanged
)
4744 _ds
.connect('suffix_changed', self
._suffixChanged
)
4745 _ds
.connect('units_changed', self
._unitsChanged
)
4746 _ds
.connect('precision_changed', self
._precisionChanged
)
4747 _ds
.connect('print_zero_changed', self
._printZeroChanged
)
4748 _ds
.connect('print_decimal_changed', self
._printDecimalChanged
)
4749 _ds
.connect('dimension_changed', self
._dimensionChanged
)
4751 def _prefixChanged(self
, ds
, *args
):
4752 # print "prefixChanged() ..."
4755 raise ValueError, "Invalid argument count: %d" % _alen
4757 if not isinstance(_prefix
, types
.StringTypes
):
4758 raise TypeError, "Invalid prefix type: " + `
type(_prefix
)`
4759 # print "old prefix: %s" % _prefix
4760 self
.saveUndoData('prefix_changed', _prefix
)
4762 def _suffixChanged(self
, ds
, *args
):
4763 # print "suffixChanged() ..."
4766 raise ValueError, "Invalid argument count: %d" % _alen
4768 if not isinstance(_suffix
, types
.StringTypes
):
4769 raise TypeError, "Invalid suffix type " + `
type(_suffix
)`
4770 # print "old suffix: %s" % _suffix
4771 self
.saveUndoData('suffix_changed', _suffix
)
4773 def _unitsChanged(self
, ds
, *args
):
4774 # print "unitsChanged() ..."
4777 raise ValueError, "Invalid argument count: %d" % _alen
4779 if not isinstance(_units
, int):
4780 raise TypeError, "Invalid unit type: " + `
type(_units
)`
4781 # print "old units: %d" % _units
4782 self
.saveUndoData('units_changed', _units
)
4784 def _precisionChanged(self
, ds
, *args
):
4785 # print "precisionChanged() ..."
4788 raise ValueError, "Invalid argument count: %d" % _alen
4790 if not isinstance(_prec
, int):
4791 raise TypeError, "Invalid precision type: " + `
type(_prec
)`
4792 # print "old precision: %d" % _prec
4793 self
.saveUndoData('precision_changed', _prec
)
4795 def _printZeroChanged(self
, ds
, *args
):
4798 raise ValueError, "Invalid argument count: %d" % _alen
4800 util
.test_boolean(_flag
)
4801 self
.saveUndoData('print_zero_changed', _flag
)
4803 def _printDecimalChanged(self
, ds
, *args
):
4806 raise ValueError, "Invalid argument count: %d" % _alen
4808 util
.test_boolean(_flag
)
4809 self
.saveUndoData('print_decimal_changed', _flag
)
4811 def _dimensionChanged(self
, ds
, *args
):
4814 raise ValueError, "Invalid argument count: %d" % _alen
4816 if not isinstance(_dim
, Dimension
):
4817 raise TypeError, "Invalid dimension type: " + `
type(_dim
)`
4818 self
.saveUndoData('dimension_changed', _dim
.getID())
4820 def execute(self
, undo
, *args
):
4821 util
.test_boolean(undo
)
4824 raise ValueError, "No arguments to execute()"
4825 _obj
= self
.getObject()
4827 if (_op
== 'prefix_changed' or
4828 _op
== 'suffix_changed' or
4829 _op
== 'units_changed' or
4830 _op
== 'precision_changed' or
4831 _op
== 'print_zero_changed' or
4832 _op
== 'print_decimal_changed'):
4834 raise ValueError, "Invalid argument count: %d" % _alen
4836 _get
= DimStringLog
.__getops
[_op
]
4840 _set
= DimStringLog
.__setops
[_op
]
4855 self
.saveData(undo
, _op
, _sdata
)
4856 elif _op
== 'dimension_changed':
4859 super(DimStringLog
, self
).execute(undo
, *args
)
4862 # Dimension history class
4865 class DimLog(entity
.EntityLog
):
4867 'offset_changed' : Dimension
.setOffset
,
4868 'extension_changed' : Dimension
.setExtension
,
4869 'endpoint_type_changed' : Dimension
.setEndpointType
,
4870 'endpoint_size_changed' : Dimension
.setEndpointSize
,
4871 'dual_mode_changed' : Dimension
.setDualDimMode
,
4872 'dual_mode_offset_changed' : Dimension
.setDualModeOffset
,
4873 'position_changed' : Dimension
.setPosition
,
4874 'position_offset_changed' : Dimension
.setPositionOffset
,
4875 'thickness_changed' : Dimension
.setThickness
,
4876 'scale_changed' : Dimension
.setScale
,
4877 'dia_mode_changed' : RadialDimension
.setDiaMode
,
4881 'offset_changed' : Dimension
.getOffset
,
4882 'extension_changed' : Dimension
.getExtension
,
4883 'endpoint_type_changed' : Dimension
.getEndpointType
,
4884 'endpoint_size_changed' : Dimension
.getEndpointSize
,
4885 'dual_mode_changed' : Dimension
.getDualDimMode
,
4886 'dual_mode_offset_changed' : Dimension
.getDualModeOffset
,
4887 'position_changed' : Dimension
.getPosition
,
4888 'position_offset_changed' : Dimension
.getPositionOffset
,
4889 'thickness_changed' : Dimension
.getThickness
,
4890 'scale_changed' : Dimension
.getScale
,
4891 'dia_mode_changed' : RadialDimension
.getDiaMode
,
4894 def __init__(self
, dim
):
4895 if not isinstance(dim
, Dimension
):
4896 raise TypeError, "Invalid dimension type: " + `
type(dim
)`
4897 super(DimLog
, self
).__init
__(dim
)
4898 _ds1
, _ds2
= dim
.getDimstrings()
4899 _ds1
.connect('modified', self
._dimstringChanged
)
4900 _ds2
.connect('modified', self
._dimstringChanged
)
4901 dim
.connect('offset_changed', self
._offsetChanged
)
4902 dim
.connect('extension_changed', self
._extensionChanged
)
4903 # dim.connect('dimstyle_changed', self._dimstyleChanged)
4904 dim
.connect('endpoint_type_changed', self
._endpointTypeChanged
)
4905 dim
.connect('endpoint_size_changed', self
._endpointSizeChanged
)
4906 dim
.connect('dual_mode_changed', self
._dualModeChanged
)
4907 dim
.connect('dual_mode_offset_changed', self
._dualModeOffsetChanged
)
4908 dim
.connect('position_changed', self
._positionChanged
)
4909 dim
.connect('position_offset_changed', self
._positionOffsetChanged
)
4910 dim
.connect('color_changed', self
._colorChanged
)
4911 dim
.connect('thickness_changed', self
._thicknessChanged
)
4912 dim
.connect('scale_changed', self
._scaleChanged
)
4913 dim
.connect('location_changed', self
._locationChanged
)
4914 if not isinstance(dim
, RadialDimension
):
4915 dim
.connect('point_changed', self
._pointChanged
)
4916 if isinstance(dim
, RadialDimension
):
4917 dim
.connect('dia_mode_changed', self
._diaModeChanged
)
4918 dim
.connect('dimobj_changed', self
._dimObjChanged
)
4919 if isinstance(dim
, AngularDimension
):
4920 dim
.connect('inverted', self
._dimInverted
)
4921 # dim.connect('moved', self._moveDim)
4924 _dim
= self
.getObject()
4925 super(DimLog
, self
).detatch()
4926 _ds1
, _ds2
= _dim
.getDimstrings()
4927 _ds1
.disconnect(self
)
4928 _log
= _ds1
.getLog()
4929 if _log
is not None:
4932 _ds2
.disconnect(self
)
4933 _log
= _ds2
.getLog()
4934 if _log
is not None:
4938 def _moveDim(self
, dim
, *args
):
4941 raise ValueError, "Invalid argument count: %d" % _alen
4942 _xmin
= util
.get_float(args
[0])
4943 _ymin
= util
.get_float(args
[1])
4944 _xmax
= util
.get_float(args
[2])
4945 _ymax
= util
.get_float(args
[3])
4946 self
.saveUndoData('moved', _xmin
, _ymin
, _xmax
, _ymax
)
4948 def _dimstringChanged(self
, dstr
, *args
):
4949 _dim
= self
.getObject()
4950 _ds1
, _ds2
= _dim
.getDimstrings()
4956 raise ValueError, "Unexpected Dimstring: " + `dstr`
4957 self
.saveUndoData('dimstring_changed', _dstr
)
4960 def _endpointTypeChanged(self
, dim
, *args
):
4963 raise ValueError, "Invalid argument count: %d" % _alen
4965 if (_ep
!= Dimension
.DIM_ENDPT_NONE
and
4966 _ep
!= Dimension
.DIM_ENDPT_ARROW
and
4967 _ep
!= Dimension
.DIM_ENDPT_FILLED_ARROW
and
4968 _ep
!= Dimension
.DIM_ENDPT_SLASH
and
4969 _ep
!= Dimension
.DIM_ENDPT_CIRCLE
):
4970 raise ValueError, "Invalid endpoint value: '%s'" % str(_ep
)
4971 self
.saveUndoData('endpoint_type_changed', _ep
)
4973 def _endpointSizeChanged(self
, dim
, *args
):
4976 raise ValueError, "Invalid argument count: %d" % _alen
4978 if not isinstance(_size
, float):
4979 raise TypeError, "Unexpected type for size: " + `
type(_size
)`
4981 raise ValueError, "Invalid endpoint size: %g" % _size
4982 self
.saveUndoData('endpoint_size_changed', _size
)
4984 def _dualModeChanged(self
, dim
, *args
):
4987 raise ValueError, "Invalid argument count: %d" % _alen
4989 util
.test_boolean(_flag
)
4990 self
.saveUndoData('dual_mode_changed', _flag
)
4992 def _dualModeOffsetChanged(self
, dim
, *args
):
4995 raise ValueError, "Invalid argument count: %d" % _alen
4997 if not isinstance(_offset
, float):
4998 raise TypeError, "Unexpected type for flag: " + `
type(_offset
)`
5000 raise ValueError, "Invalid dual mode offset: %g" % _offset
5001 self
.saveUndoData('dual_mode_offset_changed', _offset
)
5003 def _positionChanged(self
, dim
, *args
):
5006 raise ValueError, "Invalid argument count: %d" % _alen
5008 if (_pos
!= Dimension
.DIM_TEXT_POS_SPLIT
and
5009 _pos
!= Dimension
.DIM_TEXT_POS_ABOVE
and
5010 _pos
!= Dimension
.DIM_TEXT_POS_BELOW
):
5011 raise ValueError, "Invalid position: '%s'" % str(_pos
)
5012 self
.saveUndoData('position_changed', _pos
)
5014 def _positionOffsetChanged(self
, dim
, *args
):
5017 raise ValueError, "Invalid argument count: %d" % _alen
5019 if not isinstance(_offset
, float):
5020 raise TypeError, "Unexpected type for offset: " + `
type(_offset
)`
5022 raise ValueError, "Invalid position offset: %g" % _offset
5023 self
.saveUndoData('position_offset_changed', _offset
)
5025 def _thicknessChanged(self
, dim
, *args
):
5028 raise ValueError, "Invalid argument count: %d" % _alen
5030 if not isinstance(_t
, float):
5031 raise TypeError, "Unexpected type for thickness" + `
type(_t
)`
5033 raise ValueError, "Invalid thickness: %g" % _t
5034 self
.saveUndoData('thickness_changed', _t
)
5036 def _scaleChanged(self
, dim
, *args
):
5039 raise ValueError, "Invalid argument count: %d" % _alen
5041 if not isinstance(_s
, float):
5042 raise TypeError, "Unexpected type for scale" + `
type(_s
)`
5044 raise ValueError, "Invalid scale: %g" % _s
5045 self
.saveUndoData('scale_changed', _s
)
5047 def _colorChanged(self
, dim
, *args
):
5050 raise ValueError, "Invalid argument count: %d" % _alen
5052 if not isinstance(_color
, color
.Color
):
5053 raise TypeError, "Invalid color: " + str(_color
)
5054 self
.saveUndoData('color_changed', _color
.getColors())
5056 def _offsetChanged(self
, dim
, *args
):
5059 raise ValueError, "Invalid argument count: %d" % _alen
5061 if not isinstance(_offset
, float):
5062 raise TypeError, "Unexpected type for offset: " + `
type(_offset
)`
5064 raise ValueError, "Invalid offset: %g" % _offset
5065 self
.saveUndoData('offset_changed', _offset
)
5067 def _extensionChanged(self
, dim
, *args
):
5070 raise ValueError, "Invalid argument count: %d" % _alen
5072 if not isinstance(_extlen
, float):
5073 raise TypeError, "Unexpected type for length: " + `
type(_extlen
)`
5075 raise ValueError, "Invalid extension length: %g" % _extlen
5076 self
.saveUndoData('extension_changed', _extlen
)
5078 def _diaModeChanged(self
, dim
, *args
): # RadialDimensions
5081 raise ValueError, "Invalid argument count: %d" % _alen
5083 util
.test_boolean(_flag
)
5084 self
.saveUndoData('dia_mode_changed', _flag
)
5086 def _dimInverted(self
, dim
, *args
): # AngularDimensions
5087 self
.saveUndoData('inverted')
5089 def _dimstyleChanged(self
, dim
, *args
):
5092 raise ValueError, "Invalid argument count: %d" % _alen
5094 if not isinstance(_ds
, DimStyle
):
5095 raise TypeError, "Invalid DimStyle type: " + `
type(_ds
)`
5097 if not isinstance(_opts
, dict):
5098 raise TypeError, "Invalid option type: " + `
type(_opts
)`
5100 _data
['dimstyle'] = _ds
.getValues()
5102 self
.saveUndoData('dimstyle_changed', _data
)
5104 def _locationChanged(self
, dim
, *args
):
5107 raise ValueError, "Invalid argument count: %d" % _alen
5109 if not isinstance(_x
, float):
5110 raise TypeError, "Unexpected type for x location" + `
type(_x
)`
5112 if not isinstance(_y
, float):
5113 raise TypeError, "Unexpected type for y location" + `
type(_y
)`
5114 self
.saveUndoData('location_changed', _x
, _y
)
5116 def _pointChanged(self
, dim
, *args
):
5119 raise ValueError, "Invalid argument count: %d" % _alen
5121 if not isinstance(_op
, point
.Point
):
5122 raise TypeError, "Unexpected type for old point" + `
type(_op
)`
5124 if not isinstance(_np
, point
.Point
):
5125 raise TypeError, "Unexpected type for new point" + `
type(_np
)`
5126 _ol
= _op
.getParent()
5128 raise RuntimeError, "Invalid Parent for replaced Point" + `_op`
5131 _nl
= _np
.getParent()
5133 raise RuntimeError, "Invalid Parent for new Point" + `_np`
5136 self
.saveUndoData('point_changed', _olid
, _oid
, _nlid
, _nid
)
5138 def _dimObjChanged(self
, dim
, *args
):
5141 raise ValueError, "Invalid argument count: %d" % _alen
5143 if not isinstance(_oo
, (circle
.Circle
, arc
.Arc
)):
5144 raise TypeError, "Unexpected type for old object" + `
type(_oo
)`
5146 if not isinstance(_no
, (circle
.Circle
, arc
.Arc
)):
5147 raise TypeError, "Unexpected type for new object" + `
type(_no
)`
5148 _ol
= _oo
.getParent()
5150 raise RuntimeError, "Invalid Parent for replaced object" + `_oo`
5153 _nl
= _no
.getParent()
5155 raise RuntimeError, "Invalid Parent for new object" + `_no`
5158 self
.saveUndoData('dimobj_changed', _olid
, _oid
, _nlid
, _nid
)
5160 def execute(self
, undo
, *args
):
5161 util
.test_boolean(undo
)
5164 raise ValueError, "No arguments to execute()"
5165 _dim
= self
.getObject()
5167 _layer
= _dim
.getParent()
5168 if _layer
is not None:
5169 _image
= _layer
.getParent()
5171 if (_op
== 'offset_changed' or
5172 _op
== 'extension_changed' or
5173 _op
== 'endpoint_type_changed' or
5174 _op
== 'endpoint_size_changed' or
5175 _op
== 'dual_mode_changed' or
5176 _op
== 'dual_mode_offset_changed' or
5177 _op
== 'dia_mode_changed' or
5178 _op
== 'thickness_changed' or
5179 _op
== 'scale_changed' or
5180 _op
== 'position_changed' or
5181 _op
== 'position_offset_changed'):
5183 raise ValueError, "Invalid argument count: %d" % _alen
5185 _get
= DimLog
.__getops
[_op
]
5189 _set
= DimLog
.__setops
[_op
]
5204 self
.saveData(undo
, _op
, _sdata
)
5205 elif _op
== 'color_changed':
5207 raise RuntimeError, "Dimension not stored in an Image"
5209 raise ValueError, "Invalid argument count: %d" % _alen
5210 _sdata
= _dim
.getColor().getColors()
5214 for _c
in _image
.getImageEntities('color'):
5215 if _c
.getColors() == args
[1]:
5221 _dim
.setColor(_color
)
5227 _dim
.setColor(_color
)
5232 self
.saveData(undo
, _op
, _sdata
)
5233 elif _op
== 'location_changed':
5235 raise ValueError, "Invalid argument count: %d" % _alen
5237 if not isinstance(_x
, float):
5238 raise TypeError, "Unexpected type for 'x': " + `
type(_x
)`
5240 if not isinstance(_y
, float):
5241 raise TypeError, "Unexpected type for 'y': " + `
type(_y
)`
5242 _dx
, _dy
= _dim
.getLocation()
5248 _dim
.setLocation(_x
, _y
)
5254 _dim
.setLocation(_x
, _y
)
5259 self
.saveData(undo
, _op
, _dx
, _dy
)
5260 elif _op
== 'point_changed':
5262 raise RuntimeError, "Dimension not stored in an Image"
5264 raise ValueError, "Invalid argument count: %d" % _alen
5274 _layers
= [_image
.getTopLayer()]
5276 _layer
= _layers
.pop()
5277 if _lid
== _layer
.getID():
5280 _layers
.extend(_layer
.getSublayers())
5282 raise RuntimeError, "Parent layer of old point not found"
5288 if isinstance(_dim
, AngularDimension
):
5289 _vp
= _dim
.getVertexPoint()
5291 _pt
= _parent
.getObject(_oid
)
5292 if _pt
is None or not isinstance(_pt
, point
.Point
):
5293 raise ValueError, "Old endpoint missing: id=%d" % _oid
5296 if _p1
.getID() == _nid
:
5298 elif _p2
.getID() == _nid
:
5300 elif _vp
is not None and _vp
.getID() == _nid
:
5301 _dim
.setVertexPoint(_pt
)
5303 raise ValueError, "Unexpected point ID: %d" % _nid
5307 _pt
= _parent
.getObject(_nid
)
5308 if _pt
is None or not isinstance(_pt
, point
.Point
):
5309 raise ValueError, "New endpoint missing: id=%d" % _nid
5312 if _p1
.getID() == _oid
:
5314 elif _p2
.getID() == _oid
:
5316 elif _vp
is not None and _vp
.getID() == _oid
:
5317 _dim
.setVertexPoint(_pt
)
5319 raise ValueError, "Unexpected point ID: %d" % _oid
5324 self
.saveData(undo
, _op
, _olid
, _oid
, _nlid
, _nid
)
5325 elif _op
== 'dimobj_changed':
5327 raise RuntimeError, "Dimension not stored in an Image"
5329 raise ValueError, "Invalid argument count: %d" % _alen
5339 _layers
= [_image
.getTopLayer()]
5341 _layer
= _layers
.pop()
5342 if _lid
== _layer
.getID():
5345 _layers
.extend(_layer
.getSublayers())
5347 raise RuntimeError, "Parent layer of old object not found"
5351 _oo
= _parent
.getObject(_oid
)
5352 if _oo
is None or not isinstance(_oo
, (circle
.Circle
,
5354 raise ValueError, "Old object missing: id=%d" % _oid
5357 _dim
.setDimCircle(_oo
)
5361 _no
= _parent
.getObject(_nid
)
5362 if _no
is None or not isinstance(_no
, (circle
.Circle
,
5364 raise ValueError, "New object missing: id=%d" % _nid
5367 _dim
.setDimCircle(_no
)
5372 self
.saveData(undo
, _op
, _olid
, _oid
, _nlid
, _nid
)
5373 elif _op
== 'dimstring_changed':
5375 raise ValueError, "Invalid argument count: %d" % _alen
5377 _ds1
, _ds2
= _dim
.getDimstrings()
5378 self
.ignore('modified')
5385 elif _dstr
== 'ds2':
5391 raise ValueError, "Unexpected dimstring key: " + str(_dstr
)
5393 self
.receive('modified')
5394 self
.saveData(undo
, _op
, _dstr
)
5397 elif _op
== 'inverted': # AngularDimensions only
5414 self
.saveData(undo
, _op
)
5416 super(DimLog
, self
).execute(undo
, *args
)