1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
# Copyright (c) 2020-2024, Manfred Moitzi
# License: MIT License
from typing import Iterable
import pathlib
import math
import numpy as np
import ezdxf
from ezdxf import zoom
from ezdxf.math import (
Vec3,
estimate_tangents,
estimate_end_tangent_magnitude,
)
from ezdxf.math import (
local_cubic_bspline_interpolation,
global_bspline_interpolation,
fit_points_to_cad_cv,
)
CWD = pathlib.Path("~/Desktop/Outbox").expanduser()
if not CWD.exists():
CWD = pathlib.Path(".")
def sine_wave(count: int, scale: float = 1.0) -> Iterable[Vec3]:
for t in np.linspace(0, math.tau, count):
yield Vec3(t * scale, math.sin(t) * scale)
def main(method="5-p"):
"""
Args:
method: tangent estimation method
"""
doc = ezdxf.new()
msp = doc.modelspace()
# Calculate 8 points on sine wave as interpolation data
data = list(sine_wave(count=8, scale=2.0))
# --------------------------------------------------------------------------
# Reference curve as approximation
msp.add_lwpolyline(
sine_wave(count=800, scale=2.0),
dxfattribs={"color": 1, "layer": "Reference curve (LWPolyline)"},
)
# Add spline as fit-points: control point calculation by AutoCAD/BricsCAD
msp.add_spline(data, dxfattribs={"layer": "BricsCAD B-spline", "color": 2})
# --------------------------------------------------------------------------
# estimated curve tangents
# docs: https://ezdxf.mozman.at/docs/math/core.html#ezdxf.math.estimate_tangents
# disable normalization for better results of the tangent visualization
tangents = estimate_tangents(data, method, normalize=False)
# display tangents
for p, t in zip(data, tangents):
msp.add_line(
p,
p + t,
dxfattribs={"color": 5, "layer": f"Estimated tangents ({method})"},
)
# --------------------------------------------------------------------------
# local interpolation with estimated curve tangents
# a normalized tangent vector for each data point is required
# docs: https://ezdxf.mozman.at/docs/math/core.html#ezdxf.math.local_cubic_bspline_interpolation
s = local_cubic_bspline_interpolation(
data, tangents=[t.normalize() for t in tangents]
)
# alternative: set argument 'method' for automatic tangent estimation,
# default method is'5-points' interpolation
# s = local_cubic_bspline_interpolation(data, method=method)
msp.add_spline(
dxfattribs={"color": 3, "layer": f"Local interpolation ({method})"}
).apply_construction_tool(s)
# --------------------------------------------------------------------------
# global interpolation: take first and last vector from 'tangents' as start-
# and end tangent
# docs: https://ezdxf.mozman.at/docs/math/core.html#ezdxf.math.global_bspline_interpolation
m1, m2 = estimate_end_tangent_magnitude(data, method="chord")
s = global_bspline_interpolation(
data, tangents=(tangents[0].normalize(m1), tangents[-1].normalize(m2))
)
msp.add_spline(
dxfattribs={"color": 4, "layer": f"Global interpolation ({method})"}
).apply_construction_tool(s)
# --------------------------------------------------------------------------
# fit_points_to_cad_cv(): this function tries to replicate the control-vertices
# calculation of CAD applications. Works perfect if the start and end-tangent
# is defined, but diverges from the CAD result to some degree otherwise.
#
# docs: https://ezdxf.mozman.at/docs/math/core.html#ezdxf.math.fit_points_to_cad_cv
s = fit_points_to_cad_cv(data)
msp.add_spline(
dxfattribs={"color": 6, "layer": f"recreate CAD calculation"}
).apply_construction_tool(s)
zoom.extents(msp, factor=1.1)
doc.saveas(CWD / f"sine-wave-{method}.dxf")
if __name__ == "__main__":
main()
|