// SPDX-License-Identifier: LGPL-2.1-or-later /**************************************************************************** * Copyright (c) 2017 Zheng Lei (realthunder) * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ****************************************************************************/ #ifndef PATH_AREA_H #define PATH_AREA_H #include #include #include #include #include #include #include #include #include "AreaParams.h" #include "Path.h" class CArea; class CCurve; class Bnd_Box; namespace Path { /** Store libarea algorithm configuration */ struct PathExport CAreaParams { PARAM_DECLARE(PARAM_FNAME, AREA_PARAMS_CAREA) CAreaParams(); }; /** Store all Area configurations */ struct PathExport AreaParams: CAreaParams { PARAM_DECLARE(PARAM_FNAME, AREA_PARAMS_AREA) bool operator==(const AreaParams& other) const { #define AREA_COMPARE(_param) \ if (PARAM_FIELD(NAME, _param) != other.PARAM_FIELD(NAME, _param)) \ return false; PARAM_FOREACH(AREA_COMPARE, AREA_PARAMS_CAREA); PARAM_FOREACH(AREA_COMPARE, AREA_PARAMS_AREA); return true; } bool operator!=(const AreaParams& other) const { return !(*this == other); } void dump(const char*) const; AreaParams(); }; struct PathExport AreaStaticParams: AreaParams { AreaStaticParams(); }; /** libarea configurator * * It is kind of troublesome with the fact that libarea uses static variables to * config its algorithm. CAreaConfig makes it easy to safely customize libarea. */ struct PathExport CAreaConfig { /** For saving current libarea settings */ PARAM_DECLARE(PARAM_FNAME, AREA_PARAMS_CAREA) /** The constructor automatically saves current setting and apply user defined ones * * \arg \c p user defined configurations * \arg \c noFitArgs if true, will override and disable arc fitting. Because * arc unfiting and fitting is lossy. And repeatedly perform these operation * may cause shape deformation. So it is best to delay arc fitting until the * final step*/ explicit CAreaConfig(const CAreaParams& p, bool noFitArcs = true); /** The destructor restores the setting, and thus exception safe. */ ~CAreaConfig(); }; /** Base class for FreeCAD wrapping of libarea */ class PathExport Area: public Base::BaseClass { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: struct Shape { short op; TopoDS_Shape shape; Shape(short opCode, const TopoDS_Shape& s) : op(opCode) , shape(s) {} }; protected: std::list myShapes; std::unique_ptr myArea; std::unique_ptr myAreaOpen; gp_Trsf myTrsf; AreaParams myParams; TopoDS_Shape myShapePlane; TopoDS_Shape myWorkPlane; TopoDS_Shape myShape; std::vector> mySections; bool myHaveFace; bool myHaveSolid; bool myShapeDone; bool myProjecting; mutable int mySkippedShapes; static bool s_aborting; static AreaStaticParams s_params; /** Called internally to combine children shapes for further processing */ void build(); /** Called by build() to add children shape * * Mainly for checking if there is any faces for auto fill*/ void addToBuild(CArea& area, const TopoDS_Shape& shape); /** Called internally to obtain the combined children shapes */ TopoDS_Shape toShape(CArea& area, short fill, int reorient = 0); /** Obtain a list of offset areas * * See #AREA_PARAMS_OFFSET for description of the arguments. */ void makeOffset( std::list>& areas, PARAM_ARGS_DEF(PARAM_FARG, AREA_PARAMS_OFFSET), bool from_center = false ); /** Make a pocket of the combined shape * * User #AREA_PARAMS_POCKET setting in myParams. */ TopoDS_Shape makePocket(); void explode(const TopoDS_Shape& shape); TopoDS_Shape findPlane(const TopoDS_Shape& shape, gp_Trsf& trsf); std::list getProjectedShapes(const gp_Trsf& trsf, bool inverse = true) const; public: /** Declare all parameters defined in #AREA_PARAMS_ALL as member variable */ PARAM_ENUM_DECLARE(AREA_PARAMS_ALL) explicit Area(const AreaParams* params = nullptr); Area(const Area& other, bool deep_copy = true); ~Area() override; bool isBuilt() const; /** Set a working plane * * \arg \c shape: a shape defining a working plane. * * The supplied shape does not need to be planar. Area will try to find planar * sub-shape (face, wire or edge). If more than one planar sub-shape is found, * it will prefer the top plane parallel to XY0 plane. * * If no working plane are set, Area will try to find a working plane from * the added children shape using the same algorithm */ void setPlane(const TopoDS_Shape& shape); /** Return the current active workplane * * \arg \c trsf: optional return of a transformation matrix that will bring the * found plane to XY0 plane. * * If no workplane is set using setPlane(), the active workplane is derived from * the added children shapes using the same algorithm empolyed by setPlane(). */ TopoDS_Shape getPlane(gp_Trsf* trsf = nullptr); /** Add a child shape with given operation code * * No validation is done at this point. Exception will be thrown when asking * for output shape, if any of the children shapes is not valid or not * coplanar * * \arg \c shape: the child shape * \arg \c op: operation code, see #AREA_PARAMS_OPCODE */ void add(const TopoDS_Shape& shape, PARAM_ARGS_DEF(PARAM_FARG, AREA_PARAMS_OPCODE)); /** Generate an offset of the combined shape * * See #AREA_PARAMS_OFFSET for description of the arguments. * If more than one offset is requested, a compound shape is return * containing all offset shapes as wires regardless of \c Fill setting. */ TopoDS_Shape makeOffset( int index = -1, PARAM_ARGS_DEF(PARAM_FARG, AREA_PARAMS_OFFSET), int reoirent = 0, bool from_center = false ); /** Make a pocket of the combined shape * * See #AREA_PARAMS_POCKET for description of the arguments. */ TopoDS_Shape makePocket(int index = -1, PARAM_ARGS_DEF(PARAM_FARG, AREA_PARAMS_POCKET)); /** Make a pocket of the combined shape * * \arg \c heights: optional customized heights of each section. The * meaning of each height depends on section mode. If none is given, * the section heights is determined by the section settings in this * Area object (configured through setParams()). * \arg \c plane: the section plane if the section mode is * SectionModeWorkplane, otherwise ignored * * See #AREA_PARAMS_EXTRA for description of the arguments. Currently, there * is only one argument, namely \c mode for section mode. */ std::vector> makeSections( PARAM_ARGS_DEF(PARAM_FARG, AREA_PARAMS_SECTION_EXTRA), const std::vector& heights = std::vector(), const TopoDS_Shape& plane = TopoDS_Shape() ); std::shared_ptr getClearedArea( const Toolpath* path, double diameter, double zmax, Base::BoundBox3d bbox ); std::shared_ptr getRestArea(std::vector> clearedAreas, double diameter); TopoDS_Shape toTopoShape(); /** Config this Area object */ void setParams(const AreaParams& params); const std::list getChildren() const { return myShapes; } /** Get the current configuration */ const AreaParams& getParams() const { return myParams; } /** Clean internal caches * * The combined shapes is cached internally to make other operation more * efficient, such as makeOffset() and makePocket() * * \arg \c deleteShapes: if true, delete all children shapes. */ void clean(bool deleteShapes = false); /** Get the combined shape * \arg \c index: index of the section, -1 for all sections. No effect on * non-sectioned area. */ TopoDS_Shape getShape(int index = -1); /** Return the number of sections */ std::size_t getSectionCount() { build(); return mySections.size(); } /** Add a OCC wire shape to CArea * * \arg \c area: output converted curved object to here * \arg \c wire: input wire object * \arg \c trsf: optional transform matrix to transform the wire shape into * XY0 plane. * \arg \c deflection: for discretizing non circular curves * \arg \c to_edges: if true, discretize all curves, and insert as open * line segments * */ static void addWire( CArea& area, const TopoDS_Wire& wire, const gp_Trsf* trsf = nullptr, double deflection = 0.01, bool to_edges = false ); /** Add a OCC generic shape to CArea * * \arg \c area: output converted curved object to here * \arg \c shape: input shape object * \arg \c trsf: optional transform matrix to transform the wire shape into * XY0 plane. * \arg \c deflection: for defecting non circular curves * \arg \c plane: a shape for testing coplanar * \arg \c force_coplaner: if true, discard non-coplanar shapes. * \arg \c areaOpen: for collecting open curves. If not supplied, open * curves are added to \c area * \arg \c to_edges: separate open wires to individual edges * \arg \c reorient: reorient closed wires for wire only shape * * \return Returns the number of non coplaner. Planar testing only happens * if \c plane is supplied * */ static int addShape( CArea& area, const TopoDS_Shape& shape, const gp_Trsf* trsf = nullptr, double deflection = 0.01, const TopoDS_Shape* plane = nullptr, bool force_coplanar = true, CArea* areaOpen = nullptr, bool to_edges = false, bool reorient = true ); /** Convert curves in CArea into an OCC shape * * \arg \c area: input area object * \arg \c fill: if true, create a face object from the wires * \arg \c trsf: optional transform matrix to transform the shape back into * its original position. * */ static TopoDS_Shape toShape( const CArea& area, bool fill, const gp_Trsf* trsf = nullptr, int reoirent = 0 ); /** Convert a single curve into an OCC wire * * \arg \c curve: input curve object * \arg \c trsf: optional transform matrix to transform the shape back into * its original position. * */ static TopoDS_Shape toShape(const CCurve& curve, const gp_Trsf* trsf = nullptr, int reorient = 0); /** Check if two OCC shape is coplanar */ static bool isCoplanar(const TopoDS_Shape& s1, const TopoDS_Shape& s2); /** Group shapes by their plane, and return a list of sorted wires * * The output wires is ordered by its occupied plane, and sorted to * minimize traval distance * * \arg \c shapes: input list of shapes. * \arg \c has_start: if false or pstart is 0, then a start point will be * auto selected. * \arg \c pstart: optional start point. If has_start is false, then the * auto selected start point will be returned with this point if not NULL. * \arg \c pend: optional output containing the ending point of the returned * \arg \c stepdown_hint: optional output of a hint of step down as the max * distance between two sections. * \arg \c arc_plane: optional arc plane selection, if given the found plane * will be returned. See #AREA_PARAMS_ARC_PLANE for more details. * * See #AREA_PARAMS_SORT for other arguments * * \return sorted wires */ static std::list sortWires( const std::list& shapes, bool has_start = false, gp_Pnt* pstart = nullptr, gp_Pnt* pend = nullptr, double* stepdown_hint = nullptr, short* arc_plane = nullptr, PARAM_ARGS_DEF(PARAM_FARG, AREA_PARAMS_SORT) ); /** Convert a list of wires to gcode * * \arg \c path: output toolpath * \arg \c shapes: input list of shapes * \arg \c pstart: output start point, * \arg \c pend: optional output containing the ending point of the returned * * See #AREA_PARAMS_PATH for other arguments */ static void toPath( Toolpath& path, const std::list& shapes, const gp_Pnt* pstart = nullptr, gp_Pnt* pend = nullptr, PARAM_ARGS_DEF(PARAM_FARG, AREA_PARAMS_PATH) ); static int project( TopoDS_Shape& out, const TopoDS_Shape& in, const AreaParams* params = nullptr, const TopoDS_Shape* work_plane = nullptr ); static void setWireOrientation(TopoDS_Wire& wire, const gp_Dir& dir, bool ccw); PARAM_ENUM_DECLARE(AREA_PARAMS_PATH) static void abort(bool aborting); static bool aborting(); static void setDefaultParams(const AreaStaticParams& params); static const AreaStaticParams& getDefaultParams(); static void showShape(const TopoDS_Shape& shape, const char* name, const char* fmt = nullptr, ...); }; } // namespace Path #endif // PATH_AREA_H