def get_draw_angles(start: float, end: float, extrusion: Vector): if extrusion.isclose(Z_AXIS): return start, end else: ocs = OCS(extrusion) s = ocs.to_wcs(Vector.from_angle(start)) e = ocs.to_wcs(Vector.from_angle(end)) return normalize_angle(e.angle), normalize_angle(s.angle)
def random_3d_path(steps: int = 100, max_step_size: float = 1.0, max_heading: float = math.pi / 2.0, max_pitch: float = math.pi / 8.0, retarget: int = 20) -> Iterable[Vector]: """ Returns a random 3D path as iterable of :class:`~ezdxf.math.Vector` objects. Args: steps: count of vertices to generate max_step_size: max step size max_heading: limit heading angle change per step to ± max_heading/2, rotation about the z-axis in radians max_pitch: limit pitch angle change per step to ± max_pitch/2, rotation about the x-axis in radians retarget: specifies steps before changing global walking target """ max_ = max_step_size * steps def next_global_target(): return Vector((rnd(max_), rnd(max_), rnd(max_))) walker = Vector() target = next_global_target() for i in range(steps): if i % retarget == 0: target = target + next_global_target() angle = (target - walker).angle length = max_step_size * random.random() heading_angle = angle + rnd_perlin(max_heading, walker) next_step = Vector.from_angle(heading_angle, length) pitch_angle = rnd_perlin(max_pitch, walker) walker += Matrix44.x_rotate(pitch_angle).transform(next_step) yield walker
def linear_measurement(p1: Vector, p2: Vector, angle: float = 0, ocs: 'OCS' = None) -> float: """ Returns distance from `p1` to `p2` projected onto ray defined by `angle`, `angle` in radians in the xy-plane. """ if ocs is not None and ocs.uz != (0, 0, 1): p1 = ocs.to_wcs(p1) p2 = ocs.to_wcs(p2) # angle in OCS xy-plane ocs_direction = Vector.from_angle(angle) measurement_direction = ocs.to_wcs(ocs_direction) else: # angle in WCS xy-plane measurement_direction = Vector.from_angle(angle) t1 = measurement_direction.project(p1) t2 = measurement_direction.project(p2) return (t2 - t1).magnitude
def tangent(self, t: float) -> Vector: """ Get tangent at distance `t` as Vector() object. """ angle = t ** 2 / (2. * self.curvature_powers[2]) return Vector.from_angle(angle)
def cubic_bezier_arc_parameters( start_angle: float, end_angle: float, segments: int = 1) -> Sequence[Vector]: """ Yields cubic Bézier-curve parameters for a circular 2D arc with center at (0, 0) and a radius of 1 in the form of [start point, 1. control point, 2. control point, end point]. Args: start_angle: start angle in radians end_angle: end angle in radians (end_angle > start_angle!) segments: count of Bèzier-curve segments, at least one segment for each quarter (pi/2) """ if segments < 1: raise ValueError('Invalid argument segments (>= 1).') delta_angle = end_angle - start_angle if delta_angle > 0: arc_count = max(math.ceil(delta_angle / math.pi * 2.0), segments) else: raise ValueError('Delta angle from start- to end angle has to be > 0.') segment_angle = delta_angle / arc_count tangent_length = TANGENT_FACTOR * math.tan(segment_angle / 4.0) angle = start_angle end_point = None for _ in range(arc_count): start_point = Vector.from_angle( angle) if end_point is None else end_point angle += segment_angle end_point = Vector.from_angle(angle) control_point_1 = start_point + ( -start_point.y * tangent_length, start_point.x * tangent_length) control_point_2 = end_point + ( end_point.y * tangent_length, -end_point.x * tangent_length) yield start_point, control_point_1, control_point_2, end_point
def cubic_bezier_arc_parameters(start_angle: float, end_angle: float, segments: int = 1): """ Yields cubic Bezier curve parameters for a circular 2D arc with center at (0, 0) and a radius of 1 in the form of [start point, 1. control point, 2. control point, end point]. Args: start_angle: start angle in radians end_angle: end angle in radians (end_angle > start_angle!) segments: count of segments, at least one segment for each quarter (pi/2) """ # Source: https://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves if segments < 1: raise ValueError('Invalid argument segments (>= 1).') delta_angle = end_angle - start_angle if delta_angle > 0: arc_count = max(math.ceil(delta_angle / math.pi * 2.0), segments) else: raise ValueError('Delta angle from start- to end angle has to be > 0.') segment_angle = delta_angle / arc_count tangent_length = 4.0 / 3.0 * math.tan(segment_angle / 4.0) angle = start_angle end_point = None for _ in range(arc_count): start_point = Vector.from_angle( angle) if end_point is None else end_point angle += segment_angle end_point = Vector.from_angle(angle) control_point_1 = start_point + (-start_point.y * tangent_length, start_point.x * tangent_length) control_point_2 = end_point + (end_point.y * tangent_length, -end_point.x * tangent_length) yield start_point, control_point_1, control_point_2, end_point