Get rid of Contour3D

This commit is contained in:
tamasmeszaros 2021-05-26 17:38:57 +02:00
parent e6f97358bc
commit 8fdb0fddc0
13 changed files with 86 additions and 294 deletions

View File

@ -266,8 +266,6 @@ add_library(libslic3r STATIC
SLA/SupportPoint.hpp SLA/SupportPoint.hpp
SLA/SupportPointGenerator.hpp SLA/SupportPointGenerator.hpp
SLA/SupportPointGenerator.cpp SLA/SupportPointGenerator.cpp
SLA/Contour3D.hpp
SLA/Contour3D.cpp
SLA/IndexedMesh.hpp SLA/IndexedMesh.hpp
SLA/IndexedMesh.cpp SLA/IndexedMesh.cpp
SLA/Clustering.hpp SLA/Clustering.hpp

View File

@ -296,7 +296,7 @@ void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
void import_sla_archive( void import_sla_archive(
const std::string & zipfname, const std::string & zipfname,
Vec2i windowsize, Vec2i windowsize,
TriangleMesh & out, indexed_triangle_set & out,
DynamicPrintConfig & profile, DynamicPrintConfig & profile,
std::function<bool(int)> progr) std::function<bool(int)> progr)
{ {
@ -316,7 +316,7 @@ void import_sla_archive(
extract_slices_from_sla_archive(arch, rstp, progr); extract_slices_from_sla_archive(arch, rstp, progr);
if (!slices.empty()) if (!slices.empty())
out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh); out = slices_to_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
} }
using ConfMap = std::map<std::string, std::string>; using ConfMap = std::map<std::string, std::string>;

View File

@ -14,7 +14,7 @@ class SL1Archive: public SLAPrinter {
protected: protected:
uqptr<sla::RasterBase> create_raster() const override; uqptr<sla::RasterBase> create_raster() const override;
sla::RasterEncoder get_encoder() const override; sla::RasterEncoder get_encoder() const override;
public: public:
SL1Archive() = default; SL1Archive() = default;
@ -43,14 +43,14 @@ void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
void import_sla_archive( void import_sla_archive(
const std::string & zipfname, const std::string & zipfname,
Vec2i windowsize, Vec2i windowsize,
TriangleMesh & out, indexed_triangle_set & out,
DynamicPrintConfig & profile, DynamicPrintConfig & profile,
std::function<bool(int)> progr = [](int) { return true; }); std::function<bool(int)> progr = [](int) { return true; });
inline void import_sla_archive( inline void import_sla_archive(
const std::string & zipfname, const std::string & zipfname,
Vec2i windowsize, Vec2i windowsize,
TriangleMesh & out, indexed_triangle_set & out,
std::function<bool(int)> progr = [](int) { return true; }) std::function<bool(int)> progr = [](int) { return true; })
{ {
DynamicPrintConfig profile; DynamicPrintConfig profile;

View File

@ -2,7 +2,6 @@
#define OPENVDBUTILS_HPP #define OPENVDBUTILS_HPP
#include <libslic3r/TriangleMesh.hpp> #include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/SLA/Contour3D.hpp>
#ifdef _MSC_VER #ifdef _MSC_VER
// Suppress warning C4146 in include/gmp.h(2177,31): unary minus operator applied to unsigned type, result still unsigned // Suppress warning C4146 in include/gmp.h(2177,31): unary minus operator applied to unsigned type, result still unsigned
@ -19,7 +18,6 @@ namespace Slic3r {
inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; } inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; }
inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast<double>(); } inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast<double>(); }
inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; }
inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; }
// Here voxel_scale defines the scaling of voxels which affects the voxel count. // Here voxel_scale defines the scaling of voxels which affects the voxel count.
// 1.0 value means a voxel for every unit cube. 2 means the model is scaled to // 1.0 value means a voxel for every unit cube. 2 means the model is scaled to

View File

@ -1,149 +0,0 @@
#include <libslic3r/SLA/Contour3D.hpp>
#include <libslic3r/SLA/IndexedMesh.hpp>
#include <libslic3r/Format/objparser.hpp>
namespace Slic3r { namespace sla {
Contour3D::Contour3D(const TriangleMesh &trmesh)
{
points.reserve(trmesh.its.vertices.size());
faces3.reserve(trmesh.its.indices.size());
for (auto &v : trmesh.its.vertices)
points.emplace_back(v.cast<double>());
std::copy(trmesh.its.indices.begin(), trmesh.its.indices.end(),
std::back_inserter(faces3));
}
Contour3D::Contour3D(TriangleMesh &&trmesh)
{
points.reserve(trmesh.its.vertices.size());
for (auto &v : trmesh.its.vertices)
points.emplace_back(v.cast<double>());
faces3.swap(trmesh.its.indices);
}
Contour3D::Contour3D(const IndexedMesh &emesh) {
points.reserve(emesh.vertices().size());
faces3.reserve(emesh.indices().size());
for (const Vec3f& vert : emesh.vertices())
points.emplace_back(vert.cast<double>());
for (const auto& ind : emesh.indices())
faces3.emplace_back(ind);
}
Contour3D &Contour3D::merge(const Contour3D &ctr)
{
auto N = coord_t(points.size());
auto N_f3 = faces3.size();
auto N_f4 = faces4.size();
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
faces3.insert(faces3.end(), ctr.faces3.begin(), ctr.faces3.end());
faces4.insert(faces4.end(), ctr.faces4.begin(), ctr.faces4.end());
for(size_t n = N_f3; n < faces3.size(); n++) {
auto& idx = faces3[n]; idx.x() += N; idx.y() += N; idx.z() += N;
}
for(size_t n = N_f4; n < faces4.size(); n++) {
auto& idx = faces4[n]; for (int k = 0; k < 4; k++) idx(k) += N;
}
return *this;
}
Contour3D &Contour3D::merge(const Pointf3s &triangles)
{
const size_t offs = points.size();
points.insert(points.end(), triangles.begin(), triangles.end());
faces3.reserve(faces3.size() + points.size() / 3);
for(int i = int(offs); i < int(points.size()); i += 3)
faces3.emplace_back(i, i + 1, i + 2);
return *this;
}
void Contour3D::to_obj(std::ostream &stream)
{
for(auto& p : points)
stream << "v " << p.transpose() << "\n";
for(auto& f : faces3)
stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n";
for(auto& f : faces4)
stream << "f " << (f + Vec4i(1, 1, 1, 1)).transpose() << "\n";
}
void Contour3D::from_obj(std::istream &stream)
{
ObjParser::ObjData data;
ObjParser::objparse(stream, data);
points.reserve(data.coordinates.size() / 4 + 1);
auto &coords = data.coordinates;
for (size_t i = 0; i < coords.size(); i += 4)
points.emplace_back(coords[i], coords[i + 1], coords[i + 2]);
Vec3i triangle;
Vec4i quad;
size_t v = 0;
while(v < data.vertices.size()) {
size_t N = 0;
size_t i = v;
while (data.vertices[v++].coordIdx != -1) ++N;
std::function<void(int, int)> setfn;
if (N < 3 || N > 4) continue;
else if (N == 3) setfn = [&triangle](int k, int f) { triangle(k) = f; };
else setfn = [&quad](int k, int f) { quad(k) = f; };
for (size_t j = 0; j < N; ++j)
setfn(int(j), data.vertices[i + j].coordIdx);
}
}
TriangleMesh to_triangle_mesh(const Contour3D &ctour) {
if (ctour.faces4.empty()) return {ctour.points, ctour.faces3};
std::vector<Vec3i> triangles;
triangles.reserve(ctour.faces3.size() + 2 * ctour.faces4.size());
std::copy(ctour.faces3.begin(), ctour.faces3.end(),
std::back_inserter(triangles));
for (auto &quad : ctour.faces4) {
triangles.emplace_back(quad(0), quad(1), quad(2));
triangles.emplace_back(quad(2), quad(3), quad(0));
}
return {ctour.points, std::move(triangles)};
}
TriangleMesh to_triangle_mesh(Contour3D &&ctour) {
if (ctour.faces4.empty())
return {std::move(ctour.points), std::move(ctour.faces3)};
std::vector<Vec3i> triangles;
triangles.reserve(ctour.faces3.size() + 2 * ctour.faces4.size());
std::copy(ctour.faces3.begin(), ctour.faces3.end(),
std::back_inserter(triangles));
for (auto &quad : ctour.faces4) {
triangles.emplace_back(quad(0), quad(1), quad(2));
triangles.emplace_back(quad(2), quad(3), quad(0));
}
return {std::move(ctour.points), std::move(triangles)};
}
}} // namespace Slic3r::sla

View File

@ -1,48 +0,0 @@
#ifndef SLA_CONTOUR3D_HPP
#define SLA_CONTOUR3D_HPP
#include <libslic3r/TriangleMesh.hpp>
namespace Slic3r {
// Used for quads (TODO: remove this, and convert quads to triangles in OpenVDBUtils)
using Vec4i = Eigen::Matrix<int, 4, 1, Eigen::DontAlign>;
namespace sla {
class IndexedMesh;
/// Dumb vertex mesh consisting of triangles (or) quads. Capable of merging with
/// other meshes of this type and converting to and from other mesh formats.
struct Contour3D {
std::vector<Vec3d> points;
std::vector<Vec3i> faces3;
std::vector<Vec4i> faces4;
Contour3D() = default;
Contour3D(const TriangleMesh &trmesh);
Contour3D(TriangleMesh &&trmesh);
Contour3D(const IndexedMesh &emesh);
Contour3D& merge(const Contour3D& ctr);
Contour3D& merge(const Pointf3s& triangles);
// Write the index triangle structure to OBJ file for debugging purposes.
void to_obj(std::ostream& stream);
void from_obj(std::istream &stream);
inline bool empty() const
{
return points.empty() || (faces4.empty() && faces3.empty());
}
};
/// Mesh from an existing contour.
TriangleMesh to_triangle_mesh(const Contour3D& ctour);
/// Mesh from an evaporating 3D contour
TriangleMesh to_triangle_mesh(Contour3D&& ctour);
}} // namespace Slic3r::sla
#endif // CONTOUR3D_HPP

View File

@ -22,12 +22,6 @@
namespace Slic3r { namespace Slic3r {
namespace sla { namespace sla {
template<class S, class = FloatingOnly<S>>
inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); }
template<class S, class = FloatingOnly<S>>
inline void _scale(S s, Contour3D &m) { for (auto &p : m.points) p *= s; }
struct Interior { struct Interior {
indexed_triangle_set mesh; indexed_triangle_set mesh;
openvdb::FloatGrid::Ptr gridptr; openvdb::FloatGrid::Ptr gridptr;

View File

@ -2,13 +2,11 @@
#define SLA_HOLLOWING_HPP #define SLA_HOLLOWING_HPP
#include <memory> #include <memory>
#include <libslic3r/SLA/Contour3D.hpp> #include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/SLA/JobController.hpp> #include <libslic3r/SLA/JobController.hpp>
namespace Slic3r { namespace Slic3r {
class TriangleMesh;
namespace sla { namespace sla {
struct HollowingConfig struct HollowingConfig

View File

@ -1,8 +1,8 @@
#include "SlicesToTriangleMesh.hpp" #include "SlicesToTriangleMesh.hpp"
#include "libslic3r/MTUtils.hpp" //#include "libslic3r/MTUtils.hpp"
#include "libslic3r/SLA/Contour3D.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp"
#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/Tesselate.hpp" #include "libslic3r/Tesselate.hpp"
@ -11,71 +11,73 @@
namespace Slic3r { namespace Slic3r {
inline sla::Contour3D wall_strip(const Polygon &poly, inline indexed_triangle_set wall_strip(const Polygon &poly,
double lower_z_mm, double lower_z_mm,
double upper_z_mm) double upper_z_mm)
{ {
sla::Contour3D ret; indexed_triangle_set ret;
size_t startidx = ret.points.size(); size_t startidx = ret.vertices.size();
size_t offs = poly.points.size(); size_t offs = poly.points.size();
ret.points.reserve(ret.points.size() + 2 *offs); ret.vertices.reserve(ret.vertices.size() + 2 *offs);
for (const Point &p : poly.points) for (const Point &p : poly.points)
ret.points.emplace_back(to_3d(unscaled(p), lower_z_mm)); ret.vertices.emplace_back(to_3d(unscaled<float>(p), float(lower_z_mm)));
for (const Point &p : poly.points) for (const Point &p : poly.points)
ret.points.emplace_back(to_3d(unscaled(p), upper_z_mm)); ret.vertices.emplace_back(to_3d(unscaled<float>(p), float(upper_z_mm)));
for (size_t i = startidx + 1; i < startidx + offs; ++i) { for (size_t i = startidx + 1; i < startidx + offs; ++i) {
ret.faces3.emplace_back(i - 1, i, i + offs - 1); ret.indices.emplace_back(i - 1, i, i + offs - 1);
ret.faces3.emplace_back(i, i + offs, i + offs - 1); ret.indices.emplace_back(i, i + offs, i + offs - 1);
} }
ret.faces3.emplace_back(startidx + offs - 1, startidx, startidx + 2 * offs - 1); ret.indices.emplace_back(startidx + offs - 1, startidx, startidx + 2 * offs - 1);
ret.faces3.emplace_back(startidx, startidx + offs, startidx + 2 * offs - 1); ret.indices.emplace_back(startidx, startidx + offs, startidx + 2 * offs - 1);
return ret; return ret;
} }
// Same as walls() but with identical higher and lower polygons. // Same as walls() but with identical higher and lower polygons.
sla::Contour3D inline straight_walls(const Polygon &plate, indexed_triangle_set inline straight_walls(const Polygon &plate,
double lo_z, double lo_z,
double hi_z) double hi_z)
{ {
return wall_strip(plate, lo_z, hi_z); return wall_strip(plate, lo_z, hi_z);
} }
sla::Contour3D inline straight_walls(const ExPolygon &plate, indexed_triangle_set inline straight_walls(const ExPolygon &plate,
double lo_z, double lo_z,
double hi_z) double hi_z)
{ {
sla::Contour3D ret; indexed_triangle_set ret = straight_walls(plate.contour, lo_z, hi_z);
ret.merge(straight_walls(plate.contour, lo_z, hi_z)); for (auto &h : plate.holes)
for (auto &h : plate.holes) ret.merge(straight_walls(h, lo_z, hi_z)); its_merge(ret, straight_walls(h, lo_z, hi_z));
return ret; return ret;
} }
sla::Contour3D inline straight_walls(const ExPolygons &slice, indexed_triangle_set inline straight_walls(const ExPolygons &slice,
double lo_z, double lo_z,
double hi_z) double hi_z)
{ {
sla::Contour3D ret; indexed_triangle_set ret;
for (const ExPolygon &poly : slice) for (const ExPolygon &poly : slice)
ret.merge(straight_walls(poly, lo_z, hi_z)); its_merge(ret, straight_walls(poly, lo_z, hi_z));
return ret; return ret;
} }
sla::Contour3D slices_to_triangle_mesh(const std::vector<ExPolygons> &slices, indexed_triangle_set slices_to_mesh(
double zmin, const std::vector<ExPolygons> &slices,
const std::vector<float> & grid) double zmin,
const std::vector<float> & grid)
{ {
assert(slices.size() == grid.size()); assert(slices.size() == grid.size());
using Layers = std::vector<sla::Contour3D>; using Layers = std::vector<indexed_triangle_set>;
std::vector<sla::Contour3D> layers(slices.size()); Layers layers(slices.size());
size_t len = slices.size() - 1; size_t len = slices.size() - 1;
tbb::parallel_for(size_t(0), len, [&slices, &layers, &grid](size_t i) { tbb::parallel_for(size_t(0), len, [&slices, &layers, &grid](size_t i) {
@ -84,45 +86,52 @@ sla::Contour3D slices_to_triangle_mesh(const std::vector<ExPolygons> &slices,
ExPolygons dff1 = diff_ex(lower, upper); ExPolygons dff1 = diff_ex(lower, upper);
ExPolygons dff2 = diff_ex(upper, lower); ExPolygons dff2 = diff_ex(upper, lower);
layers[i].merge(triangulate_expolygons_3d(dff1, grid[i], NORMALS_UP)); its_merge(layers[i], triangulate_expolygons_3d(dff1, grid[i], NORMALS_UP));
layers[i].merge(triangulate_expolygons_3d(dff2, grid[i], NORMALS_DOWN)); its_merge(layers[i], triangulate_expolygons_3d(dff2, grid[i], NORMALS_DOWN));
layers[i].merge(straight_walls(upper, grid[i], grid[i + 1])); its_merge(layers[i], straight_walls(upper, grid[i], grid[i + 1]));
}); });
auto merge_fn = []( const indexed_triangle_set &a, const indexed_triangle_set &b ) {
indexed_triangle_set res{a}; its_merge(res, b); return res;
};
auto ret = execution::reduce(ex_tbb, layers.begin(), layers.end(),
indexed_triangle_set{}, merge_fn);
// sla::Contour3D ret = tbb::parallel_reduce(
// tbb::blocked_range(layers.begin(), layers.end()),
// sla::Contour3D{},
// [](const tbb::blocked_range<Layers::iterator>& r, sla::Contour3D
// init) {
// for(auto it = r.begin(); it != r.end(); ++it )
// init.merge(*it); return init;
// },
// []( const sla::Contour3D &a, const sla::Contour3D &b ) {
// sla::Contour3D res{a}; res.merge(b); return res;
// });
sla::Contour3D ret = tbb::parallel_reduce( its_merge(ret, triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN));
tbb::blocked_range(layers.begin(), layers.end()), its_merge(ret, straight_walls(slices.front(), zmin, grid.front()));
sla::Contour3D{}, its_merge(ret, triangulate_expolygons_3d(slices.back(), grid.back(), NORMALS_UP));
[](const tbb::blocked_range<Layers::iterator>& r, sla::Contour3D init) {
for(auto it = r.begin(); it != r.end(); ++it ) init.merge(*it);
return init;
},
[]( const sla::Contour3D &a, const sla::Contour3D &b ) {
sla::Contour3D res{a}; res.merge(b); return res;
});
ret.merge(triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN));
ret.merge(straight_walls(slices.front(), zmin, grid.front()));
ret.merge(triangulate_expolygons_3d(slices.back(), grid.back(), NORMALS_UP));
return ret; return ret;
} }
void slices_to_triangle_mesh(TriangleMesh & mesh, void slices_to_mesh(indexed_triangle_set & mesh,
const std::vector<ExPolygons> &slices, const std::vector<ExPolygons> &slices,
double zmin, double zmin,
double lh, double lh,
double ilh) double ilh)
{ {
std::vector<sla::Contour3D> wall_meshes(slices.size()); std::vector<indexed_triangle_set> wall_meshes(slices.size());
std::vector<float> grid(slices.size(), zmin + ilh); std::vector<float> grid(slices.size(), zmin + ilh);
for (size_t i = 1; i < grid.size(); ++i) grid[i] = grid[i - 1] + lh; for (size_t i = 1; i < grid.size(); ++i)
grid[i] = grid[i - 1] + lh;
sla::Contour3D cntr = slices_to_triangle_mesh(slices, zmin, grid); indexed_triangle_set cntr = slices_to_mesh(slices, zmin, grid);
mesh.merge(sla::to_triangle_mesh(cntr)); its_merge(mesh, cntr);
mesh.repaired = true;
mesh.require_shared_vertices();
} }
} // namespace Slic3r } // namespace Slic3r

View File

@ -6,16 +6,18 @@
namespace Slic3r { namespace Slic3r {
void slices_to_triangle_mesh(TriangleMesh & mesh, void slices_to_mesh(indexed_triangle_set & mesh,
const std::vector<ExPolygons> &slices, const std::vector<ExPolygons> &slices,
double zmin, double zmin,
double lh, double lh,
double ilh); double ilh);
inline TriangleMesh slices_to_triangle_mesh( inline indexed_triangle_set slices_to_mesh(
const std::vector<ExPolygons> &slices, double zmin, double lh, double ilh) const std::vector<ExPolygons> &slices, double zmin, double lh, double ilh)
{ {
TriangleMesh out; slices_to_triangle_mesh(out, slices, zmin, lh, ilh); indexed_triangle_set out;
slices_to_mesh(out, slices, zmin, lh, ilh);
return out; return out;
} }

View File

@ -113,14 +113,14 @@ public:
Plater *plater; Plater *plater;
Sel sel = Sel::modelAndProfile; Sel sel = Sel::modelAndProfile;
TriangleMesh mesh; indexed_triangle_set mesh;
DynamicPrintConfig profile; DynamicPrintConfig profile;
wxString path; wxString path;
Vec2i win = {2, 2}; Vec2i win = {2, 2};
std::string err; std::string err;
priv(Plater *plt): plater{plt} {} priv(Plater *plt) : plater{plt} {}
}; };
SLAImportJob::SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater) SLAImportJob::SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
@ -222,7 +222,8 @@ void SLAImportJob::finalize()
if (!p->mesh.empty()) { if (!p->mesh.empty()) {
bool is_centered = false; bool is_centered = false;
p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name, is_centered); p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{p->mesh},
name, is_centered);
} }
reset(); reset();

View File

@ -17,7 +17,6 @@
#include <libslic3r/TriangulateWall.hpp> #include <libslic3r/TriangulateWall.hpp>
#include <libslic3r/Tesselate.hpp> #include <libslic3r/Tesselate.hpp>
#include <libslic3r/SlicesToTriangleMesh.hpp> #include <libslic3r/SlicesToTriangleMesh.hpp>
#include <libslic3r/SLA/Contour3D.hpp>
using namespace Slic3r; using namespace Slic3r;
@ -368,10 +367,9 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
layer = std::move(layer_); layer = std::move(layer_);
} }
TriangleMesh out = slices_to_triangle_mesh(layers, bb.min.z(), double(lh), double(lh)); indexed_triangle_set out = slices_to_mesh(layers, bb.min.z(), double(lh), double(lh));
out.require_shared_vertices(); its_write_obj(out, "out_from_rasters.obj");
out.WriteOBJFile("out_from_rasters.obj");
} }
TEST_CASE("Recreate object from rasters", "[SL1Import]") { TEST_CASE("Recreate object from rasters", "[SL1Import]") {

View File

@ -223,15 +223,6 @@ TEST_CASE("RasterizedPolygonAreaShouldMatch", "[SLARasterOutput]") {
REQUIRE(raster_pxsum(raster0) == 0); REQUIRE(raster_pxsum(raster0) == 0);
} }
TEST_CASE("Triangle mesh conversions should be correct", "[SLAConversions]")
{
sla::Contour3D cntr;
{
std::fstream infile{"extruder_idler_quads.obj", std::ios::in};
cntr.from_obj(infile);
}
}
TEST_CASE("halfcone test", "[halfcone]") { TEST_CASE("halfcone test", "[halfcone]") {
sla::DiffBridge br{Vec3d{1., 1., 1.}, Vec3d{10., 10., 10.}, 0.25, 0.5}; sla::DiffBridge br{Vec3d{1., 1., 1.}, Vec3d{10., 10., 10.}, 0.25, 0.5};