# Copyright (c) 2021-2022, Manfred Moitzi
# License: MIT License
import pathlib
import ezdxf
from ezdxf import zoom
from ezdxf.math import ConstructionArc, BSpline

CWD = pathlib.Path("~/Desktop/Outbox").expanduser()
if not CWD.exists():
    CWD = pathlib.Path(".")

# ------------------------------------------------------------------------------
# This is a more theoretical example which shows how to recreate an ARC entity
# from a SPLINE entity, assuming the SPLINE represents an ARC.
# ------------------------------------------------------------------------------

doc = ezdxf.new()
msp = doc.modelspace()

# create the source ARC:
arc = msp.add_arc(
    (0, 0),
    radius=5,
    start_angle=15,
    end_angle=165,
    dxfattribs={
        "layer": "source arc",
        "color": ezdxf.const.RED,
    },
)

# create a SPLINE entity from ARC, in your scenario these are the source
# entities of your DXF document!
spline = msp.add_spline(
    dxfattribs={
        "layer": "B-Spline",
        "color": ezdxf.const.YELLOW,
    }
)
# create a B-spline construction tool from ARC
arc_tool = arc.construction_tool()
bspline = BSpline.from_arc(arc_tool)
spline.apply_construction_tool(bspline)

# ------------------------------------------------------------------------------
# Recreate ARC from SPLINE, if you ASSUME or KNOW it represents an ARC:
# for spline in msp.query("SPLINE):
#     ...
# ------------------------------------------------------------------------------

# 1. get the B-spline construction tool from the SPLINE entity
bspline = spline.construction_tool()
max_t = bspline.max_t

# calculate 3 significant points and 2 check points of the SPLINE:
start, chk1, middle, chk2, end = bspline.points(
    [0, max_t * 0.25, max_t * 0.5, max_t * 0.75, max_t]
)

# create an arc from 3 points:
arc_tool = ConstructionArc.from_3p(start, end, middle)
arc_tool.add_to_layout(
    msp,
    dxfattribs={
        "layer": "recreated arc",
        "color": ezdxf.const.MAGENTA,
    },
)

# This only works for flat B-splines in the xy-plane, a.k.a. 2D splines!

# Verify the assumption:
center = arc_tool.center
radius = arc_tool.radius
err = max(abs(radius - p.distance(center)) for p in (chk1, chk2))
print(f"max error: {err:.6f}")

# Warning: this does not proof that the assumption was correct, it's always
# possible to create a diverging B-spline which matches the check points:

foul = (4.5, 1)
fit_points = [start, foul, chk1, middle, chk2, end]
msp.add_spline(
    fit_points,
    dxfattribs={
        "layer": "foul B-spline",
        "color": ezdxf.const.RED,
    },
)

# add checkmarks
for p in fit_points:
    msp.add_circle(p, radius=0.03, dxfattribs={"color": ezdxf.const.RED})

zoom.objects(msp, [arc])
doc.saveas(CWD / "arc_recreation.dxf")
