def minor_axis(self) -> Vector: major_axis = Vector(self.dxf.major_axis) # local x-axis, 0 rad extrusion = Vector( self.dxf.extrusion ) # local z-axis, normal vector of the ellipse plane minor_axis_length = major_axis.magnitude * self.dxf.ratio return extrusion.cross(major_axis).normalize(minor_axis_length)
def vertices(self, params: Iterable[float]) -> Iterable[Vector]: """ Yields vertices on ellipse for iterable `params` in WCS. Args: params: param values in the range from ``0`` to ``2*pi`` in radians, param goes counter clockwise around the extrusion vector, major_axis = local x-axis = 0 rad. .. versionadded:: 0.11 """ # get main axis major_axis = Vector(self.dxf.major_axis) # local x-axis, 0 rad extrusion = Vector(self.dxf.extrusion) # local z-axis, normal vector of the ellipse plane minor_axis = extrusion.cross(major_axis) # local y-axis, pi/2 rad, need only normalized direction # normal vectors for local x- and y-axis x_axis = major_axis.normalize() y_axis = minor_axis.normalize() # point on ellipse calculation radius_x = major_axis.magnitude radius_y = radius_x * self.dxf.ratio center = Vector(self.dxf.center) for param in params: # Ellipse params in radians by definition (DXF Reference) x = math.cos(param) * radius_x y = math.sin(param) * radius_y # construct WCS coordinates, do not convert from OCS to WCS, extrusion defines only the normal vector of # the ellipse plane. yield center + (x_axis * x) + (y_axis * y)
def test_arbitrary_ucs(): origin = Vector(3, 3, 3) ux = Vector(1, 2, 0) def_point_in_xy_plane = Vector(3, 10, 4) uz = ux.cross(def_point_in_xy_plane - origin) ucs = UCS(origin=origin, ux=ux, uz=uz) def_point_in_ucs = ucs.from_wcs(def_point_in_xy_plane) assert def_point_in_ucs.z == 0 assert ucs.to_wcs(def_point_in_ucs) == def_point_in_xy_plane assert ucs.is_cartesian is True
def cone_2p( count: int = 16, radius: float = 1.0, base_center=(0, 0, 0), apex=(0, 0, 1)) -> MeshTransformer: """ Create a `cone <https://en.wikipedia.org/wiki/Cone>`_ as :class:`~ezdxf.render.MeshTransformer` object from two points, `base_center` is the center of the base circle and `apex` as the tip of the cone. Args: count: edge count of basis radius: radius of basis base_center: center point of base circle apex: tip of the cone Returns: :class:`~ezdxf.render.MeshTransformer` .. versionadded:: 0.11 """ # Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license. # Python port Copyright (c) 2012 Tim Knip (http://www.floorplanner.com), under the MIT license. # Additions by Alex Pletzer (Pennsylvania State University) # Adaptation for ezdxf, Copyright (c) 2020, Manfred Moitzi, MIT License. start = Vector(base_center) end = Vector(apex) slices = int(count) ray = end - start z_axis = ray.normalize() is_y = (fabs(z_axis.y) > 0.5) x_axis = Vector(float(is_y), float(not is_y), 0).cross(z_axis).normalize() y_axis = x_axis.cross(z_axis).normalize() mesh = MeshVertexMerger() def vertex(angle) -> Vector: # radial direction pointing out out = x_axis * cos(angle) + y_axis * sin(angle) return start + out * radius dt = pi * 2.0 / slices for i in range(0, slices): t0 = i * dt i1 = (i + 1) % slices t1 = i1 * dt # coordinates and associated normal pointing outwards of the cone's # side p0 = vertex(t0) p1 = vertex(t1) # polygon on the low side (disk sector) mesh.add_face([start, p0, p1]) # polygon extending from the low side to the tip mesh.add_face([p0, end, p1]) return MeshTransformer.from_builder(mesh)
def cylinder_2p( count: int = 16, radius: float = 1, base_center=(0, 0, 0), top_center=(0, 0, 1), ) -> MeshTransformer: """ Create a `cylinder <https://en.wikipedia.org/wiki/Cylinder>`_ as :class:`~ezdxf.render.MeshTransformer` object from two points, `base_center` is the center of the base circle and, `top_center` the center of the top circle. Args: count: profiles edge count radius: radius for bottom profile base_center: center of base circle top_center: center of top circle Returns: :class:`~ezdxf.render.MeshTransformer` .. versionadded:: 0.11 """ # Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license. # Python port Copyright (c) 2012 Tim Knip (http://www.floorplanner.com), under the MIT license. # Additions by Alex Pletzer (Pennsylvania State University) # Adaptation for ezdxf, Copyright (c) 2020, Manfred Moitzi, MIT License. start = Vector(base_center) end = Vector(top_center) radius = float(radius) slices = int(count) ray = end - start z_axis = ray.normalize() is_y = (fabs(z_axis.y) > 0.5) x_axis = Vector(float(is_y), float(not is_y), 0).cross(z_axis).normalize() y_axis = x_axis.cross(z_axis).normalize() mesh = MeshVertexMerger() def vertex(stack, angle): out = (x_axis * cos(angle)) + (y_axis * sin(angle)) return start + (ray * stack) + (out * radius) dt = pi * 2 / float(slices) for i in range(0, slices): t0 = i * dt i1 = (i + 1) % slices t1 = i1 * dt mesh.add_face([start, vertex(0, t0), vertex(0, t1)]) mesh.add_face( [vertex(0, t1), vertex(0, t0), vertex(1, t0), vertex(1, t1)]) mesh.add_face([end, vertex(1, t1), vertex(1, t0)]) return MeshTransformer.from_builder(mesh)
def test_arbitrary_ucs(): origin = Vector(3, 3, 3) ux = Vector(1, 2, 0) def_point_in_xy_plane = Vector(3, 10, 4) uz = ux.cross(def_point_in_xy_plane - origin) ucs = UCS(origin=origin, ux=ux, uz=uz) m = Matrix44.ucs(ucs.ux, ucs.uy, ucs.uz, ucs.origin) def_point_in_ucs = ucs.from_wcs(def_point_in_xy_plane) assert ucs.ux == m.ux assert ucs.uy == m.uy assert ucs.uz == m.uz assert ucs.origin == m.origin assert def_point_in_ucs == m.ucs_vertex_from_wcs(def_point_in_xy_plane) assert def_point_in_ucs.z == 0 assert ucs.to_wcs(def_point_in_ucs) == def_point_in_xy_plane assert ucs.is_cartesian is True
def transform(self, m: Matrix44) -> 'MText': """ Transform MTEXT entity by transformation matrix `m` inplace. .. versionadded:: 0.13 """ dxf = self.dxf old_extrusion = Vector(dxf.extrusion) new_extrusion, _ = transform_extrusion(old_extrusion, m) if dxf.hasattr('rotation') and not dxf.hasattr('text_direction'): # MTEXT is not an OCS entity, but I don't know how else to convert # a rotation angle for an entity just defined by an extrusion vector. # It's correct for the most common case: extrusion=(0, 0, 1) ocs = OCS(old_extrusion) dxf.text_direction = ocs.to_wcs(Vector.from_deg_angle( dxf.rotation)) dxf.discard('rotation') old_text_direction = Vector(dxf.text_direction) new_text_direction = m.transform_direction(old_text_direction) old_char_height_vec = old_extrusion.cross( old_text_direction).normalize(dxf.char_height) new_char_height_vec = m.transform_direction(old_char_height_vec) oblique = new_text_direction.angle_between(new_char_height_vec) dxf.char_height = new_char_height_vec.magnitude * math.sin(oblique) if dxf.hasattr('width'): width_vec = old_text_direction.normalize(dxf.width) dxf.width = m.transform_direction(width_vec).magnitude dxf.insert = m.transform(dxf.insert) dxf.text_direction = new_text_direction dxf.extrusion = new_extrusion return self