mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	WIP on structuring arrange inputs
This commit is contained in:
		
							parent
							
								
									96f6fd2d9f
								
							
						
					
					
						commit
						19e6bf58dd
					
				
					 8 changed files with 298 additions and 146 deletions
				
			
		|  | @ -276,8 +276,7 @@ using EigenVec = Eigen::Matrix<T, N, 1, Eigen::DontAlign>; | |||
| // Semantics are the following:
 | ||||
| // Upscaling (scaled()): only from floating point types (or Vec) to either
 | ||||
| //                       floating point or integer 'scaled coord' coordinates.
 | ||||
| // Downscaling (unscaled()): from arithmetic types (or Vec) to either
 | ||||
| //                           floating point only
 | ||||
| // Downscaling (unscaled()): from arithmetic (or Vec) to floating point only
 | ||||
| 
 | ||||
| // Conversion definition from unscaled to floating point scaled
 | ||||
| template<class Tout, | ||||
|  | @ -286,25 +285,25 @@ template<class Tout, | |||
|          class = FloatingOnly<Tout>> | ||||
| inline SLIC3R_CONSTEXPR Tout scaled(const Tin &v) SLIC3R_NOEXCEPT | ||||
| { | ||||
|     return static_cast<Tout>(v / static_cast<Tin>(SCALING_FACTOR)); | ||||
|     return Tout(v / Tin(SCALING_FACTOR)); | ||||
| } | ||||
| 
 | ||||
| // Conversion definition from unscaled to integer 'scaled coord'.
 | ||||
| // TODO: is the rounding necessary ? Here it is to show that it can be different
 | ||||
| // but it does not have to be. Using std::round means loosing noexcept and
 | ||||
| // constexpr modifiers
 | ||||
| // TODO: is the rounding necessary? Here it is commented  out to show that
 | ||||
| // it can be different for integers but it does not have to be. Using
 | ||||
| // std::round means loosing noexcept and constexpr modifiers
 | ||||
| template<class Tout = coord_t, class Tin, class = FloatingOnly<Tin>> | ||||
| inline SLIC3R_CONSTEXPR ScaledCoordOnly<Tout> scaled(const Tin &v) SLIC3R_NOEXCEPT | ||||
| { | ||||
|     //return static_cast<Tout>(std::round(v / SCALING_FACTOR));
 | ||||
|     return static_cast<Tout>(v / static_cast<Tin>(SCALING_FACTOR)); | ||||
|     return Tout(v / Tin(SCALING_FACTOR)); | ||||
| } | ||||
| 
 | ||||
| // Conversion for Eigen vectors (N dimensional points)
 | ||||
| template<class Tout = coord_t, class Tin, int N, class = FloatingOnly<Tin>> | ||||
| inline EigenVec<ArithmeticOnly<Tout>, N> scaled(const EigenVec<Tin, N> &v) | ||||
| { | ||||
|     return v.template cast<Tout>() / SCALING_FACTOR; | ||||
|     return v.template cast<Tout>() /*/ SCALING_FACTOR*/; | ||||
| } | ||||
| 
 | ||||
| // Conversion from arithmetic scaled type to floating point unscaled
 | ||||
|  | @ -314,7 +313,7 @@ template<class Tout = double, | |||
|          class = FloatingOnly<Tout>> | ||||
| inline SLIC3R_CONSTEXPR Tout unscaled(const Tin &v) SLIC3R_NOEXCEPT | ||||
| { | ||||
|     return static_cast<Tout>(v * static_cast<Tout>(SCALING_FACTOR)); | ||||
|     return Tout(v * Tout(SCALING_FACTOR)); | ||||
| } | ||||
| 
 | ||||
| // Unscaling for Eigen vectors. Input base type can be arithmetic, output base
 | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #include "Model.hpp" | ||||
| #include "Geometry.hpp" | ||||
| #include "MTUtils.hpp" | ||||
| 
 | ||||
| #include "Format/AMF.hpp" | ||||
| #include "Format/OBJ.hpp" | ||||
|  | @ -1800,6 +1801,35 @@ void ModelInstance::transform_polygon(Polygon* polygon) const | |||
|     polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
 | ||||
| } | ||||
| 
 | ||||
| Polygon ModelInstance::get_arrange_polygon() const | ||||
| { | ||||
|     static const double SIMPLIFY_TOLERANCE_MM = 0.1; | ||||
|      | ||||
|     assert(m_inst); | ||||
|      | ||||
|     Vec3d rotation             = get_rotation(); | ||||
|     rotation.z()               = 0.; | ||||
|     Transform3d trafo_instance = Geometry:: | ||||
|             assemble_transform(Vec3d::Zero(), | ||||
|                                rotation, | ||||
|                                get_scaling_factor(), | ||||
|                                get_mirror()); | ||||
|      | ||||
|     Polygon p = get_object()->convex_hull_2d(trafo_instance); | ||||
|      | ||||
|     assert(!p.points.empty()); | ||||
|      | ||||
|     // this may happen for malformed models, see:
 | ||||
|     // https://github.com/prusa3d/PrusaSlicer/issues/2209
 | ||||
|     if (p.points.empty()) return {}; | ||||
|      | ||||
|     Polygons pp{p}; | ||||
|     pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM)); | ||||
|     if (!pp.empty()) p = pp.front(); | ||||
|      | ||||
|     return p; | ||||
| } | ||||
| 
 | ||||
| // Test whether the two models contain the same number of ModelObjects with the same set of IDs
 | ||||
| // ordered in the same order. In that case it is not necessary to kill the background processing.
 | ||||
| bool model_object_list_equal(const Model &model_old, const Model &model_new) | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include "Point.hpp" | ||||
| #include "TriangleMesh.hpp" | ||||
| #include "Slicing.hpp" | ||||
| #include "ModelArrange.hpp" | ||||
| 
 | ||||
| #include <map> | ||||
| #include <memory> | ||||
|  | @ -490,7 +491,7 @@ private: | |||
| 
 | ||||
| // A single instance of a ModelObject.
 | ||||
| // Knows the affine transformation of an object.
 | ||||
| class ModelInstance : public ModelBase | ||||
| class ModelInstance : public ModelBase, public arr::Arrangeable | ||||
| { | ||||
| public: | ||||
|     enum EPrintVolumeState : unsigned char | ||||
|  | @ -552,6 +553,16 @@ public: | |||
|     const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } | ||||
| 
 | ||||
|     bool is_printable() const { return print_volume_state == PVS_Inside; } | ||||
|      | ||||
|     virtual void set_arrange_result(Vec2d offs, double rot_rads) final | ||||
|     { | ||||
|         // write the transformation data into the model instance
 | ||||
|         set_rotation(Z, get_rotation(Z) + rot_rads); | ||||
|         set_offset(X, get_offset(X) + offs(X)); | ||||
|         set_offset(Y, get_offset(Y) + offs(Y)); | ||||
|     } | ||||
|      | ||||
|     virtual Polygon get_arrange_polygon() const final; | ||||
| 
 | ||||
| protected: | ||||
|     friend class Print; | ||||
|  |  | |||
|  | @ -42,6 +42,8 @@ namespace arr { | |||
| 
 | ||||
| using namespace libnest2d; | ||||
| 
 | ||||
| using Shape = ClipperLib::Polygon; | ||||
| 
 | ||||
| // Only for debugging. Prints the model object vertices on stdout.
 | ||||
| //std::string toString(const Model& model, bool holes = true) {
 | ||||
| //    std::stringstream  ss;
 | ||||
|  | @ -129,9 +131,7 @@ namespace bgi = boost::geometry::index; | |||
| 
 | ||||
| using SpatElement = std::pair<Box, unsigned>; | ||||
| using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; | ||||
| using ItemGroup = std::vector<std::reference_wrapper<Item>>; | ||||
| template<class TBin> | ||||
| using TPacker = typename placers::_NofitPolyPlacer<PolygonImpl, TBin>; | ||||
| using ItemGroup = std::vector<std::reference_wrapper<_Item<Shape>>>; | ||||
| 
 | ||||
| const double BIG_ITEM_TRESHOLD = 0.02; | ||||
| 
 | ||||
|  | @ -156,10 +156,10 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) { | |||
| // at the same time, it has to provide reasonable results.
 | ||||
| std::tuple<double /*score*/, Box /*farthest point from bin center*/> | ||||
| objfunc(const PointImpl& bincenter, | ||||
|         const TMultiShape<PolygonImpl>& merged_pile, | ||||
|         const TMultiShape<Shape>& merged_pile, | ||||
|         const Box& pilebb, | ||||
|         const ItemGroup& items, | ||||
|         const Item &item, | ||||
|         const _Item<Shape> &item, | ||||
|         double bin_area, | ||||
|         double norm,            // A norming factor for physical dimensions
 | ||||
|         // a spatial index to quickly get neighbors of the candidate item
 | ||||
|  | @ -225,7 +225,7 @@ objfunc(const PointImpl& bincenter, | |||
|             mp.emplace_back(item.transformedShape()); | ||||
|             auto chull = sl::convexHull(mp); | ||||
| 
 | ||||
|             placers::EdgeCache<PolygonImpl> ec(chull); | ||||
|             placers::EdgeCache<Shape> ec(chull); | ||||
| 
 | ||||
|             double circ = ec.circumference() / norm; | ||||
|             double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm; | ||||
|  | @ -256,7 +256,7 @@ objfunc(const PointImpl& bincenter, | |||
| 
 | ||||
|             for(auto& e : result) { // now get the score for the best alignment
 | ||||
|                 auto idx = e.second; | ||||
|                 Item& p = items[idx]; | ||||
|                 _Item<Shape>& p = items[idx]; | ||||
|                 auto parea = p.area(); | ||||
|                 if(std::abs(1.0 - parea/item.area()) < 1e-6) { | ||||
|                     auto bb = boundingBox(p.boundingBox(), ibb); | ||||
|  | @ -322,12 +322,12 @@ class _ArrBase { | |||
| public: | ||||
| 
 | ||||
|     // Useful type shortcuts...
 | ||||
|     using Placer = TPacker<TBin>; | ||||
|     using Selector = FirstFitSelection; | ||||
|     using Placer = typename placers::_NofitPolyPlacer<Shape, TBin>; | ||||
|     using Selector = selections::_FirstFitSelection<Shape>; | ||||
|     using Packer = Nester<Placer, Selector>; | ||||
|     using PConfig = typename Packer::PlacementConfig; | ||||
|     using Distance = TCoord<PointImpl>; | ||||
|     using Pile = TMultiShape<PolygonImpl>; | ||||
|     using Pile = TMultiShape<Shape>; | ||||
|      | ||||
| protected: | ||||
| 
 | ||||
|  | @ -373,7 +373,7 @@ public: | |||
|             }; | ||||
| 
 | ||||
|             for(unsigned idx = 0; idx < items.size(); ++idx) { | ||||
|                 Item& itm = items[idx]; | ||||
|                 _Item<Shape>& itm = items[idx]; | ||||
|                 if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx}); | ||||
|                 m_smallsrtree.insert({itm.boundingBox(), idx}); | ||||
|             } | ||||
|  | @ -382,13 +382,13 @@ public: | |||
|         m_pck.progressIndicator(progressind); | ||||
|         m_pck.stopCondition(stopcond); | ||||
|     } | ||||
| 
 | ||||
|     template<class...Args> inline IndexedPackGroup operator()(Args&&...args) { | ||||
|      | ||||
|     template<class...Args> inline _PackGroup<Shape> operator()(Args&&...args) { | ||||
|         m_rtree.clear(); | ||||
|         return m_pck.executeIndexed(std::forward<Args>(args)...); | ||||
|         return m_pck.execute(std::forward<Args>(args)...); | ||||
|     } | ||||
| 
 | ||||
|     inline void preload(const PackGroup& pg) { | ||||
|      | ||||
|     inline void preload(const _PackGroup<Shape>& pg) { | ||||
|         m_pconf.alignment = PConfig::Alignment::DONT_ALIGN; | ||||
|         m_pconf.object_function = nullptr; // drop the special objectfunction
 | ||||
|         m_pck.preload(pg); | ||||
|  | @ -396,14 +396,14 @@ public: | |||
|         // Build the rtree for queries to work
 | ||||
|         for(const ItemGroup& grp : pg) | ||||
|         for(unsigned idx = 0; idx < grp.size(); ++idx) { | ||||
|             Item& itm = grp[idx]; | ||||
|             _Item<Shape>& itm = grp[idx]; | ||||
|             m_rtree.insert({itm.boundingBox(), idx}); | ||||
|         } | ||||
| 
 | ||||
|         m_pck.configure(m_pconf); | ||||
|     } | ||||
| 
 | ||||
|     bool is_colliding(const Item& item) { | ||||
|     bool is_colliding(const _Item<Shape>& item) { | ||||
|         if(m_rtree.empty()) return false; | ||||
|         std::vector<SpatElement> result; | ||||
|         m_rtree.query(bgi::intersects(item.boundingBox()), | ||||
|  | @ -425,7 +425,7 @@ public: | |||
|         // Here we set up the actual object function that calls the common
 | ||||
|         // object function for all bin shapes than does an additional inside
 | ||||
|         // check for the arranged pile.
 | ||||
|         m_pconf.object_function = [this, bin] (const Item &item) { | ||||
|         m_pconf.object_function = [this, bin] (const _Item<Shape> &item) { | ||||
| 
 | ||||
|             auto result = objfunc(bin.center(), | ||||
|                                   m_merged_pile, | ||||
|  | @ -468,7 +468,7 @@ public: | |||
|         _ArrBase<lnCircle>(bin, dist, progressind, stopcond) { | ||||
| 
 | ||||
|         // As with the box, only the inside check is different.
 | ||||
|         m_pconf.object_function = [this, &bin] (const Item &item) { | ||||
|         m_pconf.object_function = [this, &bin] (const _Item<Shape> &item) { | ||||
| 
 | ||||
|             auto result = objfunc(bin.center(), | ||||
|                                   m_merged_pile, | ||||
|  | @ -483,7 +483,7 @@ public: | |||
| 
 | ||||
|             double score = std::get<0>(result); | ||||
| 
 | ||||
|             auto isBig = [this](const Item& itm) { | ||||
|             auto isBig = [this](const _Item<Shape>& itm) { | ||||
|                 return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; | ||||
|             }; | ||||
| 
 | ||||
|  | @ -512,7 +512,7 @@ public: | |||
|                  std::function<bool(void)> stopcond = [](){return false;}): | ||||
|         _ArrBase<PolygonImpl>(bin, dist, progressind, stopcond) | ||||
|     { | ||||
|         m_pconf.object_function = [this, &bin] (const Item &item) { | ||||
|         m_pconf.object_function = [this, &bin] (const _Item<Shape> &item) { | ||||
| 
 | ||||
|             auto binbb = sl::boundingBox(bin); | ||||
|             auto result = objfunc(binbb.center(), | ||||
|  | @ -540,11 +540,11 @@ public: | |||
| template<> class AutoArranger<bool>: public _ArrBase<Box> { | ||||
| public: | ||||
| 
 | ||||
|     AutoArranger(Distance dist, std::function<void(unsigned)> progressind, | ||||
|     AutoArranger(bool, Distance dist, std::function<void(unsigned)> progressind, | ||||
|                  std::function<bool(void)> stopcond): | ||||
|         _ArrBase<Box>(Box(0, 0), dist, progressind, stopcond) | ||||
|     { | ||||
|         this->m_pconf.object_function = [this] (const Item &item) { | ||||
|         this->m_pconf.object_function = [this] (const _Item<Shape> &item) { | ||||
| 
 | ||||
|             auto result = objfunc({0, 0}, | ||||
|                                   m_merged_pile, | ||||
|  | @ -782,18 +782,18 @@ BedShapeHint bedShape(const Polyline &bed) { | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; | ||||
| //static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1;
 | ||||
| 
 | ||||
| //template<class BinT>
 | ||||
| //IndexedPackGroup _arrange(std::vector<std::reference_wrapper<Item>> &shapes,
 | ||||
| //                          const BinT &                               bin,
 | ||||
| //                          coord_t                       minobjd,
 | ||||
| //                          std::function<void(unsigned)> prind,
 | ||||
| //                          std::function<bool()>         stopfn)
 | ||||
| //{
 | ||||
| //    AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn};
 | ||||
| //    return arranger(shapes.begin(), shapes.end());
 | ||||
| //}
 | ||||
| template<class BinT> | ||||
| _PackGroup<Shape> _arrange(std::vector<Shape> &shapes, | ||||
|                    const BinT &                            bin, | ||||
|                    coord_t                                 minobjd, | ||||
|                    std::function<void(unsigned)>           prind, | ||||
|                    std::function<bool()>                   stopfn) | ||||
| { | ||||
|     AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn}; | ||||
|     return arranger(shapes.begin(), shapes.end()); | ||||
| } | ||||
| 
 | ||||
| //template<class BinT>
 | ||||
| //IndexedPackGroup _arrange(std::vector<std::reference_wrapper<Item>> &shapes,
 | ||||
|  | @ -845,11 +845,99 @@ static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; | |||
| //    return arranger(shapes.begin(), shapes.end());
 | ||||
| //}
 | ||||
| 
 | ||||
| inline SLIC3R_CONSTEXPR libnest2d::Coord stride_padding(Coord w) | ||||
| inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) | ||||
| { | ||||
|     return w + w / 5; | ||||
| } | ||||
| 
 | ||||
| bool arrange(ArrangeableRefs &             arrangables, | ||||
|              coord_t                       min_obj_distance, | ||||
|              BedShapeHint                  bedhint, | ||||
|              std::function<void(unsigned)> progressind, | ||||
|              std::function<bool()>         stopcondition) | ||||
| { | ||||
|     bool ret = true; | ||||
|      | ||||
|     std::vector<Shape> shapes; | ||||
|     shapes.reserve(arrangables.size()); | ||||
|     size_t id = 0; | ||||
|     for (Arrangeable &iref : arrangables) { | ||||
|         Polygon p = iref.get_arrange_polygon(); | ||||
|          | ||||
|         p.reverse(); | ||||
|         assert(!p.is_counter_clockwise()); | ||||
|          | ||||
|         Shape clpath(/*id++,*/ Slic3rMultiPoint_to_ClipperPath(p)); | ||||
|          | ||||
|         auto firstp = clpath.Contour.front(); clpath.Contour.emplace_back(firstp); | ||||
|         shapes.emplace_back(std::move(clpath)); | ||||
|     } | ||||
|      | ||||
|     _PackGroup<Shape> result; | ||||
|      | ||||
|     auto& cfn = stopcondition; | ||||
| 
 | ||||
|     // Integer ceiling the min distance from the bed perimeters
 | ||||
|     coord_t md = min_obj_distance - SCALED_EPSILON; | ||||
|     md = (md % 2) ? md / 2 + 1 : md / 2; | ||||
|     coord_t binwidth = 0; | ||||
| 
 | ||||
|     switch (bedhint.type) { | ||||
|     case BedShapeType::BOX: { | ||||
|         // Create the arranger for the box shaped bed
 | ||||
|         BoundingBox bbb = bedhint.shape.box; | ||||
| 
 | ||||
|         auto binbb = Box({ClipperLib::cInt{bbb.min(0)} - md, | ||||
|                           ClipperLib::cInt{bbb.min(1)} - md}, | ||||
|                          {ClipperLib::cInt{bbb.max(0)} + md, | ||||
|                           ClipperLib::cInt{bbb.max(1)} + md}); | ||||
| 
 | ||||
|         result = _arrange(shapes, binbb, min_obj_distance, progressind, cfn); | ||||
|         binwidth = coord_t(binbb.width()); | ||||
|         break; | ||||
|     } | ||||
|     case BedShapeType::CIRCLE: { | ||||
|         auto c  = bedhint.shape.circ; | ||||
|         auto cc = to_lnCircle(c); | ||||
|         result  = _arrange(shapes, cc, min_obj_distance, progressind, cfn); | ||||
|         binwidth = scaled(c.radius()); | ||||
|         break; | ||||
|     } | ||||
|     case BedShapeType::IRREGULAR: { | ||||
|         auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); | ||||
|         ClipperLib::Polygon irrbed = sl::create<PolygonImpl>(std::move(ctour)); | ||||
|         result = _arrange(shapes, irrbed, min_obj_distance, progressind, cfn); | ||||
|         BoundingBox polybb(bedhint.shape.polygon); | ||||
|         binwidth = (polybb.max(X) - polybb.min(X)); | ||||
|         break; | ||||
|     } | ||||
|     case BedShapeType::WHO_KNOWS: { | ||||
|         result = _arrange(shapes, false, min_obj_distance, progressind, cfn); | ||||
|         break; | ||||
|     } | ||||
|     }; | ||||
|      | ||||
|     if(result.empty() || stopcondition()) return false; | ||||
|      | ||||
|     ClipperLib::cInt stride = stride_padding(binwidth); | ||||
|     ClipperLib::cInt batch_offset = 0; | ||||
| 
 | ||||
|     for (const auto &group : result) { | ||||
|         for (_Item<Shape> &itm : group) { | ||||
|             ClipperLib::IntPoint offs = itm.translation(); | ||||
| //            arrangables[itm.id()].get().set_arrange_result({offs.X, offs.Y},
 | ||||
| //                                                           itm.rotation());
 | ||||
|         } | ||||
| 
 | ||||
|         // Only the first pack group can be placed onto the print bed. The
 | ||||
|         // other objects which could not fit will be placed next to the
 | ||||
|         // print bed
 | ||||
|         batch_offset += stride; | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| //// The final client function to arrange the Model. A progress indicator and
 | ||||
| //// a stop predicate can be also be passed to control the process.
 | ||||
| //bool arrange(Model &model,              // The model with the geometries
 | ||||
|  |  | |||
|  | @ -32,8 +32,8 @@ enum class BedShapeType { | |||
| }; | ||||
| 
 | ||||
| struct BedShapeHint { | ||||
|     BedShapeType type; | ||||
|     /*union*/ struct {  // I know but who cares...
 | ||||
|     BedShapeType type = BedShapeType::WHO_KNOWS; | ||||
|     /*union*/ struct {  // I know but who cares... TODO: use variant from cpp17?
 | ||||
|         Circle circ; | ||||
|         BoundingBox box; | ||||
|         Polyline polygon; | ||||
|  | @ -42,24 +42,17 @@ struct BedShapeHint { | |||
| 
 | ||||
| BedShapeHint bedShape(const Polyline& bed); | ||||
| 
 | ||||
| class ArrangeItem { | ||||
| class Arrangeable { | ||||
| public: | ||||
|      | ||||
|     virtual ~ArrangeItem() = default; | ||||
|     virtual ~Arrangeable() = default; | ||||
|      | ||||
|     virtual void transform(Vec2d offset, double rotation_rads) = 0; | ||||
|     virtual void set_arrange_result(Vec2d offset, double rotation_rads) = 0; | ||||
|      | ||||
|     virtual Polygon silhouette() const = 0; | ||||
|     virtual Polygon get_arrange_polygon() const = 0; | ||||
| }; | ||||
| 
 | ||||
| using ArrangeItems = std::vector<std::reference_wrapper<ArrangeItem>>; | ||||
| 
 | ||||
| //struct WipeTowerInfo {
 | ||||
| //    bool is_wipe_tower = false;
 | ||||
| //    Vec2d pos;
 | ||||
| //    Vec2d bb_size;
 | ||||
| //    double rotation;
 | ||||
| //};
 | ||||
| using ArrangeableRefs = std::vector<std::reference_wrapper<Arrangeable>>; | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief Arranges the model objects on the screen. | ||||
|  | @ -96,7 +89,7 @@ using ArrangeItems = std::vector<std::reference_wrapper<ArrangeItem>>; | |||
| //             std::function<void(unsigned)> progressind,
 | ||||
| //             std::function<bool(void)> stopcondition);
 | ||||
| 
 | ||||
| bool arrange(ArrangeItems &items, | ||||
| bool arrange(ArrangeableRefs &items, | ||||
|              coord_t min_obj_distance, | ||||
|              BedShapeHint bedhint, | ||||
|              std::function<void(unsigned)> progressind, | ||||
|  | @ -109,8 +102,8 @@ bool arrange(ArrangeItems &items, | |||
| //                       coord_t min_obj_distance,
 | ||||
| //                       const Slic3r::Polyline& bed,
 | ||||
| //                       WipeTowerInfo& wti);
 | ||||
| void find_new_position(ArrangeItems &items, | ||||
|                        const ArrangeItems &instances_to_add, | ||||
| void find_new_position(ArrangeableRefs &items, | ||||
|                        const ArrangeableRefs &instances_to_add, | ||||
|                        coord_t min_obj_distance, | ||||
|                        BedShapeHint bedhint); | ||||
| 
 | ||||
|  |  | |||
|  | @ -3329,36 +3329,24 @@ void GLCanvas3D::update_ui_from_settings() | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| arr::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const | ||||
| GLCanvas3D::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const | ||||
| { | ||||
|     arr::WipeTowerInfo wti; | ||||
|     WipeTowerInfo wti; | ||||
|      | ||||
|     for (const GLVolume* vol : m_volumes.volumes) { | ||||
|         if (vol->is_wipe_tower) { | ||||
|             wti.is_wipe_tower = true; | ||||
|             wti.pos = Vec2d(m_config->opt_float("wipe_tower_x"), | ||||
|             wti.m_pos = Vec2d(m_config->opt_float("wipe_tower_x"), | ||||
|                             m_config->opt_float("wipe_tower_y")); | ||||
|             wti.rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle"); | ||||
|             wti.m_rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle"); | ||||
|             const BoundingBoxf3& bb = vol->bounding_box; | ||||
|             wti.bb_size = Vec2d(bb.size()(0), bb.size()(1)); | ||||
|             wti.m_bb_size = Vec2d(bb.size().x(), bb.size().y()); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     return wti; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void GLCanvas3D::arrange_wipe_tower(const arr::WipeTowerInfo& wti) const | ||||
| { | ||||
|     if (wti.is_wipe_tower) { | ||||
|         DynamicPrintConfig cfg; | ||||
|         cfg.opt<ConfigOptionFloat>("wipe_tower_x", true)->value = wti.pos(0); | ||||
|         cfg.opt<ConfigOptionFloat>("wipe_tower_y", true)->value = wti.pos(1); | ||||
|         cfg.opt<ConfigOptionFloat>("wipe_tower_rotation_angle", true)->value = (180./M_PI) * wti.rotation; | ||||
|         wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos) | ||||
| { | ||||
|     float z0 = 0.0f; | ||||
|  | @ -5751,5 +5739,16 @@ const SLAPrint* GLCanvas3D::sla_print() const | |||
|     return (m_process == nullptr) ? nullptr : m_process->sla_print(); | ||||
| } | ||||
| 
 | ||||
| void GLCanvas3D::WipeTowerInfo::set_arrange_result(Vec2d offset, double rotation_rads) | ||||
| { | ||||
|     m_pos      += offset; | ||||
|     m_rotation += rotation_rads; | ||||
|     DynamicPrintConfig cfg; | ||||
|     cfg.opt<ConfigOptionFloat>("wipe_tower_x", true)->value = m_pos(X); | ||||
|     cfg.opt<ConfigOptionFloat>("wipe_tower_y", true)->value = m_pos(Y); | ||||
|     cfg.opt<ConfigOptionFloat>("wipe_tower_rotation_angle", true)->value = (180./M_PI) * m_rotation; | ||||
|     wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg); | ||||
| } | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -611,9 +611,38 @@ public: | |||
| 
 | ||||
|     int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; } | ||||
|     int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); } | ||||
|      | ||||
|     class WipeTowerInfo: public arr::Arrangeable { | ||||
|         Vec2d  m_pos = {std::nan(""), std::nan("")}; | ||||
|         Vec2d m_bb_size; | ||||
|         double m_rotation; | ||||
|         friend class GLCanvas3D; | ||||
|     public: | ||||
|          | ||||
|         inline operator bool() const | ||||
|         { | ||||
|             return std::isnan(m_pos.x()) || std::isnan(m_pos.y()); | ||||
|         } | ||||
| 
 | ||||
|     arr::WipeTowerInfo get_wipe_tower_info() const; | ||||
|     void arrange_wipe_tower(const arr::WipeTowerInfo& wti) const; | ||||
|         virtual void set_arrange_result(Vec2d offset, double rotation_rads) final; | ||||
| 
 | ||||
|         virtual Polygon get_arrange_polygon() const final | ||||
|         { | ||||
|             Polygon p({ | ||||
|                 {coord_t(0), coord_t(0)}, | ||||
|                 {scaled(m_bb_size(X)), coord_t(0)}, | ||||
|                 {scaled(m_bb_size)}, | ||||
|                 {coord_t(0), scaled(m_bb_size(Y))}, | ||||
|                 {coord_t(0), coord_t(0)}, | ||||
|             }); | ||||
| 
 | ||||
|             p.rotate(m_rotation); | ||||
|             p.translate(scaled(m_pos)); | ||||
|             return p; | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     WipeTowerInfo get_wipe_tower_info() const; | ||||
| 
 | ||||
|     // Returns the view ray line, in world coordinate, at the given mouse position.
 | ||||
|     Linef3 mouse_ray(const Point& mouse_pos); | ||||
|  |  | |||
|  | @ -1424,22 +1424,25 @@ struct Plater::priv | |||
|         priv * m_plater; | ||||
| 
 | ||||
|         class ArrangeJob : public Job | ||||
|         { | ||||
|             int count = 0; | ||||
| 
 | ||||
|         {    | ||||
|             int m_count = 0; | ||||
|             GLCanvas3D::WipeTowerInfo m_wti; | ||||
|         protected: | ||||
| 
 | ||||
|             void prepare() override | ||||
|             { | ||||
|                 count = 0; | ||||
|                 m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info(); | ||||
|                 m_count = bool(m_wti); | ||||
|                  | ||||
|                 for (auto obj : plater().model.objects) | ||||
|                     count += int(obj->instances.size()); | ||||
|                     m_count += int(obj->instances.size()); | ||||
|             } | ||||
| 
 | ||||
|         public: | ||||
|             //using Job::Job;
 | ||||
|             ArrangeJob(priv * pltr): Job(pltr) {} | ||||
|             int  status_range() const override { return count; } | ||||
|             void set_count(int c) { count = c; } | ||||
|             int  status_range() const override { return m_count; } | ||||
|             void set_count(int c) { m_count = c; } | ||||
|             void process() override; | ||||
|         } arrange_job/*{m_plater}*/; | ||||
| 
 | ||||
|  | @ -1525,6 +1528,7 @@ struct Plater::priv | |||
|     std::string get_config(const std::string &key) const; | ||||
|     BoundingBoxf bed_shape_bb() const; | ||||
|     BoundingBox scaled_bed_shape_bb() const; | ||||
|     arr::BedShapeHint get_bed_shape_hint() const; | ||||
|     std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config); | ||||
|     std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects); | ||||
|     wxString get_export_file(GUI::FileType file_type); | ||||
|  | @ -2171,9 +2175,9 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode | |||
|     auto& bedpoints = bed_shape_opt->values; | ||||
|     Polyline bed; bed.points.reserve(bedpoints.size()); | ||||
|     for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); | ||||
| 
 | ||||
|     arr::WipeTowerInfo wti = view3D->get_canvas3d()->get_wipe_tower_info(); | ||||
| 
 | ||||
|      | ||||
|     std::pair<bool, GLCanvas3D::WipeTowerInfo> wti = view3D->get_canvas3d()->get_wipe_tower_info(); | ||||
|      | ||||
|     arr::find_new_position(model, new_instances, min_obj_distance, bed, wti); | ||||
| 
 | ||||
|     // it remains to move the wipe tower:
 | ||||
|  | @ -2400,61 +2404,60 @@ void Plater::priv::sla_optimize_rotation() { | |||
|     m_ui_jobs.start(Jobs::Rotoptimize); | ||||
| } | ||||
| 
 | ||||
| void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { | ||||
|     static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; | ||||
| arr::BedShapeHint Plater::priv::get_bed_shape_hint() const { | ||||
|     arr::BedShapeHint bedshape; | ||||
|      | ||||
|     class ArrItemModelInstance: public arr::ArrangeItem { | ||||
|         ModelInstance *m_inst = nullptr; | ||||
|     public: | ||||
|          | ||||
|         ArrItemModelInstance() = default; | ||||
|         ArrItemModelInstance(ModelInstance *inst) : m_inst(inst) {} | ||||
|          | ||||
|         virtual void transform(Vec2d offs, double rot_rads) override { | ||||
|             assert(m_inst); | ||||
|              | ||||
|             // write the transformation data into the model instance
 | ||||
|             m_inst->set_rotation(Z, rot_rads); | ||||
|             m_inst->set_offset(offs); | ||||
|         } | ||||
|          | ||||
|         virtual Polygon silhouette() const override { | ||||
|             assert(m_inst); | ||||
|              | ||||
|             Vec3d          rotation    = m_inst->get_rotation(); | ||||
|             rotation.z()               = 0.; | ||||
|             Transform3d trafo_instance = Geometry::assemble_transform( | ||||
|                 Vec3d::Zero(), | ||||
|                 rotation, | ||||
|                 m_inst->get_scaling_factor(), | ||||
|                 m_inst->get_mirror()); | ||||
|              | ||||
|             Polygon p = m_inst->get_object()->convex_hull_2d(trafo_instance); | ||||
|     const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape"); | ||||
|     assert(bed_shape_opt); | ||||
|      | ||||
|     if (bed_shape_opt) { | ||||
|         auto &bedpoints = bed_shape_opt->values; | ||||
|         Polyline bedpoly; bedpoly.points.reserve(bedpoints.size()); | ||||
|         for (auto &v : bedpoints) bedpoly.append(scaled(v)); | ||||
|         bedshape = arr::bedShape(bedpoly); | ||||
|     } | ||||
|      | ||||
|     return bedshape; | ||||
| } | ||||
| 
 | ||||
|             assert(!p.points.empty()); | ||||
| void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() {     | ||||
|     static const auto arrangestr = _(L("Arranging")); | ||||
|     | ||||
|     arr::ArrangeableRefs arrangeinput; arrangeinput.reserve(m_count); | ||||
|     for(ModelObject *mo : plater().model.objects) | ||||
|         for(ModelInstance *minst : mo->instances) | ||||
|             arrangeinput.emplace_back(std::ref(*minst)); | ||||
|      | ||||
|     // FIXME: I don't know how to obtain the minimum distance, it depends
 | ||||
|     // on printer technology. I guess the following should work but it crashes.
 | ||||
|     double dist = 6; // PrintConfig::min_object_distance(config);
 | ||||
|     if (plater().printer_technology == ptFFF) { | ||||
|         dist = PrintConfig::min_object_distance(plater().config); | ||||
|     } | ||||
|      | ||||
|     coord_t min_obj_distance = scaled(dist); | ||||
|      | ||||
|     arr::BedShapeHint bedshape = plater().get_bed_shape_hint(); | ||||
|      | ||||
|     try { | ||||
|         arr::arrange(arrangeinput, | ||||
|                      min_obj_distance, | ||||
|                      bedshape, | ||||
|                      [this](unsigned st) { | ||||
|                          if (st > 0) | ||||
|                              update_status(m_count - int(st), arrangestr); | ||||
|                      }, | ||||
|                      [this]() { return was_canceled(); }); | ||||
|     } catch (std::exception & /*e*/) { | ||||
|         GUI::show_error(plater().q, | ||||
|                         _(L("Could not arrange model objects! " | ||||
|                             "Some geometries may be invalid."))); | ||||
|     } | ||||
|      | ||||
|     update_status(m_count, | ||||
|                   was_canceled() ? _(L("Arranging canceled.")) | ||||
|                                  : _(L("Arranging done."))); | ||||
| 
 | ||||
|             // this may happen for malformed models, see:
 | ||||
|             // https://github.com/prusa3d/PrusaSlicer/issues/2209
 | ||||
|             if (p.points.empty()) return {}; | ||||
| 
 | ||||
|             Polygons pp { p }; | ||||
|             pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM)); | ||||
|             if (!pp.empty()) p = pp.front(); | ||||
|              | ||||
|             return p; | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     // Count all the items on the bin (all the object's instances)
 | ||||
|     auto count = std::accumulate(plater().model.objects.begin(), | ||||
|                              plater().model.objects.end(), | ||||
|                              size_t(0), [](size_t s, ModelObject* o) | ||||
|     { | ||||
|         return s + o->instances.size(); | ||||
|     }); | ||||
|      | ||||
| //    std::vector<ArrItemInstance> items(size_t);
 | ||||
|      | ||||
|     // TODO: we should decide whether to allow arrange when the search is
 | ||||
|     // running we should probably disable explicit slicing and background
 | ||||
|     // processing
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros