Split 2d and 3d spline implementations, use tangent data; added Spline member to WorkPlane

This commit is contained in:
Matthias Rambausek 2021-12-03 11:55:02 +01:00
parent 193a7001e4
commit 31fa22626c

View File

@ -36,6 +36,7 @@
#include <Geom2d_BSplineCurve.hxx>
#include <Geom_BezierCurve.hxx>
#include <GeomAPI_PointsToBSpline.hxx>
#include <Geom2dAPI_PointsToBSpline.hxx>
#include <GeomAbs_Shape.hxx>
#include <Geom_BSplineSurface.hxx>
#include <GeomAPI_PointsToBSplineSurface.hxx>
@ -442,6 +443,75 @@ public:
return shared_from_this();
}
auto Spline(const std::vector<gp_Pnt2d> &points, bool periodic, double tol, const std::map<int, gp_Vec2d> &tangents, optional<string> name = nullopt)
{
gp_Pnt2d P1 = localpos.Location();
gp_Pnt P13d = surf->Value(P1.X(), P1.Y());
gp_Pnt2d PLast = points.back();
gp_Pnt PLast3d = surf->Value(PLast.X(), PLast.Y());
Handle(TColgp_HArray1OfPnt2d) allpoints = new TColgp_HArray1OfPnt2d(1, points.size() + 1);
allpoints->SetValue(1, P1);
for (int i = 0; i < points.size(); i++)
allpoints->SetValue(i+2, points[i]);
Geom2dAPI_Interpolate builder(allpoints, periodic, tol);
if (tangents.size() > 0)
{
const gp_Vec2d dummy_vec = tangents.begin()->second;
TColgp_Array1OfVec2d tangent_vecs(1, allpoints->Length());
Handle(TColStd_HArray1OfBoolean) tangent_flags = new TColStd_HArray1OfBoolean(1, allpoints->Length());
for (int i : Range(allpoints->Length()))
{
if (tangents.count(i) > 0)
{
tangent_vecs.SetValue(i+1, tangents.at(i));
tangent_flags->SetValue(i+1, true);
}
else
{
tangent_vecs.SetValue(i+1, dummy_vec);
tangent_flags->SetValue(i+1, false);
}
}
builder.Load(tangent_vecs, tangent_flags);
}
builder.Perform();
auto curve2d = builder.Curve();
if (startvertex.IsNull())
startvertex = lastvertex = BRepBuilderAPI_MakeVertex(P13d).Vertex();
auto endv = periodic ? startvertex : BRepBuilderAPI_MakeVertex(PLast3d).Vertex();
//create 3d edge from 2d curve using surf
auto edge = BRepBuilderAPI_MakeEdge(curve2d, surf, lastvertex, endv).Edge();
lastvertex = endv;
BRepLib::BuildCurves3d(edge);
wire_builder.Add(edge);
// update localpos
localpos.SetLocation(PLast);
//compute angle of rotation
//compute tangent t2 in PLast
const auto dir = localpos.Direction();
gp_Vec2d t = gp_Vec2d(dir.X(), dir.Y());
gp_Vec2d t2 = curve2d->DN(curve2d->LastParameter(), 1);
double angle = t.Angle(t2); //angle \in [-pi,pi]
//update localpos.Direction()
Rotate(angle*180/M_PI);
if (periodic)
Finish();
return shared_from_this();
}
auto ArcTo (double h, double v, const gp_Vec2d t)
{
gp_Pnt2d P1 = localpos.Location();
@ -1855,6 +1925,100 @@ DLL_HEADER void ExportNgOCCShapes(py::module &m)
return curve;
*/
}, py::arg("c"), py::arg("r"), "create 2d circle curve");
m.def("SplineApproximation", [](const std::vector<gp_Pnt2d> &points, Approx_ParametrizationType approx_type, int deg_min,
int deg_max, GeomAbs_Shape continuity, double tol) -> Handle(Geom2d_Curve) {
TColgp_Array1OfPnt2d hpoints(0, 0);
hpoints.Resize(0, points.size() - 1, true);
for (int i = 0; i < points.size(); i++)
hpoints.SetValue(i, points[i]);
Geom2dAPI_PointsToBSpline builder(hpoints, approx_type, deg_min, deg_max, continuity, tol);
return Handle(Geom2d_BSplineCurve)(builder.Curve());
},
py::arg("points"),
py::arg("approx_type") = Approx_ParametrizationType::Approx_ChordLength,
py::arg("deg_min") = 3,
py::arg("deg_max") = 8,
py::arg("continuity") = GeomAbs_Shape::GeomAbs_C2,
py::arg("tol")=1e-8,
R"delimiter(
Generate a piecewise continuous spline-curve approximating a list of points in 2d.
Parameters
----------
points : List|Tuple[gp_Pnt2d]
List (or tuple) of gp_Pnt.
approx_type : ApproxParamType
Assumption on location of parameters wrt points.
deg_min : int
Minimum polynomial degree of splines
deg_max : int
Maxmium polynomial degree of splines
continuity : ShapeContinuity
Continuity requirement on the approximating surface
tol : float
Tolerance for the distance from individual points to the approximating curve.
)delimiter");
m.def("SplineInterpolation", [](const std::vector<gp_Pnt2d> &points, bool periodic, double tol, const std::map<int, gp_Vec2d> &tangents) -> Handle(Geom2d_Curve) {
Handle(TColgp_HArray1OfPnt2d) hpoints = new TColgp_HArray1OfPnt2d(1, points.size());
for (int i = 0; i < points.size(); i++)
hpoints->SetValue(i+1, points[i]);
Geom2dAPI_Interpolate builder(hpoints, periodic, tol);
if (tangents.size() > 0)
{
const gp_Vec2d dummy_vec = tangents.begin()->second;
TColgp_Array1OfVec2d tangent_vecs(1, points.size());
Handle(TColStd_HArray1OfBoolean) tangent_flags = new TColStd_HArray1OfBoolean(1, points.size());
for (int i : Range(points.size()))
{
if (tangents.count(i) > 0)
{
tangent_vecs.SetValue(i+1, tangents.at(i));
tangent_flags->SetValue(i+1, true);
} else{
tangent_vecs.SetValue(i+1, dummy_vec);
tangent_flags->SetValue(i+1, false);
}
}
builder.Load(tangent_vecs, tangent_flags);
}
builder.Perform();
return Handle(Geom2d_BSplineCurve)(builder.Curve());
},
py::arg("points"),
py::arg("periodic")=false,
py::arg("tol")=1e-8,
py::arg("tangents")=std::map<int, gp_Vec2d>{},
R"delimiter(
Generate a piecewise continuous spline-curve interpolating a list of points in 2d.
Parameters
----------
points : List|Tuple[gp_Pnt2d]
List (or tuple) of gp_Pnt2d.
periodic : bool
Whether the result should be periodic
tol : float
Tolerance for the distance between points.
tangents : Dict[int, gp_Vec2d]
Tangent vectors for the points indicated by the key value (0-based).
)delimiter");
m.def("Glue", [] (const std::vector<TopoDS_Shape> shapes) -> TopoDS_Shape
{
@ -2015,30 +2179,15 @@ DLL_HEADER void ExportNgOCCShapes(py::module &m)
return BRepBuilderAPI_MakeEdge(curve).Edge();
}, py::arg("points"), "create Bezier curve");
//TODO: 2d version
m.def("SplineApproximation", [](py::object pnts, Approx_ParametrizationType approx_type, int deg_min,
m.def("SplineApproximation", [](const std::vector<gp_Pnt> &points, Approx_ParametrizationType approx_type, int deg_min,
int deg_max, GeomAbs_Shape continuity, double tol) {
TColgp_Array1OfPnt points(0, 0);
if (py::extract<std::vector<gp_Pnt>>(pnts).check()) {
std::vector<gp_Pnt> pnt_list{py::extract<std::vector<gp_Pnt>>(pnts)()};
points.Resize(0, pnt_list.size()-1, true);
for (int i = 0; i < pnt_list.size(); i++)
points.SetValue(i, pnt_list[i]);
} else if (py::extract<py::array_t<double>>(pnts).check()) {
py::array_t<double> pnt_array{py::extract<py::array_t<double>>(pnts)()};
if (pnt_array.ndim() != 2)
throw Exception("`points` array must have dimension 2.");
if (pnt_array.shape(1) != 3)
throw Exception("The second dimension must have size 3.");
TColgp_Array1OfPnt hpoints(0, 0);
hpoints.Resize(0, points.size() - 1, true);
for (int i = 0; i < points.size(); i++)
hpoints.SetValue(i, points[i]);
points.Resize(0, pnt_array.shape(0)-1, true);
auto pnts_unchecked = pnt_array.unchecked<2>();
for (int i = 0; i < pnt_array.shape(0); ++i)
points.SetValue(i, gp_Pnt(pnts_unchecked(i, 0), pnts_unchecked(i, 1), pnts_unchecked(i, 2)));
} else
throw Exception("Not able to process the data type of points");
GeomAPI_PointsToBSpline builder(points, approx_type, deg_min, deg_max, continuity, tol);
GeomAPI_PointsToBSpline builder(hpoints, approx_type, deg_min, deg_max, continuity, tol);
return BRepBuilderAPI_MakeEdge(builder.Curve()).Edge();
},
py::arg("points"),
@ -2048,13 +2197,13 @@ DLL_HEADER void ExportNgOCCShapes(py::module &m)
py::arg("continuity") = GeomAbs_Shape::GeomAbs_C2,
py::arg("tol")=1e-8,
R"delimiter(
Generate a piecewise continuous spline-curve approximating a list of points.
Generate a piecewise continuous spline-curve approximating a list of points in 3d.
Parameters
----------
points : List[gp_Pnt] or Tuple[gp_Pnt] or np.ndarray[double]
List (or tuple) of gp_Pnt. If a numpy array is provided instead, the data must contain the coordinates
points : List[gp_Pnt] or Tuple[gp_Pnt]
List (or tuple) of gp_Pnt.
approx_type : ApproxParamType
Assumption on location of parameters wrt points.
@ -2073,74 +2222,47 @@ tol : float
)delimiter");
m.def("SplineInterpolation", [](py::object pnts, bool periodic, double tol) {
m.def("SplineInterpolation", [](const std::vector<gp_Pnt> &points, bool periodic, double tol, const std::map<int, gp_Vec> &tangents) {
Handle(TColgp_HArray1OfPnt) hpoints = new TColgp_HArray1OfPnt(1, points.size());
for (int i = 0; i < points.size(); i++)
hpoints->SetValue(i+1, points[i]);
auto _2d = [](const Handle(TColgp_HArray1OfPnt2d) &points, bool periodic, double tol) {
Geom2dAPI_Interpolate builder(points, periodic, tol);
builder.Perform();
return BRepBuilderAPI_MakeEdge2d(builder.Curve()).Edge();
};
GeomAPI_Interpolate builder(hpoints, periodic, tol);
auto _3d = [](const Handle(TColgp_HArray1OfPnt) &points, bool periodic, double tol) {
GeomAPI_Interpolate builder(points, periodic, tol);
builder.Perform();
return BRepBuilderAPI_MakeEdge(builder.Curve()).Edge();
};
if (py::extract<std::vector<gp_Pnt>>(pnts).check())
if (tangents.size() > 0)
{
const gp_Vec dummy_vec = tangents.begin()->second;
TColgp_Array1OfVec tangent_vecs(1, points.size());
Handle(TColStd_HArray1OfBoolean) tangent_flags = new TColStd_HArray1OfBoolean(1, points.size());
for (int i : Range(points.size()))
{
std::vector<gp_Pnt> pnt_list{py::extract<std::vector<gp_Pnt>>(pnts)()};
Handle(TColgp_HArray1OfPnt) points = new TColgp_HArray1OfPnt(1, pnt_list.size());
for (int i = 0; i < pnt_list.size(); i++)
points->SetValue(i+1, pnt_list[i]);
return _3d(points, periodic, tol);
if (tangents.count(i) > 0)
{
tangent_vecs.SetValue(i+1, tangents.at(i));
tangent_flags->SetValue(i+1, true);
} else{
tangent_vecs.SetValue(i+1, dummy_vec);
tangent_flags->SetValue(i+1, false);
}
}
else if(py::extract<std::vector<gp_Pnt2d>>(pnts).check())
{
std::vector<gp_Pnt2d> pnt_list{py::extract<std::vector<gp_Pnt2d>>(pnts)()};
Handle(TColgp_HArray1OfPnt2d) points = new TColgp_HArray1OfPnt2d(1, pnt_list.size());
for (int i = 0; i < pnt_list.size(); i++)
points->SetValue(i+1, pnt_list[i]);
return _2d(points, periodic, tol);
}
else if (py::extract<py::array_t<double>>(pnts).check())
{
py::array_t<double> pnt_array{py::extract<py::array_t<double>>(pnts)()};
if (pnt_array.ndim() != 2)
throw Exception("`points` array must have dimension 2.");
if (pnt_array.shape(1) == 3)
{
Handle(TColgp_HArray1OfPnt) points = new TColgp_HArray1OfPnt(1, pnt_array.shape(0));
auto pnts_unchecked = pnt_array.unchecked<2>();
for (int i = 0; i < pnt_array.shape(0); ++i)
points->SetValue(i+1, gp_Pnt(pnts_unchecked(i, 0), pnts_unchecked(i, 1), pnts_unchecked(i, 2)));
return _3d(points, periodic, tol);
}
else if (pnt_array.shape(1) == 2)
{
Handle(TColgp_HArray1OfPnt2d) points = new TColgp_HArray1OfPnt2d(1, pnt_array.shape(0));
auto pnts_unchecked = pnt_array.unchecked<2>();
for (int i = 0; i < pnt_array.shape(0); ++i)
points->SetValue(i+1, gp_Pnt2d(pnts_unchecked(i, 0), pnts_unchecked(i, 1)));
return _2d(points, periodic, tol);
}
else
throw Exception("The second dimension must have size 2 or 3, but has " + to_string(pnt_array.shape(1)));
builder.Load(tangent_vecs, tangent_flags);
}
else
throw Exception("Not able to process the data type of points");
builder.Perform();
return BRepBuilderAPI_MakeEdge(builder.Curve()).Edge();
},
py::arg("points"),
py::arg("periodic")=false,
py::arg("tol")=1e-8,
py::arg("tangents")=std::map<int, gp_Vec>{},
R"delimiter(
Generate a piecewise continuous spline-curve interpolating a list of points.
Generate a piecewise continuous spline-curve interpolating a list of points in 3d.
Parameters
----------
points : List|Tuple[gp_Pnt|gp_Pnt2d] or np.ndarray[double]
List (or tuple) of gp_Pnt (or gp_Pnt2d). If a numpy array is provided instead, the data must contain the coordinates
points : List|Tuple[gp_Pnt]
List (or tuple) of gp_Pnt
periodic : bool
Whether the result should be periodic
@ -2148,6 +2270,9 @@ periodic : bool
tol : float
Tolerance for the distance between points.
tangents : Dict[int, gp_Vec]
Tangent vectors for the points indicated by the key value (0-based).
)delimiter");
@ -2324,6 +2449,7 @@ degen_tol : double
py::arg("l"), py::arg("name")=nullopt)
.def("Line", [](WorkPlane&wp,double h,double v, optional<string> name) { return wp.Line(h,v,name); },
py::arg("dx"), py::arg("dy"), py::arg("name")=nullopt)
.def("Spline", &WorkPlane::Spline, py::arg("points"), py::arg("periodic")=false, py::arg("tol")=1e-8, py::arg("tangents")=std::map<int, gp_Vec2d>{}, py::arg("name")=nullopt, "draw spline starting from current position, tangents can be given for each point (0 refers to current position)")
.def("Rectangle", &WorkPlane::Rectangle, py::arg("l"), py::arg("w"), "draw rectangle, with current position as corner, use current direction")
.def("RectangleC", &WorkPlane::RectangleCentered, py::arg("l"), py::arg("w"), "draw rectangle, with current position as center, use current direction")
.def("Circle", [](WorkPlane&wp, double x, double y, double r) {