From 0d13ecdce80567378566b28b6fc48611370a569d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 13 Feb 2019 16:44:48 +0100 Subject: [PATCH 1/7] Working proof-of-concept for manual triangulation of pad walls. --- sandboxes/slabasebed/CMakeLists.txt | 5 +- sandboxes/slabasebed/slabasebed.cpp | 45 ++- src/libslic3r/SLA/SLABasePool.cpp | 483 ++++++++++++++++++++++++++-- 3 files changed, 493 insertions(+), 40 deletions(-) diff --git a/sandboxes/slabasebed/CMakeLists.txt b/sandboxes/slabasebed/CMakeLists.txt index bff5ca5887..6efbda35f7 100644 --- a/sandboxes/slabasebed/CMakeLists.txt +++ b/sandboxes/slabasebed/CMakeLists.txt @@ -1,2 +1,3 @@ -add_executable(slabasebed EXCLUDE_FROM_ALL slabasebed.cpp) -target_link_libraries(slabasebed libslic3r) \ No newline at end of file +add_executable(slabasebed #EXCLUDE_FROM_ALL + slabasebed.cpp) +target_link_libraries(slabasebed libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) diff --git a/sandboxes/slabasebed/slabasebed.cpp b/sandboxes/slabasebed/slabasebed.cpp index 9804ea3c94..569af4faaf 100644 --- a/sandboxes/slabasebed/slabasebed.cpp +++ b/sandboxes/slabasebed/slabasebed.cpp @@ -1,15 +1,29 @@ #include +#include #include #include #include #include +#include #include const std::string USAGE_STR = { "Usage: slabasebed stlfilename.stl" }; +namespace Slic3r { namespace sla { + +Contour3D convert(const Polygons& triangles, coord_t z, bool dir); +Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, + double floor_z_mm, double ceiling_z_mm, + ThrowOnCancel thr, double offset_difference_mm = 0.0); + +void offset(ExPolygon& sh, coord_t distance); + +} +} + int main(const int argc, const char *argv[]) { using namespace Slic3r; using std::cout; using std::endl; @@ -26,18 +40,43 @@ int main(const int argc, const char *argv[]) { model.align_to_origin(); ExPolygons ground_slice; - TriangleMesh basepool; + sla::Contour3D mesh; +// TriangleMesh basepool; sla::base_plate(model, ground_slice, 0.1f); + if(ground_slice.empty()) return EXIT_FAILURE; + + ExPolygon bottom_plate = ground_slice.front(); + ExPolygon top_plate = bottom_plate; + sla::offset(top_plate, coord_t(3.0/SCALING_FACTOR)); + sla::offset(bottom_plate, coord_t(1.0/SCALING_FACTOR)); + bench.start(); - sla::create_base_pool(ground_slice, basepool); + + Polygons top_plate_triangles, bottom_plate_triangles; + top_plate.triangulate_p2t(&top_plate_triangles); + bottom_plate.triangulate_p2t(&bottom_plate_triangles); + + auto top_plate_mesh = sla::convert(top_plate_triangles, coord_t(3.0/SCALING_FACTOR), false); + auto bottom_plate_mesh = sla::convert(bottom_plate_triangles, 0, true); + + mesh.merge(bottom_plate_mesh); + mesh.merge(top_plate_mesh); + + sla::Contour3D w = sla::walls(bottom_plate, top_plate, 0, 3, [](){}, 2.0); + + mesh.merge(w); +// sla::create_base_pool(ground_slice, basepool); bench.stop(); cout << "Base pool creation time: " << std::setprecision(10) << bench.getElapsedSec() << " seconds." << endl; - basepool.write_ascii("out.stl"); +// basepool.write_ascii("out.stl"); + + std::fstream outstream("out.obj", std::fstream::out); + mesh.to_obj(outstream); return EXIT_SUCCESS; } diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index a235d52baf..b24203556d 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -5,7 +5,10 @@ #include "SLABoostAdapter.hpp" #include "ClipperUtils.hpp" -//#include "SVG.hpp" + +#include + +#include "SVG.hpp" //#include "benchmark.h" namespace Slic3r { namespace sla { @@ -30,56 +33,466 @@ Contour3D convert(const Polygons& triangles, coord_t z, bool dir) { return {points, indices}; } + +// // step 1: find the leftmost bottom vertex of each plate. + +//// auto vcmp = [](const Point& v1, const Point& v2) { +//// if(v1.y() == v2.y()) return v1.x() < v2.x(); +//// return v1.y() < v2.y(); +//// }; + +// // lb stands for Leftmost Bottom +// //auto iit = inner.points.begin(); //std::min_element(inner.points.begin(), inner.points.end(), vcmp); +// //auto oit = outer.points.begin();//std::min_element(outer.points.begin(), outer.points.end(), vcmp); + +// // step 2: find the centroid of the inner polygon +// auto bb = inner.bounding_box(); +// Point center = bb.center(); + +// const double Pi_2 = 2*PI; + +// // This will return the angle of a segment (p1, p2) to the X axis +// // from 0 to 2*PI +// auto anglefn = [Pi_2, center](const Point& p) { +// coord_t dx = p.x() - center.x(), dy = p.y() - center.y(); +// double a = std::atan2(dy, dx); +// auto s = std::signbit(a); +// if(s) a += Pi_2; +// return a; +// }; + +// ret.points.reserve(inner.points.size() + outer.points.size()); +// for(auto& p : inner.points) +// ret.points.emplace_back(unscale(p.x(), p.y(), mm(ceiling_z_mm))); + +// for(auto& p : outer.points) +// ret.points.emplace_back(unscale(p.x(), p.y(), mm(floor_z_mm))); + +// std::vector> anglediagram; +// anglediagram.reserve(inner.size() + outer.size()); + +// for(size_t i = 0; i < inner.size(); ++i) +// anglediagram.emplace_back( +// std::make_pair(long(i), anglefn(inner.points[i]) ) +// ); + +// const auto offs = long(inner.points.size()); + +// for(size_t i = 0; i < outer.size(); ++i) +// anglediagram.emplace_back( +// std::make_pair(offs + long(i), anglefn(outer.points[i]) ) +// ); + +// std::sort(anglediagram.begin(), anglediagram.end(), +// [](const std::pair& v1, +// const std::pair& v2) +// { +// return v1.second < v2.second; +// }); + + +// for(size_t i = 0; i < anglediagram.size() - 3; ++i) { +// long t1 = anglediagram[i].first; +// long t2 = anglediagram[i + 1].first; + +// if(t1 >= offs && t2 >= offs) { +// // search for an inner vertex +// size_t jd = i; +// size_t ju = i + 1; +// while(anglediagram[jd].first >= offs) { +// if(jd == 0) jd = anglediagram.size() - 1; +// else --jd; +// } +// while(anglediagram[ju].first >= offs) { +// if(ju >= anglediagram.size() - 1) ju = 0; +// else ++ju; + +// if(ju > anglediagram.size()) { +// std::cout << "mi eeez????" << std::endl; +// } +// } + +// assert(jd != i || ju != i + 1); + +// long t3 = -1; + +// if(ju > anglediagram.size() || jd > anglediagram.size()) { +// std::cout << "baj van" << std::endl; +// } + +// if(jd == i) t3 = anglediagram[ju].first; +// else if(ju == i + 1) t3 = anglediagram[jd].first; +// else { + +// double ad = anglediagram[jd].second; +// double au = anglediagram[ju].second; + +// double dd = std::abs(ad - anglediagram[i].second); +// if(dd > PI) dd = Pi_2 - dd; + +// double du = std::abs(au - anglediagram[i + 1].second); +// if(du > PI) du = Pi_2 - du; + +// t3 = dd < du ? anglediagram[jd].first: anglediagram[ju].first; +// } + +// ret.indices.emplace_back(t1, t3, t2); +// } +// } + +// This function will return a triangulation of a sheet connecting an upper +// and a lower plate given as input polygons. It will not triangulate the plates +// themselves only the robe. Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, double floor_z_mm, double ceiling_z_mm, - ThrowOnCancel thr) + ThrowOnCancel thr, double offset_difference_mm = 0) { - using std::transform; using std::back_inserter; - - ExPolygon poly; - poly.contour.points = floor_plate.contour.points; - poly.holes.emplace_back(ceiling.contour); - auto& h = poly.holes.front(); - std::reverse(h.points.begin(), h.points.end()); - Polygons tri = triangulate(poly); - Contour3D ret; - ret.points.reserve(tri.size() * 3); - double fz = floor_z_mm; - double cz = ceiling_z_mm; - auto& rp = ret.points; - auto& rpi = ret.indices; - ret.indices.reserve(tri.size() * 3); + const Polygon& inner = ceiling.contour; + const Polygon& outer = floor_plate.contour; - coord_t idx = 0; + if(inner.points.size() < 3 || outer.size() < 3) return ret; - auto hlines = h.lines(); - auto is_upper = [&hlines](const Point& p) { - return std::any_of(hlines.begin(), hlines.end(), - [&p](const Line& l) { - return l.distance_to(p) < mm(1e-6); - }); + const auto offs = long(inner.points.size()); + + ret.points.reserve(inner.points.size() + outer.points.size()); + for(auto& p : inner.points) + ret.points.emplace_back(unscale(p.x(), p.y(), mm(ceiling_z_mm))); + + for(auto& p : outer.points) + ret.points.emplace_back(unscale(p.x(), p.y(), mm(floor_z_mm))); + + auto iit = inner.points.begin(); + auto oit = outer.points.begin(); + + // We need to find the closest point on outer polygon to the first point on + // the inner polygon. These will be our starting points. + double distmin = std::numeric_limits::max(); + + for(auto ot = outer.points.begin(); ot != outer.points.end(); ++ot) { + Vec2d p = (*ot - *iit).cast(); + double d = p.transpose() * p; + if(d < distmin) { oit = ot; distmin = d; } + } + + auto inext = std::next(iit); + auto onext = std::next(oit); + if(onext == outer.points.end()) onext = outer.points.begin(); + + auto iidx = iit - inner.points.begin(); + auto inextidx = inext - inner.points.begin(); + auto oidx = offs + oit - outer.points.begin(); + auto onextidx = offs + onext - outer.points.begin(); + + auto nextinp = [&iit, &inext, &inner, &iidx, &inextidx] () { + ++iit; ++inext; + if(inext == inner.points.end()) inext = inner.points.begin(); + if(iit == inner.points.end()) iit = inner.points.begin(); + inextidx = inext - inner.points.begin(); + iidx = iit - inner.points.begin(); }; - std::for_each(tri.begin(), tri.end(), - [&rp, &rpi, thr, &idx, is_upper, fz, cz](const Polygon& pp) + auto nextoutp = [&oit, &onext, &outer, &onextidx, &oidx, offs] () { + ++oit; ++onext; + if(onext == outer.points.end()) onext = outer.points.begin(); + if(oit == outer.points.end()) oit = outer.points.begin(); + onextidx = offs + onext - outer.points.begin(); + oidx = offs + oit - outer.points.begin(); + }; + + bool isinsider = true; + bool idirty = false, odirty = false; + double obtusity = 0; + double prev_obtusity = 0; + + auto distfn = [](const Vec2d& p1, const Vec2d& p2) { + auto p = p1 - p2; + return p.transpose() * p; + }; + + double cd = ceiling_z_mm - floor_z_mm; + double slope = offset_difference_mm / std::sqrt(std::pow(offset_difference_mm, 2) + std::pow(cd, 2)); + + auto obtusityfn = [distfn](const Vec2d& p1, const Vec2d& p2, const Vec2d& p3) { - thr(); // may throw if cancellation was requested + double a = distfn(p1, p2); + double b = distfn(p2, p3); + double c = distfn(p1, p3); + double aa = std::sqrt(a); + double bb = std::sqrt(b); + double cc = std::sqrt(c); - for(auto& p : pp.points) - if(is_upper(p)) - rp.emplace_back(unscale(x(p), y(p), mm(cz))); - else rp.emplace_back(unscale(x(p), y(p), mm(fz))); +// std::array sides = {aa, bb, cc}; +// std::sort(sides.begin(), sides.end()); +// double thinness = -1 + 2 * std::pow(sides.front() / sides.back(), 2); - coord_t a = idx++, b = idx++, c = idx++; - if(fz > cz) rpi.emplace_back(c, b, a); - else rpi.emplace_back(a, b, c); - }); +// assert(thinness <= 1.0 && thinness >= -1.0); + + std::array coses; + coses[0] = (a + b - c) / (2*aa*bb); + coses[1] = (a + c - b) / (2*aa*cc); + coses[2] = (c + b - a) / (2*cc*bb); + + bool isobt = a + b < c || b + c < a || c + a < b; + double minval = *std::min_element(coses.begin(), coses.end()); + + assert(isobt && minval <= 0 || !isobt && minval >= 0); + + return minval; +// return 0.5 * (minval + thinness); + }; + +#ifndef NDEBUG + Polygons top_plate_triangles, bottom_plate_triangles; + ceiling.triangulate_p2t(&top_plate_triangles); + floor_plate.triangulate_p2t(&bottom_plate_triangles); + + auto top_plate_mesh = sla::convert(top_plate_triangles, coord_t(3.0/SCALING_FACTOR), false); + auto bottom_plate_mesh = sla::convert(bottom_plate_triangles, 0, true); + Contour3D dmesh; + dmesh.merge(top_plate_mesh); + dmesh.merge(bottom_plate_mesh); +#endif + + double idist = 0, odist = 0; + double ilen = inner.length(), olen = outer.length(); + double doffs = offset_difference_mm; + + auto iend = iit; auto oend = oit; + do { +#ifndef NDEBUG + std::fstream fout("dout.obj", std::fstream::out); + Contour3D dmeshout = dmesh; +#endif + prev_obtusity = obtusity; + double distfactor = idist/ilen - odist/olen; + + if(isinsider) { + Vec3d p1(iit->x()*SCALING_FACTOR, iit->y()*SCALING_FACTOR, ceiling_z_mm); + Vec3d p2(oit->x()*SCALING_FACTOR, oit->y()*SCALING_FACTOR, floor_z_mm); + Vec3d p3(inext->x()*SCALING_FACTOR, inext->y()*SCALING_FACTOR, ceiling_z_mm); + + if(idirty && iit == iend) { isinsider = false; continue; } + + double t1 = doffs / std::sqrt((p1 - p2).transpose() * (p1 - p2)); + t1 = slope - t1; + double t2 = doffs / std::sqrt((p3 - p2).transpose() * (p3 - p2)); + t2 = slope - t2; + double t = std::max(std::abs(t1), std::abs(t2)); + + obtusity = t; +// obtusity = obtusityfn(p1, p2, p3); +// obtusity = 0.9 * obtusity - 0.1 * distfactor; + + if(obtusity > prev_obtusity) { + isinsider = false; + } else { + ret.indices.emplace_back(iidx, oidx, inextidx); + nextinp(); + Vec2d tmp = (*iit - *inext).cast(); + idist += std::sqrt(tmp.transpose() * tmp); + idirty = true; + } + } else { + Vec3d p1(oit->x()*SCALING_FACTOR, oit->y()*SCALING_FACTOR, floor_z_mm); + Vec3d p2(onext->x()*SCALING_FACTOR, onext->y()*SCALING_FACTOR, floor_z_mm); + Vec3d p3(iit->x()*SCALING_FACTOR, iit->y()*SCALING_FACTOR, ceiling_z_mm); + + if(odirty && oit == oend) { isinsider = true; continue; } + + double t1 = slope - doffs / std::sqrt((p3 - p1).transpose() * (p3 - p1)); + double t2 = slope - doffs / std::sqrt((p3 - p2).transpose() * (p3 - p2)); + double t = std::max(std::abs(t1), std::abs(t2)); + + obtusity = t; + +// obtusity = obtusityfn(p1, p2, p3); +// obtusity = 0.9 * obtusity + 0.1 * distfactor; + + if(obtusity > prev_obtusity) { + isinsider = true; + } else { + ret.indices.emplace_back(oidx, onextidx, iidx); + nextoutp(); + Vec2d tmp = (*oit - *onext).cast(); + odist += std::sqrt(tmp.transpose() * tmp); + odirty = true; + } + } + +#ifndef NDEBUG + dmeshout.merge(ret); + dmeshout.to_obj(fout); + fout.close(); + std::cout << "triangle written" << std::endl; +#endif + + } while(!idirty || !odirty || iit != iend || oit != oend); return ret; + +// using std::transform; using std::back_inserter; + +// ExPolygon poly; +// poly.contour.points = floor_plate.contour.points; +// poly.holes.emplace_back(ceiling.contour); +// auto& h = poly.holes.front(); +// std::reverse(h.points.begin(), h.points.end()); +// Polygons tri = triangulate(poly); + +// Contour3D ret; +// ret.points.reserve(tri.size() * 3); + +// double fz = floor_z_mm; +// double cz = ceiling_z_mm; +// auto& rp = ret.points; +// auto& rpi = ret.indices; +// ret.indices.reserve(tri.size() * 3); + +// coord_t idx = 0; + +// auto hlines = h.lines(); +// auto is_upper = [&hlines](const Point& p) { +// return std::any_of(hlines.begin(), hlines.end(), +// [&p](const Line& l) { +// return l.distance_to(p) < mm(1e-6); +// }); +// }; + +// for(const Polygon& pp : tri) { +// thr(); // may throw if cancellation was requested + +// for(auto& p : pp.points) +// if(is_upper(p)) +// rp.emplace_back(unscale(x(p), y(p), mm(cz))); +// else rp.emplace_back(unscale(x(p), y(p), mm(fz))); + +// coord_t a = idx++, b = idx++, c = idx++; +// if(fz > cz) rpi.emplace_back(c, b, a); +// else rpi.emplace_back(a, b, c); +// } + +// return ret; } + +// const auto offs = long(inner.points.size()); + +// auto inext = std::next(iit); +// auto onext = std::next(oit); + +// auto nextinp = [&iit, &inext, &inner] () { +// ++iit; ++inext; +// if(inext == inner.points.end()) inext = inner.points.begin(); +// if(iit == inner.points.end()) iit = inner.points.begin(); +// }; + +// auto nextoutp = [&oit, &onext, &outer] () { +// ++oit; ++onext; +// if(onext == outer.points.end()) onext = outer.points.begin(); +// if(oit == outer.points.end()) oit = outer.points.begin(); +// }; + +// double aonext = anglefn(*onext); +// size_t n = 0; +// while(n < inner.size()) { +// double a1 = anglefn(*iit); +// double a2 = anglefn(*inext); +// if(inext < iit) a2 += Pi_2; + +// double amin = std::min(a1, a2); +// double amax = std::max(a1, a2); + +// // We have to dial the outer vertex pair to the range of the inner +// // pair +// size_t i = 0; +// while((aonext <= amin || aonext > amax) && i < outer.size()) +// { // search for the first outer vertex that is suitable +// nextoutp(); +// aonext = anglefn(*onext); +// if(inext < iit) aonext += Pi_2; +// ++i; +// } + +// // If we arrived at the end of the outer ring, and the inner is not +// // completed, we will rotate the outer. +// if(i == outer.size()) { +// nextinp(); ++n; +// continue; +// } + +// auto iidx = iit - inner.points.begin(); +// auto inextidx = inext - inner.points.begin(); +// auto oidx = offs + oit - outer.points.begin(); +// auto onextidx = offs + onext - outer.points.begin(); + +// ret.indices.emplace_back(onextidx, iidx, oidx); +// ret.indices.emplace_back(onextidx, inextidx, iidx); + +// while(true) +// { +// nextoutp(); + +// onextidx = offs + onext - outer.points.begin(); +// oidx = offs + oit - outer.points.begin(); + +// aonext = anglefn(*onext); + +// if(aonext > amin && aonext <= amax) { +// ret.indices.emplace_back(onextidx, inextidx, oidx); +// } else break; +// } + +// nextinp(); ++n; +// } + + + + +// using std::transform; using std::back_inserter; + +// ExPolygon poly; +// poly.contour.points = floor_plate.contour.points; +// poly.holes.emplace_back(ceiling.contour); +// auto& h = poly.holes.front(); +// std::reverse(h.points.begin(), h.points.end()); +// Polygons tri = triangulate(poly); + +// Contour3D ret; +// ret.points.reserve(tri.size() * 3); + +// double fz = floor_z_mm; +// double cz = ceiling_z_mm; +// auto& rp = ret.points; +// auto& rpi = ret.indices; +// ret.indices.reserve(tri.size() * 3); + +// coord_t idx = 0; + +// auto hlines = h.lines(); +// auto is_upper = [&hlines](const Point& p) { +// return std::any_of(hlines.begin(), hlines.end(), +// [&p](const Line& l) { +// return l.distance_to(p) < mm(1e-6); +// }); +// }; + +// for(const Polygon& pp : tri) { +// thr(); // may throw if cancellation was requested + +// for(auto& p : pp.points) +// if(is_upper(p)) +// rp.emplace_back(unscale(x(p), y(p), mm(cz))); +// else rp.emplace_back(unscale(x(p), y(p), mm(fz))); + +// coord_t a = idx++, b = idx++, c = idx++; +// if(fz > cz) rpi.emplace_back(c, b, a); +// else rpi.emplace_back(a, b, c); +// } + +// return ret; + /// Offsetting with clipper and smoothing the edges into a curvature. void offset(ExPolygon& sh, coord_t distance) { using ClipperLib::ClipperOffset; From daa8f7ef1bfb9f501e99872b9b300bc3696565e6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 13 Feb 2019 18:21:27 +0100 Subject: [PATCH 2/7] Refactored version of the wall triangulation algorithm, initial integration. --- sandboxes/slabasebed/slabasebed.cpp | 6 +- src/libslic3r/SLA/SLABasePool.cpp | 471 ++++++---------------------- 2 files changed, 98 insertions(+), 379 deletions(-) diff --git a/sandboxes/slabasebed/slabasebed.cpp b/sandboxes/slabasebed/slabasebed.cpp index 569af4faaf..3237416097 100644 --- a/sandboxes/slabasebed/slabasebed.cpp +++ b/sandboxes/slabasebed/slabasebed.cpp @@ -15,9 +15,9 @@ const std::string USAGE_STR = { namespace Slic3r { namespace sla { Contour3D convert(const Polygons& triangles, coord_t z, bool dir); -Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, +Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling, double floor_z_mm, double ceiling_z_mm, - ThrowOnCancel thr, double offset_difference_mm = 0.0); + double offset_difference_mm, ThrowOnCancel thr); void offset(ExPolygon& sh, coord_t distance); @@ -64,7 +64,7 @@ int main(const int argc, const char *argv[]) { mesh.merge(bottom_plate_mesh); mesh.merge(top_plate_mesh); - sla::Contour3D w = sla::walls(bottom_plate, top_plate, 0, 3, [](){}, 2.0); + sla::Contour3D w = sla::walls(bottom_plate.contour, top_plate.contour, 0, 3, 2.0, [](){}); mesh.merge(w); // sla::create_base_pool(ground_slice, basepool); diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index b24203556d..83735ea164 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -5,10 +5,7 @@ #include "SLABoostAdapter.hpp" #include "ClipperUtils.hpp" - -#include - -#include "SVG.hpp" +//#include "SVG.hpp" //#include "benchmark.h" namespace Slic3r { namespace sla { @@ -33,302 +30,132 @@ Contour3D convert(const Polygons& triangles, coord_t z, bool dir) { return {points, indices}; } - -// // step 1: find the leftmost bottom vertex of each plate. - -//// auto vcmp = [](const Point& v1, const Point& v2) { -//// if(v1.y() == v2.y()) return v1.x() < v2.x(); -//// return v1.y() < v2.y(); -//// }; - -// // lb stands for Leftmost Bottom -// //auto iit = inner.points.begin(); //std::min_element(inner.points.begin(), inner.points.end(), vcmp); -// //auto oit = outer.points.begin();//std::min_element(outer.points.begin(), outer.points.end(), vcmp); - -// // step 2: find the centroid of the inner polygon -// auto bb = inner.bounding_box(); -// Point center = bb.center(); - -// const double Pi_2 = 2*PI; - -// // This will return the angle of a segment (p1, p2) to the X axis -// // from 0 to 2*PI -// auto anglefn = [Pi_2, center](const Point& p) { -// coord_t dx = p.x() - center.x(), dy = p.y() - center.y(); -// double a = std::atan2(dy, dx); -// auto s = std::signbit(a); -// if(s) a += Pi_2; -// return a; -// }; - -// ret.points.reserve(inner.points.size() + outer.points.size()); -// for(auto& p : inner.points) -// ret.points.emplace_back(unscale(p.x(), p.y(), mm(ceiling_z_mm))); - -// for(auto& p : outer.points) -// ret.points.emplace_back(unscale(p.x(), p.y(), mm(floor_z_mm))); - -// std::vector> anglediagram; -// anglediagram.reserve(inner.size() + outer.size()); - -// for(size_t i = 0; i < inner.size(); ++i) -// anglediagram.emplace_back( -// std::make_pair(long(i), anglefn(inner.points[i]) ) -// ); - -// const auto offs = long(inner.points.size()); - -// for(size_t i = 0; i < outer.size(); ++i) -// anglediagram.emplace_back( -// std::make_pair(offs + long(i), anglefn(outer.points[i]) ) -// ); - -// std::sort(anglediagram.begin(), anglediagram.end(), -// [](const std::pair& v1, -// const std::pair& v2) -// { -// return v1.second < v2.second; -// }); - - -// for(size_t i = 0; i < anglediagram.size() - 3; ++i) { -// long t1 = anglediagram[i].first; -// long t2 = anglediagram[i + 1].first; - -// if(t1 >= offs && t2 >= offs) { -// // search for an inner vertex -// size_t jd = i; -// size_t ju = i + 1; -// while(anglediagram[jd].first >= offs) { -// if(jd == 0) jd = anglediagram.size() - 1; -// else --jd; -// } -// while(anglediagram[ju].first >= offs) { -// if(ju >= anglediagram.size() - 1) ju = 0; -// else ++ju; - -// if(ju > anglediagram.size()) { -// std::cout << "mi eeez????" << std::endl; -// } -// } - -// assert(jd != i || ju != i + 1); - -// long t3 = -1; - -// if(ju > anglediagram.size() || jd > anglediagram.size()) { -// std::cout << "baj van" << std::endl; -// } - -// if(jd == i) t3 = anglediagram[ju].first; -// else if(ju == i + 1) t3 = anglediagram[jd].first; -// else { - -// double ad = anglediagram[jd].second; -// double au = anglediagram[ju].second; - -// double dd = std::abs(ad - anglediagram[i].second); -// if(dd > PI) dd = Pi_2 - dd; - -// double du = std::abs(au - anglediagram[i + 1].second); -// if(du > PI) du = Pi_2 - du; - -// t3 = dd < du ? anglediagram[jd].first: anglediagram[ju].first; -// } - -// ret.indices.emplace_back(t1, t3, t2); -// } -// } - // This function will return a triangulation of a sheet connecting an upper // and a lower plate given as input polygons. It will not triangulate the plates // themselves only the robe. -Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, +Contour3D walls(const Polygon& lower, const Polygon& upper, double floor_z_mm, double ceiling_z_mm, - ThrowOnCancel thr, double offset_difference_mm = 0) + double offset_difference_mm, ThrowOnCancel thr) { Contour3D ret; - const Polygon& inner = ceiling.contour; - const Polygon& outer = floor_plate.contour; + if(upper.points.size() < 3 || lower.size() < 3) return ret; - if(inner.points.size() < 3 || outer.size() < 3) return ret; + // Offset in the index array for the ceiling + const auto offs = long(upper.points.size()); - const auto offs = long(inner.points.size()); - - ret.points.reserve(inner.points.size() + outer.points.size()); - for(auto& p : inner.points) + ret.points.reserve(upper.points.size() + lower.points.size()); + for(auto& p : upper.points) ret.points.emplace_back(unscale(p.x(), p.y(), mm(ceiling_z_mm))); - for(auto& p : outer.points) + for(auto& p : lower.points) ret.points.emplace_back(unscale(p.x(), p.y(), mm(floor_z_mm))); - auto iit = inner.points.begin(); - auto oit = outer.points.begin(); + auto uit = upper.points.begin(); + auto lit = lower.points.begin(); // We need to find the closest point on outer polygon to the first point on // the inner polygon. These will be our starting points. double distmin = std::numeric_limits::max(); - for(auto ot = outer.points.begin(); ot != outer.points.end(); ++ot) { - Vec2d p = (*ot - *iit).cast(); + for(auto lt = lower.points.begin(); lt != lower.points.end(); ++lt) { + thr(); + Vec2d p = (*lt - *uit).cast(); double d = p.transpose() * p; - if(d < distmin) { oit = ot; distmin = d; } + if(d < distmin) { lit = lt; distmin = d; } } - auto inext = std::next(iit); - auto onext = std::next(oit); - if(onext == outer.points.end()) onext = outer.points.begin(); + auto unext = std::next(uit); + auto lnext = std::next(lit); + if(lnext == lower.points.end()) lnext = lower.points.begin(); - auto iidx = iit - inner.points.begin(); - auto inextidx = inext - inner.points.begin(); - auto oidx = offs + oit - outer.points.begin(); - auto onextidx = offs + onext - outer.points.begin(); + auto uidx = uit - upper.points.begin(); + auto unextidx = unext - upper.points.begin(); + auto lidx = offs + lit - lower.points.begin(); + auto lnextidx = offs + lnext - lower.points.begin(); - auto nextinp = [&iit, &inext, &inner, &iidx, &inextidx] () { - ++iit; ++inext; - if(inext == inner.points.end()) inext = inner.points.begin(); - if(iit == inner.points.end()) iit = inner.points.begin(); - inextidx = inext - inner.points.begin(); - iidx = iit - inner.points.begin(); - }; + enum class Proceed { + UPPER, LOWER + } proceed = Proceed::UPPER; - auto nextoutp = [&oit, &onext, &outer, &onextidx, &oidx, offs] () { - ++oit; ++onext; - if(onext == outer.points.end()) onext = outer.points.begin(); - if(oit == outer.points.end()) oit = outer.points.begin(); - onextidx = offs + onext - outer.points.begin(); - oidx = offs + oit - outer.points.begin(); - }; + bool ustarted = false, lstarted = false; + double current_fit = 0; + double prev_fit = 0; - bool isinsider = true; - bool idirty = false, odirty = false; - double obtusity = 0; - double prev_obtusity = 0; - - auto distfn = [](const Vec2d& p1, const Vec2d& p2) { + auto distfn = [](const Vec3d& p1, const Vec3d& p2) { auto p = p1 - p2; - return p.transpose() * p; + return std::sqrt(p.transpose() * p); }; - double cd = ceiling_z_mm - floor_z_mm; - double slope = offset_difference_mm / std::sqrt(std::pow(offset_difference_mm, 2) + std::pow(cd, 2)); + const double required_fit = offset_difference_mm / + std::sqrt( std::pow(offset_difference_mm, 2) + + std::pow(ceiling_z_mm - floor_z_mm, 2)); - auto obtusityfn = [distfn](const Vec2d& p1, const Vec2d& p2, const Vec2d& p3) - { - double a = distfn(p1, p2); - double b = distfn(p2, p3); - double c = distfn(p1, p3); - double aa = std::sqrt(a); - double bb = std::sqrt(b); - double cc = std::sqrt(c); - -// std::array sides = {aa, bb, cc}; -// std::sort(sides.begin(), sides.end()); -// double thinness = -1 + 2 * std::pow(sides.front() / sides.back(), 2); - -// assert(thinness <= 1.0 && thinness >= -1.0); - - std::array coses; - coses[0] = (a + b - c) / (2*aa*bb); - coses[1] = (a + c - b) / (2*aa*cc); - coses[2] = (c + b - a) / (2*cc*bb); - - bool isobt = a + b < c || b + c < a || c + a < b; - double minval = *std::min_element(coses.begin(), coses.end()); - - assert(isobt && minval <= 0 || !isobt && minval >= 0); - - return minval; -// return 0.5 * (minval + thinness); - }; - -#ifndef NDEBUG - Polygons top_plate_triangles, bottom_plate_triangles; - ceiling.triangulate_p2t(&top_plate_triangles); - floor_plate.triangulate_p2t(&bottom_plate_triangles); - - auto top_plate_mesh = sla::convert(top_plate_triangles, coord_t(3.0/SCALING_FACTOR), false); - auto bottom_plate_mesh = sla::convert(bottom_plate_triangles, 0, true); - Contour3D dmesh; - dmesh.merge(top_plate_mesh); - dmesh.merge(bottom_plate_mesh); -#endif - - double idist = 0, odist = 0; - double ilen = inner.length(), olen = outer.length(); - double doffs = offset_difference_mm; - - auto iend = iit; auto oend = oit; + auto uend = uit; auto lend = lit; do { -#ifndef NDEBUG - std::fstream fout("dout.obj", std::fstream::out); - Contour3D dmeshout = dmesh; -#endif - prev_obtusity = obtusity; - double distfactor = idist/ilen - odist/olen; + thr(); - if(isinsider) { - Vec3d p1(iit->x()*SCALING_FACTOR, iit->y()*SCALING_FACTOR, ceiling_z_mm); - Vec3d p2(oit->x()*SCALING_FACTOR, oit->y()*SCALING_FACTOR, floor_z_mm); - Vec3d p3(inext->x()*SCALING_FACTOR, inext->y()*SCALING_FACTOR, ceiling_z_mm); + prev_fit = current_fit; + Vec2d ip = unscale(uit->x(), uit->y()); + Vec2d inextp = unscale(unext->x(), unext->y()); + Vec2d op = unscale(lit->x(), lit->y()); + Vec2d onextp = unscale(lnext->x(), lnext->y()); - if(idirty && iit == iend) { isinsider = false; continue; } + switch(proceed) { + case Proceed::UPPER: + if(!ustarted || uit != uend) { + Vec3d p1(ip.x(), ip.y(), ceiling_z_mm); + Vec3d p2(op.x(), op.y(), floor_z_mm); + Vec3d p3(inextp.x(), inextp.y(), ceiling_z_mm); - double t1 = doffs / std::sqrt((p1 - p2).transpose() * (p1 - p2)); - t1 = slope - t1; - double t2 = doffs / std::sqrt((p3 - p2).transpose() * (p3 - p2)); - t2 = slope - t2; - double t = std::max(std::abs(t1), std::abs(t2)); + double a = required_fit - offset_difference_mm / distfn(p1, p2); + double b = required_fit - offset_difference_mm / distfn(p3, p2); + current_fit = std::max(std::abs(a), std::abs(b)); - obtusity = t; -// obtusity = obtusityfn(p1, p2, p3); -// obtusity = 0.9 * obtusity - 0.1 * distfactor; + if(current_fit > prev_fit) { + proceed = Proceed::LOWER; + } else { + ret.indices.emplace_back(uidx, lidx, unextidx); - if(obtusity > prev_obtusity) { - isinsider = false; - } else { - ret.indices.emplace_back(iidx, oidx, inextidx); - nextinp(); - Vec2d tmp = (*iit - *inext).cast(); - idist += std::sqrt(tmp.transpose() * tmp); - idirty = true; - } - } else { - Vec3d p1(oit->x()*SCALING_FACTOR, oit->y()*SCALING_FACTOR, floor_z_mm); - Vec3d p2(onext->x()*SCALING_FACTOR, onext->y()*SCALING_FACTOR, floor_z_mm); - Vec3d p3(iit->x()*SCALING_FACTOR, iit->y()*SCALING_FACTOR, ceiling_z_mm); + ++uit; ++unext; + if(unext == upper.points.end()) unext = upper.points.begin(); + if(uit == upper.points.end()) uit = upper.points.begin(); + unextidx = unext - upper.points.begin(); + uidx = uit - upper.points.begin(); - if(odirty && oit == oend) { isinsider = true; continue; } + ustarted = true; + } + } else proceed = Proceed::LOWER; - double t1 = slope - doffs / std::sqrt((p3 - p1).transpose() * (p3 - p1)); - double t2 = slope - doffs / std::sqrt((p3 - p2).transpose() * (p3 - p2)); - double t = std::max(std::abs(t1), std::abs(t2)); + break; + case Proceed::LOWER: + if(!lstarted || lit != lend) { + Vec3d p1(op.x(), op.y(), floor_z_mm); + Vec3d p2(onextp.x(), onextp.y(), floor_z_mm); + Vec3d p3(ip.x(), ip.y(), ceiling_z_mm); - obtusity = t; + double a = required_fit - offset_difference_mm / distfn(p3, p1); + double b = required_fit - offset_difference_mm / distfn(p3, p2); + current_fit = std::max(std::abs(a), std::abs(b)); -// obtusity = obtusityfn(p1, p2, p3); -// obtusity = 0.9 * obtusity + 0.1 * distfactor; + if(current_fit > prev_fit) { + proceed = Proceed::UPPER; + } else { + ret.indices.emplace_back(lidx, lnextidx, uidx); - if(obtusity > prev_obtusity) { - isinsider = true; - } else { - ret.indices.emplace_back(oidx, onextidx, iidx); - nextoutp(); - Vec2d tmp = (*oit - *onext).cast(); - odist += std::sqrt(tmp.transpose() * tmp); - odirty = true; - } - } + ++lit; ++lnext; + if(lnext == lower.points.end()) lnext = lower.points.begin(); + if(lit == lower.points.end()) lit = lower.points.begin(); + lnextidx = offs + lnext - lower.points.begin(); + lidx = offs + lit - lower.points.begin(); -#ifndef NDEBUG - dmeshout.merge(ret); - dmeshout.to_obj(fout); - fout.close(); - std::cout << "triangle written" << std::endl; -#endif + lstarted = true; + } + } else proceed = Proceed::UPPER; - } while(!idirty || !odirty || iit != iend || oit != oend); + break; + } // switch + } while(!ustarted || !lstarted || uit != uend || lit != lend); return ret; @@ -377,121 +204,13 @@ Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, } -// const auto offs = long(inner.points.size()); +Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, + double floor_z_mm, double ceiling_z_mm, ThrowOnCancel thr) +{ + return walls(floor_plate.contour, ceiling.contour, floor_z_mm, ceiling_z_mm, + 0, thr); +} -// auto inext = std::next(iit); -// auto onext = std::next(oit); - -// auto nextinp = [&iit, &inext, &inner] () { -// ++iit; ++inext; -// if(inext == inner.points.end()) inext = inner.points.begin(); -// if(iit == inner.points.end()) iit = inner.points.begin(); -// }; - -// auto nextoutp = [&oit, &onext, &outer] () { -// ++oit; ++onext; -// if(onext == outer.points.end()) onext = outer.points.begin(); -// if(oit == outer.points.end()) oit = outer.points.begin(); -// }; - -// double aonext = anglefn(*onext); -// size_t n = 0; -// while(n < inner.size()) { -// double a1 = anglefn(*iit); -// double a2 = anglefn(*inext); -// if(inext < iit) a2 += Pi_2; - -// double amin = std::min(a1, a2); -// double amax = std::max(a1, a2); - -// // We have to dial the outer vertex pair to the range of the inner -// // pair -// size_t i = 0; -// while((aonext <= amin || aonext > amax) && i < outer.size()) -// { // search for the first outer vertex that is suitable -// nextoutp(); -// aonext = anglefn(*onext); -// if(inext < iit) aonext += Pi_2; -// ++i; -// } - -// // If we arrived at the end of the outer ring, and the inner is not -// // completed, we will rotate the outer. -// if(i == outer.size()) { -// nextinp(); ++n; -// continue; -// } - -// auto iidx = iit - inner.points.begin(); -// auto inextidx = inext - inner.points.begin(); -// auto oidx = offs + oit - outer.points.begin(); -// auto onextidx = offs + onext - outer.points.begin(); - -// ret.indices.emplace_back(onextidx, iidx, oidx); -// ret.indices.emplace_back(onextidx, inextidx, iidx); - -// while(true) -// { -// nextoutp(); - -// onextidx = offs + onext - outer.points.begin(); -// oidx = offs + oit - outer.points.begin(); - -// aonext = anglefn(*onext); - -// if(aonext > amin && aonext <= amax) { -// ret.indices.emplace_back(onextidx, inextidx, oidx); -// } else break; -// } - -// nextinp(); ++n; -// } - - - - -// using std::transform; using std::back_inserter; - -// ExPolygon poly; -// poly.contour.points = floor_plate.contour.points; -// poly.holes.emplace_back(ceiling.contour); -// auto& h = poly.holes.front(); -// std::reverse(h.points.begin(), h.points.end()); -// Polygons tri = triangulate(poly); - -// Contour3D ret; -// ret.points.reserve(tri.size() * 3); - -// double fz = floor_z_mm; -// double cz = ceiling_z_mm; -// auto& rp = ret.points; -// auto& rpi = ret.indices; -// ret.indices.reserve(tri.size() * 3); - -// coord_t idx = 0; - -// auto hlines = h.lines(); -// auto is_upper = [&hlines](const Point& p) { -// return std::any_of(hlines.begin(), hlines.end(), -// [&p](const Line& l) { -// return l.distance_to(p) < mm(1e-6); -// }); -// }; - -// for(const Polygon& pp : tri) { -// thr(); // may throw if cancellation was requested - -// for(auto& p : pp.points) -// if(is_upper(p)) -// rp.emplace_back(unscale(x(p), y(p), mm(cz))); -// else rp.emplace_back(unscale(x(p), y(p), mm(fz))); - -// coord_t a = idx++, b = idx++, c = idx++; -// if(fz > cz) rpi.emplace_back(c, b, a); -// else rpi.emplace_back(a, b, c); -// } - -// return ret; /// Offsetting with clipper and smoothing the edges into a curvature. void offset(ExPolygon& sh, coord_t distance) { @@ -665,7 +384,7 @@ Contour3D round_edges(const ExPolygon& base_plate, wh = ceilheight_mm - radius_mm + stepy; Contour3D pwalls; - pwalls = walls(ob, ob_prev, wh, wh_prev, throw_on_cancel); + pwalls = walls(ob.contour, ob_prev.contour, wh, wh_prev, xx, throw_on_cancel); curvedwalls.merge(pwalls); ob_prev = ob; @@ -688,7 +407,7 @@ Contour3D round_edges(const ExPolygon& base_plate, wh = ceilheight_mm - radius_mm - stepy; Contour3D pwalls; - pwalls = walls(ob_prev, ob, wh_prev, wh, throw_on_cancel); + pwalls = walls(ob_prev.contour, ob.contour, wh_prev, wh, xx, throw_on_cancel); curvedwalls.merge(pwalls); ob_prev = ob; @@ -981,7 +700,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, // Now that we have the rounded edge connencting the top plate with // the outer side walls, we can generate and merge the sidewall geometry - auto pwalls = walls(ob, inner_base, wh, -fullheight, thrcl); + auto pwalls = walls(ob.contour, inner_base.contour, wh, -fullheight, (s_thickness + s_wingdist) * SCALING_FACTOR, thrcl); pool.merge(pwalls); if(wingheight > 0) { @@ -997,7 +716,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, // Next is the cavity walls connecting to the top plate's // artificially created hole. - auto cavitywalls = walls(inner_base, ob, -wingheight, wh, thrcl); + auto cavitywalls = walls(inner_base.contour, ob.contour, -wingheight, wh, s_safety_dist * SCALING_FACTOR,thrcl); pool.merge(cavitywalls); } From 1e9b64b97131eb40d5e05cc1d6433431dfc3e181 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 14 Feb 2019 11:23:43 +0100 Subject: [PATCH 3/7] Commented and integrated new pad wall triangulation --- sandboxes/slabasebed/CMakeLists.txt | 3 +- src/libslic3r/SLA/SLABasePool.cpp | 227 ++++++++++++++-------------- 2 files changed, 117 insertions(+), 113 deletions(-) diff --git a/sandboxes/slabasebed/CMakeLists.txt b/sandboxes/slabasebed/CMakeLists.txt index 6efbda35f7..9d731a1333 100644 --- a/sandboxes/slabasebed/CMakeLists.txt +++ b/sandboxes/slabasebed/CMakeLists.txt @@ -1,3 +1,2 @@ -add_executable(slabasebed #EXCLUDE_FROM_ALL - slabasebed.cpp) +add_executable(slabasebed EXCLUDE_FROM_ALL slabasebed.cpp) target_link_libraries(slabasebed libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index 83735ea164..a7cfa40f8b 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -30,109 +30,161 @@ Contour3D convert(const Polygons& triangles, coord_t z, bool dir) { return {points, indices}; } -// This function will return a triangulation of a sheet connecting an upper -// and a lower plate given as input polygons. It will not triangulate the plates -// themselves only the robe. +/// This function will return a triangulation of a sheet connecting an upper +/// and a lower plate given as input polygons. It will not triangulate the +/// plates themselves only the sheet. The caller has to specify the lower and +/// upper z levels in world coordinates as well as the offset difference +/// between the sheets. +/// +/// IMPORTANT: This is not a universal triangulation algorithm. It assumes +/// that the lower and upper polygons are offsetted versions of the same +/// original polygon. In general, it assumes that one of the polygons is +/// completely inside the other. The offset difference is the reference +/// distance from the inner polygon's perimeter to the outer polygon's +/// perimeter. The real distance will be variable as the clipper offset has +/// different strategies (rounding, etc...). This algorithm should have +/// O(2n + 3m) complexity where n is the number of upper vertices and m is the +/// number of lower vertices. Contour3D walls(const Polygon& lower, const Polygon& upper, - double floor_z_mm, double ceiling_z_mm, + double lower_z_mm, double upper_z_mm, double offset_difference_mm, ThrowOnCancel thr) { Contour3D ret; if(upper.points.size() < 3 || lower.size() < 3) return ret; + // The concept of the algorithm is relatively simple. It will try to find + // the closest vertices from the upper and the lower polygon and use those + // as starting points. Then it will create the triangles sequentially using + // an edge from the upper polygon and a vertex from the lower or vice versa, + // depending on the resulting triangle's quality. + // The quality is measured by a scalar value. So far it looks like it is + // enough to derive it from the slope of the triangle's two edges connecting + // the upper and the lower part. A reference slope is calculated from the + // height and the offset difference. + // Offset in the index array for the ceiling const auto offs = long(upper.points.size()); - ret.points.reserve(upper.points.size() + lower.points.size()); - for(auto& p : upper.points) - ret.points.emplace_back(unscale(p.x(), p.y(), mm(ceiling_z_mm))); + // Shorthand for the vertex arrays + auto& upoints = upper.points, &lpoints = lower.points; - for(auto& p : lower.points) - ret.points.emplace_back(unscale(p.x(), p.y(), mm(floor_z_mm))); + // If the Z levels are flipped, or the offset difference is negative, we + // will interpret that as the triangles normals should be inverted. + bool inverted = upper_z_mm < lower_z_mm || offset_difference_mm < 0; - auto uit = upper.points.begin(); - auto lit = lower.points.begin(); + // Copy the points into the mesh, convert them from 2D to 3D + ret.points.reserve(upoints.size() + lpoints.size()); + for(auto& p : upoints) + ret.points.emplace_back(unscale(p.x(), p.y(), mm(upper_z_mm))); + for(auto& p : lpoints) + ret.points.emplace_back(unscale(p.x(), p.y(), mm(lower_z_mm))); - // We need to find the closest point on outer polygon to the first point on - // the inner polygon. These will be our starting points. + // Create cyclic iterators for the vertices in both polygons. + auto uit = upoints.begin(); + auto lit = lpoints.begin(); + + // We need to find the closest point on lower polygon to the first point on + // the upper polygon. These will be our starting points. double distmin = std::numeric_limits::max(); - - for(auto lt = lower.points.begin(); lt != lower.points.end(); ++lt) { + for(auto lt = lpoints.begin(); lt != lpoints.end(); ++lt) { thr(); Vec2d p = (*lt - *uit).cast(); double d = p.transpose() * p; if(d < distmin) { lit = lt; distmin = d; } } - auto unext = std::next(uit); - auto lnext = std::next(lit); - if(lnext == lower.points.end()) lnext = lower.points.begin(); + // Iterators to the polygon vertices which are always ahead of uit and lit + // in cyclic mode. + auto unextit = std::next(uit); + auto lnextit = std::next(lit); + if(lnextit == lower.points.end()) lnextit = lower.points.begin(); + // Get the integer vertex indices from the iterators. auto uidx = uit - upper.points.begin(); - auto unextidx = unext - upper.points.begin(); + auto unextidx = unextit - upper.points.begin(); auto lidx = offs + lit - lower.points.begin(); - auto lnextidx = offs + lnext - lower.points.begin(); + auto lnextidx = offs + lnextit - lower.points.begin(); + // This will be the flip switch to toggle between upper and lower triangle + // creation mode enum class Proceed { - UPPER, LOWER + UPPER, // A segment from the upper polygon and one vertex from the lower + LOWER // A segment from the lower polygon and one vertex from the upper } proceed = Proceed::UPPER; + // Flags to help evaluating loop termination. bool ustarted = false, lstarted = false; - double current_fit = 0; - double prev_fit = 0; + // The variables for the fitness values, one for the actual and one for the + // previous. + double current_fit = 0, prev_fit = 0; + + // Simple distance calculation. auto distfn = [](const Vec3d& p1, const Vec3d& p2) { - auto p = p1 - p2; - return std::sqrt(p.transpose() * p); + auto p = p1 - p2; return std::sqrt(p.transpose() * p); }; + // Calculate the reference fitness value. It will be the sine of the angle + // from the upper to the lower plate according to the triangle noted by the + // Z difference and the offset difference. const double required_fit = offset_difference_mm / std::sqrt( std::pow(offset_difference_mm, 2) + - std::pow(ceiling_z_mm - floor_z_mm, 2)); + std::pow(upper_z_mm - lower_z_mm, 2)); + // Mark the current vertex iterator positions. If the iterators return to + // the same position, the loop can be terminated. auto uend = uit; auto lend = lit; + do { thr(); prev_fit = current_fit; + + // Get the actual 2D vertices from the upper and lower polygon. Vec2d ip = unscale(uit->x(), uit->y()); - Vec2d inextp = unscale(unext->x(), unext->y()); + Vec2d inextp = unscale(unextit->x(), unextit->y()); Vec2d op = unscale(lit->x(), lit->y()); - Vec2d onextp = unscale(lnext->x(), lnext->y()); + Vec2d onextp = unscale(lnextit->x(), lnextit->y()); - switch(proceed) { + switch(proceed) { // proceed depending on the current state case Proceed::UPPER: - if(!ustarted || uit != uend) { - Vec3d p1(ip.x(), ip.y(), ceiling_z_mm); - Vec3d p2(op.x(), op.y(), floor_z_mm); - Vec3d p3(inextp.x(), inextp.y(), ceiling_z_mm); + if(!ustarted || uit != uend) { // if there are vertices remaining + // Get the 3D vertices in order + Vec3d p1(ip.x(), ip.y(), upper_z_mm); + Vec3d p2(op.x(), op.y(), lower_z_mm); + Vec3d p3(inextp.x(), inextp.y(), upper_z_mm); + // Calculate fitness: the worst of the two connecting edges double a = required_fit - offset_difference_mm / distfn(p1, p2); double b = required_fit - offset_difference_mm / distfn(p3, p2); current_fit = std::max(std::abs(a), std::abs(b)); - if(current_fit > prev_fit) { + if(current_fit > prev_fit) { // fit is worse than previously proceed = Proceed::LOWER; - } else { - ret.indices.emplace_back(uidx, lidx, unextidx); + } else { // good to go, create the triangle + inverted? ret.indices.emplace_back(unextidx, lidx, uidx) : + ret.indices.emplace_back(uidx, lidx, unextidx) ; - ++uit; ++unext; - if(unext == upper.points.end()) unext = upper.points.begin(); - if(uit == upper.points.end()) uit = upper.points.begin(); - unextidx = unext - upper.points.begin(); - uidx = uit - upper.points.begin(); + // Increment the iterators, rotate if necessary + ++uit; ++unextit; + if(unextit == upoints.end()) unextit = upoints.begin(); + if(uit == upoints.end()) uit = upoints.begin(); + unextidx = unextit - upoints.begin(); + uidx = uit - upoints.begin(); - ustarted = true; + ustarted = true; // mark the movement of the iterators + // so that the comparison to uend can be made correctly } } else proceed = Proceed::LOWER; break; case Proceed::LOWER: + // Mode with lower segment, upper vertex. Same structure: if(!lstarted || lit != lend) { - Vec3d p1(op.x(), op.y(), floor_z_mm); - Vec3d p2(onextp.x(), onextp.y(), floor_z_mm); - Vec3d p3(ip.x(), ip.y(), ceiling_z_mm); + Vec3d p1(op.x(), op.y(), lower_z_mm); + Vec3d p2(onextp.x(), onextp.y(), lower_z_mm); + Vec3d p3(ip.x(), ip.y(), upper_z_mm); double a = required_fit - offset_difference_mm / distfn(p3, p1); double b = required_fit - offset_difference_mm / distfn(p3, p2); @@ -141,77 +193,26 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, if(current_fit > prev_fit) { proceed = Proceed::UPPER; } else { - ret.indices.emplace_back(lidx, lnextidx, uidx); + inverted? ret.indices.emplace_back(uidx, lnextidx, lidx) : + ret.indices.emplace_back(lidx, lnextidx, uidx); - ++lit; ++lnext; - if(lnext == lower.points.end()) lnext = lower.points.begin(); - if(lit == lower.points.end()) lit = lower.points.begin(); - lnextidx = offs + lnext - lower.points.begin(); - lidx = offs + lit - lower.points.begin(); + ++lit; ++lnextit; + if(lnextit == lpoints.end()) lnextit = lpoints.begin(); + if(lit == lpoints.end()) lit = lpoints.begin(); + lnextidx = offs + lnextit - lpoints.begin(); + lidx = offs + lit - lpoints.begin(); lstarted = true; } } else proceed = Proceed::UPPER; break; - } // switch + } // end of switch } while(!ustarted || !lstarted || uit != uend || lit != lend); return ret; - -// using std::transform; using std::back_inserter; - -// ExPolygon poly; -// poly.contour.points = floor_plate.contour.points; -// poly.holes.emplace_back(ceiling.contour); -// auto& h = poly.holes.front(); -// std::reverse(h.points.begin(), h.points.end()); -// Polygons tri = triangulate(poly); - -// Contour3D ret; -// ret.points.reserve(tri.size() * 3); - -// double fz = floor_z_mm; -// double cz = ceiling_z_mm; -// auto& rp = ret.points; -// auto& rpi = ret.indices; -// ret.indices.reserve(tri.size() * 3); - -// coord_t idx = 0; - -// auto hlines = h.lines(); -// auto is_upper = [&hlines](const Point& p) { -// return std::any_of(hlines.begin(), hlines.end(), -// [&p](const Line& l) { -// return l.distance_to(p) < mm(1e-6); -// }); -// }; - -// for(const Polygon& pp : tri) { -// thr(); // may throw if cancellation was requested - -// for(auto& p : pp.points) -// if(is_upper(p)) -// rp.emplace_back(unscale(x(p), y(p), mm(cz))); -// else rp.emplace_back(unscale(x(p), y(p), mm(fz))); - -// coord_t a = idx++, b = idx++, c = idx++; -// if(fz > cz) rpi.emplace_back(c, b, a); -// else rpi.emplace_back(a, b, c); -// } - -// return ret; } - -Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, - double floor_z_mm, double ceiling_z_mm, ThrowOnCancel thr) -{ - return walls(floor_plate.contour, ceiling.contour, floor_z_mm, ceiling_z_mm, - 0, thr); -} - - /// Offsetting with clipper and smoothing the edges into a curvature. void offset(ExPolygon& sh, coord_t distance) { using ClipperLib::ClipperOffset; @@ -368,7 +369,7 @@ Contour3D round_edges(const ExPolygon& base_plate, // we use sin for x distance because we interpret the angle starting from // PI/2 int tos = degrees < 90? - int(radius_mm*std::cos(degrees * PI / 180 - PI/2) / stepx) : steps; + int(radius_mm*std::cos(degrees * PI / 180 - PI/2) / stepx) : steps; for(int i = 1; i <= tos; ++i) { throw_on_cancel(); @@ -384,7 +385,8 @@ Contour3D round_edges(const ExPolygon& base_plate, wh = ceilheight_mm - radius_mm + stepy; Contour3D pwalls; - pwalls = walls(ob.contour, ob_prev.contour, wh, wh_prev, xx, throw_on_cancel); + double prev_x = xx - (i - 1) * stepx; + pwalls = walls(ob.contour, ob_prev.contour, wh, wh_prev, s*prev_x, throw_on_cancel); curvedwalls.merge(pwalls); ob_prev = ob; @@ -407,7 +409,8 @@ Contour3D round_edges(const ExPolygon& base_plate, wh = ceilheight_mm - radius_mm - stepy; Contour3D pwalls; - pwalls = walls(ob_prev.contour, ob.contour, wh_prev, wh, xx, throw_on_cancel); + double prev_x = xx - radius_mm + (i - 1)*stepx; + pwalls = walls(ob_prev.contour, ob.contour, wh_prev, wh, s*prev_x, throw_on_cancel); curvedwalls.merge(pwalls); ob_prev = ob; @@ -700,7 +703,8 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, // Now that we have the rounded edge connencting the top plate with // the outer side walls, we can generate and merge the sidewall geometry - auto pwalls = walls(ob.contour, inner_base.contour, wh, -fullheight, (s_thickness + s_wingdist) * SCALING_FACTOR, thrcl); + auto pwalls = walls(ob.contour, inner_base.contour, wh, -fullheight, + (s_thickness + s_wingdist) * SCALING_FACTOR, thrcl); pool.merge(pwalls); if(wingheight > 0) { @@ -708,7 +712,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, auto cavityedges = round_edges(middle_base, r, phi - 90, // from tangent lines - 0, + 0, // z position of the input plane false, thrcl, ob, wh); @@ -716,13 +720,14 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, // Next is the cavity walls connecting to the top plate's // artificially created hole. - auto cavitywalls = walls(inner_base.contour, ob.contour, -wingheight, wh, s_safety_dist * SCALING_FACTOR,thrcl); + auto cavitywalls = walls(inner_base.contour, ob.contour, -wingheight, + wh, -s_safety_dist * SCALING_FACTOR, thrcl); pool.merge(cavitywalls); } // Now we need to triangulate the top and bottom plates as well as the // cavity bottom plate which is the same as the bottom plate but it is - // eleveted by the thickness. + // elevated by the thickness. Polygons top_triangles, bottom_triangles; triangulate(top_poly, top_triangles); From 40e6980db1a0f64af6bd83f869f67efbe4765603 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 14 Feb 2019 13:52:40 +0100 Subject: [PATCH 4/7] Fixing issue with sharp concave pad edges. --- src/libslic3r/SLA/SLABasePool.cpp | 52 +++++++++++++++++++------------ 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index a7cfa40f8b..7c28ad017e 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -5,8 +5,10 @@ #include "SLABoostAdapter.hpp" #include "ClipperUtils.hpp" +// For debugging: +//#include +//#include //#include "SVG.hpp" -//#include "benchmark.h" namespace Slic3r { namespace sla { @@ -120,17 +122,18 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, // previous. double current_fit = 0, prev_fit = 0; - // Simple distance calculation. + // Simple squared distance calculation. auto distfn = [](const Vec3d& p1, const Vec3d& p2) { - auto p = p1 - p2; return std::sqrt(p.transpose() * p); + auto p = p1 - p2; return p.transpose() * p; }; - // Calculate the reference fitness value. It will be the sine of the angle - // from the upper to the lower plate according to the triangle noted by the - // Z difference and the offset difference. - const double required_fit = offset_difference_mm / - std::sqrt( std::pow(offset_difference_mm, 2) + - std::pow(upper_z_mm - lower_z_mm, 2)); + // Every triangle of the wall has two edges connecting the upper plate with + // the lower plate. From the length of these two edges and the zdiff we + // can calculate the momentary squared offset distance at a particular + // position on the wall. The average of the differences from the reference + // (squared) offset distance will give us the driving fitness value. + const double offsdiff2 = std::pow(offset_difference_mm, 2); + const double zdiff2 = std::pow(upper_z_mm - lower_z_mm, 2); // Mark the current vertex iterator positions. If the iterators return to // the same position, the loop can be terminated. @@ -155,10 +158,10 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, Vec3d p2(op.x(), op.y(), lower_z_mm); Vec3d p3(inextp.x(), inextp.y(), upper_z_mm); - // Calculate fitness: the worst of the two connecting edges - double a = required_fit - offset_difference_mm / distfn(p1, p2); - double b = required_fit - offset_difference_mm / distfn(p3, p2); - current_fit = std::max(std::abs(a), std::abs(b)); + // Calculate fitness: the average of the two connecting edges + double a = offsdiff2 - (distfn(p1, p2) - zdiff2); + double b = offsdiff2 - (distfn(p3, p2) - zdiff2); + current_fit = (std::abs(a) + std::abs(b)) / 2; if(current_fit > prev_fit) { // fit is worse than previously proceed = Proceed::LOWER; @@ -186,9 +189,9 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, Vec3d p2(onextp.x(), onextp.y(), lower_z_mm); Vec3d p3(ip.x(), ip.y(), upper_z_mm); - double a = required_fit - offset_difference_mm / distfn(p3, p1); - double b = required_fit - offset_difference_mm / distfn(p3, p2); - current_fit = std::max(std::abs(a), std::abs(b)); + double a = offsdiff2 - (distfn(p3, p1) - zdiff2); + double b = offsdiff2 - (distfn(p3, p2) - zdiff2); + current_fit = (std::abs(a) + std::abs(b)) / 2; if(current_fit > prev_fit) { proceed = Proceed::UPPER; @@ -604,6 +607,9 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, const PoolConfig& cfg) { + // for debugging: + // Benchmark bench; + // bench.start(); double mergedist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm)+ cfg.max_merge_distance_mm; @@ -630,6 +636,8 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, auto& thrcl = cfg.throw_on_cancel; + Contour3D pool; + for(ExPolygon& concaveh : concavehs) { if(concaveh.contour.points.empty()) return; @@ -659,8 +667,6 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, std::reverse(tph.begin(), tph.end()); } - Contour3D pool; - ExPolygon ob = outer_base; double wh = 0; // now we will calculate the angle or portion of the circle from @@ -745,9 +751,15 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, auto middle_plate = convert(middle_triangles, -mm(wingheight), false); pool.merge(middle_plate); } - - out.merge(mesh(pool)); } + + // For debugging: + // bench.stop(); + // std::cout << "Pad creation time: " << bench.getElapsedSec() << std::endl; + // std::fstream fout("pad_debug.obj", std::fstream::out); + // if(fout.good()) pool.to_obj(fout); + + out.merge(mesh(pool)); } } From 9bd2f0cf539fd8ced06fd7c2fc9524efddf8561f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 14 Feb 2019 14:49:29 +0100 Subject: [PATCH 5/7] Simplifying pad wall triangulation. Removing iterators. --- src/libslic3r/SLA/SLABasePool.cpp | 118 +++++++++++++----------------- 1 file changed, 50 insertions(+), 68 deletions(-) diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index 7c28ad017e..b2df49a0ad 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -66,47 +66,44 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, // height and the offset difference. // Offset in the index array for the ceiling - const auto offs = long(upper.points.size()); + const auto offs = upper.points.size(); // Shorthand for the vertex arrays auto& upoints = upper.points, &lpoints = lower.points; + auto& rpts = ret.points; auto& rfaces = ret.indices; // If the Z levels are flipped, or the offset difference is negative, we // will interpret that as the triangles normals should be inverted. bool inverted = upper_z_mm < lower_z_mm || offset_difference_mm < 0; // Copy the points into the mesh, convert them from 2D to 3D - ret.points.reserve(upoints.size() + lpoints.size()); - for(auto& p : upoints) - ret.points.emplace_back(unscale(p.x(), p.y(), mm(upper_z_mm))); - for(auto& p : lpoints) - ret.points.emplace_back(unscale(p.x(), p.y(), mm(lower_z_mm))); + rpts.reserve(upoints.size() + lpoints.size()); + rfaces.reserve(2*upoints.size() + 2*lpoints.size()); + auto s_uz = mm(upper_z_mm), s_lz = mm(lower_z_mm); + for(auto& p : upoints) rpts.emplace_back(unscale(p.x(), p.y(), s_uz)); + for(auto& p : lpoints) rpts.emplace_back(unscale(p.x(), p.y(), s_lz)); - // Create cyclic iterators for the vertices in both polygons. - auto uit = upoints.begin(); - auto lit = lpoints.begin(); + // Create pointing indices into vertex arrays. u-upper, l-lower + size_t uidx = 0, lidx = offs, unextidx = 1, lnextidx = offs + 1; + + // Simple squared distance calculation. + auto distfn = [](const Vec3d& p1, const Vec3d& p2) { + auto p = p1 - p2; return p.transpose() * p; + }; // We need to find the closest point on lower polygon to the first point on // the upper polygon. These will be our starting points. double distmin = std::numeric_limits::max(); - for(auto lt = lpoints.begin(); lt != lpoints.end(); ++lt) { + for(size_t l = lidx; l < rpts.size(); ++l) { thr(); - Vec2d p = (*lt - *uit).cast(); - double d = p.transpose() * p; - if(d < distmin) { lit = lt; distmin = d; } + double d = distfn(rpts[l], rpts[uidx]); + if(d < distmin) { lidx = l; distmin = d; } } // Iterators to the polygon vertices which are always ahead of uit and lit // in cyclic mode. - auto unextit = std::next(uit); - auto lnextit = std::next(lit); - if(lnextit == lower.points.end()) lnextit = lower.points.begin(); - - // Get the integer vertex indices from the iterators. - auto uidx = uit - upper.points.begin(); - auto unextidx = unextit - upper.points.begin(); - auto lidx = offs + lit - lower.points.begin(); - auto lnextidx = offs + lnextit - lower.points.begin(); + lnextidx = lidx + 1; + if(lnextidx == rpts.size()) lnextidx = offs; // This will be the flip switch to toggle between upper and lower triangle // creation mode @@ -122,11 +119,6 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, // previous. double current_fit = 0, prev_fit = 0; - // Simple squared distance calculation. - auto distfn = [](const Vec3d& p1, const Vec3d& p2) { - auto p = p1 - p2; return p.transpose() * p; - }; - // Every triangle of the wall has two edges connecting the upper plate with // the lower plate. From the length of these two edges and the zdiff we // can calculate the momentary squared offset distance at a particular @@ -137,44 +129,35 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, // Mark the current vertex iterator positions. If the iterators return to // the same position, the loop can be terminated. - auto uend = uit; auto lend = lit; + size_t uendidx = uidx, lendidx = lidx; - do { - thr(); + do { thr(); // check throw if canceled prev_fit = current_fit; - // Get the actual 2D vertices from the upper and lower polygon. - Vec2d ip = unscale(uit->x(), uit->y()); - Vec2d inextp = unscale(unextit->x(), unextit->y()); - Vec2d op = unscale(lit->x(), lit->y()); - Vec2d onextp = unscale(lnextit->x(), lnextit->y()); - switch(proceed) { // proceed depending on the current state case Proceed::UPPER: - if(!ustarted || uit != uend) { // if there are vertices remaining + if(!ustarted || uidx != uendidx) { // there are vertices remaining // Get the 3D vertices in order - Vec3d p1(ip.x(), ip.y(), upper_z_mm); - Vec3d p2(op.x(), op.y(), lower_z_mm); - Vec3d p3(inextp.x(), inextp.y(), upper_z_mm); + const Vec3d& p_up1 = rpts[size_t(uidx)]; + const Vec3d& p_low = rpts[size_t(lidx)]; + const Vec3d& p_up2 = rpts[size_t(unextidx)]; // Calculate fitness: the average of the two connecting edges - double a = offsdiff2 - (distfn(p1, p2) - zdiff2); - double b = offsdiff2 - (distfn(p3, p2) - zdiff2); + double a = offsdiff2 - (distfn(p_up1, p_low) - zdiff2); + double b = offsdiff2 - (distfn(p_up2, p_low) - zdiff2); current_fit = (std::abs(a) + std::abs(b)) / 2; if(current_fit > prev_fit) { // fit is worse than previously proceed = Proceed::LOWER; } else { // good to go, create the triangle - inverted? ret.indices.emplace_back(unextidx, lidx, uidx) : - ret.indices.emplace_back(uidx, lidx, unextidx) ; + inverted? rfaces.emplace_back(unextidx, lidx, uidx) : + rfaces.emplace_back(uidx, lidx, unextidx) ; // Increment the iterators, rotate if necessary - ++uit; ++unextit; - if(unextit == upoints.end()) unextit = upoints.begin(); - if(uit == upoints.end()) uit = upoints.begin(); - unextidx = unextit - upoints.begin(); - uidx = uit - upoints.begin(); + ++uidx; ++unextidx; + if(unextidx == offs) unextidx = 0; + if(uidx == offs) uidx = 0; ustarted = true; // mark the movement of the iterators // so that the comparison to uend can be made correctly @@ -184,26 +167,24 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, break; case Proceed::LOWER: // Mode with lower segment, upper vertex. Same structure: - if(!lstarted || lit != lend) { - Vec3d p1(op.x(), op.y(), lower_z_mm); - Vec3d p2(onextp.x(), onextp.y(), lower_z_mm); - Vec3d p3(ip.x(), ip.y(), upper_z_mm); + if(!lstarted || lidx != lendidx) { + const Vec3d& p_low1 = rpts[size_t(lidx)]; + const Vec3d& p_low2 = rpts[size_t(lnextidx)]; + const Vec3d& p_up = rpts[size_t(uidx)]; - double a = offsdiff2 - (distfn(p3, p1) - zdiff2); - double b = offsdiff2 - (distfn(p3, p2) - zdiff2); + double a = offsdiff2 - (distfn(p_up, p_low1) - zdiff2); + double b = offsdiff2 - (distfn(p_up, p_low2) - zdiff2); current_fit = (std::abs(a) + std::abs(b)) / 2; if(current_fit > prev_fit) { proceed = Proceed::UPPER; } else { - inverted? ret.indices.emplace_back(uidx, lnextidx, lidx) : - ret.indices.emplace_back(lidx, lnextidx, uidx); + inverted? rfaces.emplace_back(uidx, lnextidx, lidx) : + rfaces.emplace_back(lidx, lnextidx, uidx); - ++lit; ++lnextit; - if(lnextit == lpoints.end()) lnextit = lpoints.begin(); - if(lit == lpoints.end()) lit = lpoints.begin(); - lnextidx = offs + lnextit - lpoints.begin(); - lidx = offs + lit - lpoints.begin(); + ++lidx; ++lnextidx; + if(lnextidx == rpts.size()) lnextidx = offs; + if(lidx == rpts.size()) lidx = offs; lstarted = true; } @@ -211,7 +192,7 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, break; } // end of switch - } while(!ustarted || !lstarted || uit != uend || lit != lend); + } while(!ustarted || !lstarted || uidx != uendidx || lidx != lendidx); return ret; } @@ -356,7 +337,7 @@ Contour3D round_edges(const ExPolygon& base_plate, double degrees, double ceilheight_mm, bool dir, - ThrowOnCancel throw_on_cancel, + ThrowOnCancel thr, ExPolygon& last_offset, double& last_height) { auto ob = base_plate; @@ -375,7 +356,7 @@ Contour3D round_edges(const ExPolygon& base_plate, int(radius_mm*std::cos(degrees * PI / 180 - PI/2) / stepx) : steps; for(int i = 1; i <= tos; ++i) { - throw_on_cancel(); + thr(); ob = base_plate; @@ -389,7 +370,7 @@ Contour3D round_edges(const ExPolygon& base_plate, Contour3D pwalls; double prev_x = xx - (i - 1) * stepx; - pwalls = walls(ob.contour, ob_prev.contour, wh, wh_prev, s*prev_x, throw_on_cancel); + pwalls = walls(ob.contour, ob_prev.contour, wh, wh_prev, s*prev_x, thr); curvedwalls.merge(pwalls); ob_prev = ob; @@ -401,7 +382,7 @@ Contour3D round_edges(const ExPolygon& base_plate, int tos = int(tox / stepx); for(int i = 1; i <= tos; ++i) { - throw_on_cancel(); + thr(); ob = base_plate; double r2 = radius_mm * radius_mm; @@ -413,7 +394,8 @@ Contour3D round_edges(const ExPolygon& base_plate, Contour3D pwalls; double prev_x = xx - radius_mm + (i - 1)*stepx; - pwalls = walls(ob_prev.contour, ob.contour, wh_prev, wh, s*prev_x, throw_on_cancel); + pwalls = + walls(ob_prev.contour, ob.contour, wh_prev, wh, s*prev_x, thr); curvedwalls.merge(pwalls); ob_prev = ob; From 3574fa00af70a82671f46858617f15aab5a91d1c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 14 Feb 2019 16:04:34 +0100 Subject: [PATCH 6/7] Incorporate new tessellation into pad creation. --- src/libslic3r/SLA/SLABasePool.cpp | 68 ++++++++++++---------------- src/libslic3r/SLA/SLABoilerPlate.hpp | 21 ++++----- 2 files changed, 37 insertions(+), 52 deletions(-) diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index b2df49a0ad..a3240a4a79 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -4,6 +4,7 @@ #include "boost/log/trivial.hpp" #include "SLABoostAdapter.hpp" #include "ClipperUtils.hpp" +#include "Tesselate.hpp" // For debugging: //#include @@ -12,26 +13,6 @@ namespace Slic3r { namespace sla { -/// Convert the triangulation output to an intermediate mesh. -Contour3D convert(const Polygons& triangles, coord_t z, bool dir) { - - Pointf3s points; - points.reserve(3*triangles.size()); - Indices indices; - indices.reserve(points.size()); - - for(auto& tr : triangles) { - auto c = coord_t(points.size()), b = c++, a = c++; - if(dir) indices.emplace_back(a, b, c); - else indices.emplace_back(c, b, a); - for(auto& p : tr.points) { - points.emplace_back(unscale(x(p), y(p), z)); - } - } - - return {points, indices}; -} - /// This function will return a triangulation of a sheet connecting an upper /// and a lower plate given as input polygons. It will not triangulate the /// plates themselves only the sheet. The caller has to specify the lower and @@ -324,12 +305,11 @@ ExPolygons unify(const ExPolygons& shapes) { /// Only a debug function to generate top and bottom plates from a 2D shape. /// It is not used in the algorithm directly. inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) { - Polygons triangles = triangulate(poly); - - auto lower = convert(triangles, 0, false); - auto upper = convert(triangles, z_distance, true); - lower.merge(upper); - return lower; + auto lower = triangulate_expolygons_3df(poly); + auto upper = triangulate_expolygons_3df(poly, z_distance*SCALING_FACTOR, true); + Contour3D ret; + ret.merge(lower); ret.merge(upper); + return ret; } Contour3D round_edges(const ExPolygon& base_plate, @@ -411,15 +391,18 @@ Contour3D round_edges(const ExPolygon& base_plate, /// Generating the concave part of the 3D pool with the bottom plate and the /// side walls. -Contour3D inner_bed(const ExPolygon& poly, double depth_mm, - double begin_h_mm = 0) { - - Polygons triangles = triangulate(poly); +Contour3D inner_bed(const ExPolygon& poly, + double depth_mm, + double begin_h_mm = 0) +{ + Contour3D bottom; + Pointf3s triangles = triangulate_expolygons_3df(poly, + -depth_mm + begin_h_mm); + bottom.merge(triangles); coord_t depth = mm(depth_mm); coord_t begin_h = mm(begin_h_mm); - auto bottom = convert(triangles, -depth + begin_h, false); auto lines = poly.lines(); // Generate outer walls @@ -716,22 +699,27 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, // Now we need to triangulate the top and bottom plates as well as the // cavity bottom plate which is the same as the bottom plate but it is // elevated by the thickness. - Polygons top_triangles, bottom_triangles; +// Polygons top_triangles, bottom_triangles; - triangulate(top_poly, top_triangles); - triangulate(inner_base, bottom_triangles); +// triangulate(top_poly, top_triangles); +// triangulate(inner_base, bottom_triangles); - auto top_plate = convert(top_triangles, 0, false); - auto bottom_plate = convert(bottom_triangles, -mm(fullheight), true); +// auto top_plate = convert(top_triangles, 0, false); +// auto bottom_plate = convert(bottom_triangles, -mm(fullheight), true); + + Pointf3s top_plate = triangulate_expolygons_3df(top_poly); + Pointf3s bottom_plate = triangulate_expolygons_3df(inner_base, -fullheight, true); pool.merge(top_plate); pool.merge(bottom_plate); if(wingheight > 0) { - Polygons middle_triangles; - triangulate(inner_base, middle_triangles); - auto middle_plate = convert(middle_triangles, -mm(wingheight), false); - pool.merge(middle_plate); +// Polygons middle_triangles; + +// triangulate(inner_base, middle_triangles); +// auto middle_plate = convert(middle_triangles, -mm(wingheight), false); + Pointf3s middle_triangles = triangulate_expolygons_3df(inner_base, -wingheight); + pool.merge(middle_triangles); } } diff --git a/src/libslic3r/SLA/SLABoilerPlate.hpp b/src/libslic3r/SLA/SLABoilerPlate.hpp index c1096206a2..dbe35eddb7 100644 --- a/src/libslic3r/SLA/SLABoilerPlate.hpp +++ b/src/libslic3r/SLA/SLABoilerPlate.hpp @@ -36,14 +36,6 @@ inline coord_t x(const Vec3crd& p) { return p(0); } inline coord_t y(const Vec3crd& p) { return p(1); } inline coord_t z(const Vec3crd& p) { return p(2); } -inline void triangulate(const ExPolygon& expoly, Polygons& triangles) { - expoly.triangulate_p2t(&triangles); -} - -inline Polygons triangulate(const ExPolygon& expoly) { - Polygons tri; triangulate(expoly, tri); return tri; -} - using Indices = std::vector; /// Intermediate struct for a 3D mesh @@ -63,6 +55,15 @@ struct Contour3D { } } + void merge(const Pointf3s& triangles) { + const size_t offs = points.size(); + points.insert(points.end(), triangles.begin(), triangles.end()); + indices.reserve(indices.size() + points.size() / 3); + + for(size_t i = offs; i < points.size(); i += 3) + indices.emplace_back(i, i + 1, i + 2); + } + // Write the index triangle structure to OBJ file for debugging purposes. void to_obj(std::ostream& stream) { for(auto& p : points) { @@ -75,13 +76,9 @@ struct Contour3D { } }; -//using PointSet = Eigen::Matrix; //Eigen::MatrixXd; using ClusterEl = std::vector; using ClusteredPoints = std::vector; -/// Convert the triangulation output to an intermediate mesh. -Contour3D convert(const Polygons& triangles, coord_t z, bool dir); - /// Mesh from an existing contour. inline TriangleMesh mesh(const Contour3D& ctour) { return {ctour.points, ctour.indices}; From 71480d7c5307fdd47ad6abc59cd19a88babc8c62 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 15 Feb 2019 10:09:59 +0100 Subject: [PATCH 7/7] Further refactoring --- src/libslic3r/SLA/SLABasePool.cpp | 98 ++++++++++++++----------------- 1 file changed, 45 insertions(+), 53 deletions(-) diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index a3240a4a79..80634adb46 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -17,7 +17,9 @@ namespace Slic3r { namespace sla { /// and a lower plate given as input polygons. It will not triangulate the /// plates themselves only the sheet. The caller has to specify the lower and /// upper z levels in world coordinates as well as the offset difference -/// between the sheets. +/// between the sheets. If the lower_z_mm is higher than upper_z_mm or the +/// offset difference is negative, the resulting triangle orientation will be +/// reversed. /// /// IMPORTANT: This is not a universal triangulation algorithm. It assumes /// that the lower and upper polygons are offsetted versions of the same @@ -60,9 +62,9 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, // Copy the points into the mesh, convert them from 2D to 3D rpts.reserve(upoints.size() + lpoints.size()); rfaces.reserve(2*upoints.size() + 2*lpoints.size()); - auto s_uz = mm(upper_z_mm), s_lz = mm(lower_z_mm); - for(auto& p : upoints) rpts.emplace_back(unscale(p.x(), p.y(), s_uz)); - for(auto& p : lpoints) rpts.emplace_back(unscale(p.x(), p.y(), s_lz)); + const double sf = SCALING_FACTOR; + for(auto& p : upoints) rpts.emplace_back(p.x()*sf, p.y()*sf, upper_z_mm); + for(auto& p : lpoints) rpts.emplace_back(p.x()*sf, p.y()*sf, lower_z_mm); // Create pointing indices into vertex arrays. u-upper, l-lower size_t uidx = 0, lidx = offs, unextidx = 1, lnextidx = offs + 1; @@ -81,8 +83,7 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, if(d < distmin) { lidx = l; distmin = d; } } - // Iterators to the polygon vertices which are always ahead of uit and lit - // in cyclic mode. + // Set up lnextidx to be ahead of lidx in cyclic mode lnextidx = lidx + 1; if(lnextidx == rpts.size()) lnextidx = offs; @@ -141,7 +142,7 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, if(uidx == offs) uidx = 0; ustarted = true; // mark the movement of the iterators - // so that the comparison to uend can be made correctly + // so that the comparison to uendidx can be made correctly } } else proceed = Proceed::LOWER; @@ -312,6 +313,18 @@ inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) { return ret; } +/// This method will create a rounded edge around a flat polygon in 3d space. +/// 'base_plate' parameter is the target plate. +/// 'radius' is the radius of the edges. +/// 'degrees' is tells how much of a circle should be created as the rounding. +/// It should be in degrees, not radians. +/// 'ceilheight_mm' is the Z coordinate of the flat polygon in 3D space. +/// 'dir' Is the direction of the round edges: inward or outward +/// 'thr' Throws if a cancel signal was received +/// 'last_offset' An auxiliary output variable to save the last offsetted +/// version of 'base_plate' +/// 'last_height' An auxiliary output to save the last z coordinate of the +/// offsetted base_plate. In other words, where the rounded edges end. Contour3D round_edges(const ExPolygon& base_plate, double radius_mm, double degrees, @@ -589,15 +602,14 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, const double thickness = cfg.min_wall_thickness_mm; const double wingheight = cfg.min_wall_height_mm; const double fullheight = wingheight + thickness; - const double tilt = PI/4; + const double tilt = PI/4; const double wingdist = wingheight / std::tan(tilt); // scaled values const coord_t s_thickness = mm(thickness); const coord_t s_eradius = mm(cfg.edge_radius_mm); const coord_t s_safety_dist = 2*s_eradius + coord_t(0.8*s_thickness); - // const coord_t wheight = mm(cfg.min_wall_height_mm); - coord_t s_wingdist = mm(wingdist); + const coord_t s_wingdist = mm(wingdist); auto& thrcl = cfg.throw_on_cancel; @@ -606,7 +618,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, for(ExPolygon& concaveh : concavehs) { if(concaveh.contour.points.empty()) return; - // Get rif of any holes in the concave hull output. + // Get rid of any holes in the concave hull output. concaveh.holes.clear(); // Here lies the trick that does the smooting only with clipper offset @@ -663,64 +675,44 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, // Generate the smoothed edge geometry - auto walledges = round_edges(ob, - r, - phi, - 0, // z position of the input plane - true, - thrcl, - ob, wh); - pool.merge(walledges); + pool.merge(round_edges(ob, + r, + phi, + 0, // z position of the input plane + true, + thrcl, + ob, wh)); // Now that we have the rounded edge connencting the top plate with // the outer side walls, we can generate and merge the sidewall geometry - auto pwalls = walls(ob.contour, inner_base.contour, wh, -fullheight, - (s_thickness + s_wingdist) * SCALING_FACTOR, thrcl); - pool.merge(pwalls); + pool.merge(walls(ob.contour, inner_base.contour, wh, -fullheight, + (s_thickness + s_wingdist) * SCALING_FACTOR, thrcl)); if(wingheight > 0) { // Generate the smoothed edge geometry - auto cavityedges = round_edges(middle_base, - r, - phi - 90, // from tangent lines - 0, // z position of the input plane - false, - thrcl, - ob, wh); - pool.merge(cavityedges); + pool.merge(round_edges(middle_base, + r, + phi - 90, // from tangent lines + 0, // z position of the input plane + false, + thrcl, + ob, wh)); // Next is the cavity walls connecting to the top plate's // artificially created hole. - auto cavitywalls = walls(inner_base.contour, ob.contour, -wingheight, - wh, -s_safety_dist * SCALING_FACTOR, thrcl); - pool.merge(cavitywalls); + pool.merge(walls(inner_base.contour, ob.contour, -wingheight, + wh, -s_safety_dist * SCALING_FACTOR, thrcl)); } // Now we need to triangulate the top and bottom plates as well as the // cavity bottom plate which is the same as the bottom plate but it is // elevated by the thickness. -// Polygons top_triangles, bottom_triangles; + pool.merge(triangulate_expolygons_3df(top_poly)); + pool.merge(triangulate_expolygons_3df(inner_base, -fullheight, true)); -// triangulate(top_poly, top_triangles); -// triangulate(inner_base, bottom_triangles); + if(wingheight > 0) + pool.merge(triangulate_expolygons_3df(inner_base, -wingheight)); -// auto top_plate = convert(top_triangles, 0, false); -// auto bottom_plate = convert(bottom_triangles, -mm(fullheight), true); - - Pointf3s top_plate = triangulate_expolygons_3df(top_poly); - Pointf3s bottom_plate = triangulate_expolygons_3df(inner_base, -fullheight, true); - - pool.merge(top_plate); - pool.merge(bottom_plate); - - if(wingheight > 0) { -// Polygons middle_triangles; - -// triangulate(inner_base, middle_triangles); -// auto middle_plate = convert(middle_triangles, -mm(wingheight), false); - Pointf3s middle_triangles = triangulate_expolygons_3df(inner_base, -wingheight); - pool.merge(middle_triangles); - } } // For debugging: