mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 12:41:20 -06:00 
			
		
		
		
	Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_reload_from_disk
This commit is contained in:
		
						commit
						4a45c1aa41
					
				
					 8 changed files with 101 additions and 124 deletions
				
			
		|  | @ -81,17 +81,16 @@ inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance, const PolygonTag | |||
|     using ClipperLib::etClosedPolygon; | ||||
|     using ClipperLib::Paths; | ||||
| 
 | ||||
|     // If the input is not at least a triangle, we can not do this algorithm
 | ||||
|     if(sh.Contour.size() <= 3 || | ||||
|        std::any_of(sh.Holes.begin(), sh.Holes.end(), | ||||
|                    [](const PathImpl& p) { return p.size() <= 3; }) | ||||
|        ) throw GeometryException(GeomErr::OFFSET); | ||||
| 
 | ||||
|     ClipperOffset offs; | ||||
|     Paths result; | ||||
|     offs.AddPath(sh.Contour, jtMiter, etClosedPolygon); | ||||
|     offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon); | ||||
|     offs.Execute(result, static_cast<double>(distance)); | ||||
|      | ||||
|     try { | ||||
|         ClipperOffset offs; | ||||
|         offs.AddPath(sh.Contour, jtMiter, etClosedPolygon); | ||||
|         offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon); | ||||
|         offs.Execute(result, static_cast<double>(distance)); | ||||
|     } catch (ClipperLib::clipperException &) { | ||||
|         throw GeometryException(GeomErr::OFFSET); | ||||
|     } | ||||
| 
 | ||||
|     // Offsetting reverts the orientation and also removes the last vertex
 | ||||
|     // so boost will not have a closed polygon.
 | ||||
|  |  | |||
|  | @ -1144,7 +1144,7 @@ inline bool isInside(const TBGuest& ibb, const TBHost& box, | |||
|     auto minY = getY(box.minCorner()); | ||||
|     auto maxY = getY(box.maxCorner()); | ||||
| 
 | ||||
|     return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY; | ||||
|     return iminX >= minX && imaxX <= maxX && iminY >= minY && imaxY <= maxY; | ||||
| } | ||||
| 
 | ||||
| template<class S, class TB> | ||||
|  |  | |||
|  | @ -3,9 +3,6 @@ | |||
| 
 | ||||
| #include <cassert> | ||||
| 
 | ||||
| // For caching nfps
 | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| // For parallel for
 | ||||
| #include <functional> | ||||
| #include <iterator> | ||||
|  | @ -76,55 +73,6 @@ inline void enumerate( | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| namespace __itemhash { | ||||
| 
 | ||||
| using Key = size_t; | ||||
| 
 | ||||
| template<class S> | ||||
| Key hash(const _Item<S>& item) { | ||||
|     using Point = TPoint<S>; | ||||
|     using Segment = _Segment<Point>; | ||||
| 
 | ||||
|     static const int N = 26; | ||||
|     static const int M = N*N - 1; | ||||
| 
 | ||||
|     std::string ret; | ||||
|     auto& rhs = item.rawShape(); | ||||
|     auto& ctr = sl::contour(rhs); | ||||
|     auto it = ctr.begin(); | ||||
|     auto nx = std::next(it); | ||||
| 
 | ||||
|     double circ = 0; | ||||
|     while(nx != ctr.end()) { | ||||
|         Segment seg(*it++, *nx++); | ||||
|         Radians a = seg.angleToXaxis(); | ||||
|         double deg = Degrees(a); | ||||
|         int ms = 'A', ls = 'A'; | ||||
|         while(deg > N) { ms++; deg -= N; } | ||||
|         ls += int(deg); | ||||
|         ret.push_back(char(ms)); ret.push_back(char(ls)); | ||||
|         circ += std::sqrt(seg.template sqlength<double>()); | ||||
|     } | ||||
| 
 | ||||
|     it = ctr.begin(); nx = std::next(it); | ||||
| 
 | ||||
|     while(nx != ctr.end()) { | ||||
|         Segment seg(*it++, *nx++); | ||||
|         auto l = int(M * std::sqrt(seg.template sqlength<double>()) / circ); | ||||
|         int ms = 'A', ls = 'A'; | ||||
|         while(l > N) { ms++; l -= N; } | ||||
|         ls += l; | ||||
|         ret.push_back(char(ms)); ret.push_back(char(ls)); | ||||
|     } | ||||
| 
 | ||||
|     return std::hash<std::string>()(ret); | ||||
| } | ||||
| 
 | ||||
| template<class S> | ||||
| using Hash = std::unordered_map<Key, nfp::NfpResult<S>>; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| namespace placers { | ||||
| 
 | ||||
| template<class RawShape> | ||||
|  | @ -529,17 +477,9 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin | |||
| 
 | ||||
|     using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>; | ||||
| 
 | ||||
|     using ItemKeys = std::vector<__itemhash::Key>; | ||||
| 
 | ||||
|     // Norming factor for the optimization function
 | ||||
|     const double norm_; | ||||
| 
 | ||||
|     // Caching calculated nfps
 | ||||
|     __itemhash::Hash<RawShape> nfpcache_; | ||||
| 
 | ||||
|     // Storing item hash keys
 | ||||
|     ItemKeys item_keys_; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     using Pile = nfp::Shapes<RawShape>; | ||||
|  | @ -636,15 +576,12 @@ public: | |||
| private: | ||||
| 
 | ||||
|     using Shapes = TMultiShape<RawShape>; | ||||
|     using ItemRef = std::reference_wrapper<Item>; | ||||
|     using ItemWithHash = const std::pair<ItemRef, __itemhash::Key>; | ||||
| 
 | ||||
|     Shapes calcnfp(const ItemWithHash itsh, Lvl<nfp::NfpLevel::CONVEX_ONLY>) | ||||
|     Shapes calcnfp(const Item &trsh, Lvl<nfp::NfpLevel::CONVEX_ONLY>) | ||||
|     { | ||||
|         using namespace nfp; | ||||
| 
 | ||||
|         Shapes nfps(items_.size()); | ||||
|         const Item& trsh = itsh.first; | ||||
| 
 | ||||
|         // /////////////////////////////////////////////////////////////////////
 | ||||
|         // TODO: this is a workaround and should be solved in Item with mutexes
 | ||||
|  | @ -678,12 +615,11 @@ private: | |||
| 
 | ||||
| 
 | ||||
|     template<class Level> | ||||
|     Shapes calcnfp( const ItemWithHash itsh, Level) | ||||
|     Shapes calcnfp(const Item &trsh, Level) | ||||
|     { // Function for arbitrary level of nfp implementation
 | ||||
|         using namespace nfp; | ||||
| 
 | ||||
|         Shapes nfps; | ||||
|         const Item& trsh = itsh.first; | ||||
| 
 | ||||
|         auto& orb = trsh.transformedShape(); | ||||
|         bool orbconvex = trsh.isContourConvex(); | ||||
|  | @ -849,8 +785,6 @@ private: | |||
|             remlist.insert(remlist.end(), remaining.from, remaining.to); | ||||
|         } | ||||
| 
 | ||||
|         size_t itemhash = __itemhash::hash(item); | ||||
| 
 | ||||
|         if(items_.empty()) { | ||||
|             setInitialPosition(item); | ||||
|             best_overfit = overfit(item.transformedShape(), bin_); | ||||
|  | @ -875,7 +809,7 @@ private: | |||
|                 // it is disjunct from the current merged pile
 | ||||
|                 placeOutsideOfBin(item); | ||||
| 
 | ||||
|                 nfps = calcnfp({item, itemhash}, Lvl<MaxNfpLevel::value>()); | ||||
|                 nfps = calcnfp(item, Lvl<MaxNfpLevel::value>()); | ||||
| 
 | ||||
|                 auto iv = item.referenceVertex(); | ||||
| 
 | ||||
|  | @ -1112,7 +1046,6 @@ private: | |||
| 
 | ||||
|         if(can_pack) { | ||||
|             ret = PackResult(item); | ||||
|             item_keys_.emplace_back(itemhash); | ||||
|         } else { | ||||
|             ret = PackResult(best_overfit); | ||||
|         } | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ protected: | |||
|              | ||||
|             Placer p{bin}; | ||||
|             p.configure(pcfg); | ||||
|             if (!p.pack(cpy)) it = c.erase(it); | ||||
|             if (itm.area() <= 0 || !p.pack(cpy)) it = c.erase(it); | ||||
|             else it++; | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ struct NfpImpl<S, NfpLevel::CONVEX_ONLY> | |||
| } | ||||
| } | ||||
| 
 | ||||
| std::vector<libnest2d::Item>& prusaParts() { | ||||
| static std::vector<libnest2d::Item>& prusaParts() { | ||||
|     static std::vector<libnest2d::Item> ret; | ||||
|      | ||||
|     if(ret.empty()) { | ||||
|  | @ -51,7 +51,7 @@ std::vector<libnest2d::Item>& prusaParts() { | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| TEST(BasicFunctionality, Angles) | ||||
| TEST(GeometryAlgorithms, Angles) | ||||
| { | ||||
|      | ||||
|     using namespace libnest2d; | ||||
|  | @ -109,7 +109,7 @@ TEST(BasicFunctionality, Angles) | |||
| } | ||||
| 
 | ||||
| // Simple test, does not use gmock
 | ||||
| TEST(BasicFunctionality, creationAndDestruction) | ||||
| TEST(Nesting, ItemCreationAndDestruction) | ||||
| { | ||||
|     using namespace libnest2d; | ||||
|      | ||||
|  | @ -572,26 +572,74 @@ TEST(GeometryAlgorithms, convexHull) { | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| TEST(GeometryAlgorithms, NestTest) { | ||||
| TEST(Nesting, NestPrusaPartsShouldFitIntoTwoBins) { | ||||
|      | ||||
|     // Get the input items and define the bin.
 | ||||
|     std::vector<Item> input = prusaParts(); | ||||
| 
 | ||||
|     libnest2d::nest(input, Box(250000000, 210000000), [](unsigned cnt) { | ||||
|         std::cout << "parts left: " << cnt << std::endl; | ||||
|     auto bin = Box(250000000, 210000000); | ||||
|      | ||||
|     // Do the nesting. Check in each step if the remaining items are less than
 | ||||
|     // in the previous step. (Some algorithms can place more items in one step)
 | ||||
|     size_t pcount = input.size(); | ||||
|     libnest2d::nest(input, bin, [&pcount](unsigned cnt) { | ||||
|         ASSERT_TRUE(cnt < pcount); | ||||
|         pcount = cnt; | ||||
|     }); | ||||
| 
 | ||||
|      | ||||
|     // Get the number of logical bins: search for the max binId...
 | ||||
|     auto max_binid_it = std::max_element(input.begin(), input.end(), | ||||
|                                          [](const Item &i1, const Item &i2) { | ||||
|                                              return i1.binId() < i2.binId(); | ||||
|                                          }); | ||||
| 
 | ||||
|     size_t bins = max_binid_it == input.end() ? 0 : max_binid_it->binId() + 1; | ||||
|      | ||||
|     ASSERT_EQ(bins, 2u); | ||||
| 
 | ||||
|     auto bins = size_t(max_binid_it == input.end() ? 0 : | ||||
|                                                      max_binid_it->binId() + 1); | ||||
|      | ||||
|     // For prusa parts, 2 bins should be enough...
 | ||||
|     ASSERT_LE(bins, 2u); | ||||
|      | ||||
|     // All parts should be processed by the algorithm
 | ||||
|     ASSERT_TRUE( | ||||
|         std::all_of(input.begin(), input.end(), [](const Item &itm) { | ||||
|             return itm.binId() != BIN_ID_UNSET; | ||||
|         })); | ||||
|      | ||||
|     // Gather the items into piles of arranged polygons...
 | ||||
|     using Pile = TMultiShape<ClipperLib::Polygon>; | ||||
|     std::vector<Pile> piles(bins); | ||||
|      | ||||
|     for (auto &itm : input) | ||||
|         piles[size_t(itm.binId())].emplace_back(itm.transformedShape()); | ||||
|      | ||||
|     // Now check all the piles, the bounding box of each pile should be inside
 | ||||
|     // the defined bin.
 | ||||
|     for (auto &pile : piles) { | ||||
|         auto bb = sl::boundingBox(pile); | ||||
|         ASSERT_TRUE(sl::isInside(bb, bin)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST(Nesting, NestEmptyItemShouldBeUntouched) { | ||||
|     auto bin = Box(250000000, 210000000); // dummy bin
 | ||||
|      | ||||
|     std::vector<Item> items; | ||||
|     items.emplace_back(Item{});   // Emplace empty item
 | ||||
|     items.emplace_back(Item{0, 200, 0});   // Emplace zero area item
 | ||||
|      | ||||
|     libnest2d::nest(items, bin); | ||||
|      | ||||
|     for (auto &itm : items) ASSERT_EQ(itm.binId(), BIN_ID_UNSET); | ||||
| } | ||||
| 
 | ||||
| TEST(Nesting, NestLargeItemShouldBeUntouched) { | ||||
|     auto bin = Box(250000000, 210000000); // dummy bin
 | ||||
|      | ||||
|     std::vector<Item> items; | ||||
|     items.emplace_back(Rectangle{250000001, 210000001});  // Emplace large item
 | ||||
|      | ||||
|     libnest2d::nest(items, bin); | ||||
|      | ||||
|     ASSERT_EQ(items.front().binId(), BIN_ID_UNSET); | ||||
| } | ||||
| 
 | ||||
| namespace { | ||||
|  | @ -966,26 +1014,20 @@ using Ratio = boost::rational<boost::multiprecision::int128_t>; | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| TEST(RotatingCalipers, MinAreaBBCClk) { | ||||
|     auto u = [](ClipperLib::cInt n) { return n*1000000; }; | ||||
|     PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}}); | ||||
| //TEST(GeometryAlgorithms, MinAreaBBCClk) {
 | ||||
| //    auto u = [](ClipperLib::cInt n) { return n*1000000; };
 | ||||
| //    PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}});
 | ||||
|      | ||||
|     long double arearef = refMinAreaBox(poly); | ||||
|     long double area = minAreaBoundingBox<PolygonImpl, Unit, Ratio>(poly).area(); | ||||
| //    long double arearef = refMinAreaBox(poly);
 | ||||
| //    long double area = minAreaBoundingBox<PolygonImpl, Unit, Ratio>(poly).area();
 | ||||
|      | ||||
|     ASSERT_LE(std::abs(area - arearef), 500e6 ); | ||||
| } | ||||
| //    ASSERT_LE(std::abs(area - arearef), 500e6 );
 | ||||
| //}
 | ||||
| 
 | ||||
| TEST(RotatingCalipers, AllPrusaMinBB) { | ||||
|     //    /size_t idx = 0;
 | ||||
| TEST(GeometryAlgorithms, MinAreaBBWithRotatingCalipers) { | ||||
|     long double err_epsilon = 500e6l; | ||||
|      | ||||
|     for(ClipperLib::Path rinput : PRINTER_PART_POLYGONS) { | ||||
|         //        ClipperLib::Path rinput = PRINTER_PART_POLYGONS[idx];
 | ||||
|         //        rinput.pop_back();
 | ||||
|         //        std::reverse(rinput.begin(), rinput.end());
 | ||||
|          | ||||
|         //        PolygonImpl poly(removeCollinearPoints<PathImpl, PointImpl, Unit>(rinput, 1000000));
 | ||||
|         PolygonImpl poly(rinput); | ||||
|          | ||||
|         long double arearef = refMinAreaBox(poly); | ||||
|  | @ -993,8 +1035,6 @@ TEST(RotatingCalipers, AllPrusaMinBB) { | |||
|         long double area = cast<long double>(bb.area()); | ||||
|          | ||||
|         bool succ = std::abs(arearef - area) < err_epsilon; | ||||
|         //        std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: " 
 | ||||
| //                  << arearef << " actual: " << area << std::endl;
 | ||||
|          | ||||
|         ASSERT_TRUE(succ); | ||||
|     } | ||||
|  | @ -1011,8 +1051,6 @@ TEST(RotatingCalipers, AllPrusaMinBB) { | |||
|          | ||||
|          | ||||
|         bool succ = std::abs(arearef - area) < err_epsilon; | ||||
|         //        std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: " 
 | ||||
| //                  << arearef << " actual: " << area << std::endl;
 | ||||
|          | ||||
|         ASSERT_TRUE(succ); | ||||
|     } | ||||
|  |  | |||
|  | @ -618,19 +618,21 @@ void arrange(ArrangePolygons &             arrangables, | |||
|     items.reserve(arrangables.size()); | ||||
|      | ||||
|     // Create Item from Arrangeable
 | ||||
|     auto process_arrangeable = | ||||
|         [](const ArrangePolygon &arrpoly, std::vector<Item> &outp) | ||||
|     auto process_arrangeable = [](const ArrangePolygon &arrpoly, | ||||
|                                   std::vector<Item> &   outp) | ||||
|     { | ||||
|         Polygon p        = arrpoly.poly.contour; | ||||
|         const Vec2crd &  offs     = arrpoly.translation; | ||||
|         double           rotation = arrpoly.rotation; | ||||
|         Polygon        p        = arrpoly.poly.contour; | ||||
|         const Vec2crd &offs     = arrpoly.translation; | ||||
|         double         rotation = arrpoly.rotation; | ||||
| 
 | ||||
|         if (p.is_counter_clockwise()) p.reverse(); | ||||
| 
 | ||||
|         clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p)); | ||||
| 
 | ||||
|         auto firstp = clpath.Contour.front(); | ||||
|         clpath.Contour.emplace_back(firstp); | ||||
|          | ||||
|         if (!clpath.Contour.empty()) { | ||||
|             auto firstp = clpath.Contour.front(); | ||||
|             clpath.Contour.emplace_back(firstp); | ||||
|         } | ||||
| 
 | ||||
|         outp.emplace_back(std::move(clpath)); | ||||
|         outp.back().rotation(rotation); | ||||
|  |  | |||
|  | @ -359,7 +359,7 @@ std::string GCodePreviewData::get_legend_title() const | |||
|     case Extrusion::Feedrate: | ||||
|         return L("Speed (mm/s)"); | ||||
|     case Extrusion::VolumetricRate: | ||||
|         return L("Volumetric flow rate (mm3/s)"); | ||||
|         return L("Volumetric flow rate (mm³/s)"); | ||||
|     case Extrusion::Tool: | ||||
|         return L("Tool"); | ||||
|     case Extrusion::ColorPrint: | ||||
|  |  | |||
|  | @ -810,8 +810,13 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) | |||
|      */ | ||||
| 
 | ||||
|     if (!item) { | ||||
|         if (wxOSX && col == nullptr) | ||||
|             UnselectAll(); | ||||
|         if (col == nullptr) { | ||||
|             if (wxOSX) | ||||
|                 UnselectAll(); | ||||
|             else | ||||
|                 return; | ||||
|         } | ||||
| 
 | ||||
|         if (evt_context_menu) { | ||||
|             show_context_menu(evt_context_menu); | ||||
|             return; | ||||
|  | @ -1330,7 +1335,7 @@ wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeTy | |||
| 
 | ||||
|     for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }) | ||||
|     { | ||||
|         if (type == ModelVolumeType::INVALID && item == "Slab") | ||||
|         if (type == ModelVolumeType::INVALID && strncmp(item, "Slab", 4) == 0) | ||||
|             continue; | ||||
|         append_menu_item(sub_menu, wxID_ANY, _(item), "", | ||||
|             [this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Enrico Turri
						Enrico Turri