Beispiel #1
0
def test_points_from_wcs():
    points = Vec3.list([(1, 2, 3), (3, 4, 5)])
    ucs = PassTroughUCS()
    assert list(ucs.points_from_wcs(points)) == points

    ucs2 = UCS()
    assert list(ucs.points_from_wcs(points)) == list(ucs2.points_from_wcs(points))
Beispiel #2
0
def test_from_wcs():
    ucs = PassTroughUCS()
    assert ucs.from_wcs((1, 2, 3)) == Vec3(1, 2, 3)
    assert ucs.from_wcs((3, 4, 5)) == Vec3(3, 4, 5)

    ucs2 = UCS()
    assert ucs.from_wcs((1, 2, 3)) == ucs2.from_wcs(Vec3(1, 2, 3))
    assert ucs.from_wcs((3, 4, 5)) == ucs2.from_wcs(Vec3(3, 4, 5))
def test_points_to_wcs():
    ucs = PassTroughUCS()
    assert list(ucs.points_to_wcs([(1, 2, 3), (3, 4, 5)])) == [
        Vec3(1, 2, 3),
        Vec3(3, 4, 5),
    ]

    ucs2 = UCS()
    assert list(ucs.points_to_wcs([
        (1, 2, 3), (3, 4, 5)
    ])) == list(ucs2.points_to_wcs([(1, 2, 3), (3, 4, 5)]))
Beispiel #4
0
    def __init__(self,
                 dimension: 'Dimension',
                 ucs: 'UCS' = None,
                 override: DimStyleOverride = None):
        # DXF document
        self.drawing = dimension.drawing  # type: Drawing

        # DIMENSION entity
        self.dimension = dimension  # type: Dimension

        self.dxfversion = self.drawing.dxfversion  # type: str
        self.supports_dxf_r2000 = self.dxfversion >= 'AC1015'  # type: bool
        self.supports_dxf_r2007 = self.dxfversion >= 'AC1021'  # type: bool
        # Target BLOCK of the graphical representation of the DIMENSION entity
        self.block = None  # type: GenericLayoutType

        # DimStyleOverride object, manages dimension style overriding
        if override:
            self.dim_style = override
        else:
            self.dim_style = DimStyleOverride(dimension)

        # User defined coordinate system for DIMENSION entity
        self.ucs = ucs or PassTroughUCS()
        self.requires_extrusion = not self.ucs.uz.isclose(Z_AXIS)  # type: bool

        # ezdxf specific attributes beyond DXF reference, therefore not stored in the DXF file (DSTYLE)
        # Some of these are just an rendering effect, which will be ignored by CAD applications if they modify the
        # DIMENSION entity

        # user location override as UCS coordinates, stored as text_midpoint in the DIMENSION entity
        self.user_location = OptionalVec2(
            self.dim_style.pop('user_location', None))

        # user location override relative to dimline center if True
        self.relative_user_location = self.dim_style.pop(
            'relative_user_location', False)  # type: bool

        # shift text away from default text location - implemented as user location override without leader
        # shift text along in text direction
        self.text_shift_h = self.dim_style.pop('text_shift_h',
                                               0.)  # type: float
        # shift text perpendicular to text direction
        self.text_shift_v = self.dim_style.pop('text_shift_v',
                                               0.)  # type: float

        # suppress arrow rendering - only rendering is suppressed (rendering effect), all placing related calculations
        # are done without this settings. Used for multi point linear dimensions to avoid double rendering of non arrow
        # ticks.
        self.suppress_arrow1 = self.dim_style.pop('suppress_arrow1',
                                                  False)  # type: bool
        self.suppress_arrow2 = self.dim_style.pop('suppress_arrow2',
                                                  False)  # type: bool
        # end of ezdxf specific attributes

        # ---------------------------------------------
        # GENERAL PROPERTIES
        # ---------------------------------------------
        self.default_color = self.dimension.dxf.color  # type: int
        self.default_layer = self.dimension.dxf.layer  # type: str

        # ezdxf locates attachment points always in the text center.
        self.text_attachment_point = 5  # type: int # fixed for ezdxf rendering

        # ignored by ezdxf
        self.horizontal_direction = self.dimension.get_dxf_attrib(
            'horizontal_direction', None)  # type: bool

        get = self.dim_style.get
        # overall scaling of DIMENSION entity
        self.dim_scale = get('dimscale', 1)  # type: float
        if self.dim_scale == 0:
            self.dim_scale = 1

        # Controls drawing of circle or arc center marks and centerlines, for DIMDIAMETER and DIMRADIUS, the center
        # mark is drawn only if you place the dimension line outside the circle or arc.
        # 0 = No center marks or lines are drawn
        # <0 = Center lines are drawn
        # >0 = Center marks are drawn
        self.dim_center_marks = get('dimcen',
                                    0)  # type: int  # not supported yet

        # ---------------------------------------------
        # TEXT
        # ---------------------------------------------
        # dimension measurement factor
        self.dim_measurement_factor = get('dimlfac', 1)  # type: float
        self.text_style_name = get('dimtxsty',
                                   self.default_text_style())  # type: str

        self.text_style = self.drawing.styles.get(
            self.text_style_name)  # type: Style
        self.text_height = self.char_height * self.dim_scale  # type: float
        self.text_width_factor = self.text_style.get_dxf_attrib(
            'width', 1.)  # type: float
        # text_gap: gap between dimension line an dimension text
        self.text_gap = get('dimgap', 0.625) * self.dim_scale  # type: float
        # user defined text rotation - overrides everything
        self.user_text_rotation = self.dimension.get_dxf_attrib(
            'text_rotation', None)  # type: float
        # calculated text rotation
        self.text_rotation = self.user_text_rotation  # type: float
        self.text_color = get('dimclrt', self.default_color)  # type: int
        self.text_round = get('dimrnd', None)  # type: float
        self.text_decimal_places = get('dimdec', None)  # type: int

        # Controls the suppression of zeros in the primary unit value.
        # Values 0-3 affect feet-and-inch dimensions only and are not supported
        # 4 (Bit 3) = Suppresses leading zeros in decimal dimensions (for example, 0.5000 becomes .5000)
        # 8 (Bit 4) = Suppresses trailing zeros in decimal dimensions (for example, 12.5000 becomes 12.5)
        # 12 (Bit 3+4) = Suppresses both leading and trailing zeros (for example, 0.5000 becomes .5)
        self.text_suppress_zeros = get('dimzin', 0)  # type: int

        dimdsep = self.dim_style.get('dimdsep', 0)
        self.text_decimal_separator = ',' if dimdsep == 0 else chr(
            dimdsep)  # type: str
        self.text_format = self.dim_style.get('dimpost', '<>')  # type: str
        self.text_fill = self.dim_style.get(
            'dimtfill', 0)  # type: int # 0= None, 1=Background, 2=DIMTFILLCLR
        self.text_fill_color = self.dim_style.get('dimtfillclr',
                                                  1)  # type: int
        self.text_box_fill_scale = 1.1

        # text_halign = 0: center; 1: left; 2: right; 3: above ext1; 4: above ext2
        self.text_halign = get('dimjust', 0)  # type: int

        # text_valign = 0: center; 1: above; 2: farthest away?; 3: JIS?; 4: below (2, 3 ignored by ezdxf)
        self.text_valign = get('dimtad', 0)  # type: int

        # Controls the vertical position of dimension text above or below the dimension line, when DIMTAD = 0.
        # The magnitude of the vertical offset of text is the product of the text height (+gap?) and DIMTVP.
        # Setting DIMTVP to 1.0 is equivalent to setting DIMTAD = 1.
        self.text_vertical_position = get(
            'dimtvp', 0.)  # type: float  # not supported yet

        self.text_movement_rule = get('dimtmove',
                                      2)  # type: int # move text freely

        # requires a leader?
        self.text_has_leader = self.user_location is not None and self.text_movement_rule == 1  # type: bool

        # text_rotation=0 if dimension text is 'inside', ezdxf defines 'inside' as at the default text location
        self.text_inside_horizontal = get('dimtih', 0)  # type: bool

        # text_rotation=0 if dimension text is 'outside', ezdxf defines 'outside' as NOT at the default text location
        self.text_outside_horizontal = get('dimtoh', 0)  # type: bool

        # force text location 'inside', even if the text should be moved 'outside'
        self.force_text_inside = bool(get('dimtix', 0))  # type: bool

        # how dimension text and arrows are arranged when space is not sufficient to place both 'inside'
        # 0 = Places both text and arrows outside extension lines
        # 1 = Moves arrows first, then text
        # 2 = Moves text first, then arrows
        # 3 = Moves either text or arrows, whichever fits best
        self.text_fitting_rule = get(
            'dimatfit',
            2)  # type: int  # not supported yet - ezdxf behaves like 2

        # units for all dimension types except Angular.
        # 1 = Scientific
        # 2 = Decimal
        # 3 = Engineering
        # 4 = Architectural (always displayed stacked)
        # 5 = Fractional (always displayed stacked)
        self.text_length_unit = get(
            'dimlunit',
            2)  # type: int  # not supported yet - ezdxf behaves like 2

        # fraction format when DIMLUNIT is set to 4 (Architectural) or 5 (Fractional).
        # 0 = Horizontal stacking
        # 1 = Diagonal stacking
        # 2 = Not stacked (for example, 1/2)
        self.text_fraction_format = get('dimfrac',
                                        0)  # type: int  # not supported

        # units format for angular dimensions
        # 0 = Decimal degrees
        # 1 = Degrees/minutes/seconds (not supported) same as 0
        # 2 = Grad
        # 3 = Radians
        self.text_angle_unit = get('dimaunit', 0)  # type: int

        # text_outside is only True if really placed outside of default text location
        # remark: user defined text location is always outside per definition (not by real location)
        self.text_outside = False

        # calculated or overridden dimension text location
        self.text_location = None  # type: Vec2

        # bounding box of dimension text including border space
        self.text_box = None  # type: TextBox

        # formatted dimension text
        self.text = ""

        # True if dimension text doesn't fit between extension lines
        self.is_wide_text = False

        # ---------------------------------------------
        # ARROWS & TICKS
        # ---------------------------------------------
        self.tick_size = get('dimtsz', 0) * self.dim_scale
        if self.tick_size > 0:
            # use oblique strokes as 'arrows', disables usual 'arrows' and user defined blocks
            self.arrow1_name, self.arrow2_name = None, None  # type: str
            # tick size is per definition double the size of arrow size
            # adjust arrow size to reuse the 'oblique' arrow block
            self.arrow_size = self.tick_size * 2  # type: float
        else:
            # arrow name or block name if user defined arrow
            self.arrow1_name, self.arrow2_name = self.dim_style.get_arrow_names(
            )  # type: str
            self.arrow_size = get('dimasz',
                                  0.25) * self.dim_scale  # type: float

        # Suppresses arrowheads if not enough space is available inside the extension lines.
        # Only if force_text_inside is True
        self.suppress_arrow_heads = get('dimsoxd',
                                        0)  # type: bool # not supported yet

        # ---------------------------------------------
        # DIMENSION LINE
        # ---------------------------------------------
        self.dim_line_color = get('dimclrd', self.default_color)  # type: int

        # dimension line extension, along the dimension line direction ('left' and 'right')
        self.dim_line_extension = get('dimdle',
                                      0.) * self.dim_scale  # type: float
        self.dim_linetype = get('dimltype', None)  # type: str
        self.dim_lineweight = get('dimlwd',
                                  const.LINEWEIGHT_BYBLOCK)  # type: int

        # suppress first part of the dimension line
        self.suppress_dim1_line = get('dimsd1', 0)  # type: bool

        # suppress second part of the dimension line
        self.suppress_dim2_line = get('dimsd2', 0)  # type: bool

        # Controls whether a dimension line is drawn between the extension lines even when the text is placed outside.
        # For radius and diameter dimensions (when DIMTIX is off), draws a dimension line inside the circle or arc and
        # places the text, arrowheads, and leader outside.
        # 0 = no dimension line
        # 1 = draw dimension line
        self.dim_line_if_text_outside = get(
            'dimtofl',
            1)  # type: int  # not supported yet - ezdxf behaves like 1

        # ---------------------------------------------
        # EXTENSION LINES
        # ---------------------------------------------
        self.ext_line_color = get('dimclre', self.default_color)
        self.ext1_linetype_name = get('dimltex1', None)  # type: str
        self.ext2_linetype_name = get('dimltex2', None)  # type: str
        self.ext_lineweight = get('dimlwe', const.LINEWEIGHT_BYBLOCK)
        self.suppress_ext1_line = get('dimse1', 0)  # type: bool
        self.suppress_ext2_line = get('dimse2', 0)  # type: bool

        # extension of extension line above the dimension line, in extension line direction
        # in most cases perpendicular to dimension line (oblique!)
        self.ext_line_extension = get('dimexe',
                                      0.) * self.dim_scale  # type: float

        # distance of extension line from the measurement point in extension line direction
        self.ext_line_offset = get('dimexo',
                                   0.) * self.dim_scale  # type: float

        # fixed length extension line, leenght above dimension line is still self.ext_line_extension
        self.ext_line_fixed = get('dimfxlon', 0)  # type: bool

        # length below the dimension line:
        self.ext_line_length = get(
            'dimfxl', self.ext_line_extension) * self.dim_scale  # type: float

        # ---------------------------------------------
        # TOLERANCES & LIMITS
        # ---------------------------------------------
        # appends tolerances to dimension text. Setting DIMTOL to on turns DIMLIM off.
        self.dim_tolerance = get('dimtol', 0)  # type: bool
        # generates dimension limits as the default text. Setting DIMLIM to On turns DIMTOL off.
        self.dim_limits = get('dimlim', 0)  # type: bool

        if self.dim_tolerance:
            self.dim_limits = 0

        if self.dim_limits:
            self.dim_tolerance = 0

        # scale factor for the text height of fractions and tolerance values relative to the dimension text height
        self.tol_text_scale_factor = get('dimtfac', .5)
        self.tol_line_spacing = 1.35  # default MTEXT line spacing for tolerances (BricsCAD)
        # sets the minimum (or lower) tolerance limit for dimension text when DIMTOL or DIMLIM is on.
        # DIMTM accepts signed values. If DIMTOL is on and DIMTP and DIMTM are set to the same value, a tolerance value
        # is drawn. If DIMTM and DIMTP values differ, the upper tolerance is drawn above the lower, and a plus sign is
        # added to the DIMTP value if it is positive. For DIMTM, the program uses the negative of the value you enter
        # (adding a minus sign if you specify a positive number and a plus sign if you specify a negative number).
        self.tol_minimum = get('dimtm', 0)  # type: float

        # Sets the maximum (or upper) tolerance limit for dimension text when DIMTOL or DIMLIM is on. DIMTP accepts
        # signed values. If DIMTOL is on and DIMTP and DIMTM are set to the same value, a tolerance value is drawn.
        # If DIMTM and DIMTP values differ, the upper tolerance is drawn above the lower and a plus sign is added to
        # the DIMTP value if it is positive.
        self.tol_maximum = get('dimtp', 0)  # type: float

        # number of decimal places to display in tolerance values
        self.tol_decimal_places = get('dimtdec', 4)  # type: int

        # vertical justification for tolerance values relative to the nominal dimension text
        # 0 = Bottom
        # 1 = Middle
        # 2 = Top
        self.tol_valign = get('dimtolj', 0)  # type: int

        # same as DIMZIN for tolerances (self.text_suppress_zeros)
        self.tol_suppress_zeros = get('dimtzin', 0)  # type: int
        self.tol_text = None
        self.tol_text_height = 0.
        self.tol_text_upper = None
        self.tol_text_lower = None
        self.tol_char_height = self.char_height * self.tol_text_scale_factor * self.dim_scale
        # tolerances
        if self.dim_tolerance:
            # single tolerance value +/- value
            if self.tol_minimum == self.tol_maximum:
                self.tol_text = PLUS_MINUS + self.format_tolerance_text(
                    abs(self.tol_maximum))
                self.tol_text_height = self.tol_char_height
                self.tol_text_width = self.tolerance_text_width(
                    len(self.tol_text))
            else:  # 2 stacked values: +upper tolerance <above> -lower tolerance
                self.tol_text_upper = sign_char(
                    self.tol_maximum) + self.format_tolerance_text(
                        abs(self.tol_maximum))
                self.tol_text_lower = sign_char(
                    self.tol_minimum * -1) + self.format_tolerance_text(
                        abs(self.tol_minimum))
                # requires 2 text lines
                self.tol_text_height = self.tol_char_height + (
                    self.tol_text_height * self.tol_line_spacing)
                self.tol_text_width = self.tolerance_text_width(
                    max(len(self.tol_text_upper), len(self.tol_text_lower)))
            # reset text height
            self.text_height = max(self.text_height, self.tol_text_height)

        elif self.dim_limits:
            self.tol_text = None  # always None for limits
            # limits text is always 2 stacked numbers and requires actual measurement
            self.tol_text_upper = None  # text for upper limit
            self.tol_text_lower = None  # text for lower limit
            self.tol_text_height = self.tol_char_height + (
                self.tol_text_height * self.tol_line_spacing)
            self.tol_text_width = None  # requires actual measurement
            self.text_height = max(self.text_height, self.tol_text_height)
Beispiel #5
0
def test_u_vectors():
    ucs = PassTroughUCS()

    assert ucs.ux == X_AXIS
    assert ucs.uy == Y_AXIS
    assert ucs.uz == Z_AXIS
Beispiel #6
0
def test_to_ocs():
    ucs = PassTroughUCS()
    assert ucs.to_ocs((1, 2, 3)) == Vec3(1, 2, 3)
    assert ucs.to_ocs((3, 4, 5)) == Vec3(3, 4, 5)