Ejemplo n.º 1
0
def test_polygon_with_holes_to_dxf_entity():
    res = cast(Hatch, list(geo.dxf_entities(POLYGON_2))[0])
    assert len(res.paths) == 3
    p = res.paths[1]
    assert p.PATH_TYPE == 'PolylinePath'
    assert p.vertices == Vector.list(HOLE1)
    p = res.paths[2]
    assert p.PATH_TYPE == 'PolylinePath'
    assert p.vertices == Vector.list(HOLE2)
Ejemplo n.º 2
0
Archivo: geo.py Proyecto: kloppen/ezdxf
def _parse_polygon(coordinates: Sequence) -> Sequence:
    """ Returns polygon definition as tuple (exterior, [holes]). """
    if _is_coordinate_sequence(coordinates):
        exterior = coordinates
        holes = []
    else:
        exterior = coordinates[0]
        holes = coordinates[1:]
    return Vector.list(exterior), [Vector.list(h) for h in holes]
Ejemplo n.º 3
0
Archivo: geo.py Proyecto: kloppen/ezdxf
def parse(geo_mapping: Dict) -> Dict:
    """ Parse ``__geo_interface__`` convert all coordinates into
    :class:`Vector` objects, Polygon['coordinates'] is always a
    tuple (exterior, holes), holes maybe an empty list.

    """
    geo_mapping = copy.deepcopy(geo_mapping)
    type_ = geo_mapping.get(TYPE)
    if type_ is None:
        raise ValueError(f'Required key "{TYPE}" not found.')

    if type_ == FEATURE_COLLECTION:
        # It is possible for this array to be empty.
        features = geo_mapping.get(FEATURES)
        if features:
            geo_mapping[FEATURES] = [parse(f) for f in features]
        else:
            raise ValueError(f'Missing key "{FEATURES}" in FeatureCollection.')
    elif type_ == GEOMETRY_COLLECTION:
        # It is possible for this array to be empty.
        geometries = geo_mapping.get(GEOMETRIES)
        if geometries:
            geo_mapping[GEOMETRIES] = [parse(g) for g in geometries]
        else:
            raise ValueError(
                f'Missing key "{GEOMETRIES}" in GeometryCollection.')
    elif type_ == FEATURE:
        # The value of the geometry member SHALL be either a Geometry object
        # or, in the case that the Feature is unlocated, a JSON null value.
        if GEOMETRY in geo_mapping:
            geometry = geo_mapping.get(GEOMETRY)
            geo_mapping[GEOMETRY] = parse(geometry) if geometry else None
        else:
            raise ValueError(f'Missing key "{GEOMETRY}" in Feature.')
    elif type_ in {
            POINT, LINE_STRING, POLYGON, MULTI_POINT, MULTI_LINE_STRING,
            MULTI_POLYGON
    }:
        coordinates = geo_mapping.get(COORDINATES)
        if coordinates is None:
            raise ValueError(f'Missing key "{COORDINATES}" in {type_}.')
        if type_ == POINT:
            coordinates = Vector(coordinates)
        elif type_ in (LINE_STRING, MULTI_POINT):
            coordinates = Vector.list(coordinates)
        elif type_ == POLYGON:
            coordinates = _parse_polygon(coordinates)
        elif type_ == MULTI_LINE_STRING:
            coordinates = [Vector.list(v) for v in coordinates]
        elif type_ == MULTI_POLYGON:
            coordinates = [_parse_polygon(v) for v in coordinates]
        geo_mapping[COORDINATES] = coordinates
    else:
        raise TypeError(f'Invalid type "{type_}".')
    return geo_mapping
Ejemplo n.º 4
0
def test_polygon_without_holes_to_dxf_entity():
    res = cast(Hatch, list(geo.dxf_entities(POLYGON_0))[0])
    assert res.dxftype() == 'HATCH'
    assert len(res.paths) == 1
    p = res.paths[0]
    assert p.PATH_TYPE == 'PolylinePath'
    assert p.vertices == Vector.list(EXTERIOR)
Ejemplo n.º 5
0
def spline_insert_knot():
    dwg = ezdxf.new('R2000', setup=True)
    msp = dwg.modelspace()

    def add_spline(control_points, color=3, knots=None):
        msp.add_polyline2d(control_points,
                           dxfattribs={
                               'color': color,
                               'linetype': 'DASHED'
                           })
        msp.add_open_spline(control_points,
                            degree=3,
                            knots=knots,
                            dxfattribs={'color': color})

    control_points = Vector.list([(0, 0), (10, 20), (30, 10), (40, 10),
                                  (50, 0), (60, 20), (70, 50), (80, 70)])
    add_spline(control_points, color=3, knots=None)

    bspline = BSpline(control_points, order=4)
    bspline.insert_knot(bspline.max_t / 2)
    add_spline(bspline.control_points, color=4, knots=bspline.knot_values())

    if dwg.validate():
        dwg.saveas("Spline_R2000_spline_insert_knot.dxf")
Ejemplo n.º 6
0
Archivo: mesh.py Proyecto: hh-wu/ezdxf
    def add_mesh(self,
                 vertices: List[Vector] = None,
                 faces: List[Sequence[int]] = None,
                 edges: List[Tuple[int, int]] = None,
                 mesh=None) -> None:
        """
        Add another mesh to this mesh.

        A `mesh` can be a :class:`MeshBuilder`, :class:`MeshVertexMerger` or :class:`~ezdxf.entities.Mesh` object
        or requires the attributes :attr:`vertices`, :attr:`edges` and :attr:`faces`.

        Args:
            vertices: list of vertices, a vertex is a ``(x, y, z)`` tuple or :class:`~ezdxf.math.Vector` object
            faces: list of faces, a face is a list of vertex indices
            edges: list of edges, an edge is a list of vertex indices
            mesh: another mesh entity

        """
        if mesh is not None:
            vertices = Vector.list(mesh.vertices)
            faces = mesh.faces
            edges = mesh.edges

        if vertices is None:
            raise ValueError("Requires vertices or another mesh.")
        faces = faces or []
        edges = edges or []
        indices = self.add_vertices(vertices)

        for v1, v2 in edges:
            self.edges.append((indices[v1], indices[v2]))

        for face_vertices in faces:
            self.faces.append(tuple(indices[vi] for vi in face_vertices))
Ejemplo n.º 7
0
def test_control_vertices(p1):
    vertices = list(p1.control_vertices())
    assert vertices == Vector.list([(0, 0), (2, 0), (2, 1), (4, 1), (4, 0)])
    path = Path()
    assert len(list(path.control_vertices())) == 0
    path = Path.from_vertices([(0, 0), (1, 0)])
    assert len(list(path.control_vertices())) == 2
def test_knot_generation(p, method):
    fit_points = Vector.list([(0, 0), (0, 10), (10, 10), (20, 10), (20, 0),
                              (30, 0), (30, 10), (40, 10), (40, 0)])
    count = len(fit_points)
    n = count - 1
    order = p + 1
    t_vector = distance_t_vector(fit_points)
    knots = list(knots_from_parametrization(n, p, t_vector, method))
    check_knots(n + 1, p + 1, knots)
Ejemplo n.º 9
0
 def from_vertices(cls, vertices: Iterable['Vertex'], close=False) -> 'Path':
     """ Returns a :class:`Path` from vertices.  """
     vertices = Vector.list(vertices)
     path = cls(start=vertices[0])
     for vertex in vertices[1:]:
         path.line_to(vertex)
     if close:
         path.close()
     return path
Ejemplo n.º 10
0
def cubic_bezier_interpolation(
        points: Iterable['Vertex']) -> Iterable[Bezier4P]:
    """
    Returns an interpolation curve for given data `points` as multiple cubic Bézier-curves.
    Returns n-1 cubic Bézier-curves for n given data points, curve i goes from point[i] to point[i+1].

    Args:
        points: data points

    .. versionadded:: 0.13

    """
    # Source: https://towardsdatascience.com/b%C3%A9zier-interpolation-8033e9a262c2
    points = Vector.list(points)
    if len(points) < 3:
        raise ValueError('At least 3 points required.')

    num = len(points) - 1

    # setup tri-diagonal matrix (a, b, c)
    b = [4.0] * num
    a = [1.0] * num
    c = [1.0] * num
    b[0] = 2.0
    b[num - 1] = 7.0
    a[num - 1] = 2.0

    # setup right-hand side quantities
    points_vector = [points[0] + 2.0 * points[1]]
    points_vector.extend(2.0 * (2.0 * points[i] + points[i + 1])
                         for i in range(1, num - 1))
    points_vector.append(8.0 * points[num - 1] + points[num])

    # solve tri-diagonal linear equation system
    solution = tridiagonal_matrix_solver((a, b, c), points_vector)
    control_points_1 = Vector.list(solution.rows())
    control_points_2 = [
        p * 2.0 - cp for p, cp in zip(points[1:], control_points_1[1:])
    ]
    control_points_2.append((control_points_1[num - 1] + points[num]) / 2.0)

    for defpoints in zip(points, control_points_1, control_points_2,
                         points[1:]):
        yield Bezier4P(defpoints)
def test_arc_distances():
    p = Vector.list([(0, 0), (2, 2), (4, 0), (6, -2), (8, 0)])
    # p[1]..p[3] are a straight line, radius calculation fails and
    # a straight line from p[1] to p[2] is used as replacement
    # for the second arc
    radius = 2.0
    arc_length = math.pi * 0.5 * radius
    diagonal = math.sqrt(2.0) * radius
    distances = list(arc_distances(p))
    assert len(distances) == 4
    assert isclose(distances[0], arc_length)
    assert isclose(distances[1], diagonal)  # replacement for arc
    assert isclose(distances[2], arc_length)
    assert isclose(distances[3], arc_length)
Ejemplo n.º 12
0
    def extend(self, vertices: Iterable['Vertex']) -> None:
        """ Append multiple vertices to the reference line.

        It is possible to work with 3D vertices, but all vertices have to be in
        the same plane and the normal vector of this plan is stored as
        extrusion vector in the MLINE entity.

        """
        vertices = Vector.list(vertices)
        if not vertices:
            return
        all_vertices = []
        if len(self):
            all_vertices.extend(self.get_locations())
        all_vertices.extend(vertices)
        self.generate_geometry(all_vertices)
def test_basis_vector_N_ip():
    degree = 3
    fit_points = Vector.list(POINTS2)  # data points D
    n = len(fit_points) - 1
    t_vector = list(uniform_t_vector(fit_points))
    knots = list(control_frame_knots(n, degree, t_vector))
    should_count = len(fit_points) - 2  # target control point count
    h = should_count - 1
    spline = Basis(knots, order=degree + 1, count=len(fit_points))
    matrix_N = [spline.basis(t) for t in t_vector]

    for k in range(1, n):
        basis_vector = bspline_basis_vector(u=t_vector[k],
                                            count=len(fit_points),
                                            degree=degree,
                                            knots=knots)
        for i in range(1, h):
            assert isclose(matrix_N[k][i], basis_vector[i])
Ejemplo n.º 14
0
def spline_control_frame_approximation():
    doc = new('R2000', setup=True)

    fit_points = Vector.list([(0, 0), (10, 20), (30, 10), (40, 10), (50, 0),
                              (60, 20), (70, 50), (80, 70), (65, 75)])
    msp = doc.modelspace()
    msp.add_polyline2d(fit_points, dxfattribs={'color': 2, 'linetype': 'DOT2'})

    spline = bspline_control_frame_approx(fit_points,
                                          count=7,
                                          degree=3,
                                          method='uniform')
    msp.add_polyline2d(spline.control_points,
                       dxfattribs={
                           'color': 3,
                           'linetype': 'DASHED'
                       })
    msp.add_open_spline(spline.control_points,
                        degree=spline.degree,
                        dxfattribs={'color': 3})
    msp.add_spline(fit_points, degree=3, dxfattribs={'color': 1})
    doc.saveas("Spline_R2000_spline_control_frame_approximation.dxf")
Ejemplo n.º 15
0
# Copyright (c) 2020, Manfred Moitzi
# License: MIT License
from pathlib import Path
import math
import ezdxf
from ezdxf.math import (Vector, estimate_tangents,
                        estimate_end_tangent_magnitude,
                        global_bspline_interpolation, linspace)

DIR = Path('~/Desktop/Outbox').expanduser()
points = Vector.list([(0, 0), (0, 10), (10, 10), (20, 10), (20, 0)])


def sine_wave(count: int, scale: float = 1.0):
    for t in linspace(0, math.tau, count):
        yield Vector(t * scale, math.sin(t) * scale)


def setup():
    doc = ezdxf.new()
    msp = doc.modelspace()
    msp.add_lwpolyline(points, dxfattribs={'color': 5, 'layer': 'frame'})
    for p in points:
        msp.add_circle(p,
                       radius=0.1,
                       dxfattribs={
                           'color': 1,
                           'layer': 'frame'
                       })
    doc.set_modelspace_vport(20, center=(10, 5))
    return doc, msp
def fit_points_2():
    return Vector.list([(0, 0), (0, 10), (10, 10), (20, 10), (20, 0), (30, 0),
                        (30, 10), (40, 10), (40, 0)])
def test_local_cubic_bspline_interpolation_from_tangents():
    points = Vector.list(POINTS1)
    tangents = estimate_tangents(points)
    control_points, knots = local_cubic_bspline_interpolation_from_tangents(points, tangents)
    assert len(control_points) == 8
    assert len(knots) == 8 + 4  # count + order
Ejemplo n.º 18
0
def test_line_string_to_dxf_entity():
    res = cast(LWPolyline, list(geo.dxf_entities(LINE_STRING))[0])
    assert res.dxftype() == 'LWPOLYLINE'
    assert list(res.vertices()) == Vector.list(EXTERIOR)
Ejemplo n.º 19
0

next_frame = Matrix44.translate(0, 5, 0)
right_frame = Matrix44.translate(10, 0, 0)

NAME = 'spline.dxf'
dwg = ezdxf.new('R2000')
msp = dwg.modelspace()


def draw(points):
    for point in points:
        msp.add_circle(radius=0.1, center=point, dxfattribs={'color': 1})


spline_points = Vector.list([(1., 1.), (2.5, 3.), (4.5, 2.), (6.5, 4.)])

# fit points
draw(spline_points)
Spline(spline_points).render_as_fit_points(msp, method='distance', dxfattribs={'color': 2})  # curve with definition points as fit points
Spline(spline_points).render_as_fit_points(msp, method='uniform', dxfattribs={'color': 3})
Spline(spline_points).render_as_fit_points(msp, method='centripetal', dxfattribs={'color': 4})  # distance ^ 1/2
Spline(spline_points).render_as_fit_points(msp, method='centripetal', power=1./3., dxfattribs={'color': 6})  # distance ^ 1/3

msp.add_spline(fit_points=spline_points, dxfattribs={'color': 1})
msp.add_text("Spline.render_as_fit_points() differs from AutoCAD fit point rendering", dxfattribs={'height': .1}).set_pos(spline_points[0])

# open uniform b-spline
spline_points = next_frame.transform_vectors(spline_points)
draw(spline_points)
msp.add_text("Spline.render_open_bspline() matches AutoCAD", dxfattribs={'height': .1}).set_pos(spline_points[0])
Ejemplo n.º 20
0
def virtual_leader_entities(leader: 'Leader') -> Iterable['DXFGraphic']:
    # Source: https://atlight.github.io/formats/dxf-leader.html
    # GDAL: DXF LEADER implementation:
    # https://github.com/OSGeo/gdal/blob/master/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_leader.cpp
    # LEADER DXF Reference:
    # http://help.autodesk.com/view/OARX/2018/ENU/?guid=GUID-396B2369-F89F-47D7-8223-8B7FB794F9F3
    from ezdxf.entities import DimStyleOverride
    assert leader.dxftype() == 'LEADER'

    vertices = Vector.list(leader.vertices)  # WCS
    if len(vertices) < 2:
        # This LEADER entities should be removed by the auditor if loaded or
        # ignored at exporting, if created by an ezdxf-user (log).
        raise ValueError('More than 1 vertex required.')
    dxf = leader.dxf
    doc = leader.doc

    # Some default values depend on the measurement system
    # 0/1 = imperial/metric
    if doc:
        measurement = doc.header.get('$MEASUREMENT', 0)
    else:
        measurement = 0

    # Set default styling attributes values:
    dimtad = 1
    dimgap = 0.625 if measurement else 0.0625
    dimscale = 1.0
    dimclrd = dxf.color
    dimltype = dxf.linetype
    dimlwd = dxf.lineweight
    override = None

    if doc:  # get styling attributes from associated DIMSTYLE and/or XDATA override
        override = DimStyleOverride(cast('Dimension', leader))
        dimtad = override.get('dimtad', dimtad)
        dimgap = override.get('dimgap', dimgap)
        dimscale = override.get('dimscale', dimscale)
        if dimscale == 0.0:  # special but unknown meaning
            dimscale = 1.0
        dimclrd = override.get('dimclrd', dimclrd)
        dimltype = override.get('dimltype', dimltype)
        dimlwd = override.get('dimlwd', dimlwd)

    text_width = dxf.text_width
    hook_line_vector = Vector(dxf.horizontal_direction)
    has_text_annotation = dxf.annotation_type == 0

    if has_text_annotation and dxf.has_hookline:
        if dxf.hookline_direction == 1:
            hook_line_vector = -hook_line_vector
        if dimtad != 0 and text_width > 0:
            vertices.append(vertices[-1] + hook_line_vector *
                            (dimgap * dimscale + text_width))

    dxfattribs = leader.graphic_properties()
    dxfattribs['color'] = dimclrd
    dxfattribs['linetype'] = dimltype
    dxfattribs['lineweight'] = dimlwd

    if dxfattribs.get('color') == BYBLOCK:
        dxfattribs['color'] = dxf.block_color

    if dxf.path_type == 1:  # Spline
        start_tangent = (vertices[1] - vertices[0])
        end_tangent = (vertices[-1] - vertices[-2])
        bspline = fit_points_to_cad_cv(vertices,
                                       degree=3,
                                       tangents=[start_tangent, end_tangent])
        # noinspection PyUnresolvedReferences
        spline = factory.new('SPLINE',
                             doc=doc).apply_construction_tool(bspline)
        yield spline
    else:
        attribs = dict(dxfattribs)
        prev = vertices[0]
        for vertex in vertices[1:]:
            attribs['start'] = prev
            attribs['end'] = vertex
            yield factory.new(dxftype='LINE', dxfattribs=attribs, doc=doc)
            prev = vertex

    if dxf.has_arrowhead and override:
        arrow_name = override.get('dimldrblk', '')
        if arrow_name is None:
            return
        size = override.get('dimasz',
                            2.5 if measurement else 0.1875) * dimscale
        rotation = (vertices[0] - vertices[1]).angle_deg
        if doc and arrow_name in doc.blocks:
            dxfattribs.update({
                'name': arrow_name,
                'insert': vertices[0],
                'rotation': rotation,
                'xscale': size,
                'yscale': size,
                'zscale': size,
            })
            # create a virtual block reference
            insert = factory.new('INSERT', dxfattribs=dxfattribs, doc=doc)
            yield from insert.virtual_entities()
        else:  # render standard arrows
            yield from ARROWS.virtual_entities(
                name=arrow_name,
                insert=vertices[0],
                size=size,
                rotation=rotation,
                dxfattribs=dxfattribs,
            )
Ejemplo n.º 21
0
def test_subdivide_triangle():
    t = Vector.list([(0, 0), (2, 0), (1, 2)])
    assert len(list(subdivide_face(t, quads=True))) == 3
    assert len(list(subdivide_face(t, quads=False))) == 6
Ejemplo n.º 22
0
def test_polygon_mapping_vertex_count_error(points):
    with pytest.raises(ValueError):
        geo.polygon_mapping(Vector.list(points), [])
Ejemplo n.º 23
0
from ezdxf.math import Vector
from ezdxf import units

for unit in (1, 6):
    unit_name = units.decode(unit)
    doc = ezdxf.new('R2000', setup=True, units=unit)
    msp = doc.modelspace()

    for y, ltype in enumerate(standards.linetypes()):
        msp.add_line((0, y), (20, y), dxfattribs={'linetype': ltype[0]})
    doc.set_modelspace_vport(25, center=(10, 10))
    doc.saveas(f'linetypes_{unit_name}_lines.dxf')

    doc = ezdxf.new('R2000', setup=True, units=unit)
    msp = doc.modelspace()

    for r, ltype in enumerate(standards.linetypes()):
        msp.add_circle((0, 0), radius=4 + r, dxfattribs={'linetype': ltype[0]})
    doc.set_modelspace_vport(50)
    doc.saveas(f'linetypes_{unit_name}_circle.dxf')

    doc = ezdxf.new('R2000', setup=True, units=unit)
    msp = doc.modelspace()

    points = Vector.list([(0, 0), (4, 9), (6, 9), (11, 0), (16, 9)])
    for y, ltype in enumerate(standards.linetypes()):
        fitpoints = [p + (0, y) for p in points]
        msp.add_spline(fitpoints, dxfattribs={'linetype': ltype[0]})
    doc.set_modelspace_vport(35, center=(8, 12))
    doc.saveas(f'linetypes_{unit_name}_spline.dxf')
Ejemplo n.º 24
0
msp = doc.modelspace()


def draw(points, extrusion=None):
    dxfattribs = {'color': 1}
    if extrusion is not None:
        ocs = OCS(extrusion)
        points = ocs.points_from_wcs(points)
        dxfattribs['extrusion'] = extrusion

    for point in points:
        msp.add_circle(radius=0.1, center=point, dxfattribs=dxfattribs)


spline_points = Vector.list([
    (8.55, 2.96), (8.55, -.03), (2.75, -.03), (2.76, 3.05), (4.29, 1.78),
    (6.79, 3.05)
])

# open quadratic b-spline
draw(spline_points)
msp.add_text("Open Quadratic R12Spline", dxfattribs={'height': .1}).set_pos(
    spline_points[0])
R12Spline(spline_points, degree=2, closed=False).render(
    msp, segments=SEGMENTS, dxfattribs={'color': 3})
if doc.dxfversion > 'AC1009':
    msp.add_open_spline(control_points=spline_points, degree=2,
                        dxfattribs={'color': 4})

# open cubic b-spline
spline_points = list(next_frame.transform_vertices(spline_points))
draw(spline_points)
Ejemplo n.º 25
0
# Copyright (c) 2020, Manfred Moitzi
# License: MIT License
import pytest

from ezdxf.math import is_planar_face, Vector, Vec2, subdivide_face, intersection_ray_ray_3d, normal_vector_3p
from ezdxf.math import X_AXIS, Y_AXIS, Z_AXIS, subdivide_ngons
from ezdxf.render.forms import square, circle

REGULAR_FACE = Vector.list([(0, 0, 0), (1, 0, 1), (1, 1, 1), (0, 1, 0)])
IRREGULAR_FACE = Vector.list([(0, 0, 0), (1, 0, 1), (1, 1, 0), (0, 1, 0)])
REGULAR_FACE_WRONG_ORDER = Vector.list([(0, 0, 0), (1, 1, 1), (1, 0, 1),
                                        (0, 1, 0)])


def test_face_count():
    assert is_planar_face(REGULAR_FACE[:3]) is True
    assert is_planar_face(REGULAR_FACE[:2]) is False


def test_regular_face():
    assert is_planar_face(REGULAR_FACE) is True


def test_irregular_face():
    assert is_planar_face(IRREGULAR_FACE) is False


def test_does_not_detect_wrong_order():
    assert is_planar_face(REGULAR_FACE_WRONG_ORDER) is True

Ejemplo n.º 26
0
        mesh.add_mesh(vertices=vertices, faces=faces)
    assert len(mesh.vertices) == 514
    assert len(mesh.faces) == 1024


def test_average_vertex_merger():
    pyramid = SierpinskyPyramid(level=4, sides=3)
    faces = pyramid.faces()
    mesh = MeshAverageVertexMerger()
    for vertices in pyramid:
        mesh.add_mesh(vertices=vertices, faces=faces)
    assert len(mesh.vertices) == 514
    assert len(mesh.faces) == 1024


REGULAR_FACE = Vector.list([(0, 0, 0), (1, 0, 1), (1, 1, 1), (0, 1, 0)])
IRREGULAR_FACE = Vector.list([(0, 0, 0), (1, 0, 1), (1, 1, 0), (0, 1, 0)])


def test_has_none_planar_faces():
    mesh = MeshBuilder()
    mesh.add_face(REGULAR_FACE)
    assert mesh.has_none_planar_faces() is False
    mesh.add_face(IRREGULAR_FACE)
    assert mesh.has_none_planar_faces() is True


def test_scale_mesh():
    mesh = cube(center=False)
    mesh.scale(2, 3, 4)
    bbox = BoundingBox(mesh.vertices)
import math
from ezdxf.math import Vector
from ezdxf.math.bspline import global_bspline_interpolation
from ezdxf.math.parametrize import uniform_t_vector, distance_t_vector, centripetal_t_vector, arc_t_vector, \
    arc_distances, estimate_tangents
from ezdxf.math.bspline import (
    knots_from_parametrization,
    required_knot_values,
    averaged_knots_unconstrained,
    natural_knots_constrained,
    averaged_knots_constrained,
    natural_knots_unconstrained,
    double_knots,
)

POINTS1 = Vector.list([(1, 1), (2, 4), (4, 1), (7, 6)])
POINTS2 = Vector.list([(1, 1), (2, 4), (4, 1), (7, 6), (5, 8), (3, 3), (1, 7)])


@pytest.fixture(params=[POINTS1, POINTS2])
def fit_points(request):
    return request.param


def test_uniform_t_array(fit_points):
    t_vector = list(uniform_t_vector(len(fit_points)))
    assert len(t_vector) == len(fit_points)
    assert t_vector[0] == 0.
    assert t_vector[-1] == 1.
    for t1, t2 in zip(t_vector, t_vector[1:]):
        assert t1 <= t2
def test_estimate_tangents_5p():
    tangents = estimate_tangents(Vector.list(POINTS1), method='5-points')
    assert len(tangents) == 4