def test_ray2d_intersect_with_vertical_and_horizontal(self): ray1 = ConstructionRay((-10, 10), (10, 10)) ray2 = ConstructionRay((5, 0), (5, 20)) point = ray1.intersect(ray2) assert point.y == 10 assert point.x == 5 assert point.isclose(Vec3(5.0, 10.0), abs_tol=1e-4)
def test_ray2d_intersect(self): ray1 = ConstructionRay((10, 1), (20, 10)) ray2 = ConstructionRay((17, -7), (-10, 3)) point = ray1.intersect(ray2) assert point.isclose(Vec3(5.7434, -2.8309), abs_tol=1e-4) assert ray1.is_parallel(ray2) is False
def intersect(ray1: ConstructionRay, ray2: ConstructionRay, default: Vec2) -> Vec2: """ Intersect two rays but take parallel rays into account. """ try: v = ray1.intersect(ray2) except ParallelRaysError: v = default return v
def test_ray2d_intersect_with_vertical(self): ray1 = ConstructionRay((10, 1), (10, -7)) ray2 = ConstructionRay((-10, 3), (17, -7)) point = ray1.intersect(ray2) assert point.x == 10 assert point.isclose(Vec3(10.0, -4.4074), abs_tol=1e-4) with pytest.raises(ArithmeticError): _ = ray1.yof(1)
def test_ray2d_parallel(self): ray1 = ConstructionRay((17, -8), (-10, 2)) ray2 = ConstructionRay((-10, 3), (17, -7)) ray3 = ConstructionRay((-10, 4), (17, -6)) assert ray2.is_parallel(ray3) is True assert ray1.is_parallel(ray3) is True with pytest.raises(ParallelRaysError): _ = ray2.intersect(ray3)
def intersect(ray1: ConstructionRay, ray2: ConstructionRay, default: Vec2) -> Vec2: """Intersect two rays but take parallel rays into account.""" # check for nearly parallel rays pi/100 ~1.8 degrees if ray1.direction.angle_between(ray2.direction) < 0.031415: return default try: return ray1.intersect(ray2) except ParallelRaysError: return default
def test_ray2d_parallel_vertical(self): ray1 = ConstructionRay((10, 1), (10, -7)) ray2 = ConstructionRay((11, 0), angle=HALF_PI) ray3 = ConstructionRay((12, -10), (12, 7)) ray4 = ConstructionRay((0, 0), (1, 1)) ray5 = ConstructionRay((0, 0), angle=0) with pytest.raises(ParallelRaysError): _ = ray1.intersect(ray3) assert ray1.is_parallel(ray3) is True assert ray1.is_parallel(ray2) is True assert ray2.is_parallel(ray2) is True assert ray1.is_parallel(ray4) is False assert ray2.is_parallel(ray4) is False assert ray3.is_parallel(ray4) is False assert ray1.is_parallel(ray5) is False assert ray2.is_parallel(ray5) is False assert ray3.is_parallel(ray5) is False # vertical rays can't calc a y-value with pytest.raises(ArithmeticError): _ = ray1.yof(-1.0)
def _get_point_on_dimline(point: "Vertex", dimray: ConstructionRay) -> Vec3: """get the measure target point projection on the dimension line""" return dimray.intersect(dimray.orthogonal(point))
def __init__(self, dimension: 'Dimension', ucs: 'UCS' = None, override: 'DimStyleOverride' = None): super().__init__(dimension, ucs, override) if self.text_movement_rule == 0: # moves the dimension line with dimension text, this makes no sense for ezdxf (just set `base` argument) self.text_movement_rule = 2 self.oblique_angle = self.dimension.get_dxf_attrib( 'oblique_angle', 90) # type: float self.dim_line_angle = self.dimension.get_dxf_attrib('angle', 0) # type: float self.dim_line_angle_rad = math.radians( self.dim_line_angle) # type: float self.ext_line_angle = self.dim_line_angle + self.oblique_angle # type: float self.ext_line_angle_rad = math.radians( self.ext_line_angle) # type: float # text is aligned to dimension line self.text_rotation = self.dim_line_angle # type: float if self.text_halign in ( 3, 4 ): # text above extension line, is always aligned with extension lines self.text_rotation = self.ext_line_angle self.ext1_line_start = Vec2(self.dimension.dxf.defpoint2) self.ext2_line_start = Vec2(self.dimension.dxf.defpoint3) ext1_ray = ConstructionRay(self.ext1_line_start, angle=self.ext_line_angle_rad) ext2_ray = ConstructionRay(self.ext2_line_start, angle=self.ext_line_angle_rad) dim_line_ray = ConstructionRay(self.dimension.dxf.defpoint, angle=self.dim_line_angle_rad) self.dim_line_start = dim_line_ray.intersect(ext1_ray) # type: Vec2 self.dim_line_end = dim_line_ray.intersect(ext2_ray) # type: Vec2 self.dim_line_center = self.dim_line_start.lerp( self.dim_line_end) # type: Vec2 if self.dim_line_start == self.dim_line_end: self.dim_line_vec = Vec2.from_angle(self.dim_line_angle_rad) else: self.dim_line_vec = (self.dim_line_end - self.dim_line_start).normalize() # type: Vec2 # set dimension defpoint to expected location - 3D vertex required! self.dimension.dxf.defpoint = Vector(self.dim_line_start) self.measurement = (self.dim_line_end - self.dim_line_start).magnitude # type: float self.text = self.text_override( self.measurement * self.dim_measurement_factor) # type: str # only for linear dimension in multi point mode self.multi_point_mode = override.pop('multi_point_mode', False) # 1 .. move wide text up # 2 .. move wide text down # None .. ignore self.move_wide_text = override.pop('move_wide_text', None) # type: bool # actual text width in drawing units self.dim_text_width = 0 # type: float # arrows self.required_arrows_space = 2 * self.arrow_size + self.text_gap # type: float self.arrows_outside = self.required_arrows_space > self.measurement # type: bool # text location and rotation if self.text: # text width and required space self.dim_text_width = self.text_width(self.text) # type: float if self.dim_tolerance: self.dim_text_width += self.tol_text_width elif self.dim_limits: # limits show the upper and lower limit of the measurement as stacked values # and with the size of tolerances measurement = self.measurement * self.dim_measurement_factor self.measurement_upper_limit = measurement + self.tol_maximum self.measurement_lower_limit = measurement - self.tol_minimum self.tol_text_upper = self.format_tolerance_text( self.measurement_upper_limit) self.tol_text_lower = self.format_tolerance_text( self.measurement_lower_limit) self.tol_text_width = self.tolerance_text_width( max(len(self.tol_text_upper), len(self.tol_text_lower))) # only limits are displayed so: self.dim_text_width = self.tol_text_width if self.multi_point_mode: # ezdxf has total control about vertical text position in multi point mode self.text_vertical_position = 0. if self.text_valign == 0 and abs( self.text_vertical_position) < 0.7: # vertical centered text needs also space for arrows required_space = self.dim_text_width + 2 * self.arrow_size else: required_space = self.dim_text_width self.is_wide_text = required_space > self.measurement if not self.force_text_inside: # place text outside if wide text and not forced inside self.text_outside = self.is_wide_text elif self.is_wide_text and self.text_halign < 3: # center wide text horizontal self.text_halign = 0 # use relative text shift to move wide text up or down in multi point mode if self.multi_point_mode and self.is_wide_text and self.move_wide_text: shift_value = self.text_height + self.text_gap if self.move_wide_text == 1: # move text up self.text_shift_v = shift_value if self.vertical_placement == -1: # text below dimension line # shift again self.text_shift_v += shift_value elif self.move_wide_text == 2: # move text down self.text_shift_v = -shift_value if self.vertical_placement == 1: # text above dimension line # shift again self.text_shift_v -= shift_value # get final text location - no altering after this line self.text_location = self.get_text_location() # type: Vec2 # text rotation override rotation = self.text_rotation # type: float if self.user_text_rotation is not None: rotation = self.user_text_rotation elif self.text_outside and self.text_outside_horizontal: rotation = 0 elif self.text_inside and self.text_inside_horizontal: rotation = 0 self.text_rotation = rotation self.text_box = TextBox(center=self.text_location, width=self.dim_text_width, height=self.text_height, angle=self.text_rotation, gap=self.text_gap * .75) if self.text_has_leader: p1, p2, *_ = self.text_box.corners self.leader1, self.leader2 = order_leader_points( self.dim_line_center, p1, p2) # not exact what BricsCAD (AutoCAD) expect, but close enough self.dimension.dxf.text_midpoint = self.leader1 else: # write final text location into DIMENSION entity self.dimension.dxf.text_midpoint = self.text_location
def __init__( self, dimension: "Dimension", ucs: "UCS" = None, override: "DimStyleOverride" = None, ): super().__init__(dimension, ucs, override) measurement = self.measurement if measurement.text_movement_rule == 0: # moves the dimension line with dimension text, this makes no sense # for ezdxf (just set `base` argument) measurement.text_movement_rule = 2 self.oblique_angle: float = self.dimension.get_dxf_attrib( "oblique_angle", 90) self.dim_line_angle: float = self.dimension.get_dxf_attrib("angle", 0) self.dim_line_angle_rad: float = math.radians(self.dim_line_angle) self.ext_line_angle: float = self.dim_line_angle + self.oblique_angle self.ext_line_angle_rad: float = math.radians(self.ext_line_angle) # text is aligned to dimension line measurement.text_rotation = self.dim_line_angle # text above extension line, is always aligned with extension lines if measurement.text_halign in (3, 4): measurement.text_rotation = self.ext_line_angle self.ext1_line_start = Vec2(self.dimension.dxf.defpoint2) self.ext2_line_start = Vec2(self.dimension.dxf.defpoint3) ext1_ray = ConstructionRay(self.ext1_line_start, angle=self.ext_line_angle_rad) ext2_ray = ConstructionRay(self.ext2_line_start, angle=self.ext_line_angle_rad) dim_line_ray = ConstructionRay(self.dimension.dxf.defpoint, angle=self.dim_line_angle_rad) self.dim_line_start: Vec2 = dim_line_ray.intersect(ext1_ray) self.dim_line_end: Vec2 = dim_line_ray.intersect(ext2_ray) self.dim_line_center: Vec2 = self.dim_line_start.lerp( self.dim_line_end) if self.dim_line_start == self.dim_line_end: self.dim_line_vec = Vec2.from_angle(self.dim_line_angle_rad) else: self.dim_line_vec = (self.dim_line_end - self.dim_line_start).normalize() # set dimension defpoint to expected location - 3D vertex required! self.dimension.dxf.defpoint = Vec3(self.dim_line_start) raw_measurement = (self.dim_line_end - self.dim_line_start).magnitude measurement.update(raw_measurement) # only for linear dimension in multi point mode self.multi_point_mode = self.dim_style.pop("multi_point_mode", False) # 1 .. move wide text up # 2 .. move wide text down # None .. ignore self.move_wide_text: Optional[bool] = self.dim_style.pop( "move_wide_text", None) # actual text width in drawing units self._total_text_width: float = 0 # arrows self.required_arrows_space: float = (2 * self.arrows.arrow_size + measurement.text_gap) self.arrows_outside: bool = (self.required_arrows_space > raw_measurement) # text location and rotation if measurement.text: # text width and required space self._total_text_width = self.total_text_width() if self.tol.has_limits: # limits show the upper and lower limit of the measurement as # stacked values and with the size of tolerances self.tol.update_limits(self.measurement.value) if self.multi_point_mode: # ezdxf has total control about vertical text position in multi # point mode measurement.text_vertical_position = 0.0 if (measurement.text_valign == 0 and abs(measurement.text_vertical_position) < 0.7): # vertical centered text needs also space for arrows required_space = (self._total_text_width + 2 * self.arrows.arrow_size) else: required_space = self._total_text_width measurement.is_wide_text = required_space > raw_measurement if not measurement.force_text_inside: # place text outside if wide text and not forced inside measurement.text_is_outside = measurement.is_wide_text elif measurement.is_wide_text and measurement.text_halign < 3: # center wide text horizontal measurement.text_halign = 0 # use relative text shift to move wide text up or down in multi # point mode if (self.multi_point_mode and measurement.is_wide_text and self.move_wide_text): shift_value = measurement.text_height + measurement.text_gap if self.move_wide_text == 1: # move text up measurement.text_shift_v = shift_value if (measurement.vertical_placement == -1 ): # text below dimension line # shift again measurement.text_shift_v += shift_value elif self.move_wide_text == 2: # move text down measurement.text_shift_v = -shift_value if (measurement.vertical_placement == 1 ): # text above dimension line # shift again measurement.text_shift_v -= shift_value # get final text location - no altering after this line measurement.text_location = self.get_text_location() # text rotation override rotation: float = measurement.text_rotation if measurement.user_text_rotation is not None: rotation = measurement.user_text_rotation elif (measurement.text_is_outside and measurement.text_outside_horizontal): rotation = 0.0 elif (measurement.text_is_inside and measurement.text_inside_horizontal): rotation = 0.0 measurement.text_rotation = rotation text_box = self.init_text_box() self.geometry.set_text_box(text_box) if measurement.has_leader: p1, p2, *_ = text_box.corners self.leader1, self.leader2 = order_leader_points( self.dim_line_center, p1, p2) # not exact what BricsCAD (AutoCAD) expect, but close enough self.dimension.dxf.text_midpoint = self.leader1 else: # write final text location into DIMENSION entity self.dimension.dxf.text_midpoint = measurement.text_location
def profile_construction_ray_init_once(count=COUNT): ray1 = ConstructionRay(p1=P1, p2=P2) ray2 = ConstructionRay(p1=P3, p2=P4) for _ in range(count): ray1.intersect(ray2)
def test_ray2d_normal_horizontal(self): ray = ConstructionRay((10, 10), (20, 10)) # horizontal line ortho = ray.orthogonal((3, 3)) point = ray.intersect(ortho) assert point.isclose(Vec3(3, 10))
def test_ray2d_normal(self): ray = ConstructionRay((-10, 3), (17, -7)) ortho = ray.orthogonal((3, 3)) point = ray.intersect(ortho) assert point.isclose(Vec3(1.4318, -1.234), abs_tol=1e-4)
def test_ray2d_normal_vertical(self): ray = ConstructionRay((10, 1), (10, -7)) # vertical line ortho = ray.orthogonal((3, 3)) point = ray.intersect(ortho) assert point.isclose(Vec3(10, 3))