Beispiel #1
0
def make_hatches_from_str(s: str,
                          font: fonts.FontFace,
                          size: float = 1.0,
                          align: str = 'LEFT',
                          length: float = 0,
                          segments: int = 4,
                          dxfattribs: Dict = None,
                          m: Matrix44 = None) -> List[Hatch]:
    """ Convert a single line string `s` into a list of virtual
    :class:`~ezdxf.entities.Hatch` entities.
    The text `size` is the height of the uppercase letter "X" (cap height).
    The paths are aligned about the insertion point at (0, 0).
    The HATCH entities are aligned to this insertion point. BASELINE means the
    bottom of the letter "X".

    Args:
         s: text to convert
         font: font face definition
         size: text size (cap height) in drawing units
         align: alignment as string, default is "LEFT"
         length: target length for the "ALIGNED" and "FIT" alignments
         segments: minimal segment count per Bézier curve
         dxfattribs: additional DXF attributes
         m: transformation :class:`~ezdxf.math.Matrix44`

    """
    font_properties, font_measurements = _get_font_data(font)
    # scale cap_height for 1 drawing unit!
    scaled_size = size / font_measurements.cap_height
    scaled_fm = font_measurements.scale_from_baseline(scaled_size)
    paths = _str_to_paths(s, font_properties, scaled_size)

    # HATCH is an OCS entity, transforming just the polyline paths
    # is not correct! The Hatch has to be created in the xy-plane!
    hatches = []
    dxfattribs = dxfattribs or dict()
    dxfattribs.setdefault('solid_fill', 1)
    dxfattribs.setdefault('pattern_name', 'SOLID')
    dxfattribs.setdefault('color', 7)

    for contour, holes in group_contour_and_holes(paths):
        hatch = Hatch.new(dxfattribs=dxfattribs)
        # Vec2 removes the z-axis, which would be interpreted as bulge value!
        hatch.paths.add_polyline_path(
            Vec2.generate(contour.flattening(1, segments=segments)), flags=1)
        for hole in holes:
            hatch.paths.add_polyline_path(
                Vec2.generate(hole.flattening(1, segments=segments)), flags=0)
        hatches.append(hatch)

    halign, valign = const.TEXT_ALIGN_FLAGS[align.upper()]
    bbox = path.bbox(paths, precise=False)
    matrix = get_alignment_transformation(scaled_fm, bbox, halign, valign)
    if m is not None:
        matrix *= m

    # Transform HATCH entities as a unit:
    return [hatch.transform(matrix) for hatch in hatches]
Beispiel #2
0
def build_edge_path(
    boundaries: BoundaryPaths,
    path: Path,
    flags: int,
    distance: float,
    segments: int,
    g1_tol: float,
):
    if path.has_curves:  # Edge path with LINE and SPLINE edges
        edge_path = boundaries.add_edge_path(flags)
        for edge in to_bsplines_and_vertices(path, g1_tol=g1_tol):
            if isinstance(edge, BSpline):
                edge_path.add_spline(
                    control_points=edge.control_points,
                    degree=edge.degree,
                    knot_values=edge.knots(),
                )
            else:  # add LINE edges
                prev = edge[0]
                for p in edge[1:]:
                    edge_path.add_line(prev, p)
                    prev = p
    else:  # Polyline boundary path
        boundaries.add_polyline_path(Vec2.generate(
            path.flattening(distance, segments)),
                                     flags=flags)
Beispiel #3
0
    def extend(self, vertices: Iterable) -> None:
        """ Append multiple `vertices`.

        Args:
             vertices: iterable of vertices as :class:`Vec2` compatible objects

        """
        self.vertices.extend(Vec2.generate(vertices))
Beispiel #4
0
    def transform_2d(vertices: Iterable[Vertex],
                     insert: Vertex = Vec3(0, 0, 0),
                     shift: Tuple[float, float] = (0, 0),
                     rotation: float = 0,
                     scale: Tuple[float, float] = (1, 1),
                     oblique: float = 0) -> List[Vec3]:
        """ Transform any vertices from the text line located at the base
        location at (0, 0) and alignment "LEFT".

        Args:
            vertices: iterable of vertices
            insert: insertion location
            shift: (shift-x, shift-y) as 2-tuple of float
            rotation: text rotation in radians
            scale: (scale-x, scale-y)  as 2-tuple of float
            oblique: shear angle (slanting) in x-direction in radians

        """
        # Building a transformation matrix vs. applying transformations in
        # individual steps:
        # Most text is horizontal, because people like to read horizontal text!
        # Operating in 2D is faster than building a full 3D transformation
        # matrix and a pure 2D transformation matrix is not implemented!
        # This function doesn't transform many vertices at the same time,
        # mostly only 4 vertices, therefore the matrix multiplication overhead
        # does not pay off.
        # The most expensive rotation transformation is the least frequently
        # used transformation.
        # IMPORTANT: this assumptions are not verified by profiling!

        # Use 2D vectors:
        vertices = Vec2.generate(vertices)

        # 1. slanting at the original location (very rare):
        if oblique:
            slant_x = math.tan(oblique)
            vertices = (Vec2(v.x + v.y * slant_x, v.y) for v in vertices)

        # 2. apply alignment shifting (frequently):
        shift_vector = Vec2(shift)
        if shift_vector:
            vertices = (v + shift_vector for v in vertices)

        # 3. scale (and mirror) at the aligned location (more often):
        scale_x, scale_y = scale
        if scale_x != 1 or scale_y != 1:
            vertices = (Vec2(v.x * scale_x, v.y * scale_y) for v in vertices)

        # 4. apply rotation (rare):
        if rotation:
            vertices = (v.rotate(rotation) for v in vertices)

        # 5. move to insert location in OCS/3D! (every time)
        insert = Vec3(insert)
        return [insert + v for v in vertices]
Beispiel #5
0
def build_poly_path(
    boundaries: BoundaryPaths,
    path: Path,
    flags: int,
    distance: float,
    segments: int,
):
    boundaries.add_polyline_path(
        # Vec2 removes the z-axis, which would be interpreted as bulge value!
        Vec2.generate(path.flattening(distance, segments)),
        flags=flags,
    )
Beispiel #6
0
def convex_hull_2d(points: Iterable['Vertex']) -> List['Vertex']:
    """ Returns 2D convex hull for `points`.

    Args:
        points: iterable of points as :class:`Vec3` compatible objects,
            z-axis is ignored

    """

    def _convexhull(hull):
        while len(hull) > 2:
            # the last three points
            start_point, check_point, destination_point = hull[-3:]
            # curve not turns right
            if not is_point_left_of_line(check_point, start_point,
                                         destination_point):
                # remove the penultimate point
                del hull[-2]
            else:
                break
        return hull

    points = sorted(set(Vec2.generate(points)))  # remove duplicate points

    if len(points) < 3:
        raise ValueError(
            "Convex hull calculation requires 3 or more unique points.")

    upper_hull = points[:2]  # first two points
    for next_point in points[2:]:
        upper_hull.append(next_point)
        upper_hull = _convexhull(upper_hull)
    lower_hull = [points[-1], points[-2]]  # last two points

    for next_point in reversed(points[:-2]):
        lower_hull.append(next_point)
        lower_hull = _convexhull(lower_hull)
    upper_hull.extend(lower_hull[1:-1])
    return upper_hull
Beispiel #7
0
def test_2d_tangent_computation():
    dbcurve = Bezier4P(DEFPOINTS2D)
    for index, chk in enumerate(Vec2.generate(TANGENTS2D)):
        assert dbcurve.tangent(index * .1).isclose(chk)
Beispiel #8
0
def test_accepts_2d_points():
    curve = Bezier4P(DEFPOINTS2D)
    for index, chk in enumerate(Vec2.generate(POINTS2D)):
        assert curve.point(index * .1).isclose(chk)
Beispiel #9
0
def test_first_derivative(bezier):
    dbcurve = bezier(DEFPOINTS2D)
    for index, chk in enumerate(Vec2.generate(TANGENTS2D)):
        assert dbcurve.tangent(index * .1).isclose(chk)
Beispiel #10
0
 def build_poly_path(hatch: Hatch, path: Path, flags: int):
     hatch.paths.add_polyline_path(
         # Vec2 removes the z-axis, which would be interpreted as bulge value!
         Vec2.generate(path.flattening(distance, segments)),
         flags=flags)