From dd89260db30f47c1d44f362328e66c326e818bb2 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Mon, 25 Nov 2013 16:50:03 +0100 Subject: [PATCH 01/57] Added ratsnest for GAL --- common/CMakeLists.txt | 2 +- include/ttl/halfedge/hedart.h | 150 +++ include/ttl/halfedge/hetraits.h | 300 +++++ include/ttl/halfedge/hetriang.h | 334 ++++++ include/ttl/ttl.h | 1907 +++++++++++++++++++++++++++++++ include/ttl/ttl_constr.h | 632 ++++++++++ include/ttl/ttl_util.h | 314 +++++ include/wxBasePcbFrame.h | 6 +- include/wxPcbStruct.h | 18 +- pcbnew/CMakeLists.txt | 6 +- pcbnew/basepcbframe.cpp | 111 +- pcbnew/class_board.cpp | 5 + pcbnew/class_board.h | 12 + pcbnew/pcbframe.cpp | 132 ++- pcbnew/ratsnest_data.cpp | 856 ++++++++++++++ pcbnew/ratsnest_data.h | 595 ++++++++++ pcbnew/ratsnest_viewitem.cpp | 110 ++ pcbnew/ratsnest_viewitem.h | 67 ++ pcbnew/tools/move_tool.cpp | 45 +- pcbnew/tools/move_tool.h | 3 +- 20 files changed, 5481 insertions(+), 124 deletions(-) create mode 100644 include/ttl/halfedge/hedart.h create mode 100644 include/ttl/halfedge/hetraits.h create mode 100644 include/ttl/halfedge/hetriang.h create mode 100644 include/ttl/ttl.h create mode 100644 include/ttl/ttl_constr.h create mode 100644 include/ttl/ttl_util.h create mode 100644 pcbnew/ratsnest_data.cpp create mode 100644 pcbnew/ratsnest_data.h create mode 100644 pcbnew/ratsnest_viewitem.cpp create mode 100644 pcbnew/ratsnest_viewitem.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index b4d676ef67..c701b467cc 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,4 +1,3 @@ - include_directories(BEFORE ${INC_BEFORE}) include_directories( ./dialogs @@ -37,6 +36,7 @@ set(GAL_SRCS gal/stroke_font.cpp gal/color4d.cpp view/wx_view_controls.cpp + geometry/hetriang.cpp # OpenGL GAL gal/opengl/opengl_gal.cpp diff --git a/include/ttl/halfedge/hedart.h b/include/ttl/halfedge/hedart.h new file mode 100644 index 0000000000..f85678963a --- /dev/null +++ b/include/ttl/halfedge/hedart.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, + * Applied Mathematics, Norway. + * + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. + * + * This file is part of TTL. + * + * TTL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * TTL 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with TTL. If not, see + * . + * + * In accordance with Section 7(b) of the GNU Affero General Public + * License, a covered work must retain the producer line in every data + * file that is created or manipulated using TTL. + * + * Other Usage + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial activities involving the TTL library without + * disclosing the source code of your own applications. + * + * This file may be used in accordance with the terms contained in a + * written agreement between you and SINTEF ICT. + */ + +#ifndef _HALF_EDGE_DART_ +#define _HALF_EDGE_DART_ + + +#include + + +namespace hed { + + + //------------------------------------------------------------------------------------------------ + // Dart class for the half-edge data structure + //------------------------------------------------------------------------------------------------ + + /** \class Dart + * \brief \b %Dart class for the half-edge data structure. + * + * See \ref api for a detailed description of how the member functions + * should be implemented. + */ + + class Dart { + + EdgePtr edge_; + bool dir_; // true if dart is counterclockwise in face + + public: + /// Default constructor + Dart() { dir_ = true; } + + /// Constructor + Dart(const EdgePtr& edge, bool dir = true) { edge_ = edge; dir_ = dir; } + + /// Copy constructor + Dart(const Dart& dart) { edge_ = dart.edge_; dir_ = dart.dir_; } + + /// Destructor + ~Dart() {} + + /// Assignment operator + Dart& operator = (const Dart& dart) { + if (this == &dart) + return *this; + edge_ = dart.edge_; + dir_ = dart.dir_; + return *this; + } + + /// Comparing dart objects + bool operator==(const Dart& dart) const { + if (dart.edge_ == edge_ && dart.dir_ == dir_) + return true; + return false; + } + + /// Comparing dart objects + bool operator!=(const Dart& dart) const { + return !(dart==*this); + } + + /// Maps the dart to a different node + Dart& alpha0() { dir_ = !dir_; return *this; } + + /// Maps the dart to a different edge + Dart& alpha1() { + if (dir_) { + edge_ = edge_->getNextEdgeInFace()->getNextEdgeInFace(); + dir_ = false; + } + else { + edge_ = edge_->getNextEdgeInFace(); + dir_ = true; + } + return *this; + } + + /// Maps the dart to a different triangle. \b Note: the dart is not changed if it is at the boundary! + Dart& alpha2() { + if (edge_->getTwinEdge()) { + edge_ = edge_->getTwinEdge(); + dir_ = !dir_; + } + // else, the dart is at the boundary and should not be changed + return *this; + } + + + // Utilities not required by TTL + // ----------------------------- + + /** @name Utilities not required by TTL */ + //@{ + + void init(const EdgePtr& edge, bool dir = true) { edge_ = edge; dir_ = dir; } + + double x() const { return getNode()->GetX(); } // x-coordinate of source node + double y() const { return getNode()->GetY(); } // y-coordinate of source node + + bool isCounterClockWise() const { return dir_; } + + const NodePtr& getNode() const { return dir_ ? edge_->getSourceNode() : edge_->getTargetNode(); } + const NodePtr& getOppositeNode() const { return dir_ ? edge_->getTargetNode() : edge_->getSourceNode(); } + EdgePtr& getEdge() { return edge_; } + + //@} // End of Utilities not required by TTL + + }; + +}; // End of hed namespace + +#endif diff --git a/include/ttl/halfedge/hetraits.h b/include/ttl/halfedge/hetraits.h new file mode 100644 index 0000000000..e25e993e0e --- /dev/null +++ b/include/ttl/halfedge/hetraits.h @@ -0,0 +1,300 @@ +/* + * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, + * Applied Mathematics, Norway. + * + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. + * + * This file is part of TTL. + * + * TTL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * TTL 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with TTL. If not, see + * . + * + * In accordance with Section 7(b) of the GNU Affero General Public + * License, a covered work must retain the producer line in every data + * file that is created or manipulated using TTL. + * + * Other Usage + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial activities involving the TTL library without + * disclosing the source code of your own applications. + * + * This file may be used in accordance with the terms contained in a + * written agreement between you and SINTEF ICT. + */ + +#ifndef _HALF_EDGE_TRAITS_ +#define _HALF_EDGE_TRAITS_ + + +#include +#include + + +namespace hed { + + + //------------------------------------------------------------------------------------------------ + // Traits class for the half-edge data structure + //------------------------------------------------------------------------------------------------ + + /** \struct TTLtraits + * \brief \b Traits class (static struct) for the half-edge data structure. + * + * The member functions are those required by different function templates + * in the TTL. Documentation is given here to explain what actions + * should be carried out on the actual data structure as required by the functions + * in the \ref ttl namespace. + * + * The source code of \c %HeTraits.h shows how the traits class is implemented for the + * half-edge data structure. + * + * \see \ref api + * + */ + + struct TTLtraits { + + // The actual triangulation object + static Triangulation* triang_; + + /** The floating point type used in calculations + * involving scalar products and cross products. + */ + typedef double real_type; + + + //---------------------------------------------------------------------------------------------- + // ------------------------------- Geometric Predicates Group --------------------------------- + //---------------------------------------------------------------------------------------------- + + /** @name Geometric Predicates */ + //@{ + + //---------------------------------------------------------------------------------------------- + /** Scalar product between two 2D vectors represented as darts.\n + * + * ttl_util::scalarProduct2d can be used. + */ + static real_type scalarProduct2d(const Dart& v1, const Dart& v2) { + Dart v10 = v1; v10.alpha0(); + Dart v20 = v2; v20.alpha0(); + return ttl_util::scalarProduct2d(v10.x()-v1.x(), v10.y()-v1.y(), + v20.x()-v2.x(), v20.y()-v2.y()); + } + + + //---------------------------------------------------------------------------------------------- + /** Scalar product between two 2D vectors. + * The first vector is represented by a dart \e v, and the second + * vector has direction from the source node of \e v to the point \e p.\n + * + * ttl_util::scalarProduct2d can be used. + */ + static real_type scalarProduct2d(const Dart& v, const NodePtr& p) { + Dart d0 = v; d0.alpha0(); + return ttl_util::scalarProduct2d(d0.x() - v.x(), d0.y() - v.y(), + p->GetX() - v.x(), p->GetY() - v.y()); + } + + + //---------------------------------------------------------------------------------------------- + /** Cross product between two vectors in the plane represented as darts. + * The z-component of the cross product is returned.\n + * + * ttl_util::crossProduct2d can be used. + */ + static real_type crossProduct2d(const Dart& v1, const Dart& v2) { + Dart v10 = v1; v10.alpha0(); + Dart v20 = v2; v20.alpha0(); + return ttl_util::crossProduct2d(v10.x()-v1.x(), v10.y()-v1.y(), + v20.x()-v2.x(), v20.y()-v2.y()); + } + + + //---------------------------------------------------------------------------------------------- + /** Cross product between two vectors in the plane. + * The first vector is represented by a dart \e v, and the second + * vector has direction from the source node of \e v to the point \e p. + * The z-component of the cross product is returned.\n + * + * ttl_util::crossProduct2d can be used. + */ + static real_type crossProduct2d(const Dart& v, const NodePtr& p) { + Dart d0 = v; d0.alpha0(); + return ttl_util::crossProduct2d(d0.x() - v.x(), d0.y() - v.y(), + p->GetX() - v.x(), p->GetY() - v.y()); + } + + + //---------------------------------------------------------------------------------------------- + /** Let \e n1 and \e n2 be the nodes associated with two darts, and let \e p + * be a point in the plane. Return a positive value if \e n1, \e n2, + * and \e p occur in counterclockwise order; a negative value if they occur + * in clockwise order; and zero if they are collinear. + */ + static real_type orient2d(const Dart& n1, const Dart& n2, const NodePtr& p) { + real_type pa[2]; real_type pb[2]; real_type pc[2]; + pa[0] = n1.x(); pa[1] = n1.y(); + pb[0] = n2.x(); pb[1] = n2.y(); + pc[0] = p->GetX(); pc[1] = p->GetY(); + return ttl_util::orient2dfast(pa, pb, pc); + } + + + //---------------------------------------------------------------------------------------------- + /** This is the same predicate as represented with the function above, + * but with a slighty different interface: + * The last parameter is given as a dart where the source node of the dart + * represents a point in the plane. + * This function is required for constrained triangulation. + */ + static real_type orient2d(const Dart& n1, const Dart& n2, const Dart& p) { + real_type pa[2]; real_type pb[2]; real_type pc[2]; + pa[0] = n1.x(); pa[1] = n1.y(); + pb[0] = n2.x(); pb[1] = n2.y(); + pc[0] = p.x(); pc[1] = p.y(); + return ttl_util::orient2dfast(pa, pb, pc); + } + + //@} // End of Geometric Predicates Group + + + // A rationale for directing these functions to traits is: + // e.g., constraints + + //---------------------------------------------------------------------------------------------- + /* Checks if the edge associated with \e dart should be swapped + * according to the Delaunay criterion.
+ * + * \note + * This function is also present in the TTL as ttl::swapTestDelaunay.
+ * Thus, the function can be implemented simply as: + * \code + * { return ttl::swapTestDelaunay(dart); } + * \endcode + */ + //static bool swapTestDelaunay(const Dart& dart) { + // return ttl::swapTestDelaunay(dart); + //} + + + //---------------------------------------------------------------------------------------------- + /* Checks if the edge associated with \e dart can be swapped, i.e., + * if the edge is a diagonal in a (strictly) convex quadrilateral. + * This function is also present as ttl::swappableEdge. + */ + //static bool swappableEdge(const Dart& dart) { + // return ttl::swappableEdge(dart); + //} + + + //---------------------------------------------------------------------------------------------- + /* Checks if the edge associated with \e dart should be \e fixed, meaning + * that it should never be swapped. ??? Use when constraints. + */ + //static bool fixedEdge(const Dart& dart) { + // return dart.getEdge()->isConstrained(); + //} + + + //---------------------------------------------------------------------------------------------- + // ----------------------- Functions for Delaunay Triangulation Group ------------------------- + //---------------------------------------------------------------------------------------------- + + /** @name Functions for Delaunay Triangulation */ + //@{ + + //---------------------------------------------------------------------------------------------- + /** Swaps the edge associated with \e dart in the actual data structure. + * + *
+ * \image html swapEdge.gif + *
+ * + * \param dart + * Some of the functions require a dart as output. + * If this is required by the actual function, the dart should be delivered + * back in a position as seen if it was glued to the edge when swapping (rotating) + * the edge CCW; see the figure. + * + * \note + * - If the edge is \e constrained, or if it should not be swapped for + * some other reason, this function need not do the actual swap of the edge. + * - Some functions in TTL require that \c swapEdge is implemented such that + * darts outside the quadrilateral are not affected by the swap. + */ + static void swapEdge(Dart& dart) { + if (!dart.getEdge()->isConstrained()) triang_->swapEdge(dart.getEdge()); + } + + + //---------------------------------------------------------------------------------------------- + /** Splits the triangle associated with \e dart in the actual data structure into + * three new triangles joining at \e point. + * + *
+ * \image html splitTriangle.gif + *
+ * + * \param dart + * Output: A CCW dart incident with the new node; see the figure. + */ + static void splitTriangle(Dart& dart, NodePtr point) { + EdgePtr edge = triang_->splitTriangle(dart.getEdge(), point); + dart.init(edge); + } + + //@} // End of Functions for Delaunay Triangulation group + + + //---------------------------------------------------------------------------------------------- + // --------------------------- Functions for removing nodes Group ----------------------------- + //---------------------------------------------------------------------------------------------- + + /** @name Functions for removing nodes */ + //@{ + + //---------------------------------------------------------------------------------------------- + /** The reverse operation of TTLtraits::splitTriangle. + * This function is only required for functions that involve + * removal of interior nodes; see for example ttl::removeInteriorNode. + * + *
+ * \image html reverse_splitTriangle.gif + *
+ */ + static void reverse_splitTriangle(Dart& dart) { + triang_->reverse_splitTriangle(dart.getEdge()); + } + + + //---------------------------------------------------------------------------------------------- + /** Removes a triangle with an edge at the boundary of the triangulation + * in the actual data structure + */ + static void removeBoundaryTriangle(Dart& d) { + triang_->removeTriangle(d.getEdge()); + } + + //@} // End of Functions for removing nodes Group + + }; + +}; // End of hed namespace + +#endif diff --git a/include/ttl/halfedge/hetriang.h b/include/ttl/halfedge/hetriang.h new file mode 100644 index 0000000000..ccee31bc0c --- /dev/null +++ b/include/ttl/halfedge/hetriang.h @@ -0,0 +1,334 @@ +/* + * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, + * Applied Mathematics, Norway. + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. + * + * This file is part of TTL. + * + * TTL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * TTL 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with TTL. If not, see + * . + * + * In accordance with Section 7(b) of the GNU Affero General Public + * License, a covered work must retain the producer line in every data + * file that is created or manipulated using TTL. + * + * Other Usage + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial activities involving the TTL library without + * disclosing the source code of your own applications. + * + * This file may be used in accordance with the terms contained in a + * written agreement between you and SINTEF ICT. + */ + +#ifndef _HE_TRIANG_H_ +#define _HE_TRIANG_H_ + + +#define TTL_USE_NODE_ID // Each node gets it's own unique id +#define TTL_USE_NODE_FLAG // Each node gets a flag (can be set to true or false) + + +#include +#include +#include +#include +#include +#include +#include + +//-------------------------------------------------------------------------------------------------- +// The half-edge data structure +//-------------------------------------------------------------------------------------------------- + +namespace hed { + // Helper typedefs + class Node; + class Edge; + typedef boost::shared_ptr NodePtr; + typedef boost::shared_ptr EdgePtr; + typedef std::vector NodesContainer; + + //------------------------------------------------------------------------------------------------ + // Node class for data structures + //------------------------------------------------------------------------------------------------ + + /** \class Node + * \brief \b Node class for data structures (Inherits from HandleId) + * + * \note + * - To enable node IDs, TTL_USE_NODE_ID must be defined. + * - To enable node flags, TTL_USE_NODE_FLAG must be defined. + * - TTL_USE_NODE_ID and TTL_USE_NODE_FLAG should only be enabled if this functionality is + * required by the application, because they increase the memory usage for each Node object. + */ + + class Node { + +protected: +#ifdef TTL_USE_NODE_FLAG + /// TTL_USE_NODE_FLAG must be defined + bool flag_; +#endif + +#ifdef TTL_USE_NODE_ID + /// TTL_USE_NODE_ID must be defined + static int id_count; + + /// A unique id for each node (TTL_USE_NODE_ID must be defined) + int id_; +#endif + + int x_, y_; + + unsigned int refCount_; + +public: + /// Constructor + Node( int x = 0, int y = 0 ) : +#ifdef TTL_USE_NODE_FLAG + flag_( false ), +#endif +#ifdef TTL_USE_NODE_ID + id_( id_count++ ), +#endif + x_( x ), y_( y ), refCount_( 0 ) {} + + /// Destructor + ~Node() {} + + /// Returns the x-coordinate + int GetX() const { return x_; } + + /// Returns the y-coordinate + int GetY() const { return y_; } + +#ifdef TTL_USE_NODE_ID + /// Returns the id (TTL_USE_NODE_ID must be defined) + int Id() const { return id_; } +#endif + +#ifdef TTL_USE_NODE_FLAG + /// Sets the flag (TTL_USE_NODE_FLAG must be defined) + void SetFlag(bool aFlag) { flag_ = aFlag; } + + /// Returns the flag (TTL_USE_NODE_FLAG must be defined) + const bool& GetFlag() const { return flag_; } +#endif + + void IncRefCount() { refCount_++; } + void DecRefCount() { refCount_--; } + unsigned int GetRefCount() const { return refCount_; } + }; // End of class Node + + + //------------------------------------------------------------------------------------------------ + // Edge class in the half-edge data structure + //------------------------------------------------------------------------------------------------ + + /** \class Edge + * \brief \b %Edge class in the in the half-edge data structure. + */ + + class Edge { + public: + /// Constructor + Edge() : weight_(0) + { flags_.isLeadingEdge_ = false; flags_.isConstrained_ = false; } + + /// Destructor + virtual ~Edge() {} + + /// Sets the source node + void setSourceNode(const NodePtr& node) { sourceNode_ = node; } + + /// Sets the next edge in face + void setNextEdgeInFace(const EdgePtr& edge) { nextEdgeInFace_ = edge; } + + /// Sets the twin edge + void setTwinEdge(const EdgePtr& edge) { twinEdge_ = edge; } + + /// Sets the edge as a leading edge + void setAsLeadingEdge(bool val=true) { flags_.isLeadingEdge_ = val; } + + /// Checks if an edge is a leading edge + bool isLeadingEdge() const { return flags_.isLeadingEdge_; } + + /// Sets the edge as a constrained edge + void setConstrained(bool val=true) { flags_.isConstrained_ = val; + if (twinEdge_) twinEdge_->flags_.isConstrained_ = val; } + + /// Checks if an edge is constrained + bool isConstrained() const { return flags_.isConstrained_; } + + /// Returns the twin edge + const EdgePtr& getTwinEdge() const { return twinEdge_; }; + + /// Returns the next edge in face + const EdgePtr& getNextEdgeInFace() const { return nextEdgeInFace_; } + + /// Retuns the source node + virtual const NodePtr& getSourceNode() const { return sourceNode_; } + + /// Returns the target node + virtual const NodePtr& getTargetNode() const { return getNextEdgeInFace()->getSourceNode(); } + + void setWeight( unsigned int weight ) { weight_ = weight; } + + unsigned int getWeight() const { return weight_; } + + protected: + NodePtr sourceNode_; + EdgePtr twinEdge_; + EdgePtr nextEdgeInFace_; + unsigned int weight_; + + struct { + bool isLeadingEdge_; + bool isConstrained_; + } flags_; + }; // End of class Edge + + + /** \class EdgeMST + * \brief \b %Specialization of Edge class to be used for Minimum Spanning Tree algorithm. + */ + class EdgeMST : public Edge + { + private: + NodePtr target_; + + public: + EdgeMST( const NodePtr& source, const NodePtr& target, unsigned int weight = 0 ) : + target_(target) + { sourceNode_ = source; weight_ = weight; } + + ~EdgeMST() {}; + + /// @copydoc Edge::setSourceNode() + const NodePtr& getTargetNode() const { return target_; } + }; + + + //------------------------------------------------------------------------------------------------ + class Dart; // Forward declaration (class in this namespace) + + //------------------------------------------------------------------------------------------------ + // Triangulation class in the half-edge data structure + //------------------------------------------------------------------------------------------------ + + /** \class Triangulation + * \brief \b %Triangulation class for the half-edge data structure with adaption to TTL. + */ + + class Triangulation { + + protected: + list leadingEdges_; // one half-edge for each arc + void addLeadingEdge(EdgePtr& edge) { + edge->setAsLeadingEdge(); + leadingEdges_.push_front( edge ); + } + bool removeLeadingEdgeFromList(EdgePtr& leadingEdge); + void cleanAll(); + + public: + /// Default constructor + Triangulation() {} + + /// Copy constructor + Triangulation(const Triangulation& tr) { + std::cout << "Triangulation: Copy constructor not present - EXIT."; + exit(-1); + } + + /// Destructor + ~Triangulation() { cleanAll(); } + + /// Creates a Delaunay triangulation from a set of points + void createDelaunay(NodesContainer::iterator first, + NodesContainer::iterator last); + + /// Creates an initial Delaunay triangulation from two enclosing triangles + // When using rectangular boundary - loop through all points and expand. + // (Called from createDelaunay(...) when starting) + EdgePtr initTwoEnclosingTriangles(NodesContainer::iterator first, + NodesContainer::iterator last); + + + // These two functions are required by TTL for Delaunay triangulation + + /// Swaps the edge associated with diagonal + void swapEdge(EdgePtr& diagonal); + + /// Splits the triangle associated with edge into three new triangles joining at point + EdgePtr splitTriangle(EdgePtr& edge, NodePtr& point); + + + // Functions required by TTL for removing nodes in a Delaunay triangulation + + /// Removes the boundary triangle associated with edge + void removeTriangle(EdgePtr& edge); // boundary triangle required + + /// The reverse operation of removeTriangle + void reverse_splitTriangle(EdgePtr& edge); + + /// Creates an arbitrary CCW dart + Dart createDart(); + + /// Returns a list of "triangles" (one leading half-edge for each triangle) + const list& getLeadingEdges() const { return leadingEdges_; } + + /// Returns the number of triangles + int noTriangles() const { return (int)leadingEdges_.size(); } + + /// Returns a list of half-edges (one half-edge for each arc) + list* getEdges(bool skip_boundary_edges = false) const; + +#ifdef TTL_USE_NODE_FLAG + /// Sets flag in all the nodes + void flagNodes(bool flag) const; + + /// Returns a list of nodes. This function requires TTL_USE_NODE_FLAG to be defined. \see Node. + list* getNodes() const; +#endif + + /// Swaps edges until the triangulation is Delaunay (constrained edges are not swapped) + void optimizeDelaunay(); + + /// Checks if the triangulation is Delaunay + bool checkDelaunay() const; + + /// Returns an arbitrary interior node (as the source node of the returned edge) + EdgePtr getInteriorNode() const; + + /// Returns an arbitrary boundary edge + EdgePtr getBoundaryEdge() const; + + /// Print edges for plotting with, e.g., gnuplot + void printEdges(std::ofstream& os) const; + + }; // End of class Triangulation + + +}; // End of hed namespace + +#endif diff --git a/include/ttl/ttl.h b/include/ttl/ttl.h new file mode 100644 index 0000000000..003e0d81c6 --- /dev/null +++ b/include/ttl/ttl.h @@ -0,0 +1,1907 @@ +/* + * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, + * Applied Mathematics, Norway. + * + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. + * + * This file is part of TTL. + * + * TTL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * TTL 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with TTL. If not, see + * . + * + * In accordance with Section 7(b) of the GNU Affero General Public + * License, a covered work must retain the producer line in every data + * file that is created or manipulated using TTL. + * + * Other Usage + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial activities involving the TTL library without + * disclosing the source code of your own applications. + * + * This file may be used in accordance with the terms contained in a + * written agreement between you and SINTEF ICT. + */ + +#ifndef _TTL_H_ +#define _TTL_H_ + + +#include +#include + +// Debugging +#ifdef DEBUG_TTL + static void errorAndExit(char* message) { + cout << "\n!!! ERROR: " << message << " !!!\n" << endl; + exit(-1); + } +#endif + + using std::list; + + +// Next on TOPOLOGY: +// - get triangle strips +// - weighted graph, algorithms using a weight (real) for each edge, +// e.g. an "abstract length". Use for minimum spanning tree +// or some arithmetics on weights? +// - Circulators as defined in CGAL with more STL compliant code + +// - analyze in detail locateFace: e.g. detect 0-orbit in case of infinite loop +// around a node etc. + + +/** \brief Main interface to TTL +* +* This namespace contains the basic generic algorithms for the TTL, +* the Triangulation Template Library.\n +* +* Examples of functionality are: +* - Incremental Delaunay triangulation +* - Constrained triangulation +* - Insert/remove nodes and constrained edges +* - Traversal operations +* - Misc. queries for extracting information for visualisation systems etc. +* +* \par General requirements and assumptions: +* - \e DartType and \e TraitsType should be implemented in accordance with the description +* in \ref api. +* - A \b "Requires:" section in the documentation of a function template +* shows which functionality is required in \e TraitsType to +* support that specific function.\n +* Functionalty required in \e DartType is the same (almost) for all +* function templates; see \ref api and the example referred to. +* - When a reference to a \e dart object is passed to a function in TTL, +* it is assumed that it is oriented \e counterclockwise (CCW) in a triangle +* unless it is explicitly mentioned that it can also be \e clockwise (CW). +* The same applies for a dart that is passed from a function in TTL to +* the users TraitsType class (or struct). +* - When an edge (represented with a dart) is swapped, it is assumed that darts +* outside the quadrilateral where the edge is a diagonal are not affected by +* the swap. Thus, \ref hed::TTLtraits::swapEdge "TraitsType::swapEdge" +* must be implemented in accordance with this rule. +* +* \par Glossary: +* - General terms are explained in \ref api. +* - \e CCW - counterclockwise +* - \e CW - clockwise +* - \e 0_orbit, \e 1_orbit and \e 2_orbit: A sequence of darts around +* a node, around an edge and in a triangle respectively; +* see ttl::get_0_orbit_interior and ttl::get_0_orbit_boundary +* - \e arc - In a triangulation an arc is equivalent with an edge +* +* \see +* \ref ttl_util and \ref api +* +* \author +* �yvind Hjelle, oyvindhj@ifi.uio.no +*/ + + +namespace ttl { + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + //------------------------------------------------------------------------------------------------ + // ----------------------------------- Forward declarations ------------------------------------- + //------------------------------------------------------------------------------------------------ + +#if ((_MSC_VER > 0) && (_MSC_VER < 1300)) +#else + + // Delaunay Triangulation + // ---------------------- + template + bool insertNode(DartType& dart, PointType& point); + + template + void removeRectangularBoundary(DartType& dart); + + template + void removeNode(DartType& dart); + + template + void removeBoundaryNode(DartType& dart); + + template + void removeInteriorNode(DartType& dart); + + + // Topological and Geometric Queries + // --------------------------------- + template + bool locateFaceSimplest(const PointType& point, DartType& dart); + + template + bool locateTriangle(const PointType& point, DartType& dart); + + template + bool inTriangleSimplest(const PointType& point, const DartType& dart); + + template + bool inTriangle(const PointType& point, const DartType& dart); + + template + void getBoundary(const DartType& dart, DartListType& boundary); + + template + bool isBoundaryEdge(const DartType& dart); + + template + bool isBoundaryFace(const DartType& dart); + + template + bool isBoundaryNode(const DartType& dart); + + template + int getDegreeOfNode(const DartType& dart); + + template + void get_0_orbit_interior(const DartType& dart, DartListType& orbit); + + template + void get_0_orbit_boundary(const DartType& dart, DartListType& orbit); + + template + bool same_0_orbit(const DartType& d1, const DartType& d2); + + template + bool same_1_orbit(const DartType& d1, const DartType& d2); + + template + bool same_2_orbit(const DartType& d1, const DartType& d2); + + template + bool swappableEdge(const DartType& dart, bool allowDegeneracy = false); + + template + void positionAtNextBoundaryEdge(DartType& dart); + + template + bool convexBoundary(const DartType& dart); + + + // Utilities for Delaunay Triangulation + // ------------------------------------ + template + void optimizeDelaunay(DartListType& elist); + + template + void optimizeDelaunay(DartListType& elist, const typename DartListType::iterator end); + + template + bool swapTestDelaunay(const DartType& dart, bool cycling_check = false); + + template + void recSwapDelaunay(DartType& diagonal); + + template + void swapEdgesAwayFromInteriorNode(DartType& dart, ListType& swapped_edges); + + template + void swapEdgesAwayFromBoundaryNode(DartType& dart, ListType& swapped_edges); + + template + void swapEdgeInList(const typename DartListType::iterator& it, DartListType& elist); + + + // Constrained Triangulation + // ------------------------- + template + DartType insertConstraint(DartType& dstart, DartType& dend, bool optimize_delaunay); + +#endif + +#endif // DOXYGEN_SHOULD_SKIP_THIS + + + //------------------------------------------------------------------------------------------------ + // ------------------------------- Delaunay Triangulation Group --------------------------------- + //------------------------------------------------------------------------------------------------ + + /** @name Delaunay Triangulation */ + //@{ + + //------------------------------------------------------------------------------------------------ + /** Inserts a new node in an existing Delaunay triangulation and + * swaps edges to obtain a new Delaunay triangulation. + * This is the basic function for incremental Delaunay triangulation. + * When starting from a set of points, an initial Delaunay triangulation + * can be created as two triangles forming a rectangle that contains + * all the points. + * After \c insertNode has been called repeatedly with all the points, + * ttl::removeRectangularBoundary can be called to remove triangles + * at the boundary of the triangulation so that the boundary + * form the convex hull of the points. + * + * Note that this incremetal scheme will run much faster if the points + * have been sorted lexicographically on \e x and \e y. + * + * \param dart + * An arbitrary CCW dart in the tringulation.\n + * Output: A CCW dart incident to the new node. + * + * \param point + * A point (node) to be inserted in the triangulation. + * + * \retval bool + * \c true if \e point was inserted; \c false if not.\n + * If \e point is outside the triangulation, or the input dart is not valid, + * \c false is returned. + * + * \require + * - \ref hed::TTLtraits::splitTriangle "TraitsType::splitTriangle" (DartType&, const PointType&) + * + * \using + * - ttl::locateTriangle + * - ttl::recSwapDelaunay + * + * \note + * - For efficiency reasons \e dart should be close to the insertion \e point. + * + * \see + * ttl::removeRectangularBoundary + */ + template + bool insertNode(DartType& dart, PointType& point) { + + bool found = ttl::locateTriangle(point, dart); + if (!found) { +#ifdef DEBUG_TTL + cout << "ERROR: Triangulation::insertNode: NO triangle found. /n"; +#endif + return false; + } + + // ??? can we hide the dart? this is not possible if one triangle only + TraitsType::splitTriangle(dart, point); + + DartType d1 = dart; + d1.alpha2().alpha1().alpha2().alpha0().alpha1(); + + DartType d2 = dart; + d2.alpha1().alpha0().alpha1(); + + // Preserve a dart as output incident to the node and CCW + DartType d3 = dart; + d3.alpha2(); + dart = d3; // and see below + //DartType dsav = d3; + d3.alpha0().alpha1(); + + //if (!TraitsType::fixedEdge(d1) && !ttl::isBoundaryEdge(d1)) { + if (!ttl::isBoundaryEdge(d1)) { + d1.alpha2(); + recSwapDelaunay(d1); + } + + //if (!TraitsType::fixedEdge(d2) && !ttl::isBoundaryEdge(d2)) { + if (!ttl::isBoundaryEdge(d2)) { + d2.alpha2(); + recSwapDelaunay(d2); + } + + // Preserve the incoming dart as output incident to the node and CCW + //d = dsav.alpha2(); + dart.alpha2(); + //if (!TraitsType::fixedEdge(d3) && !ttl::isBoundaryEdge(d3)) { + if (!ttl::isBoundaryEdge(d3)) { + d3.alpha2(); + recSwapDelaunay(d3); + } + + return true; + } + + + //------------------------------------------------------------------------------------------------ + // Private/Hidden function (might change later) + template + void insertNodes(ForwardIterator first, ForwardIterator last, DartType& dart) { + + // Assumes that the dereferenced point objects are pointers. + // References to the point objects are then passed to TTL. + + ForwardIterator it; + for (it = first; it != last; ++it) { + bool status = insertNode(dart, **it); + } + } + + + //------------------------------------------------------------------------------------------------ + /** Removes the rectangular boundary of a triangulation as a final step of an + * incremental Delaunay triangulation. + * The four nodes at the corners will be removed and the resulting triangulation + * will have a convex boundary and be Delaunay. + * + * \param dart + * A CCW dart at the boundary of the triangulation\n + * Output: A CCW dart at the new boundary + * + * \using + * - ttl::removeBoundaryNode + * + * \note + * - This function requires that the boundary of the triangulation is + * a rectangle with four nodes (one in each corner). + */ + template + void removeRectangularBoundary(DartType& dart) { + + DartType d_next = dart; + DartType d_iter; + + for (int i = 0; i < 4; i++) { + d_iter = d_next; + d_next.alpha0(); + ttl::positionAtNextBoundaryEdge(d_next); + ttl::removeBoundaryNode(d_iter); + } + + dart = d_next; // Return a dart at the new boundary + } + + + //------------------------------------------------------------------------------------------------ + /** Removes the node associated with \e dart and + * updates the triangulation to be Delaunay. + * + * \using + * - ttl::removeBoundaryNode if \e dart represents a node at the boundary + * - ttl::removeInteriorNode if \e dart represents an interior node + * + * \note + * - The node cannot belong to a fixed (constrained) edge that is not + * swappable. (An endless loop is likely to occur in this case). + */ + template + void removeNode(DartType& dart) { + + if (ttl::isBoundaryNode(dart)) + ttl::removeBoundaryNode(dart); + else + ttl::removeInteriorNode(dart); + } + + + //------------------------------------------------------------------------------------------------ + /** Removes the boundary node associated with \e dart and + * updates the triangulation to be Delaunay. + * + * \using + * - ttl::swapEdgesAwayFromBoundaryNode + * - ttl::optimizeDelaunay + * + * \require + * - \ref hed::TTLtraits::removeBoundaryTriangle "TraitsType::removeBoundaryTriangle" (Dart&) + */ + template + void removeBoundaryNode(DartType& dart) { + + // ... and update Delaunay + // - CCW dart must be given (for remove) + // - No dart is delivered back now (but this is possible if + // we assume that there is not only one triangle left in the triangulation. + + // Position at boundary edge and CCW + if (!ttl::isBoundaryEdge(dart)) { + dart.alpha1(); // ensures that next function delivers back a CCW dart (if the given dart is CCW) + ttl::positionAtNextBoundaryEdge(dart); + } + + list swapped_edges; + ttl::swapEdgesAwayFromBoundaryNode(dart, swapped_edges); + + // Remove boundary triangles and remove the new boundary from the list + // of swapped edges, see below. + DartType d_iter = dart; + DartType dnext = dart; + bool bend = false; + while (bend == false) { + dnext.alpha1().alpha2(); + if (ttl::isBoundaryEdge(dnext)) + bend = true; // Stop when boundary + + // Generic: Also remove the new boundary from the list of swapped edges + DartType n_bedge = d_iter; + n_bedge.alpha1().alpha0().alpha1().alpha2(); // new boundary edge + + // ??? can we avoid find if we do this in swap away? + typename list::iterator it; + it = find(swapped_edges.begin(), swapped_edges.end(), n_bedge); + + if (it != swapped_edges.end()) + swapped_edges.erase(it); + + // Remove the boundary triangle + TraitsType::removeBoundaryTriangle(d_iter); + d_iter = dnext; + } + + // Optimize Delaunay + typedef list DartListType; + ttl::optimizeDelaunay(swapped_edges); + } + + + //------------------------------------------------------------------------------------------------ + /** Removes the interior node associated with \e dart and + * updates the triangulation to be Delaunay. + * + * \using + * - ttl::swapEdgesAwayFromInteriorNode + * - ttl::optimizeDelaunay + * + * \require + * - \ref hed::TTLtraits::reverse_splitTriangle "TraitsType::reverse_splitTriangle" (Dart&) + * + * \note + * - The node cannot belong to a fixed (constrained) edge that is not + * swappable. (An endless loop is likely to occur in this case). + */ + template + void removeInteriorNode(DartType& dart) { + + // ... and update to Delaunay. + // Must allow degeneracy temporarily, see comments in swap edges away + // Assumes: + // - revese_splitTriangle does not affect darts + // outside the resulting triangle. + + // 1) Swaps edges away from the node until degree=3 (generic) + // 2) Removes the remaining 3 triangles and creates a new to fill the hole + // unsplitTriangle which is required + // 3) Runs LOP on the platelet to obtain a Delaunay triangulation + // (No dart is delivered as output) + + // Assumes dart is counterclockwise + + list swapped_edges; + ttl::swapEdgesAwayFromInteriorNode(dart, swapped_edges); + + // The reverse operation of split triangle: + // Make one triangle of the three triangles at the node associated with dart + // TraitsType:: + TraitsType::reverse_splitTriangle(dart); + + // ???? Not generic yet if we are very strict: + // When calling unsplit triangle, darts at the three opposite sides may + // change! + // Should we hide them longer away??? This is possible since they cannot + // be boundary edges. + // ----> Or should we just require that they are not changed??? + + // Make the swapped-away edges Delaunay. + // Note the theoretical result: if there are no edges in the list, + // the triangulation is Delaunay already + + ttl::optimizeDelaunay(swapped_edges); + } + + //@} // End of Delaunay Triangulation Group + + + //------------------------------------------------------------------------------------------------ + // -------------------------- Topological and Geometric Queries Group --------------------------- + //------------------------------------------------------------------------------------------------ + + /** @name Topological and Geometric Queries */ + //@{ + + //------------------------------------------------------------------------------------------------ + // Private/Hidden function (might change later) + template + bool isMemberOfFace(const TopologyElementType& topologyElement, const DartType& dart) { + + // Check if the given topology element (node, edge or face) is a member of the face + // Assumes: + // - DartType::isMember(TopologyElementType) + + DartType dart_iter = dart; + do { + if (dart_iter.isMember(topologyElement)) + return true; + dart_iter.alpha0().alpha1(); + } while (dart_iter != dart); + + return false; + } + + + //------------------------------------------------------------------------------------------------ + // Private/Hidden function (might change later) + template + bool locateFaceWithNode(const NodeType& node, DartType& dart_iter) { + // Locate a face in the topology structure with the given node as a member + // Assumes: + // - TraitsType::orient2d(DartType, DartType, NodeType) + // - DartType::isMember(NodeType) + // - Note that if false is returned, the node might still be in the + // topology structure. Application programmer + // should check all if by hypothesis the node is in the topology structure; + // see doc. on locateTriangle. + + bool status = locateFaceSimplest(node, dart_iter); + if (status == false) + return status; + + // True was returned from locateFaceSimplest, but if the located triangle is + // degenerate and the node is on the extension of the edges, + // the node might still be inside. Check if node is a member and return false + // if not. (Still the node might be in the topology structure, see doc. above + // and in locateTriangle(const PointType& point, DartType& dart_iter) + + return isMemberOfFace(node, dart_iter); + } + + + //------------------------------------------------------------------------------------------------ + /** Locates the face containing a given point. + * It is assumed that the tessellation (e.g. a triangulation) is \e regular in the sense that + * there are no holes, the boundary is convex and there are no degenerate faces. + * + * \param point + * A point to be located + * + * \param dart + * An arbitrary CCW dart in the triangulation\n + * Output: A CCW dart in the located face + * + * \retval bool + * \c true if a face is found; \c false if not. + * + * \require + * - \ref hed::TTLtraits::orient2d "TraitsType::orient2d" (DartType&, DartType&, PointType&) + * + * \note + * - If \c false is returned, \e point may still be inside a face if the tessellation is not + * \e regular as explained above. + * + * \see + * ttl::locateTriangle + */ + template + bool locateFaceSimplest(const PointType& point, DartType& dart) { + // Not degenerate triangles if point is on the extension of the edges + // But inTriangle may be called in case of true (may update to inFace2) + // Convex boundary + // no holes + // convex faces (works for general convex faces) + // Not specialized for triangles, but ok? + // + // TraitsType::orint2d(PointType) is the half open half-plane defined + // by the dart: + // n1 = dart.node() + // n2 = dart.alpha0().node + // Only the following gives true: + // ((n2->x()-n1->x())*(point.y()-n1->y()) >= (point.x()-n1->x())*(n2->y()-n1->y())) + + DartType dart_start; + dart_start = dart; + DartType dart_prev; + + DartType d0; + for (;;) { + d0 = dart; + d0.alpha0(); + if (TraitsType::orient2d(dart, d0, point) >= 0) { + dart.alpha0().alpha1(); + if (dart == dart_start) + return true; // left to all edges in face + } + else { + dart_prev = dart; + dart.alpha2(); + if (dart == dart_prev) + return false; // iteration to outside boundary + + dart_start = dart; + dart_start.alpha0(); + + dart.alpha1(); // avoid twice on same edge and ccw in next + } + } + } + + + //------------------------------------------------------------------------------------------------ + /** Locates the triangle containing a given point. + * It is assumed that the triangulation is \e regular in the sense that there + * are no holes and the boundary is convex. + * This function deals with degeneracy to some extent, but round-off errors may still + * lead to a wrong result if triangles are degenerate. + * + * \param point + * A point to be located + * + * \param dart + * An arbitrary CCW dart in the triangulation\n + * Output: A CCW dart in the located triangle + * + * \retval bool + * \c true if a triangle is found; \c false if not.\n + * If \e point is outside the triangulation, in which case \c false is returned, + * then the edge associated with \e dart will be at the boundary of the triangulation. + * + * \using + * - ttl::locateFaceSimplest + * - ttl::inTriangle + */ + template + bool locateTriangle(const PointType& point, DartType& dart) { + // The purpose is to have a fast and stable procedure that + // i) avoids concluding that a point is inside a triangle if it is not inside + // ii) avoids infinite loops + + // Thus, if false is returned, the point might still be inside a triangle in + // the triangulation. But this will probably only occur in the following cases: + // i) There are holes in the triangulation which causes the procedure to stop. + // ii) The boundary of the triangulation is not convex. + // ii) There might be degenerate triangles interior to the triangulation, or on the + // the boundary, which in some cases might cause the procedure to stop there due + // to the logic of the algorithm. + + // It is the application programmer's responsibility to check further if false is + // returned. For example, if by hypothesis the point is inside a triangle + // in the triangulation and and false is returned, then all triangles in the + // triangulation should be checked by the application. This can be done using + // the function: + // bool inTriangle(const PointType& point, const DartType& dart). + + + // Assumes: + // - crossProduct2d, scalarProduct2d etc., see functions called + + bool status = locateFaceSimplest(point, dart); + if (status == false) + return status; + + // There may be degeneracy, i.e., the point might be outside the triangle + // on the extension of the edges of a degenerate triangle. + + // The next call returns true if inside a non-degenerate or a degenerate triangle, + // but false if the point coincides with the "supernode" in the case where all + // edges are degenerate. + return inTriangle(point, dart); + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if \e point is inside the triangle associated with \e dart. + * A fast and simple function that does not deal with degeneracy. + * + * \param dart + * A CCW dart in the triangle + * + * \require + * - \ref hed::TTLtraits::orient2d "TraitsType::orient2d" (DartType&, DartType&, PointType&) + * + * \see + * ttl::inTriangle for a more robust function + */ + template + bool inTriangleSimplest(const PointType& point, const DartType& dart) { + + // Fast and simple: Do not deal with degenerate faces, i.e., if there is + // degeneracy, true will be returned if the point is on the extension of the + // edges of a degenerate triangle + + DartType d_iter = dart; + DartType d0 = d_iter; + d0.alpha0(); + if (!TraitsType::orient2d(d_iter, d0, point) >= 0) + return false; + + d_iter.alpha0().alpha1(); + d0 = d_iter; + d0.alpha0(); + if (!TraitsType::orient2d(d_iter, d0, point) >= 0) + return false; + + d_iter.alpha0().alpha1(); + d0 = d_iter; + d0.alpha0(); + if (!TraitsType::orient2d(d_iter, d0, point) >= 0) + return false; + + return true; + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if \e point is inside the triangle associated with \e dart. + * This function deals with degeneracy to some extent, but round-off errors may still + * lead to wrong result if the triangle is degenerate. + * + * \param dart + * A CCW dart in the triangle + * + * \require + * - \ref hed::TTLtraits::crossProduct2d "TraitsType::crossProduct2d" (DartType&, PointType&) + * - \ref hed::TTLtraits::scalarProduct2d "TraitsType::scalarProduct2d" (DartType&, PointType&) + * + * \see + * ttl::inTriangleSimplest + */ + template + bool inTriangle(const PointType& point, const DartType& dart) { + + // SHOULD WE INCLUDE A STRATEGY WITH EDGE X e_1 ETC? TO GUARANTEE THAT + // ONLY ON ONE EDGE? BUT THIS DOES NOT SOLVE PROBLEMS WITH + // notInE1 && notInE1.neghbour ? + + + // Returns true if inside (but not necessarily strictly inside) + // Works for degenerate triangles, but not when all edges are degenerate, + // and the point coincides with all nodes; + // then false is always returned. + + typedef typename TraitsType::real_type real_type; + + DartType dart_iter = dart; + + real_type cr1 = TraitsType::crossProduct2d(dart_iter, point); + if (cr1 < 0) + return false; + + dart_iter.alpha0().alpha1(); + real_type cr2 = TraitsType::crossProduct2d(dart_iter, point); + + if (cr2 < 0) + return false; + + dart_iter.alpha0().alpha1(); + real_type cr3 = TraitsType::crossProduct2d(dart_iter, point); + if (cr3 < 0) + return false; + + // All cross products are >= 0 + // Check for degeneracy + + if (cr1 != 0 || cr2 != 0 || cr3 != 0) + return true; // inside non-degenerate face + + // All cross-products are zero, i.e. degenerate triangle, check if inside + // Strategy: d.scalarProduct2d >= 0 && alpha0(d).d.scalarProduct2d >= 0 for one of + // the edges. But if all edges are degenerate and the point is on (all) the nodes, + // then "false is returned". + + DartType dart_tmp = dart_iter; + real_type sc1 = TraitsType::scalarProduct2d(dart_tmp,point); + real_type sc2 = TraitsType::scalarProduct2d(dart_tmp.alpha0(), point); + + if (sc1 >= 0 && sc2 >= 0) { + // test for degenerate edge + if (sc1 != 0 || sc2 != 0) + return true; // interior to this edge or on a node (but see comment above) + } + + dart_tmp = dart_iter.alpha0().alpha1(); + sc1 = TraitsType::scalarProduct2d(dart_tmp,point); + sc2 = TraitsType::scalarProduct2d(dart_tmp.alpha0(),point); + if (sc1 >= 0 && sc2 >= 0) { + // test for degenerate edge + if (sc1 != 0 || sc2 != 0) + return true; // interior to this edge or on a node (but see comment above) + } + + dart_tmp = dart_iter.alpha1(); + sc1 = TraitsType::scalarProduct2d(dart_tmp,point); + sc2 = TraitsType::scalarProduct2d(dart_tmp.alpha0(),point); + if (sc1 >= 0 && sc2 >= 0) { + // test for degenerate edge + if (sc1 != 0 || sc2 != 0) + return true; // interior to this edge or on a node (but see comment above) + } + + // Not on any of the edges of the degenerate triangle. + // The only possibility for the point to be "inside" is that all edges are degenerate + // and the point coincide with all nodes. So false is returned in this case. + + return false; + } + + + //------------------------------------------------------------------------------------------------ + // Private/Hidden function (might change later) + template + void getAdjacentTriangles(const DartType& dart, DartType& t1, DartType& t2, DartType& t3) { + + DartType dart_iter = dart; + + // add first + if (dart_iter.alpha2() != dart) { + t1 = dart_iter; + dart_iter = dart; + } + + // add second + dart_iter.alpha0(); + dart_iter.alpha1(); + DartType dart_prev = dart_iter; + if ((dart_iter.alpha2()) != dart_prev) { + t2 = dart_iter; + dart_iter = dart_prev; + } + + // add third + dart_iter.alpha0(); + dart_iter.alpha1(); + dart_prev = dart_iter; + if ((dart_iter.alpha2()) != dart_prev) + t3 = dart_iter; + } + + + //------------------------------------------------------------------------------------------------ + /** Gets the boundary as sequence of darts, where the edges associated with the darts are boundary + * edges, given a dart with an associating edge at the boundary of a topology structure. + * The first dart in the sequence will be the given one, and the others will have the same + * orientation (CCW or CW) as the first. + * Assumes that the given dart is at the boundary. + * + * \param dart + * A dart at the boundary (CCW or CW) + * + * \param boundary + * A sequence of darts, where the associated edges are the boundary edges + * + * \require + * - DartListType::push_back (DartType&) + */ + template + void getBoundary(const DartType& dart, DartListType& boundary) { + // assumes the given dart is at the boundary (by edge) + + DartType dart_iter(dart); + boundary.push_back(dart_iter); // Given dart as first element + dart_iter.alpha0(); + positionAtNextBoundaryEdge(dart_iter); + + while (dart_iter != dart) { + boundary.push_back(dart_iter); + dart_iter.alpha0(); + positionAtNextBoundaryEdge(dart_iter); + } + } + + + //------------------------------------------------------------------------------------------------ + /* + // Asumes a fixed point (a boundary edge) is given + // + template + class boundary_1_Iterator { // i.e. "circulator" + + DartType current_; + public: + boundaryEdgeIterator(const DartType& dart) {current_ = dart;} + DartType& operator * () const {return current_;} + void operator ++ () {current_.alpha0(); positionAtNextBoundaryEdge(current_);} + }; + */ + + + //------------------------------------------------------------------------------------------------ + /** Checks if the edge associated with \e dart is at + * the boundary of the triangulation. + * + * \par Implements: + * \code + * DartType dart_iter = dart; + * if (dart_iter.alpha2() == dart) + * return true; + * else + * return false; + * \endcode + */ + template + bool isBoundaryEdge(const DartType& dart) { + + DartType dart_iter = dart; + if (dart_iter.alpha2() == dart) + return true; + else + return false; + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if the face associated with \e dart is at + * the boundary of the triangulation. + */ + template + bool isBoundaryFace(const DartType& dart) { + + // Strategy: boundary if alpha2(d)=d + + DartType dart_iter(dart); + DartType dart_prev; + + do { + dart_prev = dart_iter; + + if (dart_iter.alpha2() == dart_prev) + return true; + else + dart_iter = dart_prev; // back again + + dart_iter.alpha0(); + dart_iter.alpha1(); + + } while (dart_iter != dart); + + return false; + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if the node associated with \e dart is at + * the boundary of the triangulation. + */ + template + bool isBoundaryNode(const DartType& dart) { + + // Strategy: boundary if alpha2(d)=d + + DartType dart_iter(dart); + DartType dart_prev; + + // If input dart is reached again, then internal node + // If alpha2(d)=d, then boundary + + do { + dart_iter.alpha1(); + dart_prev = dart_iter; + dart_iter.alpha2(); + + if (dart_iter == dart_prev) + return true; + + } while (dart_iter != dart); + + return false; + } + + + //------------------------------------------------------------------------------------------------ + /** Returns the degree of the node associated with \e dart. + * + * \par Definition: + * The \e degree (or valency) of a node \e V in a triangulation, + * is defined as the number of edges incident with \e V, i.e., + * the number of edges joining \e V with another node in the triangulation. + */ + template + int getDegreeOfNode(const DartType& dart) { + + DartType dart_iter(dart); + DartType dart_prev; + + // If input dart is reached again, then interior node + // If alpha2(d)=d, then boundary + + int degree = 0; + bool boundaryVisited = false; + do { + dart_iter.alpha1(); + degree++; + dart_prev = dart_iter; + + dart_iter.alpha2(); + + if (dart_iter == dart_prev) { + if (!boundaryVisited) { + boundaryVisited = true; + // boundary is reached first time, count in the reversed direction + degree++; // count the start since it is not done above + dart_iter = dart; + dart_iter.alpha2(); + } + else + return degree; + } + + } while (dart_iter != dart); + + return degree; + } + + + //------------------------------------------------------------------------------------------------ + // Modification of getDegreeOfNode: + // Strategy, reverse the list and start in the other direction if the boundary + // is reached. NB. copying of darts but ok., or we could have collected pointers, + // but the memory management. + + // NOTE: not symmetry if we choose to collect opposite edges + // now we collect darts with radiating edges + + // Remember that we must also copy the node, but ok with push_back + // The size of the list will be the degree of the node + + // No CW/CCW since topology only + + + // Each dart consists of an incident edge and an adjacent node. + // But note that this is only how we interpret the dart in this implementation. + // Given this list, how can we find the opposite edges: + // We can perform alpha1 on each, but for boundary nodes we will get one edge twice. + // But this is will always be the last dart! + // The darts in the list are in sequence and starts with the alpha0(dart) + // alpha0, alpha1 and alpha2 + + // Private/Hidden function + template + void getNeighborNodes(const DartType& dart, std::list& node_list, bool& boundary) { + + DartType dart_iter(dart); + + dart_iter.alpha0(); // position the dart at an opposite node + + DartType dart_prev = dart_iter; + + bool start_at_boundary = false; + dart_iter.alpha2(); + if (dart_iter == dart_prev) + start_at_boundary = true; + else + dart_iter = dart_prev; // back again + + DartType dart_start = dart_iter; + + do { + node_list.push_back(dart_iter); + dart_iter.alpha1(); + dart_iter.alpha0(); + dart_iter.alpha1(); + dart_prev = dart_iter; + dart_iter.alpha2(); + if (dart_iter == dart_prev) { + // boundary reached + boundary = true; + if (start_at_boundary == true) { + // add the dart which now is positioned at the opposite boundary + node_list.push_back(dart_iter); + return; + } + else { + // call the function again such that we start at the boundary + // first clear the list and reposition to the initial node + dart_iter.alpha0(); + node_list.clear(); + getNeighborNodes(dart_iter, node_list, boundary); + return; // after one recursive step + } + } + } while (dart_iter != dart_start); + + boundary = false; + } + + + //------------------------------------------------------------------------------------------------ + /** Gets the 0-orbit around an interior node. + * + * \param dart + * A dart (CCW or CW) positioned at an \e interior node. + * + * \retval orbit + * Sequence of darts with one orbit for each arc. All the darts have the same + * orientation (CCW or CW) as \e dart, and \e dart is the first element + * in the sequence. + * + * \require + * - DartListType::push_back (DartType&) + * + * \see + * ttl::get_0_orbit_boundary + */ + template + void get_0_orbit_interior(const DartType& dart, DartListType& orbit) { + + DartType d_iter = dart; + orbit.push_back(d_iter); + d_iter.alpha1().alpha2(); + + while (d_iter != dart) { + orbit.push_back(d_iter); + d_iter.alpha1().alpha2(); + } + } + + + //------------------------------------------------------------------------------------------------ + /** Gets the 0-orbit around a node at the boundary + * + * \param dart + * A dart (CCW or CW) positioned at a \e boundary \e node and at a \e boundary \e edge. + * + * \retval orbit + * Sequence of darts with one orbit for each arc. All the darts, \e exept \e the \e last one, + * have the same orientation (CCW or CW) as \e dart, and \e dart is the first element + * in the sequence. + * + * \require + * - DartListType::push_back (DartType&) + * + * \note + * - The last dart in the sequence have opposite orientation compared to the others! + * + * \see + * ttl::get_0_orbit_interior + */ + template + void get_0_orbit_boundary(const DartType& dart, DartListType& orbit) { + + DartType dart_prev; + DartType d_iter = dart; + do { + orbit.push_back(d_iter); + d_iter.alpha1(); + dart_prev = d_iter; + d_iter.alpha2(); + } while (d_iter != dart_prev); + + orbit.push_back(d_iter); // the last one with opposite orientation + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if the two darts belong to the same 0-orbit, i.e., + * if they share a node. + * \e d1 and/or \e d2 can be CCW or CW. + * + * (This function also examines if the the node associated with + * \e d1 is at the boundary, which slows down the function (slightly). + * If it is known that the node associated with \e d1 is an interior + * node and a faster version is needed, the user should implement his/her + * own version.) + */ + template + bool same_0_orbit(const DartType& d1, const DartType& d2) { + + // Two copies of the same dart + DartType d_iter = d2; + DartType d_end = d2; + + if (ttl::isBoundaryNode(d_iter)) { + // position at both boundary edges + ttl::positionAtNextBoundaryEdge(d_iter); + d_end.alpha1(); + ttl::positionAtNextBoundaryEdge(d_end); + } + + for (;;) { + if (d_iter == d1) + return true; + d_iter.alpha1(); + if (d_iter == d1) + return true; + d_iter.alpha2(); + if (d_iter == d_end) + break; + } + + return false; + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if the two darts belong to the same 1-orbit, i.e., + * if they share an edge. + * \e d1 and/or \e d2 can be CCW or CW. + */ + template + bool same_1_orbit(const DartType& d1, const DartType& d2) { + + DartType d_iter = d2; + // (Also works at the boundary) + if (d_iter == d1 || d_iter.alpha0() == d1 || d_iter.alpha2() == d1 || d_iter.alpha0() == d1) + return true; + return false; + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if the two darts belong to the same 2-orbit, i.e., + * if they lie in the same triangle. + * \e d1 and/or \e d2 can be CCW or CW + */ + template + bool same_2_orbit(const DartType& d1, const DartType& d2) { + + DartType d_iter = d2; + if (d_iter == d1 || d_iter.alpha0() == d1 || + d_iter.alpha1() == d1 || d_iter.alpha0() == d1 || + d_iter.alpha1() == d1 || d_iter.alpha0() == d1) + return true; + return false; + } + + + //------------------------------------------------------------------------------------------------ + // Private/Hidden function + template + bool degenerateTriangle(const DartType& dart) { + + // Check if triangle is degenerate + // Assumes CCW dart + + DartType d1 = dart; + DartType d2 = d1; + d2.alpha1(); + if (TraitsType::crossProduct2d(d1,d2) == 0) + return true; + + return false; + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if the edge associated with \e dart is swappable, i.e., if the edge + * is a diagonal in a \e strictly convex (or convex) quadrilateral. + * + * \param allowDegeneracy + * If set to true, the function will also return true if the numerical calculations + * indicate that the quadrilateral is convex only, and not necessarily strictly + * convex. + * + * \require + * - \ref hed::TTLtraits::crossProduct2d "TraitsType::crossProduct2d" (Dart&, Dart&) + */ + template + bool swappableEdge(const DartType& dart, bool allowDegeneracy) { + + // How "safe" is it? + + if (isBoundaryEdge(dart)) + return false; + + // "angles" are at the diagonal + DartType d1 = dart; + d1.alpha2().alpha1(); + DartType d2 = dart; + d2.alpha1(); + if (allowDegeneracy) { + if (TraitsType::crossProduct2d(d1,d2) < 0.0) + return false; + } + else { + if (TraitsType::crossProduct2d(d1,d2) <= 0.0) + return false; + } + + // Opposite side (still angle at the diagonal) + d1 = dart; + d1.alpha0(); + d2 = d1; + d1.alpha1(); + d2.alpha2().alpha1(); + + if (allowDegeneracy) { + if (TraitsType::crossProduct2d(d1,d2) < 0.0) + return false; + } + else { + if (TraitsType::crossProduct2d(d1,d2) <= 0.0) + return false; + } + return true; + } + + + //------------------------------------------------------------------------------------------------ + /** Given a \e dart, CCW or CW, positioned in a 0-orbit at the boundary of a tessellation. + * Position \e dart at a boundary edge in the same 0-orbit.\n + * If the given \e dart is CCW, \e dart is positioned at the left boundary edge + * and will be CW.\n + * If the given \e dart is CW, \e dart is positioned at the right boundary edge + * and will be CCW. + * + * \note + * - The given \e dart must have a source node at the boundary, otherwise an + * infinit loop occurs. + */ + template + void positionAtNextBoundaryEdge(DartType& dart) { + + DartType dart_prev; + + // If alpha2(d)=d, then boundary + + //old convention: dart.alpha0(); + do { + dart.alpha1(); + dart_prev = dart; + dart.alpha2(); + } while (dart != dart_prev); + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if the boundary of a triangulation is convex. + * + * \param dart + * A CCW dart at the boundary of the triangulation + * + * \require + * - \ref hed::TTLtraits::crossProduct2d "TraitsType::crossProduct2d" (const Dart&, const Dart&) + */ + template + bool convexBoundary(const DartType& dart) { + + list blist; + ttl::getBoundary(dart, blist); + + int no; + no = (int)blist.size(); + typename list::const_iterator bit = blist.begin(); + DartType d1 = *bit; + ++bit; + DartType d2; + bool convex = true; + for (; bit != blist.end(); ++bit) { + d2 = *bit; + double crossProd = TraitsType::crossProduct2d(d1, d2); + if (crossProd < 0.0) { + //cout << "!!! Boundary is NOT convex: crossProd = " << crossProd << endl; + convex = false; + return convex; + } + d1 = d2; + } + + // Check the last angle + d2 = *blist.begin(); + double crossProd = TraitsType::crossProduct2d(d1, d2); + if (crossProd < 0.0) { + //cout << "!!! Boundary is NOT convex: crossProd = " << crossProd << endl; + convex = false; + } + + //if (convex) + // cout << "\n---> Boundary is convex\n" << endl; + //cout << endl; + return convex; + } + + //@} // End of Topological and Geometric Queries Group + + + //------------------------------------------------------------------------------------------------ + // ------------------------ Utilities for Delaunay Triangulation Group -------------------------- + //------------------------------------------------------------------------------------------------ + + /** @name Utilities for Delaunay Triangulation */ + //@{ + + //------------------------------------------------------------------------------------------------ + /** Optimizes the edges in the given sequence according to the + * \e Delaunay criterion, i.e., such that the edge will fullfill the + * \e circumcircle criterion (or equivalently the \e MaxMin + * angle criterion) with respect to the quadrilaterals where + * they are diagonals. + * + * \param elist + * The sequence of edges + * + * \require + * - \ref hed::TTLtraits::swapEdge "TraitsType::swapEdge" (DartType& \e dart)\n + * \b Note: Must be implemented such that \e dart is delivered back in a position as + * seen if it was glued to the edge when swapping (rotating) the edge CCW + * + * \using + * - ttl::swapTestDelaunay + */ + template + void optimizeDelaunay(DartListType& elist) { + optimizeDelaunay(elist, elist.end()); + } + + + //------------------------------------------------------------------------------------------------ + template + void optimizeDelaunay(DartListType& elist, const typename DartListType::iterator end) { + + // CCW darts + // Optimize here means Delaunay, but could be any criterion by + // requiring a "should swap" in the traits class, or give + // a function object? + // Assumes that elist has only one dart for each arc. + // Darts outside the quadrilateral are preserved + + // For some data structures it is possible to preserve + // all darts when swapping. Thus a preserve_darts_when swapping + // ccould be given to indicate this and we would gain performance by avoiding + // find in list. + + // Requires that swap retuns a dart in the "same position when rotated CCW" + // (A vector instead of a list may be better.) + + // First check that elist is not empty + if (elist.empty()) + return; + + // Avoid cycling by more extensive circumcircle test + bool cycling_check = true; + bool optimal = false; + typename DartListType::iterator it; + + typename DartListType::iterator end_opt = end; + + // Hmm... The following code is trying to derefence an iterator that may + // be invalid. This may lead to debug error on Windows, so we comment out + // this code. Checking elist.empty() above will prevent some + // problems... + // + // last_opt is passed the end of the "active list" + //typename DartListType::iterator end_opt; + //if (*end != NULL) + // end_opt = end; + //else + // end_opt = elist.end(); + + while(!optimal) { + optimal = true; + for (it = elist.begin(); it != end_opt; ++it) { + if (ttl::swapTestDelaunay(*it, cycling_check)) { + + // Preserve darts. Potential darts in the list are: + // - The current dart + // - the four CCW darts on the boundary of the quadrilateral + // (the current arc has only one dart) + + ttl::swapEdgeInList(it, elist); + + optimal = false; + } // end if should swap + } // end for + } // end pass + } + + + //------------------------------------------------------------------------------------------------ + /** Checks if the edge associated with \e dart should be swapped according + * to the \e Delaunay criterion, i.e., the \e circumcircle criterion (or + * equivalently the \e MaxMin angle criterion). + * + * \param cycling_check + * Must be set to \c true when used in connection with optimization algorithms, + * e.g., optimizeDelaunay. This will avoid cycling and infinite loops in nearly + * neutral cases. + * + * \require + * - \ref hed::TTLtraits::scalarProduct2d "TraitsType::scalarProduct2d" (DartType&, DartType&) + * - \ref hed::TTLtraits::crossProduct2d "TraitsType::crossProduct2d" (DartType&, DartType&) + */ + template +#if ((_MSC_VER > 0) && (_MSC_VER < 1300))//#ifdef _MSC_VER + bool swapTestDelaunay(const DartType& dart, bool cycling_check = false) { +#else + bool swapTestDelaunay(const DartType& dart, bool cycling_check) { +#endif + + // The general strategy is taken from Cline & Renka. They claim that + // their algorithm insure numerical stability, but experiments show + // that this is not correct for neutral, or almost neutral cases. + // I have extended this strategy (without using tolerances) to avoid + // cycling and infinit loops when used in connection with LOP algorithms; + // see the comments below. + + typedef typename TraitsType::real_type real_type; + + if (isBoundaryEdge(dart)) + return false; + + DartType v11 = dart; + v11.alpha1().alpha0(); + DartType v12 = v11; + v12.alpha1(); + + DartType v22 = dart; + v22.alpha2().alpha1().alpha0(); + DartType v21 = v22; + v21.alpha1(); + + real_type cos1 = TraitsType::scalarProduct2d(v11,v12); + real_type cos2 = TraitsType::scalarProduct2d(v21,v22); + + // "Angles" are opposite to the diagonal. + // The diagonals should be swapped iff (t1+t2) .gt. 180 + // degrees. The following two tests insure numerical + // stability according to Cline & Renka. But experiments show + // that cycling may still happen; see the aditional test below. + if (cos1 >= 0 && cos2 >= 0) // both angles are grater or equual 90 + return false; + if (cos1 < 0 && cos2 < 0) // both angles are less than 90 + return true; + + real_type sin1 = TraitsType::crossProduct2d(v11,v12); + real_type sin2 = TraitsType::crossProduct2d(v21,v22); + real_type sin12 = sin1*cos2 + cos1*sin2; + if (sin12 >= 0) // equality represents a neutral case + return false; + + if (cycling_check) { + // situation so far is sin12 < 0. Test if this also + // happens for the swapped edge. + + // The numerical calculations so far indicate that the edge is + // not Delaunay and should not be swapped. But experiments show that + // in neutral cases, or almost neutral cases, it may happen that + // the swapped edge may again be found to be not Delaunay and thus + // be swapped if we return true here. This may lead to cycling and + // an infinte loop when used, e.g., in connection with optimizeDelaunay. + // + // In an attempt to avoid this we test if the swapped edge will + // also be found to be not Delaunay by repeating the last test above + // for the swapped edge. + // We now rely on the general requirement for TraitsType::swapEdge which + // should deliver CCW dart back in "the same position"; see the general + // description. This will insure numerical stability as the next calculation + // is the same as if this function was called again with the swapped edge. + // Cycling is thus impossible provided that the initial tests above does + // not result in ambiguity (and they should probably not do so). + + v11.alpha0(); + v12.alpha0(); + v21.alpha0(); + v22.alpha0(); + // as if the edge was swapped/rotated CCW + cos1 = TraitsType::scalarProduct2d(v22,v11); + cos2 = TraitsType::scalarProduct2d(v12,v21); + sin1 = TraitsType::crossProduct2d(v22,v11); + sin2 = TraitsType::crossProduct2d(v12,v21); + sin12 = sin1*cos2 + cos1*sin2; + if (sin12 < 0) { + // A neutral case, but the tests above lead to swapping + return false; + } + } + + return true; + } + + + //----------------------------------------------------------------------- + // + // x + //" / \ " + // / | \ Darts: + //oe2 / | \ oe2 = oppEdge2 + // x....|....x + // \ d| d/ d = diagonal (input and output) + // \ | / + // oe1 \ / oe1 = oppEdge1 + // x + // + //----------------------------------------------------------------------- + /** Recursively swaps edges in the triangulation according to the \e Delaunay criterion. + * + * \param diagonal + * A CCW dart representing the edge where the recursion starts from. + * + * \require + * - \ref hed::TTLtraits::swapEdge "TraitsType::swapEdge" (DartType&)\n + * \b Note: Must be implemented such that the darts outside the quadrilateral + * are not affected by the swap. + * + * \using + * - Calls itself recursively + */ + template + void recSwapDelaunay(DartType& diagonal) { + + if (!ttl::swapTestDelaunay(diagonal)) + // ??? ttl::swapTestDelaunay also checks if boundary, so this can be optimized + return; + + // Get the other "edges" of the current triangle; see illustration above. + DartType oppEdge1 = diagonal; + oppEdge1.alpha1(); + bool b1; + if (ttl::isBoundaryEdge(oppEdge1)) + b1 = true; + else { + b1 = false; + oppEdge1.alpha2(); + } + + + DartType oppEdge2 = diagonal; + oppEdge2.alpha0().alpha1().alpha0(); + bool b2; + if (ttl::isBoundaryEdge(oppEdge2)) + b2 = true; + else { + b2 = false; + oppEdge2.alpha2(); + } + + // Swap the given diagonal + TraitsType::swapEdge(diagonal); + + if (!b1) + recSwapDelaunay(oppEdge1); + if (!b2) + recSwapDelaunay(oppEdge2); + } + + + //------------------------------------------------------------------------------------------------ + /** Swaps edges away from the (interior) node associated with + * \e dart such that that exactly three edges remain incident + * with the node. + * This function is used as a first step in ttl::removeInteriorNode + * + * \retval dart + * A CCW dart incident with the node + * + * \par Assumes: + * - The node associated with \e dart is interior to the + * triangulation. + * + * \require + * - \ref hed::TTLtraits::swapEdge "TraitsType::swapEdge" (DartType& \e dart)\n + * \b Note: Must be implemented such that \e dart is delivered back in a position as + * seen if it was glued to the edge when swapping (rotating) the edge CCW + * + * \note + * - A degenerate triangle may be left at the node. + * - The function is not unique as it depends on which dart + * at the node that is given as input. + * + * \see + * ttl::swapEdgesAwayFromBoundaryNode + */ + template + void swapEdgesAwayFromInteriorNode(DartType& dart, ListType& swapped_edges) { + + // Same iteration as in fixEdgesAtCorner, but not boundary + DartType dnext = dart; + + // Allow degeneracy, otherwise we might end up with degree=4. + // For example, the reverse operation of inserting a point on an + // existing edge gives a situation where all edges are non-swappable. + // Ideally, degeneracy in this case should be along the actual node, + // but there is no strategy for this now. + // ??? An alternative here is to wait with degeneracy till we get an + // infinite loop with degree > 3. + bool allowDegeneracy = true; + + int degree = ttl::getDegreeOfNode(dart); + DartType d_iter; + while (degree > 3) { + d_iter = dnext; + dnext.alpha1().alpha2(); + + if (ttl::swappableEdge(d_iter, allowDegeneracy)) { + TraitsType::swapEdge(d_iter); // swap the edge away + // Collect swapped edges in the list + // "Hide" the dart on the other side of the edge to avoid it being changed for + // other swaps + DartType swapped_edge = d_iter; // it was delivered back + swapped_edge.alpha2().alpha0(); // CCW (if not at boundary) + swapped_edges.push_back(swapped_edge); + + degree--; + } + } + // Output, incident to the node + dart = dnext; + } + + + //------------------------------------------------------------------------------------------------ + /** Swaps edges away from the (boundary) node associated with + * \e dart in such a way that when removing the edges that remain incident + * with the node, the boundary of the triangulation will be convex. + * This function is used as a first step in ttl::removeBoundaryNode + * + * \retval dart + * A CCW dart incident with the node + * + * \require + * - \ref hed::TTLtraits::swapEdge "TraitsType::swapEdge" (DartType& \e dart)\n + * \b Note: Must be implemented such that \e dart is delivered back in a position as + * seen if it was glued to the edge when swapping (rotating) the edge CCW + * + * \par Assumes: + * - The node associated with \e dart is at the boundary of the triangulation. + * + * \see + * ttl::swapEdgesAwayFromInteriorNode + */ + template + void swapEdgesAwayFromBoundaryNode(DartType& dart, ListType& swapped_edges) { + + // All darts that are swappable. + // To treat collinear nodes at an existing boundary, we must allow degeneracy + // when swapping to the boundary. + // dart is CCW and at the boundary. + // The 0-orbit runs CCW + // Deliver the dart back in the "same position". + // Assume for the swap in the traits class: + // - A dart on the swapped edge is delivered back in a position as + // seen if it was glued to the edge when swapping (rotating) the edge CCW + + //int degree = ttl::getDegreeOfNode(dart); + +passes: + + // Swap swappable edges that radiate from the node away + DartType d_iter = dart; // ???? can simply use dart + d_iter.alpha1().alpha2(); // first not at boundary + DartType d_next = d_iter; + bool bend = false; + bool swapped_next_to_boundary = false; + bool swapped_in_pass = false; + + bool allowDegeneracy; // = true; + DartType tmp1, tmp2; + + while (!bend) { + + d_next.alpha1().alpha2(); + if (ttl::isBoundaryEdge(d_next)) + bend = true; // then it is CW since alpha2 + + // To allow removing among collinear nodes at the boundary, + // degenerate triangles must be allowed + // (they will be removed when used in connection with removeBoundaryNode) + tmp1 = d_iter; tmp1.alpha1(); + tmp2 = d_iter; tmp2.alpha2().alpha1(); // don't bother with boundary (checked later) + + if (ttl::isBoundaryEdge(tmp1) && ttl::isBoundaryEdge(tmp2)) + allowDegeneracy = true; + else + allowDegeneracy = false; + + if (ttl::swappableEdge(d_iter, allowDegeneracy)) { + TraitsType::swapEdge(d_iter); + + // Collect swapped edges in the list + // "Hide" the dart on the other side of the edge to avoid it being changed for + // other swapps + DartType swapped_edge = d_iter; // it was delivered back + swapped_edge.alpha2().alpha0(); // CCW + swapped_edges.push_back(swapped_edge); + + //degree--; // if degree is 2, or bend=true, we are done + swapped_in_pass = true; + if (bend) + swapped_next_to_boundary = true; + } + if (!bend) + d_iter = d_next; + } + + // Deliver a dart as output in the same position as the incoming dart + if (swapped_next_to_boundary) { + // Assume that "swapping is CCW and dart is preserved in the same position + d_iter.alpha1().alpha0().alpha1(); // CW and see below + } + else { + d_iter.alpha1(); // CW and see below + } + ttl::positionAtNextBoundaryEdge(d_iter); // CCW + + dart = d_iter; // for next pass or output + + // If a dart was swapped in this iteration we must run it more + if (swapped_in_pass) + goto passes; + } + + + //------------------------------------------------------------------------------------------------ + /** Swap the the edge associated with iterator \e it and update affected darts + * in \e elist accordingly. + * The darts affected by the swap are those in the same quadrilateral. + * Thus, if one want to preserve one or more of these darts on should + * keep them in \e elist. + */ + template + void swapEdgeInList(const typename DartListType::iterator& it, DartListType& elist) { + + typename DartListType::iterator it1, it2, it3, it4; + DartType dart(*it); + + //typename TraitsType::DartType d1 = dart; d1.alpha2().alpha1(); + //typename TraitsType::DartType d2 = d1; d2.alpha0().alpha1(); + //typename TraitsType::DartType d3 = dart; d3.alpha0().alpha1(); + //typename TraitsType::DartType d4 = d3; d4.alpha0().alpha1(); + DartType d1 = dart; d1.alpha2().alpha1(); + DartType d2 = d1; d2.alpha0().alpha1(); + DartType d3 = dart; d3.alpha0().alpha1(); + DartType d4 = d3; d4.alpha0().alpha1(); + + // Find pinters to the darts that may change. + // ??? Note, this is not very efficient since we must use find, which is O(N), + // four times. + // - Solution?: replace elist with a vector of pair (dart,number) + // and avoid find? + // - make a function for swapping generically? + // - sould we use another container type or, + // - erase them and reinsert? + // - or use two lists? + it1 = find(elist.begin(), elist.end(), d1); + it2 = find(elist.begin(), elist.end(), d2); + it3 = find(elist.begin(), elist.end(), d3); + it4 = find(elist.begin(), elist.end(), d4); + + TraitsType::swapEdge(dart); + // Update the current dart which may have changed + *it = dart; + + // Update darts that may have changed again (if they were present) + // Note that dart is delivered back after swapping + if (it1 != elist.end()) { + d1 = dart; d1.alpha1().alpha0(); + *it1 = d1; + } + if (it2 != elist.end()) { + d2 = dart; d2.alpha2().alpha1(); + *it2 = d2; + } + if (it3 != elist.end()) { + d3 = dart; d3.alpha2().alpha1().alpha0().alpha1(); + *it3 = d3; + } + if (it4 != elist.end()) { + d4 = dart; d4.alpha0().alpha1(); + *it4 = d4; + } + } + + //@} // End of Utilities for Delaunay Triangulation Group + +}; // End of ttl namespace scope (but other files may also contain functions for ttl) + + + //------------------------------------------------------------------------------------------------ + // ----------------------------- Constrained Triangulation Group -------------------------------- + //------------------------------------------------------------------------------------------------ + + // Still namespace ttl + +#include + +#endif // _TTL_H_ diff --git a/include/ttl/ttl_constr.h b/include/ttl/ttl_constr.h new file mode 100644 index 0000000000..67b46fcbf0 --- /dev/null +++ b/include/ttl/ttl_constr.h @@ -0,0 +1,632 @@ +/* + * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, + * Applied Mathematics, Norway. + * + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. + * + * This file is part of TTL. + * + * TTL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * TTL 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with TTL. If not, see + * . + * + * In accordance with Section 7(b) of the GNU Affero General Public + * License, a covered work must retain the producer line in every data + * file that is created or manipulated using TTL. + * + * Other Usage + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial activities involving the TTL library without + * disclosing the source code of your own applications. + * + * This file may be used in accordance with the terms contained in a + * written agreement between you and SINTEF ICT. + */ + +#ifndef _TTL_CONSTR_H_ +#define _TTL_CONSTR_H_ + + +#include +#include + + +// Debugging +#ifdef DEBUG_TTL_CONSTR_PLOT + #include + static ofstream ofile_constr("qweCons.dat"); +#endif + + +//using namespace std; + +/** \brief Constrained Delaunay triangulation +* +* Basic generic algorithms in TTL for inserting a constrained edge between two existing nodes.\n +* +* See documentation for the namespace ttl for general requirements and assumptions. +* +* \author +* Øyvind Hjelle, oyvindhj@ifi.uio.no +*/ + +namespace ttl_constr { + + // ??? A constant used to evluate a numerical expression against a user spesified + // roundoff-zero number +#ifdef DEBUG_TTL_CONSTR + static const double ROUNDOFFZERO = 0.0; // 0.1e-15; +#endif + + + //------------------------------------------------------------------------------------------------ + /* Checks if \e dart has start and end points in \e dstart and \e dend. + * + * \param dart + * The dart that should be controlled to see if it's the constraint + * + * \param dstart + * A CCW dart with the startnode of the constraint as the startnode + * + * \param dend + * A CCW dart with the endnode of the constraint as the startnode + * + * \retval bool + * A bool confirming that it's the constraint or not + * + * \using + * ttl::same_0_orbit + */ + template + bool isTheConstraint(const DartType& dart, const DartType& dstart, const DartType& dend) { + DartType d0 = dart; + d0.alpha0(); // CW + if ((ttl::same_0_orbit(dstart, dart) && ttl::same_0_orbit(dend, d0)) || + (ttl::same_0_orbit(dstart, d0) && ttl::same_0_orbit(dend, dart))) { + return true; + } + return false; + } + + + //------------------------------------------------------------------------------------------------ + /* Checks if \e d1 and \e d2 are on the same side of the line between \e dstart and \e dend. + * (The start nodes of \e d1 and \e d2 represent an edge). + * + * \param dstart + * A CCW dart with the start node of the constraint as the source node of the dart. + * + * \param dend + * A CCW dart with the end node of the constraint as the source node of the dart. + * + * \param d1 + * A CCW dart with the first node as the start node of the dart. + * + * \param d2 + * A CCW dart with the other node as the start node of the dart. + * + * \using + * TraitsType::orient2d + */ + template + bool crossesConstraint(DartType& dstart, DartType& dend, DartType& d1, DartType& d2) { + + typename TraitsType::real_type orient_1 = TraitsType::orient2d(dstart,d1,dend); + typename TraitsType::real_type orient_2 = TraitsType::orient2d(dstart,d2,dend); + // ??? Should we refine this? e.g. find if (dstart,dend) (d1,d2) represent the same edge + if ((orient_1 <= 0 && orient_2 <= 0) || (orient_1 >= 0 && orient_2 >= 0)) + return false; + + return true; + } + + + //------------------------------------------------------------------------------------------------ + /* Return the dart \e d making the smallest non-negative angle, + * as calculated with: orient2d(dstart, d.alpha0(), dend), + * at the 0-orbit of dstart. + * If (dstart,dend) is a CCW boundary edge \e d will be CW, otherwise CCW (since CCW in) + * at the 0-orbit of dstart. + * + * \par Assumes: + * - CCW dstart and dend, but returned dart can be CW at the boundary. + * - Boundary is convex? + * + * \param dstart + * A CCW dart dstart + * + * \param dend + * A CCW dart dend + * + * \retval DartType + * The dart \e d making the smallest positive (or == 0) angle + * + * \using + * ttl::isBoundaryNode + * ttl::positionAtNextBoundaryEdge + * TraitsType::orient2d + */ + template + DartType getAtSmallestAngle(const DartType& dstart, const DartType& dend) { + + // - Must boundary be convex??? + // - Handle the case where the constraint is already present??? + // - Handle dstart and/or dend at the boundary + // (dstart and dend may define a boundary edge) + + DartType d_iter = dstart; + if (ttl::isBoundaryNode(d_iter)) { + d_iter.alpha1(); // CW + ttl::positionAtNextBoundaryEdge(d_iter); // CCW (was rotated CW to the boundary) + } + + // assume convex boundary; see comments + + DartType d0 = d_iter; + d0.alpha0(); + bool ccw = true; // the rotation later + typename TraitsType::real_type o_iter = TraitsType::orient2d(d_iter, d0, dend); + if (o_iter == 0) { // collinear BUT can be on "back side" + d0.alpha1().alpha0(); // CW + if (TraitsType::orient2d(dstart, dend, d0) > 0) + return d_iter; //(=dstart) collinear + else { + // collinear on "back side" + d_iter.alpha1().alpha2(); // assume convex boundary + ccw = true; + } + } + else if (o_iter < 0) { + // Prepare for rotating CW and with d_iter CW + d_iter.alpha1(); + ccw = false; + } + + // Set first angle + d0 = d_iter; d0.alpha0(); + o_iter = TraitsType::orient2d(dstart, d0, dend); + + typename TraitsType::real_type o_next; + + // Rotate towards the constraint CCW or CW. + // Here we assume that the boundary is convex. + DartType d_next = d_iter; + for (;;) { + d_next.alpha1(); // CW !!! (if ccw == true) + d0 = d_next; d0.alpha0(); + o_next = TraitsType::orient2d(dstart, d0, dend); + + if (ccw && o_next < 0) // and o_iter > 0 + return d_iter; + else if (!ccw && o_next > 0) + return d_next; // CCW + else if (o_next == 0) { + if (ccw) + return d_next.alpha2(); // also ok if boundary + else + return d_next; + } + + // prepare next + d_next.alpha2(); // CCW if ccw + d_iter = d_next; // also ok if boundary CCW if ccw == true + } + } + + + //------------------------------------------------------------------------------------------------ + /* This function finds all the edges in the triangulation crossing + * the spesified constraint and puts them in a list. + * In the case of collinearity, an attempt is made to detect this. + * The first collinear node between dstart and dend is then returned. + * + * Strategy: + * - Iterate such that \e d_iter is always strictly "below" the constraint + * as seen with \e dstart to the left and \e dend to the right. + * - Add CCW darts, whose edges intersect the constrait, to a list. + * These edges are found by the orient2d predicate: + * If two nodes of an edge are on opposite sides of the constraint, + * the edge between them intersect. + * - Must handle collinnear cases, i.e., if a node falls on the constraint, + * and possibly restarting collection of edges. Detecting collinearity + * heavily relies on the orient2d predicate which is provided by the + * traits class. + * + * Action: + * 1) Find cone/opening angle containing \e dstart and \e dend + * 2) Find first edge from the first 0-orbit that intersects + * 3) Check which of the two opposite that intersects + * + * 1) + * Rotate CCW and find the (only) case where \e d_iter and \e d_next satisfy: + * - orient2d(d_iter, d_iter.alpha0(), dend) > 0 + * - orient2d(d_next, d_next.alpha0(), dend) < 0 + * + * - check if we are done, i.e., if (d_next.alpha0() == my_dend) + * - Note also the situation if, e.g., the constraint is a boundary edge in which case + * \e my_dend wil be CW + * + * \param dstart + * A CCW dart with the startnode of the constraint as the startnode + * + * \param dend + * A CCW dart with the endnode of the constraint as the startnode + * + * \param elist + * A list where all the edges crossing the spesified constraint will be put + * + * \retval dartType + * Returns the next "collinear" starting node such that dend is returned when done. + */ + template + DartType findCrossingEdges(const DartType& dstart, const DartType& dend, ListType& elist) { + + const DartType my_start = getAtSmallestAngle(dstart, dend); + DartType my_end = getAtSmallestAngle(dend, dstart); + + DartType d_iter = my_start; + if (d_iter.alpha0().alpha2() == my_end) + return d_iter; // The constraint is an existing edge and we are done + + // Facts/status so far: + // - my_start and my_end are now both CCW and the constraint is not a boundary edge. + // - Further, the constraint is not one single existing edge, but it might be a collection + // of collinear edges in which case we return the current collinear edge + // and calling this function until all are collected. + + my_end.alpha1(); // CW! // ??? this is probably ok for testing now? + + d_iter = my_start; + d_iter.alpha0().alpha1(); // alpha0 is downwards or along the constraint + + // Facts: + // - d_iter is guaranteed to intersect, but can be in start point. + // - d_iter.alpha0() is not at dend yet + typename TraitsType::real_type orient = TraitsType::orient2d(dstart, d_iter, dend); + + // Use round-off error/tolerance or rely on the orient2d predicate ??? + // Make a warning message if orient != exact 0 + if (orient == 0) + return d_iter; + +#ifdef DEBUG_TTL_CONSTR + else if (fabs(orient) <= ROUNDOFFZERO) { + cout << "The darts are not exactly colinear, but |d1 x d2| <= " << ROUNDOFFZERO << endl; + return d_iter; // collinear, not done (and not collect in the list) + } +#endif + + // Collect intersecting edges + // -------------------------- + elist.push_back(d_iter); // The first with interior intersection point + + // Facts, status so far: + // - The first intersecting edge is now collected + // (- d_iter.alpha0() is still not at dend) + + // d_iter should always be the edge that intersects and be below or on the constraint + // One of the two edges opposite to d_iter must intersect, or we have collinearity + + // Note: Almost collinear cases can be handled on the + // application side with orient2d. We should probably + // return an int and the application will set it to zero + for(;;) { + // assume orient have been calc. and collinearity has been tested, + // above the first time and below later + d_iter.alpha2().alpha1(); // 2a same node + + DartType d0 = d_iter; + d0.alpha0(); // CW + if (d0 == my_end) + return dend; // WE ARE DONE (but can we enter an endless loop???) + + // d_iter or d_iter.alpha0().alpha1() must intersect + orient = TraitsType::orient2d(dstart, d0, dend); + + if (orient == 0) + return d0.alpha1(); + +#ifdef DEBUG_TTL_CONSTR + else if (fabs(orient) <= ROUNDOFFZERO) { + return d0.alpha1(); // CCW, collinear + } +#endif + + else if (orient > 0) { // orient > 0 and still below + // This one must intersect! + d_iter = d0.alpha1(); + } + elist.push_back(d_iter); + } + } + + + //------------------------------------------------------------------------------------------------ + /* This function recives a constrained edge and a list of all the edges crossing a constraint. + * It then swaps the crossing edges away from the constraint. This is done according to a + * scheme suggested by Dyn, Goren & Rippa (slightly modified). + * The resulting triangulation is a constrained one, but not necessarily constrained Delaunay. + * In other to run optimization later to obtain a constrained Delaunay triangulation, + * the swapped edges are maintained in a list. + * + * Strategy : + * - Situation A: Run through the list and swap crossing edges away from the constraint. + * All the swapped edges are moved to the end of the list, and are "invisible" to this procedure. + * - Situation B: We may come in a situation where none of the crossing edges can be swapped away + * from the constraint. + * Then we follow the strategy of Dyn, Goren & Rippa and allow edges to be swapped, + * even if they are not swapped away from the constraint. + * These edges are NOT moved to the end of the list. They are later swapped to none-crossing + * edges when the locked situation is solved. + * - We keep on swapping edges in Situation B until we have iterated trough the list. + * We then resume Situation A. + * - This is done until the list is virtualy empty. The resulting \c elist has the constraint + * as the last element. + * + * \param dstart + * A CCW dart dstart + * + * \param dend + * A CCW dart dend + * + * \param elist + * A list containing all the edges crossing the spesified constraint + * + * \using + * ttl::swappableEdge + * ttl::swapEdgeInList + * ttl::crossesConstraint + * ttl::isTheConstraint + */ + template + void transformToConstraint(DartType& dstart, DartType& dend, std::list& elist) { + + typename list::iterator it, used; + + // We may enter in a situation where dstart and dend are altered because of a swap. + // (The general rule is that darts inside the actual quadrilateral can be changed, + // but not those outside.) + // So, we need some look-ahead strategies for dstart and dend and change these + // after a swap if necessary. + + int dartsInList = (int)elist.size(); + if (dartsInList == 0) + return; + + bool erase; // indicates if an edge is swapped away from the constraint such that it can be + // moved to the back of the list + bool locked = false; + do { + int noswap = 0; + it = elist.begin(); + + // counts how many edges that have been swapped per list-cycle + int counter = 1; + while(it != elist.end()) { // ??? change this test with counter > dartsInList + erase = false; + // Check if our virtual end of the list has been crossed. It breaks the + // while and starts all over again in the do-while loop + if (counter > dartsInList) + break; + + if (ttl::swappableEdge(*it, true)) { + // Dyn & Goren & Rippa 's notation: + // The node assosiated with dart *it is denoted u_m. u_m has edges crossing the constraint + // named w_1, ... , w_r . The other node to the edge assosiated with dart *it is w_s. + // We want to swap from edge u_m<->w_s to edge w_{s-1}<->w_{s+1}. + DartType op1 = *it; + DartType op2 = op1; + op1.alpha1().alpha0(); //finds dart with node w_{s-1} + op2.alpha2().alpha1().alpha0(); // (CW) finds dart with node w_{s+1} + DartType tmp = *it; tmp.alpha0(); // Dart with assosiated node opposite to node of *it allong edge + // If there is a locked situation we swap, even if the result is crossing the constraint + // If there is a looked situation, but we do an ordinary swap, it should be treated as + // if we were not in a locked situation!! + + // The flag swap_away indicates if the edge is swapped away from the constraint such that + // it does not cross the constraint. + bool swap_away = (crossesConstraint(dstart, dend, *it, tmp) && + !crossesConstraint(dstart, dend, op1, op2)); + if (swap_away || locked) { + // Do a look-ahead to see if dstart and/or dend are in the quadrilateral + // If so, we mark it with a flag to make sure we update them after the swap + // (they may have been changed during the swap according to the general rule!) + bool start = false; + bool end = false; + + DartType d = *it; + if (d.alpha1().alpha0() == dstart) + start = true; + d = *it; + if (d.alpha2().alpha1().alpha0().alpha1() == dend) + end = true; + + // This is the only place swapping is called when inserting a constraint + ttl::swapEdgeInList(it,elist); + + // If we, during look-ahead, found that dstart and/or dend were in the quadrilateral, + // we update them. + if (end) + dend = *it; + if (start) { + dstart = *it; + dstart.alpha0().alpha2(); + } + + if (swap_away) { // !locked || //it should be sufficient with swap_away ??? + noswap++; + erase = true; + } + + if (isTheConstraint(*it, dstart, dend)) { + // Move the constraint to the end of the list + DartType the_constraint = *it; + elist.erase(it); + elist.push_back(the_constraint); + return; + } //endif + } //endif + } //endif "swappable edge" + + + // Move the edge to the end of the list if it was swapped away from the constraint + if (erase) { + used = it; + elist.push_back(*it); + ++it; + elist.erase(used); + --dartsInList; + } + else { + ++it; + ++counter; + } + + } //end while + + if (noswap == 0) + locked = true; + + } while (dartsInList != 0); + + +#ifdef DEBUG_TTL_CONSTR + // We will never enter here. (If elist is empty, we return above). + cout << "??????? ERROR 2, should never enter here ????????????????????????? SKIP ???? " << endl; + exit(-1); +#endif + + } + +}; // End of ttl_constr namespace scope + + +namespace ttl { // (extension) + + /** @name Constrained (Delaunay) Triangulation */ + //@{ + + //------------------------------------------------------------------------------------------------ + /** Inserts a constrained edge between two existing nodes in a triangulation. + * If the constraint falls on one or more existing nodes and this is detected by the + * predicate \c TraitsType::orient2d, which should return zero in this case, the + * constraint is split. Otherwise a degenerate triangle will be made along + * the constraint. + * + * \param dstart + * A CCW dart with the start node of the constraint as the source node + * + * \param dend + * A CCW dart with the end node of the constraint as the source node + * + * \param optimize_delaunay + * If set to \c true, the resulting triangulation will be + * a \e constrained \e Delaunay \e triangulation. If set to \c false, the resulting + * triangulation will not necessarily be of constrained Delaunay type. + * + * \retval DartType + * A dart representing the constrained edge. + * + * \require + * - \ref hed::TTLtraits::orient2d "TraitsType::orient2d" (DartType&, DartType&, PointType&) + * - \ref hed::TTLtraits::swapEdge "TraitsType::swapEdge" (DartType&) + * + * \using + * - ttl::optimizeDelaunay if \e optimize_delaunay is set to \c true + * + * \par Assumes: + * - The constrained edge must be inside the existing triangulation (and it cannot + * cross the boundary of the triangulation). + */ + template + DartType insertConstraint(DartType& dstart, DartType& dend, bool optimize_delaunay) { + + // Assumes: + // - It is the users responsibility to avoid crossing constraints + // - The constraint cannot cross the boundary, i.e., the boundary must be + // convex in the area of crossing edges. + // - dtart and dend are preserved (same node associated.) + + + // Find edges crossing the constraint and put them in elist. + // If findCrossingEdges reaches a Node lying on the constraint, this function + // calls itself recursively. + + // RECURSION + list elist; + DartType next_start = ttl_constr::findCrossingEdges(dstart, dend, elist); + + // If there are no crossing edges (elist is empty), we assume that the constraint + // is an existing edge. + // In this case, findCrossingEdges returns the constraint. + // Put the constraint in the list to fit with the procedures below + // (elist can also be empty in the case of invalid input data (the constraint is in + // a non-convex area) but this is the users responsibility.) + + //by Thomas Sevaldrud if (elist.size() == 0) + //by Thomas Sevaldrud elist.push_back(next_start); + + // findCrossingEdges stops if it finds a node lying on the constraint. + // A dart with this node as start node is returned + // We call insertConstraint recursivly until the received dart is dend + if (!ttl::same_0_orbit(next_start, dend)) { + +#ifdef DEBUG_TTL_CONSTR_PLOT + cout << "RECURSION due to collinearity along constraint" << endl; +#endif + + insertConstraint(next_start, dend, optimize_delaunay); + } + + // Swap edges such that the constraint edge is present in the transformed triangulation. + if (elist.size() > 0) // by Thomas Sevaldrud + ttl_constr::transformToConstraint(dstart, next_start, elist); + +#ifdef DEBUG_TTL_CONSTR_PLOT + cout << "size of elist = " << elist.size() << endl; + if (elist.size() > 0) { + DartType the_constraint = elist.back(); + ofile_constr << the_constraint.x() << " " << the_constraint.y() << " " << 0 << endl; + the_constraint.alpha0(); + ofile_constr << the_constraint.x() << " " << the_constraint.y() << " " << 0 << endl << endl; + } +#endif + + // Optimize to constrained Delaunay triangulation if required. + typename list::iterator end_opt = elist.end(); + if (optimize_delaunay) { + + // Indicate that the constrained edge, which is the last element in the list, + // should not be swapped + --end_opt; + ttl::optimizeDelaunay(elist, end_opt); + } + + if(elist.size() == 0) // by Thomas Sevaldrud + return next_start; // by Thomas Sevaldrud + + // Return the constraint, which is still the last element in the list + end_opt = elist.end(); + --end_opt; + return *end_opt; + } + + //@} // End of Constrained Triangulation Group + +}; // End of ttl namespace scope (extension) + +#endif // _TTL_CONSTR_H_ diff --git a/include/ttl/ttl_util.h b/include/ttl/ttl_util.h new file mode 100644 index 0000000000..cc311edb69 --- /dev/null +++ b/include/ttl/ttl_util.h @@ -0,0 +1,314 @@ +/* + * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, + * Applied Mathematics, Norway. + * + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. + * + * This file is part of TTL. + * + * TTL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * TTL 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with TTL. If not, see + * . + * + * In accordance with Section 7(b) of the GNU Affero General Public + * License, a covered work must retain the producer line in every data + * file that is created or manipulated using TTL. + * + * Other Usage + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial activities involving the TTL library without + * disclosing the source code of your own applications. + * + * This file may be used in accordance with the terms contained in a + * written agreement between you and SINTEF ICT. + */ + +#ifndef _TTL_UTIL_H_ +#define _TTL_UTIL_H_ + + +#include +#include + + +#ifdef _MSC_VER +# if _MSC_VER < 1300 +# include +# endif +#endif + + +//using namespace std; + + +/** \brief Utilities +* +* This name space contains utility functions for TTL.\n +* +* Point and vector algebra such as scalar product and cross product +* between vectors are implemented here. +* These functions are required by functions in the \ref ttl namespace, +* where they are assumed to be present in the \ref hed::TTLtraits "TTLtraits" class. +* Thus, the user can call these functions from the traits class. +* For efficiency reasons, the user may consider implementing these +* functions in the the API directly on the actual data structure; +* see \ref api. +* +* \note +* - Cross product between vectors in the xy-plane delivers a scalar, +* which is the z-component of the actual cross product +* (the x and y components are both zero). +* +* \see +* ttl and \ref api +* +* \author +* Øyvind Hjelle, oyvindhj@ifi.uio.no +*/ + + +namespace ttl_util { + + + //------------------------------------------------------------------------------------------------ + // ------------------------------ Computational Geometry Group ---------------------------------- + //------------------------------------------------------------------------------------------------ + + /** @name Computational geometry */ + //@{ + + //------------------------------------------------------------------------------------------------ + /** Scalar product between two 2D vectors. + * + * \par Returns: + * \code + * dx1*dx2 + dy1*dy2 + * \endcode + */ + template + real_type scalarProduct2d(real_type dx1, real_type dy1, real_type dx2, real_type dy2) { + return dx1*dx2 + dy1*dy2; + } + + + //------------------------------------------------------------------------------------------------ + /** Cross product between two 2D vectors. (The z-component of the actual cross product.) + * + * \par Returns: + * \code + * dx1*dy2 - dy1*dx2 + * \endcode + */ + template + real_type crossProduct2d(real_type dx1, real_type dy1, real_type dx2, real_type dy2) { + return dx1*dy2 - dy1*dx2; + } + + + //------------------------------------------------------------------------------------------------ + /** Returns a positive value if the 2D nodes/points \e pa, \e pb, and + * \e pc occur in counterclockwise order; a negative value if they occur + * in clockwise order; and zero if they are collinear. + * + * \note + * - This is a finite arithmetic fast version. It can be made more robust using + * exact arithmetic schemes by Jonathan Richard Shewchuk. See + * http://www-2.cs.cmu.edu/~quake/robust.html + */ + template + real_type orient2dfast(real_type pa[2], real_type pb[2], real_type pc[2]) { + real_type acx = pa[0] - pc[0]; + real_type bcx = pb[0] - pc[0]; + real_type acy = pa[1] - pc[1]; + real_type bcy = pb[1] - pc[1]; + return acx * bcy - acy * bcx; + } + + + //------------------------------------------------------------------------------------------------ + /* Scalar product between 2D vectors represented as darts. + * + * \par Requires: + * - real_type DartType::x() + * - real_type DartType::y() + */ + /* + template + typename TTLtraits::real_type scalarProduct2d(const DartType& d1, const DartType& d2) { + + DartType d10 = d1; + d10.alpha0(); + + DartType d20 = d2; + d20.alpha0(); + + return scalarProduct2d(d10.x() - d1.x(), d10.y() - d1.y(), d20.x() - d2.x(), d20.y() - d2.y()); + } + */ + + + //------------------------------------------------------------------------------------------------ + /* Scalar product between 2D vectors. + * The first vector is represented by the given dart, and the second vector has + * direction from the node of the given dart - and to the given point. + * + * \par Requires: + * - real_type DartType::x(), real_type DartType::y() + * - real_type PointType2d::x(), real_type PointType2d::y() + */ + /* + template + typename TTLtraits::real_type scalarProduct2d(const typename TTLtraits::DartType& d, + const typename TTLtraits::PointType2d& p) { + typename TTLtraits::DartType d0 = d; + d0.alpha0(); + + return scalarProduct2d(d0.x() - d.x(), d0.y() - d.y(), p.x() - d.x(), p.y() - d.y()); + } + */ + + + //------------------------------------------------------------------------------------------------ + /* Cross product between 2D vectors represented as darts. + * + * \par Requires: + * - real_type DartType::x(), real_type DartType::y() + */ + /* + template + typename TTLtraits::real_type crossProduct2d(const typename TTLtraits::DartType& d1, + const typename TTLtraits::DartType& d2) { + + TTLtraits::DartType d10 = d1; + d10.alpha0(); + + TTLtraits::DartType d20 = d2; + d20.alpha0(); + + return crossProduct2d(d10.x() - d1.x(), d10.y() - d1.y(), d20.x() - d2.x(), d20.y() - d2.y()); + } + */ + + + //------------------------------------------------------------------------------------------------ + /* Cross product between 2D vectors. + * The first vector is represented by the given dart, and the second vector has + * direction from the node associated with given dart - and to the given point. + * + * \par Requires: + * - real_type DartType::x() + * - real_type DartType::y() + */ + /* + template + typename TTLtraits::real_type crossProduct2d(const typename TTLtraits::DartType& d, + const typename TTLtraits::PointType2d& p) { + + TTLtraits::DartType d0 = d; + d0.alpha0(); + + return crossProduct2d(d0.x() - d.x(), d0.y() - d.y(), p.x() - d.x(), p.y() - d.y()); + } + */ + // Geometric predicates; see more robust schemes by Jonathan Richard Shewchuk at + // http://www.cs.cmu.edu/~quake/robust.html + + + //------------------------------------------------------------------------------------------------ + /* Return a positive value if the 2d nodes/points \e d, \e d.alpha0(), and + * \e p occur in counterclockwise order; a negative value if they occur + * in clockwise order; and zero if they are collinear. The + * result is also a rough approximation of twice the signed + * area of the triangle defined by the three points. + * + * \par Requires: + * - DartType::x(), DartType::y(), + * - PointType2d::x(), PointType2d::y() + */ + /* + template + typename TTLtraits::real_type orient2dfast(const DartType& n1, const DartType& n2, + const PointType2d& p) { + return ((n2.x()-n1.x())*(p.y()-n1.y()) - (p.x()-n1.x())*(n2.y()-n1.y())); + } + */ + + //@} // End of Computational geometry + + + //------------------------------------------------------------------------------------------------ + // ---------------------------- Utilities Involving Points Group -------------------------------- + //------------------------------------------------------------------------------------------------ + + /** @name Utilities involving points */ + //@{ + + //------------------------------------------------------------------------------------------------ + /** Creates random data on the unit square. + * + * \param noPoints + * Number of random points to be generated + * + * \param seed + * Initial value for pseudorandom number generator + * + * \require + * - Constructor \c PointType::PointType(double x, double y).\n + * For example, one can use \c pair. + * + * \note + * - To deduce template argument for PointType the function must be + * called with the syntax: \c createRandomData(...) where \c MyPoint + * is the actual point type. + */ + template + std::vector* createRandomData(int noPoints, int seed=1) { + +#ifdef _MSC_VER + srand(seed); +#else + srand48((long int)seed); +#endif + + double x, y; + std::vector* points = new std::vector(noPoints); + typename std::vector::iterator it; + for (it = points->begin(); it != points->end(); ++it) { + +#ifdef _MSC_VER + int random = rand(); + x = ((double)random/(double)RAND_MAX); + random = rand(); + y = ((double)random/(double)RAND_MAX); + *it = new PointType(x,y); +#else + double random = drand48(); + x = random; + random = drand48(); + y = random; + *it = new PointType(x,y); +#endif + + } + return points; + } + + //@} // End of Utilities involving points + +}; // End of ttl_util namespace scope + +#endif // _TTL_UTIL_H_ diff --git a/include/wxBasePcbFrame.h b/include/wxBasePcbFrame.h index f89a4ed383..1eaae3598f 100644 --- a/include/wxBasePcbFrame.h +++ b/include/wxBasePcbFrame.h @@ -183,7 +183,7 @@ public: * BOARD. * @param aBoard The BOARD to put into the frame. */ - void SetBoard( BOARD* aBoard ); + virtual void SetBoard( BOARD* aBoard ); BOARD* GetBoard() const { @@ -191,8 +191,6 @@ public: return m_Pcb; } - void ViewReloadBoard( const BOARD* aBoard ) const; - /** * Function SetFootprintLibTable * set the footprint library table to \a aFootprintLibTable. @@ -717,8 +715,6 @@ public: void OnUpdateSelectGrid( wxUpdateUIEvent& aEvent ); void OnUpdateSelectZoom( wxUpdateUIEvent& aEvent ); - virtual void UseGalCanvas( bool aEnable ); - DECLARE_EVENT_TABLE() }; diff --git a/include/wxPcbStruct.h b/include/wxPcbStruct.h index 17be2ccdfd..c6b67b8086 100644 --- a/include/wxPcbStruct.h +++ b/include/wxPcbStruct.h @@ -225,7 +225,7 @@ public: const wxPoint& pos, const wxSize& size, long style = KICAD_DEFAULT_DRAWFRAME_STYLE ); - ~PCB_EDIT_FRAME(); + virtual ~PCB_EDIT_FRAME(); void OnQuit( wxCommandEvent& event ); @@ -603,6 +603,13 @@ public: */ void Show3D_Frame( wxCommandEvent& event ); + /** + * Function UseGalCanvas + * Enables/disables GAL canvas. + * @param aEnable determines if GAL should be active or not. + */ + void UseGalCanvas( bool aEnable ); + /** * Function ChangeCanvas * switches currently used canvas (default / Cairo / OpenGL). @@ -888,6 +895,15 @@ public: */ bool Clear_Pcb( bool aQuery ); + /// @copydoc PCB_BASE_FRAME::SetBoard() + void SetBoard( BOARD* aBoard ); + + /** + * Function ViewReloadBoard + * adds all items from the current board to the VIEW, so they can be displayed by GAL. + */ + void ViewReloadBoard( const BOARD* aBoard ) const; + // Drc control /* function GetDrcController diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index f243ce065f..b5f5e621fe 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -211,6 +211,8 @@ set( PCBNEW_CLASS_SRCS print_board_functions.cpp printout_controler.cpp ratsnest.cpp + ratsnest_data.cpp + ratsnest_viewitem.cpp # specctra.cpp #moved in pcbcommon lib # specctra_export.cpp # specctra_keywords.cpp @@ -370,8 +372,8 @@ if( KICAD_SCRIPTING_MODULES ) polygon bitmaps gal - ${GLEW_LIBRARIES} - ${CAIRO_LIBRARIES} + ${GLEW_LIBRARIES} + ${CAIRO_LIBRARIES} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${GDI_PLUS_LIBRARIES} diff --git a/pcbnew/basepcbframe.cpp b/pcbnew/basepcbframe.cpp index 89c5a8866e..7b3f66df58 100644 --- a/pcbnew/basepcbframe.cpp +++ b/pcbnew/basepcbframe.cpp @@ -54,6 +54,8 @@ #include #include #include +#include +#include #include #include @@ -79,6 +81,7 @@ const LAYER_NUM PCB_BASE_FRAME::GAL_LAYER_ORDER[] = ITEM_GAL_LAYER( MOD_TEXT_FR_VISIBLE ), ITEM_GAL_LAYER( MOD_REFERENCES_VISIBLE), ITEM_GAL_LAYER( MOD_VALUES_VISIBLE ), + ITEM_GAL_LAYER( RATSNEST_VISIBLE ), ITEM_GAL_LAYER( VIAS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_HOLES_VISIBLE ), ITEM_GAL_LAYER( VIAS_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ), @@ -171,102 +174,6 @@ void PCB_BASE_FRAME::SetBoard( BOARD* aBoard ) { delete m_Pcb; m_Pcb = aBoard; - - if( m_galCanvas ) - { - KIGFX::VIEW* view = m_galCanvas->GetView(); - - try - { - ViewReloadBoard( m_Pcb ); - } - catch( const std::exception& ex ) - { - DBG(printf( "ViewReloadBoard: exception: %s\n", ex.what() );) - } - - // update the tool manager with the new board and its view. - if( m_toolManager ) - m_toolManager->SetEnvironment( m_Pcb, view, m_galCanvas->GetViewControls(), this ); - } -} - - -void PCB_BASE_FRAME::ViewReloadBoard( const BOARD* aBoard ) const -{ - KIGFX::VIEW* view = m_galCanvas->GetView(); - view->Clear(); - - // All of PCB drawing elements should be added to the VIEW - // in order to be displayed - - // Load zones - for( int i = 0; i < aBoard->GetAreaCount(); ++i ) - { - view->Add( (KIGFX::VIEW_ITEM*) ( aBoard->GetArea( i ) ) ); - } - - // Load drawings - for( BOARD_ITEM* drawing = aBoard->m_Drawings; drawing; drawing = drawing->Next() ) - { - view->Add( drawing ); - } - - // Load tracks - for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) - { - view->Add( track ); - } - - // Load modules and its additional elements - for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) - { - // Load module's pads - for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) - { - view->Add( pad ); - } - - // Load module's drawing (mostly silkscreen) - for( BOARD_ITEM* drawing = module->GraphicalItems().GetFirst(); drawing; - drawing = drawing->Next() ) - { - view->Add( drawing ); - } - - // Load module's texts (name and value) - view->Add( &module->Reference() ); - view->Add( &module->Value() ); - - // Add the module itself - view->Add( module ); - } - - // Segzones (equivalent of ZONE_CONTAINER for legacy boards) - for( SEGZONE* zone = aBoard->m_Zone; zone; zone = zone->Next() ) - { - view->Add( zone ); - } - - // Add an entry for the worksheet layout - KIGFX::WORKSHEET_VIEWITEM* worksheet = new KIGFX::WORKSHEET_VIEWITEM( - std::string( aBoard->GetFileName().mb_str() ), - std::string( GetScreenDesc().mb_str() ), - &GetPageSettings(), &GetTitleBlock() ); - BASE_SCREEN* screen = GetScreen(); - if( screen != NULL ) - { - worksheet->SetSheetNumber( GetScreen()->m_ScreenNumber ); - worksheet->SetSheetCount( GetScreen()->m_NumberOfScreens ); - } - - view->Add( worksheet ); - - view->SetPanBoundary( worksheet->ViewBBox() ); - view->RecacheAllItems( true ); - - if( m_galCanvasActive ) - m_galCanvas->Refresh(); } @@ -604,17 +511,6 @@ void PCB_BASE_FRAME::OnUpdateSelectZoom( wxUpdateUIEvent& aEvent ) } -void PCB_BASE_FRAME::UseGalCanvas( bool aEnable ) -{ - EDA_DRAW_FRAME::UseGalCanvas( aEnable ); - - m_toolManager->SetEnvironment( m_Pcb, m_galCanvas->GetView(), - m_galCanvas->GetViewControls(), this ); - - ViewReloadBoard( m_Pcb ); -} - - void PCB_BASE_FRAME::ProcessItemSelection( wxCommandEvent& aEvent ) { int id = aEvent.GetId(); @@ -908,6 +804,7 @@ void PCB_BASE_FRAME::LoadSettings() view->SetRequired( SOLDERMASK_N_BACK, ITEM_GAL_LAYER( PAD_BK_VISIBLE ) ); view->SetLayerTarget( ITEM_GAL_LAYER( GP_OVERLAY ), KIGFX::TARGET_OVERLAY ); + view->SetLayerTarget( ITEM_GAL_LAYER( RATSNEST_VISIBLE ), KIGFX::TARGET_OVERLAY ); // Apply layer coloring scheme & display options if( view->GetPainter() ) diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index ca97243fc2..22b082fc9d 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -102,11 +103,15 @@ BOARD::BOARD() : m_NetClasses.GetDefault()->SetParams(); SetCurrentNetClass( m_NetClasses.GetDefault()->GetName() ); + + m_ratsnest = new RN_DATA( this ); } BOARD::~BOARD() { + delete m_ratsnest; + while( m_ZoneDescriptorList.size() ) { ZONE_CONTAINER* area_to_remove = m_ZoneDescriptorList[0]; diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index 4cd30d1f6f..cd20413baa 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -56,6 +56,7 @@ class MARKER_PCB; class MSG_PANEL_ITEM; class NETLIST; class REPORTER; +class RN_DATA; // non-owning container of item candidates when searching for items on the same track. @@ -225,6 +226,7 @@ private: EDA_RECT m_BoundingBox; NETINFO_LIST m_NetInfo; ///< net info list (name, design constraints .. + RN_DATA* m_ratsnest; BOARD_DESIGN_SETTINGS m_designSettings; ZONE_SETTINGS m_zoneSettings; @@ -355,6 +357,16 @@ public: */ BOARD_ITEM* Remove( BOARD_ITEM* aBoardItem ); + /** + * Function GetRatsnest() + * returns list of missing connections between components/tracks. + * @return RATSNEST* is an object that contains informations about missing connections. + */ + RN_DATA* GetRatsnest() const + { + return m_ratsnest; + } + /** * Function DeleteMARKERs * deletes ALL MARKERS from the board. diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index 7e415cd831..c639f88024 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -59,6 +59,13 @@ #include #include +#include +#include +#include +#include +#include +#include + #include #include @@ -329,6 +336,16 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( wxWindow* parent, const wxString& title, SetBoard( new BOARD() ); + if( m_galCanvas ) + { + ViewReloadBoard( m_Pcb ); + + // update the tool manager with the new board and its view. + if( m_toolManager ) + m_toolManager->SetEnvironment( m_Pcb, m_galCanvas->GetView(), + m_galCanvas->GetViewControls(), this ); + } + // Create the PCB_LAYER_WIDGET *after* SetBoard(): wxFont font = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); @@ -522,6 +539,106 @@ PCB_EDIT_FRAME::~PCB_EDIT_FRAME() } +void PCB_EDIT_FRAME::SetBoard( BOARD* aBoard ) +{ + PCB_BASE_FRAME::SetBoard( aBoard ); + + if( m_galCanvas ) + { + ViewReloadBoard( aBoard ); + + // update the tool manager with the new board and its view. + if( m_toolManager ) + m_toolManager->SetEnvironment( aBoard, m_galCanvas->GetView(), + m_galCanvas->GetViewControls(), this ); + } +} + + +void PCB_EDIT_FRAME::ViewReloadBoard( const BOARD* aBoard ) const +{ + KIGFX::VIEW* view = m_galCanvas->GetView(); + view->Clear(); + + // All of PCB drawing elements should be added to the VIEW + // in order to be displayed + + // Load zones + for( int i = 0; i < aBoard->GetAreaCount(); ++i ) + { + view->Add( (KIGFX::VIEW_ITEM*) ( aBoard->GetArea( i ) ) ); + } + + // Load drawings + for( BOARD_ITEM* drawing = aBoard->m_Drawings; drawing; drawing = drawing->Next() ) + { + view->Add( drawing ); + } + + // Load tracks + for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) + { + view->Add( track ); + } + + // Load modules and its additional elements + for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) + { + // Load module's pads + for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) + { + view->Add( pad ); + } + + // Load module's drawing (mostly silkscreen) + for( BOARD_ITEM* drawing = module->GraphicalItems().GetFirst(); drawing; + drawing = drawing->Next() ) + { + view->Add( drawing ); + } + + // Load module's texts (name and value) + view->Add( &module->Reference() ); + view->Add( &module->Value() ); + + // Add the module itself + view->Add( module ); + } + + // Segzones (equivalent of ZONE_CONTAINER for legacy boards) + for( SEGZONE* zone = aBoard->m_Zone; zone; zone = zone->Next() ) + { + view->Add( zone ); + } + + // Add an entry for the worksheet layout + KIGFX::WORKSHEET_VIEWITEM* worksheet = new KIGFX::WORKSHEET_VIEWITEM( + std::string( aBoard->GetFileName().mb_str() ), + std::string( GetScreenDesc().mb_str() ), + &GetPageSettings(), &GetTitleBlock() ); + BASE_SCREEN* screen = GetScreen(); + if( screen != NULL ) + { + worksheet->SetSheetNumber( GetScreen()->m_ScreenNumber ); + worksheet->SetSheetCount( GetScreen()->m_NumberOfScreens ); + } + + view->Add( worksheet ); + + // Add an entry for the ratsnest + RN_DATA* ratsnest = aBoard->GetRatsnest(); + ratsnest->ProcessBoard(); + ratsnest->Recalculate(); + view->Add( new KIGFX::RATSNEST_VIEWITEM( ratsnest ) ); + + view->SetPanBoundary( worksheet->ViewBBox() ); + view->RecacheAllItems( true ); + + if( m_galCanvasActive ) + m_galCanvas->Refresh(); +} + + bool PCB_EDIT_FRAME::isAutoSaveRequired() const { return GetScreen()->IsSave(); @@ -633,6 +750,17 @@ void PCB_EDIT_FRAME::Show3D_Frame( wxCommandEvent& event ) } +void PCB_EDIT_FRAME::UseGalCanvas( bool aEnable ) +{ + EDA_DRAW_FRAME::UseGalCanvas( aEnable ); + + m_toolManager->SetEnvironment( m_Pcb, m_galCanvas->GetView(), + m_galCanvas->GetViewControls(), this ); + + ViewReloadBoard( m_Pcb ); +} + + void PCB_EDIT_FRAME::SwitchCanvas( wxCommandEvent& aEvent ) { int id = aEvent.GetId(); @@ -795,7 +923,7 @@ void PCB_EDIT_FRAME::setHighContrastLayer( LAYER_NUM aLayer ) GetNetnameLayer( aLayer ), ITEM_GAL_LAYER( VIAS_VISIBLE ), ITEM_GAL_LAYER( VIAS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ), ITEM_GAL_LAYER( PADS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_NETNAMES_VISIBLE ), - ITEM_GAL_LAYER( GP_OVERLAY ) + ITEM_GAL_LAYER( GP_OVERLAY ), ITEM_GAL_LAYER( RATSNEST_VISIBLE ) }; for( unsigned int i = 0; i < sizeof( layers ) / sizeof( LAYER_NUM ); ++i ) @@ -835,7 +963,7 @@ void PCB_EDIT_FRAME::setTopLayer( LAYER_NUM aLayer ) GetNetnameLayer( aLayer ), ITEM_GAL_LAYER( VIAS_VISIBLE ), ITEM_GAL_LAYER( VIAS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ), ITEM_GAL_LAYER( PADS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_NETNAMES_VISIBLE ), - ITEM_GAL_LAYER( GP_OVERLAY ), DRAW_N + ITEM_GAL_LAYER( GP_OVERLAY ), ITEM_GAL_LAYER( RATSNEST_VISIBLE ), DRAW_N }; for( unsigned int i = 0; i < sizeof( layers ) / sizeof( LAYER_NUM ); ++i ) diff --git a/pcbnew/ratsnest_data.cpp b/pcbnew/ratsnest_data.cpp new file mode 100644 index 0000000000..85ca642808 --- /dev/null +++ b/pcbnew/ratsnest_data.cpp @@ -0,0 +1,856 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file ratsnest_data.cpp + * @brief Class that computes missing connections on a PCB. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +uint64_t getDistance( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2 ) +{ + // Drop the least significant bits to avoid overflow + int64_t x = ( aNode1->GetX() - aNode2->GetX() ) >> 16; + int64_t y = ( aNode1->GetY() - aNode2->GetY() ) >> 16; + + // We do not need sqrt() here, as the distance is computed only for comparison + return ( x * x + y * y ); +} + + +bool sortDistance( const RN_NODE_PTR& aOrigin, const RN_NODE_PTR& aNode1, + const RN_NODE_PTR& aNode2 ) +{ + return getDistance( aOrigin, aNode1 ) < getDistance( aOrigin, aNode2 ); +} + + +bool sortWeight( const RN_EDGE_PTR& aEdge1, const RN_EDGE_PTR& aEdge2 ) +{ + return aEdge1->getWeight() < aEdge2->getWeight(); +} + + +bool sortArea( const RN_POLY& aP1, const RN_POLY& aP2 ) +{ + return aP1.m_bbox.GetArea() < aP2.m_bbox.GetArea(); +} + + +bool isEdgeConnectingNode( const RN_EDGE_PTR& aEdge, const RN_NODE_PTR& aNode ) +{ + return ( aEdge->getSourceNode().get() == aNode.get() ) || + ( aEdge->getTargetNode().get() == aNode.get() ); +} + + +std::vector* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges, + const std::vector& aNodes ) +{ + unsigned int nodeNumber = aNodes.size(); + unsigned int mstExpectedSize = nodeNumber - 1; + unsigned int mstSize = 0; + + // The output + std::vector* mst = new std::vector; + mst->reserve( mstExpectedSize ); + + // Set tags for marking cycles + boost::unordered_map tags; + unsigned int tag = 0; + BOOST_FOREACH( const RN_NODE_PTR& node, aNodes ) + tags[node] = tag++; + + // Lists of nodes connected together (subtrees) to detect cycles in the graph + std::vector > cycles( nodeNumber ); + for( unsigned int i = 0; i < nodeNumber; ++i ) + cycles[i].push_back( i ); + + // Kruskal algorithm requires edges to be sorted by their weight + aEdges.sort( sortWeight ); + + while( mstSize < mstExpectedSize && !aEdges.empty() ) + { + RN_EDGE_PTR& dt = *aEdges.begin(); + + int srcTag = tags[dt->getSourceNode()]; + int trgTag = tags[dt->getTargetNode()]; + + // Check if by adding this edge we are going to join two different forests + if( srcTag != trgTag ) + { + // Update tags + std::list::iterator it, itEnd; + for( it = cycles[trgTag].begin(), itEnd = cycles[trgTag].end(); it != itEnd; ++it ) + tags[aNodes[*it]] = srcTag; + + // Move nodes that were marked with old tag to the list marked with the new tag + cycles[srcTag].splice( cycles[srcTag].end(), cycles[trgTag] ); + + if( dt->getWeight() == 0 ) // Skip already existing connections (weight == 0) + mstExpectedSize--; + else + { + mst->push_back( dt ); + ++mstSize; + } + } + + // Remove the edge that was just processed + aEdges.erase( aEdges.begin() ); + } + + // Probably we have discarded some of edges, so reduce the size + mst->resize( mstSize ); + + return mst; +} + + +void RN_NET::validateEdge( RN_EDGE_PTR& aEdge ) +{ + RN_NODE_PTR source = aEdge->getSourceNode(); + RN_NODE_PTR target = aEdge->getTargetNode(); + bool valid = true; + + // If any of nodes belonging to the edge has the flag set, + // change it to the closest node that has flag cleared + if( source->GetFlag() ) + { + valid = false; + + std::list closest = GetClosestNodes( source, WITHOUT_FLAG() ); + BOOST_FOREACH( RN_NODE_PTR& node, closest ) + { + if( node && node != target ) + { + source = node; + break; + } + } + } + + if( target->GetFlag() ) + { + valid = false; + + WITHOUT_FLAG without_flag; + std::list closest = GetClosestNodes( target, WITHOUT_FLAG() ); + BOOST_FOREACH( RN_NODE_PTR& node, closest ) + { + if( node && node != source ) + { + target = node; + break; + } + } + } + + // Replace an invalid edge with new, valid one + if( !valid ) + aEdge.reset( new RN_EDGE_MST( source, target ) ); +} + + +const RN_NODE_PTR& RN_LINKS::AddNode( int aX, int aY ) +{ + RN_NODE_SET::iterator node; + bool wasNewElement; + + boost::tie( node, wasNewElement ) = m_nodes.emplace( boost::make_shared( aX, aY ) ); + (*node)->IncRefCount(); // TODO use the shared_ptr use_count + + return *node; +} + + +void RN_LINKS::RemoveNode( const RN_NODE_PTR& aNode ) +{ + aNode->DecRefCount(); // TODO use the shared_ptr use_count + + if( aNode->GetRefCount() == 0 ) + m_nodes.erase( aNode ); +} + + +const RN_EDGE_PTR& RN_LINKS::AddConnection( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2, + unsigned int aDistance ) +{ + m_edges.push_back( boost::make_shared( aNode1, aNode2, aDistance ) ); + + return m_edges.back(); +} + + +void RN_LINKS::RemoveConnection( const RN_EDGE_PTR& aEdge ) +{ + m_edges.remove( aEdge ); +} + + +void RN_NET::compute() +{ + const RN_LINKS::RN_NODE_SET& boardNodes = m_links.GetNodes(); + const RN_LINKS::RN_EDGE_LIST& boardEdges = m_links.GetConnections(); + + // Special case that does need so complicated algorithm + if( boardNodes.size() == 2 ) + { + m_rnEdges.reset( new std::vector( 0 ) ); + + // Check if the only possible connection exists + if( boardEdges.size() == 0 ) + { + RN_LINKS::RN_NODE_SET::iterator last = ++boardNodes.begin(); + + // There can be only one possible connection, but it is missing + m_rnEdges->push_back( boost::make_shared( *boardNodes.begin(), *last ) ); + } + + return; + } + else if( boardNodes.size() == 1 ) // This case is even simpler + { + m_rnEdges.reset( new std::vector( 0 ) ); + + return; + } + + // Move and sort (sorting speeds up) all nodes to a vector for the Delaunay triangulation + std::vector nodes( boardNodes.size() ); + std::partial_sort_copy( boardNodes.begin(), boardNodes.end(), nodes.begin(), nodes.end() ); + + TRIANGULATOR triangulator; + triangulator.createDelaunay( nodes.begin(), nodes.end() ); + boost::scoped_ptr triangEdges( triangulator.getEdges() ); + + // Compute weight/distance for edges resulting from triangulation + RN_LINKS::RN_EDGE_LIST::iterator eit, eitEnd; + for( eit = (*triangEdges).begin(), eitEnd = (*triangEdges).end(); eit != eitEnd; ++eit ) + (*eit)->setWeight( getDistance( (*eit)->getSourceNode(), (*eit)->getTargetNode() ) ); + + // Add the currently existing connections list to the results of triangulation + std::copy( boardEdges.begin(), boardEdges.end(), std::front_inserter( *triangEdges ) ); + + // Get the minimal spanning tree + m_rnEdges.reset( kruskalMST( *triangEdges, nodes ) ); +} + + +void RN_NET::clearNode( const RN_NODE_PTR& aNode ) +{ + std::vector::iterator newEnd; + + // Remove all ratsnest edges for associated with the node + newEnd = std::remove_if( m_rnEdges->begin(), m_rnEdges->end(), + boost::bind( isEdgeConnectingNode, _1, aNode ) ); + + m_rnEdges->resize( std::distance( m_rnEdges->begin(), newEnd ) ); +} + + +RN_POLY::RN_POLY( const CPolyPt* aBegin, const CPolyPt* aEnd, const ZONE_CONTAINER* aParent, + RN_LINKS& aConnections, const BOX2I& aBBox ) : + m_parent( aParent), m_begin( aBegin ), m_end( aEnd ), m_bbox( aBBox ) +{ + m_node = aConnections.AddNode( m_begin->x, m_begin->y ); + + // Mark it as not feasible as a destination of ratsnest edges + // (edges coming out from a polygon vertex look weird) + m_node->SetFlag( true ); +} + + +bool RN_POLY::HitTest( const RN_NODE_PTR& aNode ) const +{ + long xt = aNode->GetX(); + long yt = aNode->GetY(); + + // If the point lies outside the bounding box, there is no point to check it further + if( !m_bbox.Contains( xt, yt ) ) + return false; + + long xNew, yNew, xOld, yOld, x1, y1, x2, y2; + bool inside = false; + + // For the first loop we have to use the last point as the previous point + xOld = m_end->x; + yOld = m_end->y; + + for( const CPolyPt* point = m_begin; point <= m_end; ++point ) + { + xNew = point->x; + yNew = point->y; + + // Swap points if needed, so always x2 >= x1 + if( xNew > xOld ) + { + x1 = xOld; y1 = yOld; + x2 = xNew; y2 = yNew; + } + else + { + x1 = xNew; y1 = yNew; + x2 = xOld; y2 = yOld; + } + + if( ( xNew < xt ) == ( xt <= xOld ) /* edge "open" at left end */ + && ( yt - y1 ) * ( x2 - x1 ) < ( y2 - y1 ) * ( xt - x1 ) ) + { + inside = !inside; + } + + xOld = xNew; + yOld = yNew; + } + + return inside; +} + + +void RN_NET::Update() +{ + // Add edges resulting from nodes being connected by zones + processZones(); + + compute(); + + BOOST_FOREACH( RN_EDGE_PTR& edge, *m_rnEdges ) + validateEdge( edge ); + + m_dirty = false; +} + + +void RN_NET::AddItem( const D_PAD* aPad ) +{ + RN_NODE_PTR nodePtr = m_links.AddNode( aPad->GetPosition().x, aPad->GetPosition().y ); + m_pads[aPad] = nodePtr; + + m_dirty = true; +} + + +void RN_NET::AddItem( const SEGVIA* aVia ) +{ + m_vias[aVia] = m_links.AddNode( aVia->GetPosition().x, aVia->GetPosition().y ); + + m_dirty = true; +} + + +void RN_NET::AddItem( const TRACK* aTrack ) +{ + RN_NODE_PTR start = m_links.AddNode( aTrack->GetStart().x, aTrack->GetStart().y ); + RN_NODE_PTR end = m_links.AddNode( aTrack->GetEnd().x, aTrack->GetEnd().y ); + + m_tracks[aTrack] = m_links.AddConnection( start, end ); + + m_dirty = true; +} + + +void RN_NET::AddItem( const ZONE_CONTAINER* aZone ) +{ + // Prepare a list of polygons (every zone can contain one or more polygons) + const std::vector& polyPoints = aZone->GetFilledPolysList().GetList(); + if( polyPoints.size() == 0 ) + return; + + // Origin and end of bounding box for a polygon + VECTOR2I origin( polyPoints[0].x, polyPoints[0].y ); + VECTOR2I end( polyPoints[0].x, polyPoints[0].y ); + int idxStart = 0; + + // Extract polygons from zones + for( unsigned int i = 0; i < polyPoints.size(); ++i ) + { + const CPolyPt& point = polyPoints[i]; + + // Determine bounding box + if( point.x < origin.x ) + origin.x = point.x; + else if( point.x > end.x ) + end.x = point.x; + + if( point.y < origin.y ) + origin.y = point.y; + else if( point.y > end.y ) + end.y = point.y; + + if( point.end_contour ) + { + // The last vertex is enclosing the polygon (it repeats at the beginning and + // at the end), so we skip it + m_zonePolygons[aZone].push_back( RN_POLY( &polyPoints[idxStart], &point, aZone, + m_links, BOX2I( origin, end - origin ) ) ); + + idxStart = i + 1; + origin.x = polyPoints[idxStart].x; + origin.y = polyPoints[idxStart].y; + end.x = polyPoints[idxStart].x; + end.y = polyPoints[idxStart].y; + } + } + + m_dirty = true; +} + + +void RN_NET::RemoveItem( const D_PAD* aPad ) +{ + RN_NODE_PTR& node = m_pads[aPad]; + if( !node ) + return; + + // Remove edges associated with the node + clearNode( node ); + m_links.RemoveNode( node ); + + m_pads.erase( aPad ); + + m_dirty = true; +} + + +void RN_NET::RemoveItem( const SEGVIA* aVia ) +{ + RN_NODE_PTR& node = m_vias[aVia]; + if( !node ) + return; + + // Remove edges associated with the node + clearNode( node ); + m_links.RemoveNode( node ); + + m_vias.erase( aVia ); + + m_dirty = true; +} + + +void RN_NET::RemoveItem( const TRACK* aTrack ) +{ + RN_EDGE_PTR& edge = m_tracks[aTrack]; + if( !edge ) + return; + + // Save nodes, so they can be cleared later + const RN_NODE_PTR& aBegin = edge->getSourceNode(); + const RN_NODE_PTR& aEnd = edge->getTargetNode(); + m_links.RemoveConnection( edge ); + + // Remove nodes associated with the edge. It is done in a safe way, there is a check + // if nodes are not used by other edges. + clearNode( aBegin ); + clearNode( aEnd ); + m_links.RemoveNode( aBegin ); + m_links.RemoveNode( aEnd ); + + m_tracks.erase( aTrack ); + + m_dirty = true; +} + + +void RN_NET::RemoveItem( const ZONE_CONTAINER* aZone ) +{ + // Remove all subpolygons that make the zone + std::deque& polygons = m_zonePolygons[aZone]; + BOOST_FOREACH( RN_POLY& polygon, polygons ) + m_links.RemoveNode( polygon.GetNode() ); + polygons.clear(); + + // Remove all connections added by the zone + std::deque& edges = m_zoneConnections[aZone]; + BOOST_FOREACH( RN_EDGE_PTR& edge, edges ) + m_links.RemoveConnection( edge ); + edges.clear(); + + m_dirty = true; +} + + +const RN_NODE_PTR RN_NET::GetClosestNode( const RN_NODE_PTR& aNode ) const +{ + const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); + RN_LINKS::RN_NODE_SET::const_iterator it, itEnd; + + unsigned int minDistance = std::numeric_limits::max(); + RN_NODE_PTR closest; + + for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it ) + { + // Obviously the distance between node and itself is the shortest, + // that's why we have to skip it + if( *it != aNode ) + { + unsigned int distance = getDistance( *it, aNode ); + if( distance < minDistance ) + { + minDistance = distance; + closest = *it; + } + } + } + + return closest; +} + + +const RN_NODE_PTR RN_NET::GetClosestNode( const RN_NODE_PTR& aNode, RN_NODE_FILTER aFilter ) const +{ + const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); + RN_LINKS::RN_NODE_SET::const_iterator it, itEnd; + + unsigned int minDistance = std::numeric_limits::max(); + RN_NODE_PTR closest; + + for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it ) + { + RN_NODE_PTR baseNode = *it; + + // Obviously the distance between node and itself is the shortest, + // that's why we have to skip it + if( *it != aNode && aFilter( baseNode ) ) + { + unsigned int distance = getDistance( *it, aNode ); + if( distance < minDistance ) + { + minDistance = distance; + closest = *it; + } + } + } + + return closest; +} + + +std::list RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode, int aNumber ) const +{ + std::list closest; + const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); + + // Copy nodes + BOOST_FOREACH( const RN_NODE_PTR& node, nodes ) + closest.push_back( node ); + + // Sort by the distance from aNode + closest.sort( boost::bind( sortDistance, aNode, _1, _2 ) ); + + // Remove the first node (==aNode), as it is surely located within the smallest distance + closest.pop_front(); + + // Trim the result to the asked size + if( aNumber > 0 ) + closest.resize( std::min( (size_t)aNumber, nodes.size() ) ); + + return closest; +} + + +std::list RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode, + RN_NODE_FILTER aFilter, int aNumber ) const +{ + std::list closest; + const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); + + // Copy nodes + BOOST_FOREACH( const RN_NODE_PTR& node, nodes ) + closest.push_back( node ); + + // Sort by the distance from aNode + closest.sort( boost::bind( sortDistance, aNode, _1, _2 ) ); + + // Remove the first node (==aNode), as it is surely located within the smallest distance + closest.pop_front(); + + // Filter out by condition + std::remove_if( closest.begin(), closest.end(), aFilter ); + + // Trim the result to the asked size + if( aNumber > 0 ) + closest.resize( std::min( static_cast( aNumber ), nodes.size() ) ); + + return closest; +} + + +std::list RN_NET::GetNodes( const BOARD_CONNECTED_ITEM* aItem ) const +{ + std::list nodes; + + switch( aItem->Type() ) + { + case PCB_PAD_T: + { + const D_PAD* pad = static_cast( aItem ); + nodes.push_back( m_pads.at( pad ) ); + } + break; + + case PCB_VIA_T: + { + const SEGVIA* via = static_cast( aItem ); + nodes.push_back( m_vias.at( via ) ); + } + break; + + case PCB_TRACE_T: + { + const TRACK* track = static_cast( aItem ); + RN_EDGE_PTR edge = m_tracks.at( track ); + + nodes.push_back( edge->getSourceNode() ); + nodes.push_back( edge->getTargetNode() ); + } + break; + + default: + break; + } + + return nodes; +} + + +void RN_NET::ClearSimple() +{ + BOOST_FOREACH( const RN_NODE_PTR& node, m_simpleNodes ) + node->SetFlag( false ); + + m_simpleNodes.clear(); +} + + +void RN_DATA::AddSimple( const BOARD_CONNECTED_ITEM* aItem ) +{ + int net = aItem->GetNet(); + if( net < 1 ) // do not process unconnected items + return; + + // Get list of nodes responding to the item + std::list nodes = m_nets[net].GetNodes( aItem ); + std::list::iterator it, itEnd; + + for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it ) + m_nets[net].AddSimpleNode( *it ); +} + + +void RN_DATA::AddSimple( const MODULE* aModule ) +{ + for( const D_PAD* pad = aModule->Pads().GetFirst(); pad; pad = pad->Next() ) + AddSimple( pad ); +} + + +void RN_NET::processZones() +{ + BOOST_FOREACH( std::deque& edges, m_zoneConnections | boost::adaptors::map_values ) + { + BOOST_FOREACH( RN_EDGE_PTR& edge, edges ) + m_links.RemoveConnection( edge ); + + edges.clear(); + } + + RN_LINKS::RN_NODE_SET candidates = m_links.GetNodes(); + + BOOST_FOREACH( std::deque& polygons, m_zonePolygons | boost::adaptors::map_values ) + { + RN_LINKS::RN_NODE_SET::iterator point, pointEnd; + std::deque::iterator poly, polyEnd; + + // Sorting by area should speed up the processing, as smaller polygons are computed + // faster and may reduce the number of points for further checks + std::sort( polygons.begin(), polygons.end(), sortArea ); + + for( poly = polygons.begin(), polyEnd = polygons.end(); poly != polyEnd; ++poly ) + { + point = candidates.begin(); + pointEnd = candidates.end(); + + while( point != pointEnd ) + { + if( poly->HitTest( *point ) ) + { + const RN_EDGE_PTR& connection = m_links.AddConnection( poly->GetNode(), *point ); + m_zoneConnections[poly->GetParent()].push_back( connection ); + + // This point already belongs to a polygon, we do not need to check it anymore + point = candidates.erase( point ); + pointEnd = candidates.end(); + } + else + { + ++point; + } + } + } + } +} + + +void RN_DATA::updateNet( int aNetCode ) +{ + assert( aNetCode < (int) m_nets.size() ); + if( aNetCode < 1 ) + return; + + m_nets[aNetCode].ClearSimple(); + m_nets[aNetCode].Update(); +} + + +void RN_DATA::Update( const BOARD_CONNECTED_ITEM* aItem ) +{ + int net = aItem->GetNet(); + if( net < 1 ) // do not process unconnected items + return; + + switch( aItem->Type() ) + { + case PCB_PAD_T: + { + const D_PAD* pad = static_cast( aItem ); + m_nets[net].RemoveItem( pad ); + m_nets[net].AddItem( pad ); + } + break; + + case PCB_TRACE_T: + { + const TRACK* track = static_cast( aItem ); + m_nets[net].RemoveItem( track ); + m_nets[net].AddItem( track ); + } + break; + + case PCB_VIA_T: + { + const SEGVIA* via = static_cast( aItem ); + m_nets[net].RemoveItem( via ); + m_nets[net].AddItem( via ); + } + break; + + case PCB_ZONE_AREA_T: + { + const ZONE_CONTAINER* zone = static_cast( aItem ); + m_nets[net].RemoveItem( zone); + m_nets[net].AddItem( zone ); + } + break; + + default: + break; + } +} + + +void RN_DATA::Update( const MODULE* aModule ) +{ + for( const D_PAD* pad = aModule->Pads().GetFirst(); pad; pad = pad->Next() ) + { + int net = pad->GetNet(); + + if( net > 0 ) // do not process unconnected items + { + m_nets[net].RemoveItem( pad ); + m_nets[net].AddItem( pad ); + } + } +} + + +void RN_DATA::ProcessBoard() +{ + m_nets.clear(); + m_nets.resize( m_board->GetNetCount() ); + + // Iterate over all items that may need to be connected + for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) + { + for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) + m_nets[pad->GetNet()].AddItem( pad ); + } + + for( TRACK* track = m_board->m_Track; track; track = track->Next() ) + { + if( track->Type() == PCB_VIA_T ) + m_nets[track->GetNet()].AddItem( static_cast( track ) ); + else if( track->Type() == PCB_TRACE_T ) + m_nets[track->GetNet()].AddItem( track ); + } + + for( int i = 0; i < m_board->GetAreaCount(); ++i ) + { + ZONE_CONTAINER* zone = m_board->GetArea( i ); + m_nets[zone->GetNet()].AddItem( zone ); + } +} + + +void RN_DATA::Recalculate( int aNet ) +{ + if( aNet < 0 ) // Recompute everything + { + // Start with net number 1, as 0 stand for not connected + for( unsigned int i = 1; i < m_board->GetNetCount(); ++i ) + { + if( m_nets[i].IsDirty() ) + updateNet( i ); + } + } + else // Recompute only specific net + { + updateNet( aNet ); + } +} + + +void RN_DATA::ClearSimple() +{ + BOOST_FOREACH( RN_NET& net, m_nets ) + net.ClearSimple(); +} diff --git a/pcbnew/ratsnest_data.h b/pcbnew/ratsnest_data.h new file mode 100644 index 0000000000..dc5e4016b1 --- /dev/null +++ b/pcbnew/ratsnest_data.h @@ -0,0 +1,595 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file ratsnest_data.h + * @brief Class that computes missing connections on a PCB. + */ + +#ifndef RATSNEST_DATA_H +#define RATSNEST_DATA_H + +#include +#include + +#include + +#include +#include + +class BOARD; +class BOARD_ITEM; +class BOARD_CONNECTED_ITEM; +class MODULE; +class D_PAD; +class SEGVIA; +class TRACK; +class ZONE_CONTAINER; +class CPolyPt; + +// Preserve KiCad coding style policy +typedef hed::Node RN_NODE; +typedef hed::NodePtr RN_NODE_PTR; +typedef hed::Edge RN_EDGE; +typedef hed::EdgePtr RN_EDGE_PTR; +typedef hed::EdgeMST RN_EDGE_MST; +typedef boost::shared_ptr RN_EDGE_MST_PTR; +typedef hed::Triangulation TRIANGULATOR; + +///> General interface for filtering out nodes in search functions. +struct RN_NODE_FILTER : public std::unary_function +{ + virtual ~RN_NODE_FILTER() {} + virtual bool operator()( const RN_NODE_PTR& aNode ) + { + return true; // By default everything passes + } +}; + +///> Filters out nodes that have the flag set. +struct WITHOUT_FLAG : public RN_NODE_FILTER +{ + bool operator()( const RN_NODE_PTR& aNode ) + { + return !aNode->GetFlag(); + } +}; + +///> Functor comparing if two nodes are equal by their coordinates. It is required to make set of +///> shared pointers work properly. +struct RN_NODE_COMPARE : std::binary_function +{ + bool operator()( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2 ) const + { + return ( aNode1->GetX() == aNode2->GetX() && aNode1->GetY() == aNode2->GetY() ); + } +}; + +///> Functor calculating hash for a given node. It is required to make set of shared pointers +///> work properly. +struct RN_NODE_HASH : std::unary_function +{ + std::size_t operator()( const RN_NODE_PTR& aNode ) const + { + std::size_t hash = 2166136261u; + + hash ^= aNode->GetX(); + hash *= 16777619; + hash ^= aNode->GetY(); + + return hash; + } +}; + + +/** + * Class RN_LINKS + * Manages data describing nodes and connections for a given net. + */ +class RN_LINKS +{ +public: + // Helper typedefs + typedef boost::unordered_set RN_NODE_SET; + typedef std::list RN_EDGE_LIST; + + /** + * Function AddNode() + * Adds a node with given coordinates and returns pointer to the newly added node. If the node + * existed before, only appropriate pointer is returned. + * @param aX is the x coordinate of a node. + * @param aY is the y coordinate of a node. + * @return Pointer to the node with given coordinates. + */ + const RN_NODE_PTR& AddNode( int aX, int aY ); + + /** + * Function RemoveNode() + * Removes a node described by a given node pointer. + * @param aNode is a pointer to node to be removed. + */ + void RemoveNode( const RN_NODE_PTR& aNode ); + + /** + * Function GetNodes() + * Returns the set of currently used nodes. + * @return The set of currently used nodes. + */ + const RN_NODE_SET& GetNodes() const + { + return m_nodes; + } + + /** + * Function AddConnection() + * Adds a connection between two nodes and of given distance. Edges with distance equal 0 are + * considered to be existing connections. Distance different than 0 means that the connection + * is missing. + * @param aNode1 is the origin node of a new connection. + * @param aNode2 is the end node of a new connection. + * @param aDistance is the distance of the connection (0 means that nodes are actually + * connected, >0 means a missing connection). + */ + const RN_EDGE_PTR& AddConnection( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2, + unsigned int aDistance = 0 ); + + /** + * Function RemoveConnection() + * Removes a connection described by a given edge pointer. + * @param aEdge is a pointer to edge to be removed. + */ + void RemoveConnection( const RN_EDGE_PTR& aEdge ); + + /** + * Function GetConnections() + * Returns the list of edges that currently connect nodes. + * @return the list of edges that currently connect nodes. + */ + const RN_EDGE_LIST& GetConnections() const + { + return m_edges; + } + +protected: + ///> Set of nodes that are used are expected to be connected together. + RN_NODE_SET m_nodes; + + ///> List of edges that currently connect nodes. + RN_EDGE_LIST m_edges; +}; + + +/** + * Class RN_POLY + * Describes a single subpolygon (ZONE_CONTAINER is supposed to contain one or more of those) and + * performs fast point-inside-polygon test. + */ +class RN_POLY +{ +public: + RN_POLY( const CPolyPt* aBegin, const CPolyPt* aEnd, const ZONE_CONTAINER* aParent, + RN_LINKS& aConnections, const BOX2I& aBBox ); + + /** + * Function GetNode() + * Returns node representing a polygon (it has the same coordinates as the first point of its + * bounding polyline. + */ + const RN_NODE_PTR& GetNode() const + { + return m_node; + } + + + /** + * Function GetParent() + * Returns pointer to zone that is the owner of subpolygon. + * @return Pointer to zone that is the owner of subpolygon. + */ + const ZONE_CONTAINER* GetParent() const + { + return m_parent; + } + + /** + * Function HitTest() + * Tests if selected node is located within polygon boundaries. + * @param aNode is a node to be checked. + * @return True is the node is located within polygon boundaries. + */ + bool HitTest( const RN_NODE_PTR& aNode ) const; + +private: + ///> Owner of this subpolygon. + const ZONE_CONTAINER* m_parent; + + ///> Pointer to the first point of polyline bounding the polygon. + const CPolyPt* m_begin; + + ///> Pointer to the last point of polyline bounding the polygon. + const CPolyPt* m_end; + + ///> Bounding box of the polygon. + BOX2I m_bbox; + + ///> Node representing a polygon (it has the same coordinates as the first point of its + ///> bounding polyline. + RN_NODE_PTR m_node; + + friend bool sortArea( const RN_POLY& aP1, const RN_POLY& aP2 ); +}; + + +/** + * Class RN_NET + * Describes ratsnest for a single net. + */ +class RN_NET +{ +public: + ///> Default constructor. + RN_NET() : m_dirty( true ), m_visible( true ) + {} + + /** + * Function SetVisible() + * Sets state of the visibility flag. + * @param aEnabled is new state. True if ratsnest for a given net is meant to be displayed, + * false otherwise. + */ + void SetVisible( bool aEnabled ) + { + m_visible = aEnabled; + } + + /** + * Function IsVisible() + * Returns the visibility flag state. + * @return True if ratsnest for given net is set as visible, false otherwise, + */ + bool IsVisible() const + { + return m_visible; + } + + /** + * Function MarkDirty() + * Marks ratsnest for given net as 'dirty', ie. requiring recomputation. + */ + void MarkDirty() + { + m_dirty = true; + } + + /** + * Function IsDirty() + * Returns state of the 'dirty' flag, indicating that ratsnest for a given net is invalid + * and requires an update. + * @return True if ratsnest requires recomputation, false otherwise. + */ + bool IsDirty() const + { + return m_dirty; + } + + /** + * Function GetUnconnected() + * Returns pointer to a vector of edges that makes ratsnest for a given net. + * @return Pointer to a vector of edges that makes ratsnest for a given net. + */ + const std::vector* GetUnconnected() const + { + return m_rnEdges.get(); + } + + /** + * Function Update() + * Recomputes ratsnest for a net. + */ + void Update(); + + /** + * Function AddItem() + * Adds an appropriate node associated with selected pad, so it is + * taken into account during ratsnest computations. + * @param aPad is a pad for which node is added. + */ + void AddItem( const D_PAD* aPad ); + + /** + * Function AddItem() + * Adds an appropriate node associated with selected via, so it is + * taken into account during ratsnest computations. + * @param aVia is a via for which node is added. + */ + void AddItem( const SEGVIA* aVia ); + + /** + * Function AddItem() + * Adds appropriate nodes and edges associated with selected track, so they are + * taken into account during ratsnest computations. + * @param aTrack is a track for which nodes and edges are added. + */ + void AddItem( const TRACK* aTrack ); + + /** + * Function AddItem() + * Processes zone to split it into subpolygons and adds appropriate nodes for them, so they are + * taken into account during ratsnest computations. + * @param aZone is a zone to be processed. + */ + void AddItem( const ZONE_CONTAINER* aZone ); + + /** + * Function RemoveItem() + * Removes all nodes and edges associated with selected pad, so they are not + * taken into account during ratsnest computations anymore. + * @param aPad is a pad for which nodes and edges are removed. + */ + void RemoveItem( const D_PAD* aPad ); + + /** + * Function RemoveItem() + * Removes all nodes and edges associated with selected via, so they are not + * taken into account during ratsnest computations anymore. + * @param aVia is a via for which nodes and edges are removed. + */ + void RemoveItem( const SEGVIA* aVia ); + + /** + * Function RemoveItem() + * Removes all nodes and edges associated with selected track, so they are not + * taken into account during ratsnest computations anymore. + * @param aTrack is a track for which nodes and edges are removed. + */ + void RemoveItem( const TRACK* aTrack ); + + /** + * Function RemoveItem() + * Removes all nodes and edges associated with selected zone, so they are not + * taken into account during ratsnest computations anymore. + * @param aZone is a zone for which nodes and edges are removed. + */ + void RemoveItem( const ZONE_CONTAINER* aZone ); + + /** + * Function GetNodes() + * Returns list of nodes that are associated with a given item. + * @param aItem is an item for which the list is generated. + * @return List of associated nodes. + */ + std::list GetNodes( const BOARD_CONNECTED_ITEM* aItem ) const; + + /** + * Function GetClosestNode() + * Returns a single node that lies in the shortest distance from a specific node. + * @param aNode is the node for which the closest node is searched. + */ + const RN_NODE_PTR GetClosestNode( const RN_NODE_PTR& aNode ) const; + + /** + * Function GetClosestNode() + * Returns a single node that lies in the shortest distance from a specific node and meets + * selected filter criterion.. + * @param aNode is the node for which the closest node is searched. + * @param aFilter is a functor that filters nodes. + */ + const RN_NODE_PTR GetClosestNode( const RN_NODE_PTR& aNode, RN_NODE_FILTER aFilter ) const; + + /** + * Function GetClosestNodes() + * Returns list of nodes sorted by the distance from a specific node. + * @param aNode is the node for which the closest nodes are searched. + * @param aNumber is asked number of returned nodes. If it is negative then all nodes that + * belong to the same net are returned. If asked number is greater than number of possible + * nodes then the size of list is limited to number of possible nodes. + */ + std::list GetClosestNodes( const RN_NODE_PTR& aNode, int aNumber = -1 ) const; + + /** + * Function GetClosestNodes() + * Returns filtered list of nodes sorted by the distance from a specific node. + * @param aNode is the node for which the closest nodes are searched. + * @param aFilter is a functor that filters nodes. + * @param aNumber is asked number of returned nodes. If it is negative then all nodes that + * belong to the same net are returned. If asked number is greater than number of possible + * nodes then the size of list is limited to number of possible nodes. + */ + std::list GetClosestNodes( const RN_NODE_PTR& aNode, + RN_NODE_FILTER aFilter, int aNumber = -1 ) const; + + /** + * Function GetEdges() + * Returns pointer to the vector of edges that makes ratsnest for a given net. + * @return Pointer to the vector of edges that makes ratsnest for a given net + */ + const std::vector* GetEdges() const + { + return m_rnEdges.get(); + } + + /** + * Function AddSimpleNode() + * Changes drawing mode for a node to simple (ie. one ratsnest line per node). + * @param aNode is a node that changes its drawing mode.. + */ + void AddSimpleNode( RN_NODE_PTR& aNode ) + { + m_simpleNodes.push_back( aNode ); + aNode->SetFlag( true ); + } + + /** + * Function GetSimpleNodes() + * Returns list of nodes for which ratsnest is drawn in simple mode (ie. one + * ratsnest line per node). + * @return list of nodes for which ratsnest is drawn in simple mode. + */ + const std::deque& GetSimpleNodes() const + { + return m_simpleNodes; + } + + /** + * Function ClearSimple() + * Removes all nodes and edges that are used for displaying ratsnest in simple mode. + */ + void ClearSimple(); + +protected: + ///> Validates edge, ie. modifies source and target nodes for an edge + ///> to make sure that they are not ones with the flag set. + void validateEdge( RN_EDGE_PTR& aEdge ); + + ///> Removes all ratsnest edges for a given node. + void clearNode( const RN_NODE_PTR& aNode ); + + ///> Adds appropriate edges for nodes that are connected by zones. + void processZones(); + + ///> Recomputes ratsnset from scratch. + void compute(); + + ////> Stores information about connections for a given net. + RN_LINKS m_links; + + ///> Vector of edges that makes ratsnest for a given net. + boost::shared_ptr< std::vector > m_rnEdges; + + ///> List of nodes for which ratsnest is drawn in simple mode. + std::deque m_simpleNodes; + + ///> Flag indicating necessity of recalculation of ratsnest for a net. + bool m_dirty; + + ///> Map that associates nodes in the ratsnest model to respective nodes. + boost::unordered_map m_pads; + + ///> Map that associates nodes in the ratsnest model to respective vias. + boost::unordered_map m_vias; + + ///> Map that associates edges in the ratsnest model to respective tracks. + boost::unordered_map m_tracks; + + ///> Map that associates groups of subpolygons in the ratsnest model to their respective zones. + boost::unordered_map > m_zonePolygons; + + ///> Map that associates groups of edges in the ratsnest model to their respective zones. + boost::unordered_map > m_zoneConnections; + + ///> Visibility flag. + bool m_visible; +}; + + +/** + * Class RN_DATA + * + * Stores information about unconnected items for the whole PCB. + */ +class RN_DATA +{ +public: + /** + * Default constructor + * @param aBoard is the board to be processed in order to look for unconnected items. + */ + RN_DATA( const BOARD* aBoard ) : m_board( aBoard ) {} + + /** + * Function UpdateItem() + * Updates ratsnest data for an item. + * @param aItem is an item to be updated. + */ + void Update( const BOARD_CONNECTED_ITEM* aItem ); + + /** + * Function UpdateItem() + * Updates ratsnest data for a module. + * @param aItem is a module to be updated. + */ + void Update( const MODULE* aModule ); + + /** + * Function AddSimple() + * Sets an item to be drawn in simple mode (ie. one line per node, instead of full ratsnest). + * It is used for drawing temporary ratsnest, eg. while moving an item. + * @param aItem is an item to be drawn in simple node. + */ + void AddSimple( const BOARD_CONNECTED_ITEM* aItem ); + + /** + * Function AddSimple() + * Sets a module to be drawn in simple mode (ie. one line per node, instead of full ratsnest). + * It is used for drawing temporary ratsnest, eg. while moving a module. + * @param aModule is a module to be drawn in simple node. + */ + void AddSimple( const MODULE* aModule ); + + /** + * Function ClearSimple() + * Clears the list of nodes for which ratsnest is drawn in simple mode (one line per node). + */ + void ClearSimple(); + + /** + * Function ProcessBoard() + * Prepares data for computing (computes a list of current nodes and connections). It is + * required to run only once after loading a board. + */ + void ProcessBoard(); + + /** + * Function Recalculate() + * Recomputes ratsnest for selected net number or all nets that need updating. + * @param aNet is a net number. If it is negative, all nets that need updating are recomputed. + */ + void Recalculate( int aNet = -1 ); + + /** + * Function GetNets() + * Returns ratsnest grouped by net numbers. + * @return Vector of ratsnest grouped by net numbers. + */ + const std::vector& GetNets() const + { + return m_nets; + } + +protected: + /** + * Function updateNet() + * Recomputes ratsnest for a single net. + * @param aNetCode is the net number to be recomputed. + */ + void updateNet( int aNetCode ); + + ///> Board to be processed. + const BOARD* m_board; + + ///> Stores information about ratsnest grouped by net numbers. + std::vector m_nets; +}; + +#endif /* RATSNEST_DATA_H */ diff --git a/pcbnew/ratsnest_viewitem.cpp b/pcbnew/ratsnest_viewitem.cpp new file mode 100644 index 0000000000..8a511d08d4 --- /dev/null +++ b/pcbnew/ratsnest_viewitem.cpp @@ -0,0 +1,110 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file ratsnest_viewitem.cpp + * @brief Class that draws missing connections on a PCB. + */ + +#include +#include +#include +#include + +#include + +using namespace KIGFX; + +RATSNEST_VIEWITEM::RATSNEST_VIEWITEM( RN_DATA* aData ) : + EDA_ITEM( NOT_USED ), m_data( aData ) +{ +} + + +const BOX2I RATSNEST_VIEWITEM::ViewBBox() const +{ + // Make it always visible + BOX2I bbox; + bbox.SetMaximum(); + + return bbox; +} + + +void RATSNEST_VIEWITEM::ViewDraw( int aLayer, GAL* aGal ) const +{ + aGal->SetIsStroke( true ); + aGal->SetIsFill( false ); + aGal->SetLineWidth( 1.0 ); + aGal->SetStrokeColor( COLOR4D( 1.0, 1.0, 1.0, 0.4 ) ); + + WITHOUT_FLAG without_flag; + + // Draw the temporary ratsnest + BOOST_FOREACH( const RN_NET& net, m_data->GetNets() ) + { + if( !net.IsVisible() ) + continue; + + // Avoid duplicate destinations for ratsnest lines by storing already used nodes + boost::unordered_set usedDestinations; + + // Draw the "dynamic" ratsnest (ie. for objects that may be currently being moved) + BOOST_FOREACH( const RN_NODE_PTR& node, net.GetSimpleNodes() ) + { + RN_NODE_PTR dest = net.GetClosestNode( node, without_flag ); + + if( dest && usedDestinations.find( dest ) == usedDestinations.end() ) + { + VECTOR2D origin( node->GetX(), node->GetY() ); + VECTOR2D end( dest->GetX(), dest->GetY() ); + + aGal->DrawLine( origin, end ); + usedDestinations.insert( dest ); + } + } + + // Draw the "static" ratsnest + const std::vector* edges = net.GetUnconnected(); + if( edges == NULL ) + continue; + + BOOST_FOREACH( const RN_EDGE_PTR& edge, *edges ) + { + const RN_NODE_PTR& sourceNode = edge->getSourceNode(); + const RN_NODE_PTR& targetNode = edge->getTargetNode(); + VECTOR2D source( sourceNode->GetX(), sourceNode->GetY() ); + VECTOR2D target( targetNode->GetX(), targetNode->GetY() ); + + aGal->DrawLine( source, target ); + } + } +} + + +void RATSNEST_VIEWITEM::ViewGetLayers( int aLayers[], int& aCount ) const +{ + aCount = 1; + aLayers[0] = ITEM_GAL_LAYER( RATSNEST_VISIBLE ); +} diff --git a/pcbnew/ratsnest_viewitem.h b/pcbnew/ratsnest_viewitem.h new file mode 100644 index 0000000000..0ccace0987 --- /dev/null +++ b/pcbnew/ratsnest_viewitem.h @@ -0,0 +1,67 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file ratsnest_viewitem.h + * @brief Class that draws missing connections on a PCB. + */ + +#ifndef RATSNEST_VIEWITEM_H +#define RATSNEST_VIEWITEM_H + +#include +#include + +class GAL; +class RN_DATA; + +namespace KIGFX +{ +class RATSNEST_VIEWITEM : public EDA_ITEM +{ +public: + RATSNEST_VIEWITEM( RN_DATA* aData ); + + /// @copydoc VIEW_ITEM::ViewBBox() + const BOX2I ViewBBox() const; + + /// @copydoc VIEW_ITEM::ViewDraw() + void ViewDraw( int aLayer, GAL* aGal ) const; + + /// @copydoc VIEW_ITEM::ViewGetLayers() + void ViewGetLayers( int aLayers[], int& aCount ) const; + + /// @copydoc EDA_ITEM::Show() + void Show( int x, std::ostream& st ) const + { + } + +protected: + ///> Object containing ratsnest data. + RN_DATA* m_data; +}; + +} // namespace KIGFX + +#endif /* RATSNEST_VIEWITEM_H */ diff --git a/pcbnew/tools/move_tool.cpp b/pcbnew/tools/move_tool.cpp index 6750d3e8fd..2cc3b0752e 100644 --- a/pcbnew/tools/move_tool.cpp +++ b/pcbnew/tools/move_tool.cpp @@ -26,8 +26,11 @@ #include #include #include +#include #include +#include + #include "common_actions.h" #include "selection_tool.h" #include "move_tool.h" @@ -41,11 +44,6 @@ MOVE_TOOL::MOVE_TOOL() : } -MOVE_TOOL::~MOVE_TOOL() -{ -} - - void MOVE_TOOL::Reset() { // The tool launches upon reception of action event ("pcbnew.InteractiveMove") @@ -111,11 +109,13 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent ) { m_state.Rotate( cursorPos, 900.0 ); selection.group->ViewUpdate( VIEW_ITEM::GEOMETRY ); + updateRatsnest( true ); } else if( evt->IsAction( &COMMON_ACTIONS::flip ) ) // got flip event? { m_state.Flip( cursorPos ); selection.group->ViewUpdate( VIEW_ITEM::GEOMETRY ); + updateRatsnest( true ); } } @@ -126,6 +126,8 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent ) // Drag items to the current cursor position VECTOR2D movement = ( evt->Position() - dragPosition ); m_state.Move( movement ); + + updateRatsnest( true ); } else { @@ -153,17 +155,50 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent ) // Modifications has to be rollbacked, so restore the previous state of items selection.group->ItemsViewUpdate( VIEW_ITEM::APPEARANCE ); m_state.RestoreAll(); + + updateRatsnest( false ); } else { // Changes are applied, so update the items selection.group->ItemsViewUpdate( m_state.GetUpdateFlag() ); m_state.Apply(); + } + RN_DATA* ratsnest = static_cast( m_toolMgr->GetModel() )->GetRatsnest(); + ratsnest->Recalculate(); + controls->ShowCursor( false ); controls->SetSnapping( false ); controls->SetAutoPan( false ); return 0; } + + +void MOVE_TOOL::updateRatsnest( bool aRedraw ) +{ + const SELECTION_TOOL::SELECTION& selection = m_selectionTool->GetSelection(); + RN_DATA* ratsnest = static_cast( m_toolMgr->GetModel() )->GetRatsnest(); + + ratsnest->ClearSimple(); + BOOST_FOREACH( BOARD_ITEM* item, selection.items ) + { + if( item->Type() == PCB_PAD_T || item->Type() == PCB_TRACE_T || + item->Type() == PCB_VIA_T || item->Type() == PCB_ZONE_AREA_T ) + { + ratsnest->Update( static_cast( item ) ); + + if( aRedraw ) + ratsnest->AddSimple( static_cast( item ) ); + } + else if( item->Type() == PCB_MODULE_T ) + { + ratsnest->Update( static_cast( item ) ); + + if( aRedraw ) + ratsnest->AddSimple( static_cast( item ) ); + } + } +} diff --git a/pcbnew/tools/move_tool.h b/pcbnew/tools/move_tool.h index 1140d0067e..10783a5ed4 100644 --- a/pcbnew/tools/move_tool.h +++ b/pcbnew/tools/move_tool.h @@ -49,7 +49,6 @@ class MOVE_TOOL : public TOOL_INTERACTIVE { public: MOVE_TOOL(); - ~MOVE_TOOL(); /// @copydoc TOOL_INTERACTIVE::Reset() void Reset(); @@ -65,6 +64,8 @@ public: int Main( TOOL_EVENT& aEvent ); private: + void updateRatsnest( bool aRedraw ); + /// Saves the state of items and allows to restore them ITEM_STATE m_state; From 86a8bbee9a18a7655f9ad9e8d7746fb724b2bf84 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Fri, 29 Nov 2013 16:13:09 +0100 Subject: [PATCH 02/57] Added a missing file --- common/geometry/hetriang.cpp | 710 +++++++++++++++++++++++++++++++++++ 1 file changed, 710 insertions(+) create mode 100644 common/geometry/hetriang.cpp diff --git a/common/geometry/hetriang.cpp b/common/geometry/hetriang.cpp new file mode 100644 index 0000000000..c1523ac347 --- /dev/null +++ b/common/geometry/hetriang.cpp @@ -0,0 +1,710 @@ +/* + * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, + * Applied Mathematics, Norway. + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. + * + * This file is part of TTL. + * + * TTL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * TTL 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with TTL. If not, see + * . + * + * In accordance with Section 7(b) of the GNU Affero General Public + * License, a covered work must retain the producer line in every data + * file that is created or manipulated using TTL. + * + * Other Usage + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial activities involving the TTL library without + * disclosing the source code of your own applications. + * + * This file may be used in accordance with the terms contained in a + * written agreement between you and SINTEF ICT. + */ + +#include +#include +#include +#include +#include +#include + + +using namespace hed; +using namespace std; + + +Triangulation* TTLtraits::triang_ = NULL; + +#ifdef TTL_USE_NODE_ID + int Node::id_count = 0; +#endif + + +//#define DEBUG_HE +#ifdef DEBUG_HE +#include + static void errorAndExit(char* message) { + cout << "\n!!! ERROR: "<< message << " !!!\n" << endl; exit(-1); + } +#endif + + +//-------------------------------------------------------------------------------------------------- +static EdgePtr getLeadingEdgeInTriangle(const EdgePtr& e) { + EdgePtr edge = e; + + // Code: 3EF (assumes triangle) + if (!edge->isLeadingEdge()) { + edge = edge->getNextEdgeInFace(); + if (!edge->isLeadingEdge()) + edge = edge->getNextEdgeInFace(); + } + + if (!edge->isLeadingEdge()) { + return EdgePtr(); + } + + return edge; +} + + +//-------------------------------------------------------------------------------------------------- +static void getLimits(NodesContainer::iterator first, + NodesContainer::iterator last, + int& xmin, int& ymin, + int& xmax, int& ymax) { + + xmin = ymin = std::numeric_limits::min(); + xmax = ymax = std::numeric_limits::max(); + + NodesContainer::iterator it; + for (it = first; it != last; ++it) { + xmin = min(xmin, (*it)->GetX()); + ymin = min(ymin, (*it)->GetY()); + xmax = max(xmax, (*it)->GetX()); + ymax = max(ymax, (*it)->GetY()); + } +} + + +//-------------------------------------------------------------------------------------------------- +EdgePtr Triangulation::initTwoEnclosingTriangles(NodesContainer::iterator first, + NodesContainer::iterator last) { + + int xmin, ymin, xmax, ymax; + getLimits(first, last, xmin, ymin, xmax, ymax); + + // Add 10% of range: + double fac = 10.0; + double dx = (xmax-xmin)/fac; + double dy = (ymax-ymin)/fac; + + NodePtr n1(new Node(xmin-dx,ymin-dy)); + NodePtr n2(new Node(xmax+dx,ymin-dy)); + NodePtr n3(new Node(xmax+dx,ymax+dy)); + NodePtr n4(new Node(xmin-dx,ymax+dy)); + + // diagonal + EdgePtr e1d(new Edge); // lower + EdgePtr e2d(new Edge); // upper, the twin edge + + // lower triangle + EdgePtr e11(new Edge); + EdgePtr e12(new Edge); + + // upper triangle + EdgePtr e21(new Edge); // upper upper + EdgePtr e22(new Edge); + + // lower triangle + e1d->setSourceNode(n3); + e1d->setNextEdgeInFace(e11); + e1d->setTwinEdge(e2d); + e1d->setAsLeadingEdge(); + addLeadingEdge(e1d); + + e11->setSourceNode(n1); + e11->setNextEdgeInFace(e12); + + e12->setSourceNode(n2); + e12->setNextEdgeInFace(e1d); + + // upper triangle + e2d->setSourceNode(n1); + e2d->setNextEdgeInFace(e21); + e2d->setTwinEdge(e1d); + e2d->setAsLeadingEdge(); + addLeadingEdge(e2d); + + e21->setSourceNode(n3); + e21->setNextEdgeInFace(e22); + + e22->setSourceNode(n4); + e22->setNextEdgeInFace(e2d); + + return e11; +} + + +//-------------------------------------------------------------------------------------------------- +void Triangulation::createDelaunay(NodesContainer::iterator first, + NodesContainer::iterator last) { + + TTLtraits::triang_ = this; + cleanAll(); + + EdgePtr bedge = initTwoEnclosingTriangles(first, last); + Dart dc(bedge); + + Dart d_iter = dc; + + NodesContainer::iterator it; + for (it = first; it != last; ++it) { + ttl::insertNode(d_iter, *it); + } + + // In general (e.g. for the triangle based data structure), the initial dart + // may have been changed. + // It is the users responsibility to get a valid boundary dart here. + // The half-edge data structure preserves the initial dart. + // (A dart at the boundary can also be found by trying to locate a + // triangle "outside" the triangulation.) + + // Assumes rectangular domain + ttl::removeRectangularBoundary(dc); +} + + +//-------------------------------------------------------------------------------------------------- +void Triangulation::removeTriangle(EdgePtr& edge) { + + EdgePtr e1 = getLeadingEdgeInTriangle(edge); + +#ifdef DEBUG_HE + if (!e1) + errorAndExit("Triangulation::removeTriangle: could not find leading edge"); +#endif + + removeLeadingEdgeFromList(e1); + // cout << "No leading edges = " << leadingEdges_.size() << endl; + // Remove the triangle + EdgePtr e2 = e1->getNextEdgeInFace(); + EdgePtr e3 = e2->getNextEdgeInFace(); + + if (e1->getTwinEdge()) + e1->getTwinEdge()->setTwinEdge(EdgePtr()); + if (e2->getTwinEdge()) + e2->getTwinEdge()->setTwinEdge(EdgePtr()); + if (e3->getTwinEdge()) + e3->getTwinEdge()->setTwinEdge(EdgePtr()); +} + + +//-------------------------------------------------------------------------------------------------- +void Triangulation::reverse_splitTriangle(EdgePtr& edge) { + + // Reverse operation of splitTriangle + + EdgePtr e1 = edge->getNextEdgeInFace(); + EdgePtr le = getLeadingEdgeInTriangle(e1); +#ifdef DEBUG_HE + if (!le) + errorAndExit("Triangulation::removeTriangle: could not find leading edge"); +#endif + removeLeadingEdgeFromList(le); + + EdgePtr e2 = e1->getNextEdgeInFace()->getTwinEdge()->getNextEdgeInFace(); + le = getLeadingEdgeInTriangle(e2); +#ifdef DEBUG_HE + if (!le) + errorAndExit("Triangulation::removeTriangle: could not find leading edge"); +#endif + removeLeadingEdgeFromList(le); + + EdgePtr e3 = edge->getTwinEdge()->getNextEdgeInFace()->getNextEdgeInFace(); + le = getLeadingEdgeInTriangle(e3); +#ifdef DEBUG_HE + if (!le) + errorAndExit("Triangulation::removeTriangle: could not find leading edge"); +#endif + removeLeadingEdgeFromList(le); + + // The three triangles at the node have now been removed + // from the triangulation, but the arcs have not been deleted. + // Next delete the 6 half edges radiating from the node + // The node is maintained by handle and need not be deleted explicitly + + // Create the new triangle + e1->setNextEdgeInFace(e2); + e2->setNextEdgeInFace(e3); + e3->setNextEdgeInFace(e1); + addLeadingEdge(e1); +} + + +//-------------------------------------------------------------------------------------------------- +// This is a "template" for iterating the boundary +/* +static void iterateBoundary(const Dart& dart) { +cout << "Iterate boundary 2" << endl; +// input is a dart at the boundary + + Dart dart_iter = dart; + do { + if (ttl::isBoundaryEdge(dart_iter)) + dart_iter.alpha0().alpha1(); + else + dart_iter.alpha2().alpha1(); + + } while(dart_iter != dart); +} +*/ + + +//-------------------------------------------------------------------------------------------------- +Dart Triangulation::createDart() { + + // Return an arbitrary CCW dart + return Dart(*leadingEdges_.begin()); +} + + +//-------------------------------------------------------------------------------------------------- +bool Triangulation::removeLeadingEdgeFromList(EdgePtr& leadingEdge) { + + // Remove the edge from the list of leading edges, + // but don't delete it. + // Also set flag for leading edge to false. + // Must search from start of list. Since edges are added to the + // start of the list during triangulation, this operation will + // normally be fast (when used in the triangulation algorithm) + list::iterator it; + for (it = leadingEdges_.begin(); it != leadingEdges_.end(); ++it) { + + EdgePtr edge = *it; + if (edge == leadingEdge) { + + edge->setAsLeadingEdge(false); + it = leadingEdges_.erase(it); + + break; + } + } + + if (it == leadingEdges_.end()) + return false; + + return true; +} + + +//-------------------------------------------------------------------------------------------------- +void Triangulation::cleanAll() { + leadingEdges_.clear(); +} + + +#ifdef TTL_USE_NODE_FLAG +//-------------------------------------------------------------------------------------------------- +// This is a "template" for accessing all nodes (but multiple tests) +void Triangulation::flagNodes(bool flag) const { + + list::const_iterator it; + for (it = leadingEdges_.begin(); it != leadingEdges_.end(); ++it) { + EdgePtr edge = *it; + + for (int i = 0; i < 3; ++i) { + edge->getSourceNode()->SetFlag(flag); + edge = edge->getNextEdgeInFace(); + } + } +} + + +//-------------------------------------------------------------------------------------------------- +list* Triangulation::getNodes() const { + + flagNodes(false); + list* nodeList = new list; + + list::const_iterator it; + for (it = leadingEdges_.begin(); it != leadingEdges_.end(); ++it) { + EdgePtr edge = *it; + + for (int i = 0; i < 3; ++i) { + const NodePtr& node = edge->getSourceNode(); + + if (node->GetFlag() == false) { + nodeList->push_back(node); + node->SetFlag(true); + } + edge = edge->getNextEdgeInFace(); + } + } + return nodeList; +} +#endif + + +//-------------------------------------------------------------------------------------------------- +list* Triangulation::getEdges(bool skip_boundary_edges) const { + + // collect all arcs (one half edge for each arc) + // (boundary edges are also collected). + + list::const_iterator it; + list* elist = new list; + for (it = leadingEdges_.begin(); it != leadingEdges_.end(); ++it) { + EdgePtr edge = *it; + for (int i = 0; i < 3; ++i) { + EdgePtr twinedge = edge->getTwinEdge(); + // only one of the half-edges + + if ( (!twinedge && !skip_boundary_edges) || + (twinedge && ((size_t)edge.get() > (size_t)twinedge.get())) ) + elist->push_front(edge); + + edge = edge->getNextEdgeInFace(); + } + } + return elist; +} + + +//-------------------------------------------------------------------------------------------------- +EdgePtr Triangulation::splitTriangle(EdgePtr& edge, NodePtr& point) { + + // Add a node by just splitting a triangle into three triangles + // Assumes the half edge is located in the triangle + // Returns a half edge with source node as the new node + +// double x, y, z; +// x = point.x(); +// y = point.y(); +// z = point.z(); + + // e#_n are new edges + // e# are existing edges + // e#_n and e##_n are new twin edges + // e##_n are edges incident to the new node + + // Add the node to the structure + //NodePtr new_node(new Node(x,y,z)); + + NodePtr n1 = edge->getSourceNode(); + EdgePtr e1 = edge; + + EdgePtr e2 = edge->getNextEdgeInFace(); + NodePtr n2 = e2->getSourceNode(); + + EdgePtr e3 = e2->getNextEdgeInFace(); + NodePtr n3 = e3->getSourceNode(); + + EdgePtr e1_n(new Edge); + EdgePtr e11_n(new Edge); + EdgePtr e2_n(new Edge); + EdgePtr e22_n(new Edge); + EdgePtr e3_n(new Edge); + EdgePtr e33_n(new Edge); + + e1_n->setSourceNode(n1); + e11_n->setSourceNode(point); + e2_n->setSourceNode(n2); + e22_n->setSourceNode(point); + e3_n->setSourceNode(n3); + e33_n->setSourceNode(point); + + e1_n->setTwinEdge(e11_n); + e11_n->setTwinEdge(e1_n); + e2_n->setTwinEdge(e22_n); + e22_n->setTwinEdge(e2_n); + e3_n->setTwinEdge(e33_n); + e33_n->setTwinEdge(e3_n); + + e1_n->setNextEdgeInFace(e33_n); + e2_n->setNextEdgeInFace(e11_n); + e3_n->setNextEdgeInFace(e22_n); + + e11_n->setNextEdgeInFace(e1); + e22_n->setNextEdgeInFace(e2); + e33_n->setNextEdgeInFace(e3); + + + // and update old's next edge + e1->setNextEdgeInFace(e2_n); + e2->setNextEdgeInFace(e3_n); + e3->setNextEdgeInFace(e1_n); + + // add the three new leading edges, + // Must remove the old leading edge from the list. + // Use the field telling if an edge is a leading edge + // NOTE: Must search in the list!!! + + + EdgePtr leadingEdge; + if (e1->isLeadingEdge()) + leadingEdge = e1; + else if (e2->isLeadingEdge()) + leadingEdge = e2; + else if(e3->isLeadingEdge()) + leadingEdge = e3; + else + return EdgePtr(); + + removeLeadingEdgeFromList(leadingEdge); + + addLeadingEdge(e1_n); + addLeadingEdge(e2_n); + addLeadingEdge(e3_n); + + // Return a half edge incident to the new node (with the new node as source node) + + return e11_n; +} + + +//-------------------------------------------------------------------------------------------------- +void Triangulation::swapEdge(EdgePtr& diagonal) { + + // Note that diagonal is both input and output and it is always + // kept in counterclockwise direction (this is not required by all + // finctions in ttl:: now) + + // Swap by rotating counterclockwise + // Use the same objects - no deletion or new objects + EdgePtr eL = diagonal; + EdgePtr eR = eL->getTwinEdge(); + EdgePtr eL_1 = eL->getNextEdgeInFace(); + EdgePtr eL_2 = eL_1->getNextEdgeInFace(); + EdgePtr eR_1 = eR->getNextEdgeInFace(); + EdgePtr eR_2 = eR_1->getNextEdgeInFace(); + + // avoid node to be dereferenced to zero and deleted + NodePtr nR = eR_2->getSourceNode(); + NodePtr nL = eL_2->getSourceNode(); + + eL->setSourceNode(nR); + eR->setSourceNode(nL); + + // and now 6 1-sewings + eL->setNextEdgeInFace(eL_2); + eL_2->setNextEdgeInFace(eR_1); + eR_1->setNextEdgeInFace(eL); + + eR->setNextEdgeInFace(eR_2); + eR_2->setNextEdgeInFace(eL_1); + eL_1->setNextEdgeInFace(eR); + + EdgePtr leL; + if (eL->isLeadingEdge()) + leL = eL; + else if (eL_1->isLeadingEdge()) + leL = eL_1; + else if (eL_2->isLeadingEdge()) + leL = eL_2; + + EdgePtr leR; + if (eR->isLeadingEdge()) + leR = eR; + else if (eR_1->isLeadingEdge()) + leR = eR_1; + else if (eR_2->isLeadingEdge()) + leR = eR_2; + + removeLeadingEdgeFromList(leL); + removeLeadingEdgeFromList(leR); + addLeadingEdge(eL); + addLeadingEdge(eR); +} + + +////-------------------------------------------------------------------------- +//static void printEdge(const Dart& dart, ostream& ofile) { +// +// Dart d0 = dart; +// d0.alpha0(); +// +// ofile << dart.x() << " " << dart.y() << endl; +// ofile << d0.x() << " " << d0.y() << endl; +//} + + +//-------------------------------------------------------------------------- +bool Triangulation::checkDelaunay() const { + + // ???? outputs !!!! + // ofstream os("qweND.dat"); + const list& leadingEdges = getLeadingEdges(); + + list::const_iterator it; + bool ok = true; + int noNotDelaunay = 0; + + for (it = leadingEdges.begin(); it != leadingEdges.end(); ++it) { + EdgePtr edge = *it; + + for (int i = 0; i < 3; ++i) { + EdgePtr twinedge = edge->getTwinEdge(); + + // only one of the half-edges + if (!twinedge || (size_t)edge.get() > (size_t)twinedge.get()) { + Dart dart(edge); + if (ttl::swapTestDelaunay(dart)) { + noNotDelaunay++; + + //printEdge(dart,os); os << "\n"; + ok = false; + //cout << "............. not Delaunay .... " << endl; + } + } + edge = edge->getNextEdgeInFace(); + } + } + +#ifdef DEBUG_HE + cout << "!!! Triangulation is NOT Delaunay: " << noNotDelaunay << " edges\n" << endl; +#endif + + return ok; +} + + +//-------------------------------------------------------------------------------------------------- +void Triangulation::optimizeDelaunay() { + + // This function is also present in ttl where it is implemented + // generically. + // The implementation below is tailored for the half-edge data structure, + // and is thus more efficient + + // Collect all interior edges (one half edge for each arc) + bool skip_boundary_edges = true; + list* elist = getEdges(skip_boundary_edges); + + // Assumes that elist has only one half-edge for each arc. + bool cycling_check = true; + bool optimal = false; + list::const_iterator it; + while(!optimal) { + optimal = true; + for (it = elist->begin(); it != elist->end(); ++it) { + EdgePtr edge = *it; + + Dart dart(edge); + // Constrained edges should not be swapped + if (!edge->isConstrained() && ttl::swapTestDelaunay(dart, cycling_check)) { + optimal = false; + swapEdge(edge); + } + } + } + delete elist; +} + + +//-------------------------------------------------------------------------------------------------- +EdgePtr Triangulation::getInteriorNode() const { + + const list& leadingEdges = getLeadingEdges(); + list::const_iterator it; + for (it = leadingEdges.begin(); it != leadingEdges.end(); ++it) { + EdgePtr edge = *it; + + // multiple checks, but only until found + for (int i = 0; i < 3; ++i) { + if (edge->getTwinEdge()) { + + if (!ttl::isBoundaryNode(Dart(edge))) + return edge; + } + edge = edge->getNextEdgeInFace(); + } + } + return EdgePtr(); // no boundary nodes +} + + +//-------------------------------------------------------------------------------------------------- +static EdgePtr getBoundaryEdgeInTriangle(const EdgePtr& e) { + EdgePtr edge = e; + + if (ttl::isBoundaryEdge(Dart(edge))) + return edge; + + edge = edge->getNextEdgeInFace(); + if (ttl::isBoundaryEdge(Dart(edge))) + return edge; + + edge = edge->getNextEdgeInFace(); + if (ttl::isBoundaryEdge(Dart(edge))) + return edge; + + return EdgePtr(); +} + + +//-------------------------------------------------------------------------------------------------- +EdgePtr Triangulation::getBoundaryEdge() const { + + // Get an arbitrary (CCW) boundary edge + // If the triangulation is closed, NULL is returned + + const list& leadingEdges = getLeadingEdges(); + list::const_iterator it; + EdgePtr edge; + + for (it = leadingEdges.begin(); it != leadingEdges.end(); ++it) { + edge = getBoundaryEdgeInTriangle(*it); + + if (edge) + return edge; + } + return EdgePtr(); +} + + +//-------------------------------------------------------------------------------------------------- +void Triangulation::printEdges(ofstream& os) const { + + // Print source node and target node for each edge face by face, + // but only one of the half-edges. + + const list& leadingEdges = getLeadingEdges(); + list::const_iterator it; + for (it = leadingEdges.begin(); it != leadingEdges.end(); ++it) { + EdgePtr edge = *it; + + for (int i = 0; i < 3; ++i) { + EdgePtr twinedge = edge->getTwinEdge(); + + // Print only one edge (the highest value of the pointer) + if (!twinedge || (size_t)edge.get() > (size_t)twinedge.get()) { + // Print source node and target node + NodePtr node = edge->getSourceNode(); + os << node->GetX() << " " << node->GetY() << endl; + node = edge->getTargetNode(); + os << node->GetX() << " " << node->GetY() << endl; + os << '\n'; // blank line + } + edge = edge->getNextEdgeInFace(); + } + } +} From 3afbd8f2e1a1dc35e005872e34aec48fca6a94b0 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Fri, 6 Dec 2013 10:00:51 +0100 Subject: [PATCH 03/57] Corrected polygon outline width. --- pcbnew/pcb_painter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 692f73f5e7..84fd8ed459 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -771,7 +771,7 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone ) // Set up drawing options m_gal->SetFillColor( color ); - m_gal->SetLineWidth( aZone->GetThermalReliefCopperBridge() / 2.0 ); + m_gal->SetLineWidth( aZone->GetMinThickness() ); if( displayMode == PCB_RENDER_SETTINGS::DZ_SHOW_FILLED ) { From b67faf780ee042da097113d4117859d0eb6e1cf8 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Fri, 6 Dec 2013 11:25:23 +0100 Subject: [PATCH 04/57] Corrected drawing of zone outlines. --- pcbnew/pcb_painter.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 84fd8ed459..00aca91053 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -756,11 +756,15 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone ) for( int i = 0; i < outline->GetCornersCount(); ++i ) { corners.push_back( VECTOR2D( outline->GetPos( i ) ) ); + + if( outline->IsEndContour( i ) ) + { + // The last point for closing the polyline + corners.push_back( corners[0] ); + m_gal->DrawPolyline( corners ); + corners.clear(); + } } - // The last point for closing the polyline - corners.push_back( VECTOR2D( outline->GetPos( 0 ) ) ); - m_gal->DrawPolyline( corners ); - corners.clear(); // Draw the filling if( displayMode != PCB_RENDER_SETTINGS::DZ_HIDE_FILLED ) From 84721d045e890889297eddf4a90bf45e7304436d Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Fri, 6 Dec 2013 15:01:36 +0100 Subject: [PATCH 05/57] Removed unnecessary parts. --- include/ttl/ttl_util.h | 179 +---------------------------------------- 1 file changed, 4 insertions(+), 175 deletions(-) diff --git a/include/ttl/ttl_util.h b/include/ttl/ttl_util.h index cc311edb69..cfdf509531 100644 --- a/include/ttl/ttl_util.h +++ b/include/ttl/ttl_util.h @@ -56,7 +56,7 @@ /** \brief Utilities -* +* * This name space contains utility functions for TTL.\n * * Point and vector algebra such as scalar product and cross product @@ -73,10 +73,10 @@ * which is the z-component of the actual cross product * (the x and y components are both zero). * -* \see +* \see * ttl and \ref api * -* \author +* \author * Øyvind Hjelle, oyvindhj@ifi.uio.no */ @@ -118,7 +118,7 @@ namespace ttl_util { return dx1*dy2 - dy1*dx2; } - + //------------------------------------------------------------------------------------------------ /** Returns a positive value if the 2D nodes/points \e pa, \e pb, and * \e pc occur in counterclockwise order; a negative value if they occur @@ -138,177 +138,6 @@ namespace ttl_util { return acx * bcy - acy * bcx; } - - //------------------------------------------------------------------------------------------------ - /* Scalar product between 2D vectors represented as darts. - * - * \par Requires: - * - real_type DartType::x() - * - real_type DartType::y() - */ - /* - template - typename TTLtraits::real_type scalarProduct2d(const DartType& d1, const DartType& d2) { - - DartType d10 = d1; - d10.alpha0(); - - DartType d20 = d2; - d20.alpha0(); - - return scalarProduct2d(d10.x() - d1.x(), d10.y() - d1.y(), d20.x() - d2.x(), d20.y() - d2.y()); - } - */ - - - //------------------------------------------------------------------------------------------------ - /* Scalar product between 2D vectors. - * The first vector is represented by the given dart, and the second vector has - * direction from the node of the given dart - and to the given point. - * - * \par Requires: - * - real_type DartType::x(), real_type DartType::y() - * - real_type PointType2d::x(), real_type PointType2d::y() - */ - /* - template - typename TTLtraits::real_type scalarProduct2d(const typename TTLtraits::DartType& d, - const typename TTLtraits::PointType2d& p) { - typename TTLtraits::DartType d0 = d; - d0.alpha0(); - - return scalarProduct2d(d0.x() - d.x(), d0.y() - d.y(), p.x() - d.x(), p.y() - d.y()); - } - */ - - - //------------------------------------------------------------------------------------------------ - /* Cross product between 2D vectors represented as darts. - * - * \par Requires: - * - real_type DartType::x(), real_type DartType::y() - */ - /* - template - typename TTLtraits::real_type crossProduct2d(const typename TTLtraits::DartType& d1, - const typename TTLtraits::DartType& d2) { - - TTLtraits::DartType d10 = d1; - d10.alpha0(); - - TTLtraits::DartType d20 = d2; - d20.alpha0(); - - return crossProduct2d(d10.x() - d1.x(), d10.y() - d1.y(), d20.x() - d2.x(), d20.y() - d2.y()); - } - */ - - - //------------------------------------------------------------------------------------------------ - /* Cross product between 2D vectors. - * The first vector is represented by the given dart, and the second vector has - * direction from the node associated with given dart - and to the given point. - * - * \par Requires: - * - real_type DartType::x() - * - real_type DartType::y() - */ - /* - template - typename TTLtraits::real_type crossProduct2d(const typename TTLtraits::DartType& d, - const typename TTLtraits::PointType2d& p) { - - TTLtraits::DartType d0 = d; - d0.alpha0(); - - return crossProduct2d(d0.x() - d.x(), d0.y() - d.y(), p.x() - d.x(), p.y() - d.y()); - } - */ - // Geometric predicates; see more robust schemes by Jonathan Richard Shewchuk at - // http://www.cs.cmu.edu/~quake/robust.html - - - //------------------------------------------------------------------------------------------------ - /* Return a positive value if the 2d nodes/points \e d, \e d.alpha0(), and - * \e p occur in counterclockwise order; a negative value if they occur - * in clockwise order; and zero if they are collinear. The - * result is also a rough approximation of twice the signed - * area of the triangle defined by the three points. - * - * \par Requires: - * - DartType::x(), DartType::y(), - * - PointType2d::x(), PointType2d::y() - */ - /* - template - typename TTLtraits::real_type orient2dfast(const DartType& n1, const DartType& n2, - const PointType2d& p) { - return ((n2.x()-n1.x())*(p.y()-n1.y()) - (p.x()-n1.x())*(n2.y()-n1.y())); - } - */ - - //@} // End of Computational geometry - - - //------------------------------------------------------------------------------------------------ - // ---------------------------- Utilities Involving Points Group -------------------------------- - //------------------------------------------------------------------------------------------------ - - /** @name Utilities involving points */ - //@{ - - //------------------------------------------------------------------------------------------------ - /** Creates random data on the unit square. - * - * \param noPoints - * Number of random points to be generated - * - * \param seed - * Initial value for pseudorandom number generator - * - * \require - * - Constructor \c PointType::PointType(double x, double y).\n - * For example, one can use \c pair. - * - * \note - * - To deduce template argument for PointType the function must be - * called with the syntax: \c createRandomData(...) where \c MyPoint - * is the actual point type. - */ - template - std::vector* createRandomData(int noPoints, int seed=1) { - -#ifdef _MSC_VER - srand(seed); -#else - srand48((long int)seed); -#endif - - double x, y; - std::vector* points = new std::vector(noPoints); - typename std::vector::iterator it; - for (it = points->begin(); it != points->end(); ++it) { - -#ifdef _MSC_VER - int random = rand(); - x = ((double)random/(double)RAND_MAX); - random = rand(); - y = ((double)random/(double)RAND_MAX); - *it = new PointType(x,y); -#else - double random = drand48(); - x = random; - random = drand48(); - y = random; - *it = new PointType(x,y); -#endif - - } - return points; - } - - //@} // End of Utilities involving points - }; // End of ttl_util namespace scope #endif // _TTL_UTIL_H_ From 5c0d62ac2eb6c5ee5c930b1f3b7a280254b0d456 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Mon, 16 Dec 2013 10:36:33 +0100 Subject: [PATCH 06/57] Fixed drawing of rotated texts that are not horizontally center aligned. --- common/gal/stroke_font.cpp | 3 +-- pcbnew/class_pcb_text.cpp | 37 ------------------------------------- pcbnew/class_pcb_text.h | 3 --- 3 files changed, 1 insertion(+), 42 deletions(-) diff --git a/common/gal/stroke_font.cpp b/common/gal/stroke_font.cpp index ab83eba22e..bd536dca7e 100644 --- a/common/gal/stroke_font.cpp +++ b/common/gal/stroke_font.cpp @@ -153,6 +153,7 @@ void STROKE_FONT::Draw( const wxString& aText, const VECTOR2D& aPosition, double m_gal->Save(); m_gal->Translate( aPosition ); + m_gal->Rotate( -aRotationAngle ); // Single line height int lineHeight = getInterline(); @@ -177,8 +178,6 @@ void STROKE_FONT::Draw( const wxString& aText, const VECTOR2D& aPosition, double break; } - m_gal->Rotate( -aRotationAngle ); - m_gal->SetIsStroke( true ); m_gal->SetIsFill( false ); diff --git a/pcbnew/class_pcb_text.cpp b/pcbnew/class_pcb_text.cpp index c375713b82..3542c1945f 100644 --- a/pcbnew/class_pcb_text.cpp +++ b/pcbnew/class_pcb_text.cpp @@ -205,40 +205,3 @@ EDA_ITEM* TEXTE_PCB::Clone() const { return new TEXTE_PCB( *this ); } - - -const BOX2I TEXTE_PCB::ViewBBox() const -{ - EDA_RECT rect = GetTextBox(); - - if( m_Orient != 0.0 ) - { - // If the text is rotated, we need to take it into account - wxPoint p1 = rect.GetOrigin(); - wxPoint p2 = wxPoint( p1.x + rect.GetWidth(), p1.y ); - wxPoint p3 = rect.GetEnd(); - wxPoint p4 = wxPoint( p1.x, p1.y + rect.GetHeight() ); - - // Transform all the corners of the bounding box according to the rotation angle - RotatePoint( &p1, m_Pos, -m_Orient ); - RotatePoint( &p2, m_Pos, -m_Orient ); - RotatePoint( &p3, m_Pos, -m_Orient ); - RotatePoint( &p4, m_Pos, -m_Orient ); - - // Find the new bounding box origin and dimensions - int minX = std::min( std::min( p1.x, p2.x ), std::min( p3.x, p4.x ) ); - int minY = std::min( std::min( p1.y, p2.y ), std::min( p3.y, p4.y ) ); - int maxX = std::max( std::max( p1.x, p2.x ), std::max( p3.x, p4.x ) ); - int maxY = std::max( std::max( p1.y, p2.y ), std::max( p3.y, p4.y ) ); - - int width = maxX - minX; - int height = maxY - minY; - - return BOX2I( VECTOR2I( minX, minY ), VECTOR2I( width, height ) ); - } - else - { - return BOX2I( rect.GetOrigin(), rect.GetSize() ); - } -} - diff --git a/pcbnew/class_pcb_text.h b/pcbnew/class_pcb_text.h index cdc36fc6c9..0ebd4119bf 100644 --- a/pcbnew/class_pcb_text.h +++ b/pcbnew/class_pcb_text.h @@ -135,9 +135,6 @@ public: EDA_ITEM* Clone() const; - /// @copydoc VIEW_ITEM::ViewBBox() - virtual const BOX2I ViewBBox() const; - #if defined(DEBUG) virtual void Show( int nestLevel, std::ostream& os ) const { ShowDummy( os ); } // override #endif From 909b7fb4251422e92920bdceeb968cf0bac13ac1 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Thu, 19 Dec 2013 12:33:57 +0100 Subject: [PATCH 07/57] Dxf export: fix an issue in exported arcs. Update libdfx. Pcbnew:, libedit, Save lib as...: the new .pretty lib format is the default, instead of legacy .mod format. The legacy format is still selectable in the file selection dialog. --- common/common_plotDXF_functions.cpp | 11 ++++++++++- lib_dxf/libdxfrw.cpp | 8 ++++---- pcbnew/class_drawsegment.h | 16 ++++++++++++++-- pcbnew/import_dxf/dxf2brd_items.cpp | 17 +++++++++++------ pcbnew/librairi.cpp | 11 ++++++----- pcbnew/specctra_export.cpp | 2 +- 6 files changed, 46 insertions(+), 19 deletions(-) diff --git a/common/common_plotDXF_functions.cpp b/common/common_plotDXF_functions.cpp index cf216e03d4..2368cb98f7 100644 --- a/common/common_plotDXF_functions.cpp +++ b/common/common_plotDXF_functions.cpp @@ -386,7 +386,7 @@ void DXF_PLOTTER::ThickSegment( const wxPoint& aStart, const wxPoint& aEnd, int } } -/** Plot an arc in DXF format +/* Plot an arc in DXF format * Filling is not supported */ void DXF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, @@ -397,6 +397,14 @@ void DXF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, i if( radius <= 0 ) return; + // In DXF, arcs are drawn CCW. + // In Kicad, arcs are CW or CCW + // If StAngle > EndAngle, it is CW. So transform it to CCW + if( StAngle > EndAngle ) + { + EXCHG( StAngle, EndAngle ); + } + DPOINT centre_dev = userToDeviceCoordinates( centre ); double radius_dev = userToDeviceSize( radius ); @@ -425,6 +433,7 @@ void DXF_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double EXCHG( size.x, size.y ); orient = AddAngles( orient, 900 ); } + sketchOval( pos, size, orient, -1 ); } diff --git a/lib_dxf/libdxfrw.cpp b/lib_dxf/libdxfrw.cpp index ffbba1161a..2390b38bed 100644 --- a/lib_dxf/libdxfrw.cpp +++ b/lib_dxf/libdxfrw.cpp @@ -1683,7 +1683,7 @@ DRW_ImageDef* dxfRW::writeImage( DRW_Image* ent, std::string name ) writer->writeInt16( 282, ent->contrast ); writer->writeInt16( 283, ent->fade ); writer->writeString( 360, idReactor ); - id->reactors[idReactor] = ent->handle; + id->reactors[idReactor] = toHexStr( ent->handle ); return id; } @@ -3817,9 +3817,9 @@ bool dxfRW::processImageDef() std::string dxfRW::toHexStr( int n ) { #if defined(__APPLE__) - std::string buffer( 9, '\0' ); - snprintf( &buffer[0], 9, "%X", n ); - return buffer; + char buffer[9] = { '\0' }; + snprintf( buffer, 9, "%X", n ); + return std::string( buffer ); #else std::ostringstream Convert; Convert << std::uppercase << std::hex << n; diff --git a/pcbnew/class_drawsegment.h b/pcbnew/class_drawsegment.h index 66fc4c1d4e..dacd8061d2 100644 --- a/pcbnew/class_drawsegment.h +++ b/pcbnew/class_drawsegment.h @@ -116,8 +116,9 @@ public: void SetEndY( int y ) { m_End.y = y; } void SetEndX( int x ) { m_End.x = x; } - // Arc attributes are read only, since they are "calculated" from - // m_Start, m_End, and m_Angle. No Set...() functions. + // Some attributes are read only, since they are "calculated" from + // m_Start, m_End, and m_Angle. + // No Set...() function for these attributes. const wxPoint& GetCenter() const { return m_Start; } const wxPoint& GetArcStart() const { return m_End; } @@ -140,6 +141,17 @@ public: return KiROUND( radius ); } + /** + * Initialize the start arc point. can be used for circles + * to initialize one point of the cicumference + */ + void SetArcStart( const wxPoint& aArcStartPoint ) + { m_End = aArcStartPoint; } + + /** For arcs and circles: + */ + void SetCenter( const wxPoint& aCenterPoint ) { m_Start = aCenterPoint; } + /** * Function GetParentModule * returns a pointer to the parent module, or NULL if DRAWSEGMENT does not diff --git a/pcbnew/import_dxf/dxf2brd_items.cpp b/pcbnew/import_dxf/dxf2brd_items.cpp index a077dde28b..13a8c4b429 100644 --- a/pcbnew/import_dxf/dxf2brd_items.cpp +++ b/pcbnew/import_dxf/dxf2brd_items.cpp @@ -41,9 +41,11 @@ #include #include +#include #include #include #include +#include DXF2BRD_CONVERTER::DXF2BRD_CONVERTER() : DRW_Interface() { @@ -156,10 +158,10 @@ void DXF2BRD_CONVERTER::addCircle( const DRW_Circle& data ) segm->SetLayer( m_brdLayer ); segm->SetShape( S_CIRCLE ); wxPoint center( mapX( data.basePoint.x ), mapY( data.basePoint.y ) ); - segm->SetPosition( center ); + segm->SetCenter( center ); wxPoint circle_start( mapX( data.basePoint.x + data.radious ), mapY( data.basePoint.y ) ); - segm->SetEnd( circle_start ); + segm->SetArcStart( circle_start ); segm->SetWidth( mapDim( data.thickness == 0 ? m_defaultThickness : data.thickness ) ); appendToBoard( segm ); @@ -178,18 +180,21 @@ void DXF2BRD_CONVERTER::addArc( const DRW_Arc& data ) // Init arc centre: wxPoint center( mapX( data.basePoint.x ), mapY( data.basePoint.y ) ); - segm->SetPosition( center ); + segm->SetCenter( center ); // Init arc start point double arcStartx = data.radious; double arcStarty = 0; - RotatePoint( &arcStartx, &arcStarty, -RAD2DECIDEG( data.staangle ) ); + double startangle = data.staangle; + double endangle = data.endangle; + + RotatePoint( &arcStartx, &arcStarty, -RAD2DECIDEG( startangle ) ); wxPoint arcStart( mapX( arcStartx + data.basePoint.x ), mapY( arcStarty + data.basePoint.y ) ); - segm->SetEnd( arcStart ); + segm->SetArcStart( arcStart ); // calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew) - double angle = -RAD2DECIDEG( data.endangle - data.staangle ); + double angle = -RAD2DECIDEG( endangle - startangle ); if( angle > 0.0 ) angle -= 3600.0; diff --git a/pcbnew/librairi.cpp b/pcbnew/librairi.cpp index ab4c32630b..e767664afd 100644 --- a/pcbnew/librairi.cpp +++ b/pcbnew/librairi.cpp @@ -378,13 +378,14 @@ wxString FOOTPRINT_EDIT_FRAME::CreateNewLibrary() wxString wildcard; - wildcard << wxGetTranslation( LegacyFootprintLibPathWildcard ) << wxChar( '|' ) - << wxGetTranslation( KiCadFootprintLibPathWildcard ); +// wildcard << wxGetTranslation( LegacyFootprintLibPathWildcard ) << wxChar( '|' ) +// << wxGetTranslation( KiCadFootprintLibPathWildcard ); + wildcard << wxGetTranslation( KiCadFootprintLibPathWildcard ) << wxChar( '|' ) + << wxGetTranslation( LegacyFootprintLibPathWildcard ); // prompt user for libPath and PLUGIN (library) type wxFileDialog dlg( this, FMT_CREATE_LIB, fn.GetPath(), wxEmptyString, - wildcard, - wxFD_SAVE + wildcard, wxFD_SAVE // | wxFD_OVERWRITE_PROMPT overwrite is tested below // after file extension has been added. ); @@ -400,7 +401,7 @@ wxString FOOTPRINT_EDIT_FRAME::CreateNewLibrary() } // wildcard's filter index has legacy in position 0. - IO_MGR::PCB_FILE_T piType = ( dlg.GetFilterIndex() == 0 ) ? IO_MGR::LEGACY : IO_MGR::KICAD; + IO_MGR::PCB_FILE_T piType = ( dlg.GetFilterIndex() == 1 ) ? IO_MGR::LEGACY : IO_MGR::KICAD; // wxFileDialog does not supply nor enforce the file extension, add it here. if( piType == IO_MGR::LEGACY ) diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp index 193486946f..9d0327f097 100644 --- a/pcbnew/specctra_export.cpp +++ b/pcbnew/specctra_export.cpp @@ -1008,7 +1008,7 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) throw( IO_ER // Set maximum proximity threshold for point to point nearness metric for // board perimeter only, not interior keepouts yet. - prox = Mils2iu( 0 ); + prox = Mils2iu( 1 ); // Output the Edge.Cuts perimeter as circle or polygon. if( graphic->GetShape() == S_CIRCLE ) From 9a532f2cda7ea5e735b0e44fa2174966da92a15e Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Thu, 19 Dec 2013 12:43:16 +0100 Subject: [PATCH 08/57] Undo the change in specctra_export I erroneously committed, which is not fully tested. --- pcbnew/specctra_export.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp index 9d0327f097..193486946f 100644 --- a/pcbnew/specctra_export.cpp +++ b/pcbnew/specctra_export.cpp @@ -1008,7 +1008,7 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) throw( IO_ER // Set maximum proximity threshold for point to point nearness metric for // board perimeter only, not interior keepouts yet. - prox = Mils2iu( 1 ); + prox = Mils2iu( 0 ); // Output the Edge.Cuts perimeter as circle or polygon. if( graphic->GetShape() == S_CIRCLE ) From a8ac725564c55f8806ba20679199f6d0aa236547 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Fri, 20 Dec 2013 11:54:48 +0100 Subject: [PATCH 09/57] Fixed broken search filters. --- pcbnew/ratsnest_data.cpp | 12 +++--------- pcbnew/ratsnest_data.h | 15 ++++++++++----- pcbnew/ratsnest_viewitem.cpp | 4 +--- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/pcbnew/ratsnest_data.cpp b/pcbnew/ratsnest_data.cpp index 85ca642808..6d38406444 100644 --- a/pcbnew/ratsnest_data.cpp +++ b/pcbnew/ratsnest_data.cpp @@ -172,7 +172,6 @@ void RN_NET::validateEdge( RN_EDGE_PTR& aEdge ) { valid = false; - WITHOUT_FLAG without_flag; std::list closest = GetClosestNodes( target, WITHOUT_FLAG() ); BOOST_FOREACH( RN_NODE_PTR& node, closest ) { @@ -220,12 +219,6 @@ const RN_EDGE_PTR& RN_LINKS::AddConnection( const RN_NODE_PTR& aNode1, const RN_ } -void RN_LINKS::RemoveConnection( const RN_EDGE_PTR& aEdge ) -{ - m_edges.remove( aEdge ); -} - - void RN_NET::compute() { const RN_LINKS::RN_NODE_SET& boardNodes = m_links.GetNodes(); @@ -536,7 +529,8 @@ const RN_NODE_PTR RN_NET::GetClosestNode( const RN_NODE_PTR& aNode ) const } -const RN_NODE_PTR RN_NET::GetClosestNode( const RN_NODE_PTR& aNode, RN_NODE_FILTER aFilter ) const +const RN_NODE_PTR RN_NET::GetClosestNode( const RN_NODE_PTR& aNode, + const RN_NODE_FILTER& aFilter ) const { const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); RN_LINKS::RN_NODE_SET::const_iterator it, itEnd; @@ -589,7 +583,7 @@ std::list RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode, int aN std::list RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode, - RN_NODE_FILTER aFilter, int aNumber ) const + const RN_NODE_FILTER& aFilter, int aNumber ) const { std::list closest; const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes(); diff --git a/pcbnew/ratsnest_data.h b/pcbnew/ratsnest_data.h index dc5e4016b1..6d0e7ee896 100644 --- a/pcbnew/ratsnest_data.h +++ b/pcbnew/ratsnest_data.h @@ -61,7 +61,8 @@ typedef hed::Triangulation TRIANGULATOR; struct RN_NODE_FILTER : public std::unary_function { virtual ~RN_NODE_FILTER() {} - virtual bool operator()( const RN_NODE_PTR& aNode ) + + virtual bool operator()( const RN_NODE_PTR& aNode ) const { return true; // By default everything passes } @@ -70,7 +71,7 @@ struct RN_NODE_FILTER : public std::unary_function ///> Filters out nodes that have the flag set. struct WITHOUT_FLAG : public RN_NODE_FILTER { - bool operator()( const RN_NODE_PTR& aNode ) + bool operator()( const RN_NODE_PTR& aNode ) const { return !aNode->GetFlag(); } @@ -159,7 +160,10 @@ public: * Removes a connection described by a given edge pointer. * @param aEdge is a pointer to edge to be removed. */ - void RemoveConnection( const RN_EDGE_PTR& aEdge ); + void RemoveConnection( const RN_EDGE_PTR& aEdge ) + { + m_edges.remove( aEdge ); + } /** * Function GetConnections() @@ -395,7 +399,8 @@ public: * @param aNode is the node for which the closest node is searched. * @param aFilter is a functor that filters nodes. */ - const RN_NODE_PTR GetClosestNode( const RN_NODE_PTR& aNode, RN_NODE_FILTER aFilter ) const; + const RN_NODE_PTR GetClosestNode( const RN_NODE_PTR& aNode, + const RN_NODE_FILTER& aFilter ) const; /** * Function GetClosestNodes() @@ -417,7 +422,7 @@ public: * nodes then the size of list is limited to number of possible nodes. */ std::list GetClosestNodes( const RN_NODE_PTR& aNode, - RN_NODE_FILTER aFilter, int aNumber = -1 ) const; + const RN_NODE_FILTER& aFilter, int aNumber = -1 ) const; /** * Function GetEdges() diff --git a/pcbnew/ratsnest_viewitem.cpp b/pcbnew/ratsnest_viewitem.cpp index 8a511d08d4..282c0998a0 100644 --- a/pcbnew/ratsnest_viewitem.cpp +++ b/pcbnew/ratsnest_viewitem.cpp @@ -59,8 +59,6 @@ void RATSNEST_VIEWITEM::ViewDraw( int aLayer, GAL* aGal ) const aGal->SetLineWidth( 1.0 ); aGal->SetStrokeColor( COLOR4D( 1.0, 1.0, 1.0, 0.4 ) ); - WITHOUT_FLAG without_flag; - // Draw the temporary ratsnest BOOST_FOREACH( const RN_NET& net, m_data->GetNets() ) { @@ -73,7 +71,7 @@ void RATSNEST_VIEWITEM::ViewDraw( int aLayer, GAL* aGal ) const // Draw the "dynamic" ratsnest (ie. for objects that may be currently being moved) BOOST_FOREACH( const RN_NODE_PTR& node, net.GetSimpleNodes() ) { - RN_NODE_PTR dest = net.GetClosestNode( node, without_flag ); + RN_NODE_PTR dest = net.GetClosestNode( node, WITHOUT_FLAG() ); if( dest && usedDestinations.find( dest ) == usedDestinations.end() ) { From ebdddf0bc04afed04d0f6780f4222c45439cad78 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Fri, 20 Dec 2013 15:18:41 +0100 Subject: [PATCH 10/57] Ratsnest is updated, when there are tracks added using the P&S router. --- pcbnew/ratsnest_data.cpp | 2 +- pcbnew/router/pns_router.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pcbnew/ratsnest_data.cpp b/pcbnew/ratsnest_data.cpp index 6d38406444..e66451b86b 100644 --- a/pcbnew/ratsnest_data.cpp +++ b/pcbnew/ratsnest_data.cpp @@ -836,7 +836,7 @@ void RN_DATA::Recalculate( int aNet ) updateNet( i ); } } - else // Recompute only specific net + else if( aNet > 0 ) // Recompute only specific net { updateNet( aNet ); } diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index 5b6b5f6b31..151b28ffa1 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include // an ugly singleton for drawing debug items within the router context. @@ -626,6 +627,7 @@ void PNS_ROUTER::commitRouting( PNS_NODE* aNode ) newBI->ClearFlags(); m_view->Add( newBI ); m_board->Add( newBI ); + m_board->GetRatsnest()->Update( static_cast( newBI ) ); newBI->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); } } @@ -757,6 +759,8 @@ void PNS_ROUTER::StopRouting() // highlightCurrent(false); + // Update the ratsnest + m_board->GetRatsnest()->Recalculate( m_currentNet ); EraseView(); m_state = IDLE; From c801fcd835b1c523a6fe6618fc431c2442e7e65a Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Sun, 22 Dec 2013 15:55:05 +0100 Subject: [PATCH 11/57] [MacOSX] Treats LLVM as GCC + adds support for boost multiple processor(ppc/intel) and address(32/64 bit) --- CMakeLists.txt | 6 +++--- CMakeModules/download_boost.cmake | 32 ++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c095557860..8e5fcde1a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,10 +75,10 @@ mark_as_advanced( KICAD_USER_CONFIG_DIR ) #================================================ -# Set flags for GCC. +# Set flags for GCC, or treat llvm as GCC #================================================ -if( CMAKE_COMPILER_IS_GNUCXX ) +if( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) execute_process( COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION @@ -157,7 +157,7 @@ if( CMAKE_COMPILER_IS_GNUCXX ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-local-typedefs" ) endif() -endif( CMAKE_COMPILER_IS_GNUCXX ) +endif( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") if( KICAD_KEEPCASE ) add_definitions( -DKICAD_KEEPCASE ) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 5f19823c71..74d0b766d4 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -113,6 +113,33 @@ else() unset( b2_libs ) endif() +if( APPLE ) + # I set this to being compatible with wxWidgets + # wxWidgets still using libstdc++ (gcc), meanwhile OSX + # has switched to libc++ (llvm) by default + set(BOOST_CXXFLAGS "cxxflags=-mmacosx-version-min=10.5" ) + set(BOOST_LINKFLAGS "linkflags=-mmacosx-version-min=10.5" ) + + if( CMAKE_OSX_ARCHITECTURES ) + + if( (CMAKE_OSX_ARCHITECTURES MATCHES "386" OR CMAKE_OSX_ARCHITECTURES MATCHES "ppc ") AND + (CMAKE_OSX_ARCHITECTURES MATCHES "64")) + message("-- BOOST found 32/64 Address Model") + + set(BOOST_ADDRESSMODEL "address-model=32_64") + endif() + + if( (${CMAKE_OSX_ARCHITECTURES} MATCHES "x86_64" OR ${CMAKE_OSX_ARCHITECTURES} MATCHES "386") AND + (${CMAKE_OSX_ARCHITECTURES} MATCHES "ppc")) + message("-- BOOST found ppc/intel Architecture") + + set(BOOST_ARCHITECTURE "architecture=combined") + endif() + + endif() + +endif() + ExternalProject_Add( boost PREFIX "${PREFIX}" DOWNLOAD_DIR "${DOWNLOAD_DIR}" @@ -139,8 +166,11 @@ ExternalProject_Add( boost BUILD_COMMAND ./b2 variant=release threading=multi - toolset=gcc ${PIC_STUFF} + ${BOOST_CXXFLAGS} + ${BOOST_LINKFLAGS} + ${BOOST_ADDRESSMODEL} + ${BOOST_ARCHITECTURE} ${b2_libs} #link=static --prefix= From 4270fece64faff2186468c82c3f4834008ab2842 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Sun, 22 Dec 2013 18:29:57 +0100 Subject: [PATCH 12/57] [MacOSX] Fixing crash when changing module text properties - bug #1230090 --- pcbnew/dialogs/dialog_edit_module_text.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pcbnew/dialogs/dialog_edit_module_text.cpp b/pcbnew/dialogs/dialog_edit_module_text.cpp index 18917824ac..267bc05cd6 100644 --- a/pcbnew/dialogs/dialog_edit_module_text.cpp +++ b/pcbnew/dialogs/dialog_edit_module_text.cpp @@ -183,11 +183,13 @@ void DialogEditModuleText::OnOkClick( wxCommandEvent& event ) if ( m_module) m_parent->SaveCopyInUndoList( m_module, UR_CHANGED ); +#ifndef USE_WX_OVERLAY if( m_dc ) //Erase old text on screen { m_currentText->Draw( m_parent->GetCanvas(), m_dc, GR_XOR, (m_currentText->IsMoving()) ? MoveVector : wxPoint( 0, 0 ) ); } +#endif m_currentText->SetText( m_Name->GetValue() ); m_currentText->SetItalic( m_Style->GetSelection() == 1 ); @@ -238,11 +240,15 @@ void DialogEditModuleText::OnOkClick( wxCommandEvent& event ) m_currentText->SetDrawCoord(); +#ifndef USE_WX_OVERLAY if( m_dc ) // Display new text { m_currentText->Draw( m_parent->GetCanvas(), m_dc, GR_XOR, (m_currentText->IsMoving()) ? MoveVector : wxPoint( 0, 0 ) ); } +#else + m_parent->Refresh(); +#endif m_parent->OnModify(); From 0328cfe83e33c42134a2854675151ffc2f318626 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Sun, 22 Dec 2013 18:39:37 +0100 Subject: [PATCH 13/57] [MacOSX] Fixing crash/missing refresh in pcb text --- pcbnew/dialogs/dialog_pcb_text_properties.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pcbnew/dialogs/dialog_pcb_text_properties.cpp b/pcbnew/dialogs/dialog_pcb_text_properties.cpp index ceaf62f5cc..820b0f6ec3 100644 --- a/pcbnew/dialogs/dialog_pcb_text_properties.cpp +++ b/pcbnew/dialogs/dialog_pcb_text_properties.cpp @@ -187,11 +187,13 @@ void DIALOG_PCB_TEXT_PROPERTIES::OnOkClick( wxCommandEvent& event ) if( m_SelectedPCBText->GetFlags() != 0 ) m_SelectedPCBText->SetFlags( IN_EDIT ); +#ifndef USE_WX_OVERLAY // Erase old text on screen if context is available if( m_DC ) { m_SelectedPCBText->Draw( m_Parent->GetCanvas(), m_DC, GR_XOR ); } +#endif // Set the new text content if( !m_TextContentCtrl->GetValue().IsEmpty() ) @@ -268,12 +270,15 @@ void DIALOG_PCB_TEXT_PROPERTIES::OnOkClick( wxCommandEvent& event ) break; } +#ifndef USE_WX_OVERLAY // Finally, display new text if there is a context to do so if( m_DC ) { m_SelectedPCBText->Draw( m_Parent->GetCanvas(), m_DC, GR_OR ); } - +#else + m_parent->Refresh(); +#endif m_Parent->OnModify(); EndModal( 1 ); } From 58b96efe068c3b6e7d3740cb65861190013feb1a Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 22 Dec 2013 20:02:04 +0100 Subject: [PATCH 14/57] Fix broken download_boost.cmake. Fix minor other issues: dialog fp lib table display not updated (Windows specific) when an option is changed from the lib table option editor overloaded function in dialog_fp_lib_table.cpp not compiled with gcc 4.4.7 (try to fix Bug #1262002). specctra_export.cpp: set min dist to find segment ends when building the board outline to 2 microns (should break anything, but should be enough to fix rounding issues when creating/importing board outlines with arcs) --- CMakeModules/download_boost.cmake | 11 +++++++---- pcbnew/dialogs/dialog_fp_lib_table.cpp | 8 +++++++- pcbnew/dialogs/dialog_fp_lib_table_base.cpp | 6 +++--- pcbnew/dialogs/dialog_fp_lib_table_base.fbp | 4 +++- pcbnew/dialogs/dialog_fp_lib_table_base.h | 4 ++-- pcbnew/specctra_export.cpp | 5 +++-- 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 74d0b766d4..12aa350267 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -113,12 +113,15 @@ else() unset( b2_libs ) endif() + set( TOOLSET "toolset=gcc" ) + if( APPLE ) # I set this to being compatible with wxWidgets - # wxWidgets still using libstdc++ (gcc), meanwhile OSX + # wxWidgets still using libstdc++ (gcc), meanwhile OSX # has switched to libc++ (llvm) by default set(BOOST_CXXFLAGS "cxxflags=-mmacosx-version-min=10.5" ) set(BOOST_LINKFLAGS "linkflags=-mmacosx-version-min=10.5" ) + set( TOOLSET "" ) if( CMAKE_OSX_ARCHITECTURES ) @@ -137,7 +140,6 @@ if( APPLE ) endif() endif() - endif() ExternalProject_Add( boost @@ -167,8 +169,9 @@ ExternalProject_Add( boost variant=release threading=multi ${PIC_STUFF} - ${BOOST_CXXFLAGS} - ${BOOST_LINKFLAGS} + ${TOOLSET} + ${BOOST_CXXFLAGS} + ${BOOST_LINKFLAGS} ${BOOST_ADDRESSMODEL} ${BOOST_ARCHITECTURE} ${b2_libs} diff --git a/pcbnew/dialogs/dialog_fp_lib_table.cpp b/pcbnew/dialogs/dialog_fp_lib_table.cpp index 8c287a35bb..57d33b6076 100644 --- a/pcbnew/dialogs/dialog_fp_lib_table.cpp +++ b/pcbnew/dialogs/dialog_fp_lib_table.cpp @@ -599,6 +599,12 @@ private: m_cur_grid->AutoSizeColumn( COL_NICKNAME, false ); m_cur_grid->AutoSizeColumn( COL_URI, false ); m_cur_grid->AutoSizeColumn( COL_TYPE, false ); + + // On Windows, the grid is not refresh, + // so force resfresh after a change +#ifdef __WINDOWS__ + Refresh(); +#endif } } @@ -607,7 +613,7 @@ private: EndModal( 0 ); } - void onCancelButtonClick( wxCloseEvent& event ) + void onCancelCaptionButtonClick( wxCloseEvent& event ) { EndModal( 0 ); } diff --git a/pcbnew/dialogs/dialog_fp_lib_table_base.cpp b/pcbnew/dialogs/dialog_fp_lib_table_base.cpp index 11a220201d..b88fd75902 100644 --- a/pcbnew/dialogs/dialog_fp_lib_table_base.cpp +++ b/pcbnew/dialogs/dialog_fp_lib_table_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Apr 30 2013) +// C++ code generated with wxFormBuilder (version Nov 6 2013) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! @@ -190,7 +190,7 @@ DIALOG_FP_LIB_TABLE_BASE::DIALOG_FP_LIB_TABLE_BASE( wxWindow* parent, wxWindowID this->Centre( wxBOTH ); // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_FP_LIB_TABLE_BASE::onCancelButtonClick ) ); + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_FP_LIB_TABLE_BASE::onCancelCaptionButtonClick ) ); this->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( DIALOG_FP_LIB_TABLE_BASE::onKeyDown ) ); m_auinotebook->Connect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler( DIALOG_FP_LIB_TABLE_BASE::pageChangedHandler ), NULL, this ); m_append_button->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( DIALOG_FP_LIB_TABLE_BASE::appendRowHandler ), NULL, this ); @@ -205,7 +205,7 @@ DIALOG_FP_LIB_TABLE_BASE::DIALOG_FP_LIB_TABLE_BASE( wxWindow* parent, wxWindowID DIALOG_FP_LIB_TABLE_BASE::~DIALOG_FP_LIB_TABLE_BASE() { // Disconnect Events - this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_FP_LIB_TABLE_BASE::onCancelButtonClick ) ); + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_FP_LIB_TABLE_BASE::onCancelCaptionButtonClick ) ); this->Disconnect( wxEVT_KEY_DOWN, wxKeyEventHandler( DIALOG_FP_LIB_TABLE_BASE::onKeyDown ) ); m_auinotebook->Disconnect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler( DIALOG_FP_LIB_TABLE_BASE::pageChangedHandler ), NULL, this ); m_append_button->Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( DIALOG_FP_LIB_TABLE_BASE::appendRowHandler ), NULL, this ); diff --git a/pcbnew/dialogs/dialog_fp_lib_table_base.fbp b/pcbnew/dialogs/dialog_fp_lib_table_base.fbp index 11e3687fb3..52dfac0974 100644 --- a/pcbnew/dialogs/dialog_fp_lib_table_base.fbp +++ b/pcbnew/dialogs/dialog_fp_lib_table_base.fbp @@ -20,8 +20,10 @@ . 1 + 1 1 1 + UI 0 0 @@ -59,7 +61,7 @@ - onCancelButtonClick + onCancelCaptionButtonClick diff --git a/pcbnew/dialogs/dialog_fp_lib_table_base.h b/pcbnew/dialogs/dialog_fp_lib_table_base.h index 7ea5300b1b..a9a456b1f5 100644 --- a/pcbnew/dialogs/dialog_fp_lib_table_base.h +++ b/pcbnew/dialogs/dialog_fp_lib_table_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Apr 30 2013) +// C++ code generated with wxFormBuilder (version Nov 6 2013) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! @@ -57,7 +57,7 @@ class DIALOG_FP_LIB_TABLE_BASE : public DIALOG_SHIM wxButton* m_sdbSizer1Cancel; // Virtual event handlers, overide them in your derived class - virtual void onCancelButtonClick( wxCloseEvent& event ) = 0; + virtual void onCancelCaptionButtonClick( wxCloseEvent& event ) = 0; virtual void onKeyDown( wxKeyEvent& event ) = 0; virtual void pageChangedHandler( wxAuiNotebookEvent& event ) = 0; virtual void appendRowHandler( wxMouseEvent& event ) = 0; diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp index 193486946f..a2794c1b1f 100644 --- a/pcbnew/specctra_export.cpp +++ b/pcbnew/specctra_export.cpp @@ -1008,7 +1008,8 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) throw( IO_ER // Set maximum proximity threshold for point to point nearness metric for // board perimeter only, not interior keepouts yet. - prox = Mils2iu( 0 ); + prox = Millimeter2iu( 0.002 ); // should be enough to fix rounding issues + // is arc start and end point calculations // Output the Edge.Cuts perimeter as circle or polygon. if( graphic->GetShape() == S_CIRCLE ) @@ -1118,7 +1119,7 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) throw( IO_ER // Output the interior Edge.Cuts graphics as keepouts, using nearness metric // for sloppy graphical items. - prox = Mils2iu( 10 ); + prox = Millimeter2iu( 0.025 ); while( items.GetCount() ) { From 5a6e6b273365c8890a5a777de2debd35c5a562cd Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Sun, 22 Dec 2013 20:52:52 +0100 Subject: [PATCH 15/57] [MacOSX] Cleanup --- CMakeModules/download_boost.cmake | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 12aa350267..6bd5a3a7a5 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -88,6 +88,9 @@ endfunction() string( REPLACE "unit_test_framework" "test" boost_libs_list "${BOOST_LIBS_BUILT}" ) #message( STATUS "REPLACE libs_csv:${boost_libs_list}" ) +# Default Toolset +set( BOOST_TOOLSET "toolset=gcc" ) + if( MINGW ) if( MSYS ) # The Boost system does not build properly on MSYS using bootstrap.sh. Running @@ -113,7 +116,6 @@ else() unset( b2_libs ) endif() - set( TOOLSET "toolset=gcc" ) if( APPLE ) # I set this to being compatible with wxWidgets @@ -121,7 +123,7 @@ if( APPLE ) # has switched to libc++ (llvm) by default set(BOOST_CXXFLAGS "cxxflags=-mmacosx-version-min=10.5" ) set(BOOST_LINKFLAGS "linkflags=-mmacosx-version-min=10.5" ) - set( TOOLSET "" ) + set(BOOST_TOOLSET "toolset=darwin" ) if( CMAKE_OSX_ARCHITECTURES ) @@ -169,7 +171,7 @@ ExternalProject_Add( boost variant=release threading=multi ${PIC_STUFF} - ${TOOLSET} + ${BOOST_TOOLSET} ${BOOST_CXXFLAGS} ${BOOST_LINKFLAGS} ${BOOST_ADDRESSMODEL} From f56e4fe1a4ac7370245c4b45de16db0005187afb Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Mon, 23 Dec 2013 09:07:08 +0100 Subject: [PATCH 16/57] specctra_export.cpp: Fix a conversion error from mils to mm I made i n may last commit, which set min dist to find a segment end for internal outlines holes to 1 mil instead of 10 mils. --- pcbnew/specctra_export.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp index a2794c1b1f..535d6834fa 100644 --- a/pcbnew/specctra_export.cpp +++ b/pcbnew/specctra_export.cpp @@ -1119,7 +1119,7 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) throw( IO_ER // Output the interior Edge.Cuts graphics as keepouts, using nearness metric // for sloppy graphical items. - prox = Millimeter2iu( 0.025 ); + prox = Millimeter2iu( 0.25 ); while( items.GetCount() ) { From 335329785884bc0f3d66a7bd50b2efc4ed796dec Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Mon, 23 Dec 2013 16:14:22 +0100 Subject: [PATCH 17/57] [MacOSX] Fixing Zone visualization and removing not working code --- common/gr_basic.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/common/gr_basic.cpp b/common/gr_basic.cpp index 9052ac74f5..bc2ad9daed 100644 --- a/common/gr_basic.cpp +++ b/common/gr_basic.cpp @@ -599,25 +599,6 @@ void GRLineArray( EDA_RECT* aClipBox, wxDC* aDC, std::vector& aLines, { GRSetColorPen( aDC, aColor, aWidth ); -#if defined( USE_WX_GRAPHICS_CONTEXT ) || defined(__WXMAC__) - wxGraphicsContext* gc = wxGraphicsContext::Create( aDC ); - wxASSERT( gc ); - gc->Clip( aClipBox->GetX(), aClipBox->GetY(), aClipBox->GetRight(), aClipBox->GetHeight() ); - wxGraphicsPath path = gc->CreatePath(); - - for( unsigned i = 0; i < aLines.size(); ) - { - path.MoveToPoint( aLines[i].x, aLines[i].y ); - i++; - path.AddLineToPoint( aLines[i].x, aLines[i].y ); - i++; - } - - gc->StrokePath( path ); - gc->ResetClip(); - delete gc; -#else - if( aClipBox ) aClipBox->Inflate(aWidth/2); for( unsigned i = 0; i < aLines.size(); i += 2) @@ -633,7 +614,6 @@ void GRLineArray( EDA_RECT* aClipBox, wxDC* aDC, std::vector& aLines, } if( aClipBox ) aClipBox->Inflate(-aWidth/2); -#endif } From 68d43a49df3f0c1ad0c455dd0828df92d44a1988 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Mon, 23 Dec 2013 09:39:40 -0600 Subject: [PATCH 18/57] kicad-install.sh: abort on first failure. --- scripts/kicad-install.sh | 48 ++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/scripts/kicad-install.sh b/scripts/kicad-install.sh index b527a7d8ea..5db47b6612 100755 --- a/scripts/kicad-install.sh +++ b/scripts/kicad-install.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e # Install KiCad from source onto either: # -> a Ubuntu/Debian/Mint or # -> a Red Hat @@ -25,6 +25,12 @@ # bzr-git seems not up to the task. wget or curl would also work. +# Since bash is invoked with -e by the first line of this script, all the steps in this script +# must succeed otherwise bash will abort at the first non-zero error code. Therefore any script +# functions must be crafted to anticipate numerous conditions, such that no command fails unless it +# is a serious situation. + + # Set where the 3 source trees will go, use a full path WORKING_TREES=~/kicad_sources @@ -113,9 +119,17 @@ install_prerequisites() rm_build_dir() { local dir="$1" - # this file is often created as root, so remove as root - sudo rm "$dir/install_manifest.txt" 2> /dev/null - rm -rf "$dir" + + echo "removing directory $dir" + + if [ -e "$dir/install_manifest.txt" ]; then + # this file is often created as root, so remove as root + sudo rm "$dir/install_manifest.txt" 2> /dev/null + fi + + if [ -d "$dir" ]; then + rm -rf "$dir" + fi } @@ -142,24 +156,24 @@ cmake_uninstall() # sets an environment variable globally. set_env_var() { - local VAR=$1 - local VAL=$2 + local var=$1 + local val=$2 if [ -d /etc/profile.d ]; then - if [ ! -e /etc/profile.d/kicad.sh ] || ! grep "$VAR" /etc/profile.d/kicad.sh; then + if [ ! -e /etc/profile.d/kicad.sh ] || ! grep "$var" /etc/profile.d/kicad.sh >> /dev/null; then echo - echo "Adding environment variable $VAR to file /etc/profile.d/kicad.sh" + echo "Adding environment variable $var to file /etc/profile.d/kicad.sh" echo "Please logout and back in after this script completes for environment" echo "variable to get set into environment." - sudo sh -c "echo export $VAR=$VAL >> /etc/profile.d/kicad.sh" + sudo sh -c "echo export $var=$val >> /etc/profile.d/kicad.sh" fi elif [ -e /etc/environment ]; then - if ! grep "$VAR" /etc/environment; then + if ! grep "$var" /etc/environment >> /dev/null; then echo - echo "Adding environment variable $VAR to file /etc/environment" + echo "Adding environment variable $var to file /etc/environment" echo "Please reboot after this script completes for environment variable to get set into environment." - sudo sh -c "echo $VAR=$VAL >> /etc/environment" + sudo sh -c "echo $var=$val >> /etc/environment" fi fi } @@ -239,7 +253,7 @@ install_or_update() mkdir build && cd build cmake ../ sudo make install - echo " kicad-lib installed." + echo " kicad-lib.bzr installed." echo "step 9) as non-root, install user configuration files..." @@ -256,12 +270,14 @@ install_or_update() sudo make install echo " kicad-doc.bzr installed." - echo - echo 'All KiCad "--install-or-update" steps completed, you are up to date.' - + echo "step 11) check for environment variables..." if [ -z "${KIGITHUB}" ]; then set_env_var KIGITHUB https://github.com/KiCad fi + + echo + echo 'All KiCad "--install-or-update" steps completed, you are up to date.' + echo } From 447bb98480fbb770211c0cf2a2efac68185e5705 Mon Sep 17 00:00:00 2001 From: Baranovskiy Konstantin Date: Mon, 23 Dec 2013 11:25:13 -0500 Subject: [PATCH 19/57] Fix worksheet multiple line text plotting bug. (fixes lp:1261906) --- common/common_plot_functions.cpp | 3 +- common/drawtxt.cpp | 55 +++++++++++++++++++++++++++----- include/plot_common.h | 3 +- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/common/common_plot_functions.cpp b/common/common_plot_functions.cpp index 1a7312756b..3d10d81223 100644 --- a/common/common_plot_functions.cpp +++ b/common/common_plot_functions.cpp @@ -133,7 +133,8 @@ void PlotWorkSheet( PLOTTER* plotter, const TITLE_BLOCK& aTitleBlock, text->GetSize(), text->GetHorizJustify(), text->GetVertJustify(), text->GetPenWidth(), - text->IsItalic(), text->IsBold() ); + text->IsItalic(), text->IsBold(), + text->IsMultilineAllowed() ); } break; diff --git a/common/drawtxt.cpp b/common/drawtxt.cpp index 5fe1d5f768..29286be5b4 100644 --- a/common/drawtxt.cpp +++ b/common/drawtxt.cpp @@ -602,6 +602,7 @@ void DrawGraphicHaloText( EDA_RECT* aClipBox, wxDC * aDC, * Use a value min(aSize.x, aSize.y) / 5 for a bold text * @param aItalic = true to simulate an italic font * @param aBold = true to use a bold font Useful only with default width value (aWidth = 0) + * @param aMultilineAllowed = true to plot text as multiline, otherwise single line */ void PLOTTER::Text( const wxPoint& aPos, enum EDA_COLOR_T aColor, @@ -612,7 +613,8 @@ void PLOTTER::Text( const wxPoint& aPos, enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, - bool aBold ) + bool aBold, + bool aMultilineAllowed ) { int textPensize = aWidth; @@ -630,13 +632,50 @@ void PLOTTER::Text( const wxPoint& aPos, if( aColor >= 0 ) SetColor( aColor ); - DrawGraphicText( NULL, NULL, aPos, aColor, aText, - aOrient, aSize, - aH_justify, aV_justify, - textPensize, aItalic, - aBold, - NULL, - this ); + if( aMultilineAllowed ) + { + // EDA_TEXT needs for calculations of the position of every + // line according to orientation and justifications + EDA_TEXT* multilineText = new EDA_TEXT( aText ); + multilineText->SetSize( aSize ); + multilineText->SetTextPosition( aPos ); + multilineText->SetOrientation( aOrient ); + multilineText->SetHorizJustify( aH_justify ); + multilineText->SetVertJustify( aV_justify ); + multilineText->SetThickness( aWidth ); + multilineText->SetMultilineAllowed( aMultilineAllowed ); + + std::vector positions; + wxArrayString* list = wxStringSplit( aText, '\n' ); + positions.reserve( list->Count() ); + + multilineText->GetPositionsOfLinesOfMultilineText( + positions, list->Count() ); + + for( unsigned ii = 0; ii < list->Count(); ii++ ) + { + wxString& txt = list->Item( ii ); + DrawGraphicText( NULL, NULL, positions[ii], aColor, txt, + aOrient, aSize, + aH_justify, aV_justify, + textPensize, aItalic, + aBold, + NULL, + this ); + } + delete multilineText; + delete list; + } + else + { + DrawGraphicText( NULL, NULL, aPos, aColor, aText, + aOrient, aSize, + aH_justify, aV_justify, + textPensize, aItalic, + aBold, + NULL, + this ); + } if( aWidth != textPensize ) SetCurrentLineWidth( aWidth ); diff --git a/include/plot_common.h b/include/plot_common.h index 1dff640eb4..f59a2fbd82 100644 --- a/include/plot_common.h +++ b/include/plot_common.h @@ -253,7 +253,8 @@ public: enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, - bool aBold ); + bool aBold, + bool aMultilineAllowed = false ); /** * Draw a marker (used for the drill map) From b7996a4ad416489fe83d2a099e92bcc928471b53 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Tue, 24 Dec 2013 19:16:56 +0100 Subject: [PATCH 20/57] [MacOSX] Thanks for the hint, nullset@freenode, Merry Xmas ! --- CMakeModules/download_boost.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 6bd5a3a7a5..1548ab5a6f 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -134,8 +134,8 @@ if( APPLE ) set(BOOST_ADDRESSMODEL "address-model=32_64") endif() - if( (${CMAKE_OSX_ARCHITECTURES} MATCHES "x86_64" OR ${CMAKE_OSX_ARCHITECTURES} MATCHES "386") AND - (${CMAKE_OSX_ARCHITECTURES} MATCHES "ppc")) + if( (CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" OR CMAKE_OSX_ARCHITECTURES MATCHES "386") AND + (CMAKE_OSX_ARCHITECTURES MATCHES "ppc")) message("-- BOOST found ppc/intel Architecture") set(BOOST_ARCHITECTURE "architecture=combined") From 5f023e1e256c20e8053d87b489c71f3bc97fd0ac Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 24 Dec 2013 12:46:56 -0600 Subject: [PATCH 21/57] Update resources/linux/mime with zip from Fabrizio --- .../applications/bitmap2component.desktop | 11 + .../linux/mime/applications/cvpcb.desktop | 11 + .../linux/mime/applications/eeschema.desktop | 2 +- .../linux/mime/applications/gerbview.desktop | 11 + .../mime/applications/pcbcalculator.desktop | 11 + .../linux/mime/applications/pcbnew.desktop | 11 + .../mime/icons/hicolor/128x128/apps/3d.png | Bin 0 -> 10917 bytes .../hicolor/128x128/apps/bitmap2component.png | Bin 0 -> 13176 bytes .../mime/icons/hicolor/128x128/apps/cvpcb.png | Bin 0 -> 12149 bytes .../icons/hicolor/128x128/apps/eeschema.png | Bin 0 -> 11777 bytes .../icons/hicolor/128x128/apps/gerbview.png | Bin 0 -> 21775 bytes .../mime/icons/hicolor/128x128/apps/kicad.png | Bin 0 -> 5144 bytes .../hicolor/128x128/apps/pcbcalculator.png | Bin 0 -> 9585 bytes .../icons/hicolor/128x128/apps/pcbnew.png | Bin 0 -> 12292 bytes .../mimetypes/application-x-3d-project.png | Bin 0 -> 10917 bytes ...application-x-bitmap2component-project.png | Bin 0 -> 13176 bytes .../mimetypes/application-x-cvpcb-project.png | Bin 0 -> 12149 bytes .../application-x-eeschema-project.png | Bin 0 -> 11777 bytes .../application-x-gerbview-project.png | Bin 0 -> 21775 bytes .../mimetypes/application-x-kicad-project.png | Bin 0 -> 5144 bytes .../application-x-pcbcalculator-project.png | Bin 0 -> 9585 bytes .../application-x-pcbnew-project.png | Bin 0 -> 12292 bytes .../mimetypes/application-x-kicad-project.png | Bin 947 -> 679 bytes .../mime/icons/hicolor/22x22/apps/kicad.png | Bin 0 -> 1083 bytes .../mimetypes/application-x-kicad-project.png | Bin 1483 -> 1083 bytes .../mime/icons/hicolor/24x24/apps/kicad.png | Bin 0 -> 1160 bytes .../mimetypes/application-x-kicad-project.png | Bin 1611 -> 1160 bytes .../mime/icons/hicolor/32x32/apps/kicad.png | Bin 0 -> 1448 bytes .../mimetypes/application-x-kicad-project.png | Bin 1458 -> 1448 bytes .../hicolor/48x48/apps/bitmap2component.png | Bin 0 -> 3598 bytes .../mime/icons/hicolor/48x48/apps/cvpcb.png | Bin 0 -> 2435 bytes .../icons/hicolor/48x48/apps/eeschema.png | Bin 0 -> 3347 bytes .../icons/hicolor/48x48/apps/gerbview.png | Bin 0 -> 5333 bytes .../mime/icons/hicolor/48x48/apps/kicad.png | Bin 3896 -> 2178 bytes .../hicolor/48x48/apps/pcbcalculator.png | Bin 0 -> 3256 bytes .../mime/icons/hicolor/48x48/apps/pcbnew.png | Bin 0 -> 3366 bytes ...application-x-bitmap2component-project.png | Bin 0 -> 3598 bytes .../mimetypes/application-x-cvpcb-project.png | Bin 0 -> 2435 bytes .../application-x-eeschema-project.png | Bin 0 -> 3347 bytes .../application-x-gerbview-project.png | Bin 0 -> 5333 bytes .../mimetypes/application-x-kicad-project.png | Bin 3896 -> 2178 bytes .../application-x-pcbcalculator-project.png | Bin 0 -> 3256 bytes .../application-x-pcbnew-project.png | Bin 0 -> 3366 bytes .../mime/icons/hicolor/scalable/apps/3d.svg | 32 + .../scalable/apps/bitmap2component.svg | 1125 ++++++++++++ .../icons/hicolor/scalable/apps/cvpcb.svg | 41 + .../icons/hicolor/scalable/apps/eeschema.svg | 394 +++++ .../icons/hicolor/scalable/apps/gerbview.svg | 109 ++ .../hicolor/scalable/apps/pcbcalculator.svg | 1100 ++++++++++++ .../icons/hicolor/scalable/apps/pcbnew.svg | 1554 +++++++++++++++++ .../mimetypes/application-x-3d-project.svg | 32 + ...application-x-bitmap2component-project.svg | 1125 ++++++++++++ .../mimetypes/application-x-cvpcb-project.svg | 41 + .../application-x-eeschema-project.svg | 394 +++++ .../application-x-gerbview-project.svg | 109 ++ .../application-x-pcbcalculator-project.svg | 1100 ++++++++++++ .../application-x-pcbnew-project.svg | 1554 +++++++++++++++++ 57 files changed, 8766 insertions(+), 1 deletion(-) create mode 100644 resources/linux/mime/applications/bitmap2component.desktop create mode 100644 resources/linux/mime/applications/cvpcb.desktop create mode 100644 resources/linux/mime/applications/gerbview.desktop create mode 100644 resources/linux/mime/applications/pcbcalculator.desktop create mode 100644 resources/linux/mime/applications/pcbnew.desktop create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/3d.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/bitmap2component.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/cvpcb.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/eeschema.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/gerbview.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/kicad.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/pcbcalculator.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/pcbnew.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-3d-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-bitmap2component-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-cvpcb-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-eeschema-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-gerbview-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-kicad-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbcalculator-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbnew-project.png create mode 100644 resources/linux/mime/icons/hicolor/22x22/apps/kicad.png create mode 100644 resources/linux/mime/icons/hicolor/24x24/apps/kicad.png create mode 100644 resources/linux/mime/icons/hicolor/32x32/apps/kicad.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/bitmap2component.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/cvpcb.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/eeschema.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/gerbview.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/pcbcalculator.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/pcbnew.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-bitmap2component-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-cvpcb-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-eeschema-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-gerbview-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbcalculator-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbnew-project.png create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/3d.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/bitmap2component.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/cvpcb.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/eeschema.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/gerbview.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/pcbcalculator.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/pcbnew.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-3d-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-bitmap2component-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-cvpcb-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-eeschema-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-gerbview-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbcalculator-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbnew-project.svg diff --git a/resources/linux/mime/applications/bitmap2component.desktop b/resources/linux/mime/applications/bitmap2component.desktop new file mode 100644 index 0000000000..09990b1d1f --- /dev/null +++ b/resources/linux/mime/applications/bitmap2component.desktop @@ -0,0 +1,11 @@ + +[Desktop Entry] +Categories=Development;Electronics +Comment=Design a printed circuit board +Exec=bitmap2component +GenericName=EDA Suite +Icon=bitmap2component +MimeType=application/x-bitmap2component-project; +Name=bitmap2component +Type=Application +Name[en_US]=bitmap2component diff --git a/resources/linux/mime/applications/cvpcb.desktop b/resources/linux/mime/applications/cvpcb.desktop new file mode 100644 index 0000000000..4d2b818aa1 --- /dev/null +++ b/resources/linux/mime/applications/cvpcb.desktop @@ -0,0 +1,11 @@ + +[Desktop Entry] +Categories=Development;Electronics +Comment=Design a printed circuit board +Exec=cvpcb +GenericName=EDA Suite +Icon=cvpcb +MimeType=application/x-cvpcb-project; +Name=cvpcb +Type=Application +Name[en_US]=cvpcb diff --git a/resources/linux/mime/applications/eeschema.desktop b/resources/linux/mime/applications/eeschema.desktop index 3042eaefa4..888598c5a6 100644 --- a/resources/linux/mime/applications/eeschema.desktop +++ b/resources/linux/mime/applications/eeschema.desktop @@ -6,6 +6,6 @@ Exec=eeschema GenericName=Electronic schematic design GenericName[fr]=Saisie de schéma électronique Icon=eeschema -MimeType=application/x-kicad-schematic; +MimeType=application/x-eeschema-schematic; Name=Eeschema Type=Application diff --git a/resources/linux/mime/applications/gerbview.desktop b/resources/linux/mime/applications/gerbview.desktop new file mode 100644 index 0000000000..620f731769 --- /dev/null +++ b/resources/linux/mime/applications/gerbview.desktop @@ -0,0 +1,11 @@ + +[Desktop Entry] +Categories=Development;Electronics +Comment=Design a printed circuit board +Exec=gerbview +GenericName=EDA Suite +Icon=gerbview +MimeType=application/x-gerbview-project; +Name=gerbview +Type=Application +Name[en_US]=gerbview diff --git a/resources/linux/mime/applications/pcbcalculator.desktop b/resources/linux/mime/applications/pcbcalculator.desktop new file mode 100644 index 0000000000..7c449f3fef --- /dev/null +++ b/resources/linux/mime/applications/pcbcalculator.desktop @@ -0,0 +1,11 @@ + +[Desktop Entry] +Categories=Development;Electronics +Comment=Design a printed circuit board +Exec=pcbcalculator +GenericName=EDA Suite +Icon=pcbcalculator +MimeType=application/x-pcbcalculator-project; +Name=pcbcalculator +Type=Application +Name[en_US]=pcbcalculator diff --git a/resources/linux/mime/applications/pcbnew.desktop b/resources/linux/mime/applications/pcbnew.desktop new file mode 100644 index 0000000000..f1052326a6 --- /dev/null +++ b/resources/linux/mime/applications/pcbnew.desktop @@ -0,0 +1,11 @@ + +[Desktop Entry] +Categories=Development;Electronics +Comment=Design a printed circuit board +Exec=pcbnew +GenericName=EDA Suite +Icon=pcbnew +MimeType=application/x-pcbnew-project; +Name=pcbnew +Type=Application +Name[en_US]=pcbnew diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/3d.png b/resources/linux/mime/icons/hicolor/128x128/apps/3d.png new file mode 100644 index 0000000000000000000000000000000000000000..cfb90da551ed18807d4cb524cf853c25ed9eacec GIT binary patch literal 10917 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 literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/cvpcb.png b/resources/linux/mime/icons/hicolor/128x128/apps/cvpcb.png new file mode 100644 index 0000000000000000000000000000000000000000..9bd762ece5cb11f168adf362ed743290378e8508 GIT binary patch literal 12149 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 Y-quNVBmz$kw8(>GKPpPriW>#}4{wsC)&Kwi literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/eeschema.png b/resources/linux/mime/icons/hicolor/128x128/apps/eeschema.png new file mode 100644 index 0000000000000000000000000000000000000000..9d85ac91c80429312a11c0e2c57f473141362268 GIT binary patch literal 11777 zcmX9^1yEbh*A4Dc2(B&eTHKxDUff-advJG`;#QzI6f0h=I23nxcmLk+|7GSSlQ)y> zzTJDzJ?GqwR92KmLncH9fk0@oG7_r5XTX0KB0TWkCuBSVe89Sh$*Lm)FJDBn2;e)C zlZ>_t2t;}K-vy;ix&Hv%Byg4d>Z;~w;p$=RY!349@L;xfuyrvtb~0ynbhgYs6CebE z$U(9aqUxUCPjkG!gJ)asyJnA1p642RY8gJ!!}sSGO2HTuZK1bH#b!y2Dmb-Mx6a!| zWQ|b~s;?a<{a}j-3cv!ZOHe_7LaqOc%8JU*F?z$*F?;nkr7t+^qcMT%ygt8wU9RMj z!?*vh{7GCOvptB>~9Qe?hz=Ldu-Ry&+lQq}~T#WRphg8BwANeaApF)N~-~WLd84v8yC1 zIU%o;x*?Zbu>#CMBTSj_&V(Dst@4D%q(8+w zkJHVfV+lfoNIg9SOBraH3e=3aCs|ZeP)SAwg?+-DrW!T9E~pCuZ7;cq6jpaw}eqB7Y7jbT5=RH*N!R(Kf7I zh*>o9*JyCT`cgguFvV5E9JP_7`C2QMC=LU**42YMQ;FQx{v_t8vu@w%C}0lMpf(%d z&Te7geC;?8;4f;RSM1Kp#i8-FpdtgPo6nRaHX5J=wxi#penShIeT6%*T_2gN(zUj; zGus(b$za-R`7O3|?ZiasM+Z{@f3qi>%pz1{JAx~~!~~8dRp_ZTHq4}#;a|%RL@ftN zCVD-eHnU~Qx2!tY*x1lAuxPm)U0gpay&s>Q`oRinR}drDZAuH9yuUn7HGUL+V=EJQ zaT=(H5%p`0|A=E$_qDvbT1o}{MNW>6o<2%%X6m>BibuB;q;v0a`DgvTf1e+nx~ho9 z^=!`Lac|DUWZGTu7ED$4U98WeE4%XosT3WUg=|vm(^2K&!9o7--^9_gQIxXZyq+y? z?{PeDpEznD0~KiYvd4!Eu?y`Vt)JfDP0TK(pSF#4Uq@j!=EpKN4JZhJSUP`?NQWf1_f^> zqNVdr(4aI#NrudIJ9dy8dI6(&1Rh`c0uA5uFhI$p>Q_a=JjBEAC01>nTTN&e{+HlF z_qa5fBzh?8{vP{N4?=nC&2)^p+bRdjUrjW8WvI*^C3xqqdvBO-8qH~h$?fXAn{F7? zu4CrkRaM{^;z(X@?nkDX<{#^{%g^|lP(4Nog6;Rpy2ttXwXA<(U#E}J+I-qFKFN{_ z=9l>uB>qn86^l~hXL9iuOsV3N5kBUpAW(&hD!ho-GAyaAjQ;!gOLfcD7i45)_&@Lm z+#56c^STB#JFLo`O1IWBkvLOfBSflD)X7L)>q?T6a3v)r5d{g%pA+Z>ZNKG(5>DC+ z;}0hn#1zzPCnaok8BCW$_X;kVv9q}yso$L}itFo>q-tnH6yh%jbh1lu9nzhNW*xsm zKWS}T-EtqI!iJJ5q?uP#RF*qhiD32r>{ddt5XOgu%?2c1`N8S!;|G5@%7)9AgQL`W zD^RAjCq1AElSwD_Pyp!#q8sSEqX?}&Q@i2G{Ijn2KPS6qo%{AdIOpM$W`iKZq+75n zF_pOCF_-yxVQZ#=sS;*Ayy0YaE^G#dj-iR$GSSeNEmSL6+(+Mce%}=?VI8j0@dbnk zQn5DQORrgRwLJ+flrX!1jBBUD&c>FTpFV!@)=opKS;f0`jWs^DUFCs4d&Z1v%qp5| zObq41>_CX^K_;{z{WksJclIO@7!_`@&71=PogjWJTfqVz!(nSDS*pzc(%QhH&Kfo9 z)3)j_$_duV{Ck83%F174$LwY>v{Aj`3|jT(Be-Fx1ihhn`VxwYiaOS{{;zyoepBDw zVD1}FOB)+e!;82|$nuUtU+(x>DE>m7X|oGix4N2h%&zXgyo&I@HD%sLzlUN%%gLh0 zsklWHTKzz3(*S{@dIw^O=xAxX_pdjd`G&`aWh;?a9GVMsDT|C)Gg4>sj&!;L)(itI ztyw#-U$9a>GyfgNpz-W(KgW!!d~RPS^?FP2aQA(noJ=eyedPif{rdVaz}?w1RU}n$F*Bk6+Kxv1{0f)j_7>If z(Z~V(!2Ll2Z4fyoR{z%vwHAx8>7-W6Z--`I;vId&3J1*PRG8OFxO)2kr)tUmwGs9_NiK6qz$*SL9E1swV`;H@ZV&$x@T`wx#ls+z#%Q26X+ zk4ad`y=xSUT$Y@6Pe41jg;NFmjW?&*)kyWmJGo!XTGl$IxnL|WmQV(18dMqR%8+|A z{qpV|=p04MOjr6%nZnD<>*3Ykj*AFxGqeCUSTv}IoVp>hM?4F_V@oZ$=)0M} z8ms%AkCNi-ob?dmDE!N>4*Q>akEB9|@de=;<&DKD$2Hk;{U*bTD`bwJMcxBQlpKYg zF#Za{Ex0)2v)j==K0X5fJ!-)b8XBsyAzq-oU==45n>39z5JNI@XTOZU?(~Gy)p9WY z4&|V6JWGN!%7xkJ_nh{#2jSr8tgyt*^2s3Y%gfP+>&)t7kg$RJJ$IGAEzaEOX5xHr z5cF>c4E>y(hyqMFI5?RkusI0FHx(G{5$^H3`;+z8pST0_d6%y~$xN*#`&p%HX+5aCiHteW{%T4Xqsu<$WSjoeGWM}&<4EeTH z`45m_Lv3M1(kRmy4Pu7PdUu^Sop^p!R4XF;7vsc``0(-a^0w%WqjG;a*|cZ~{Xik5 zs-v34=9njA$s$Zz+z~&xZG7~v+-|WxwC|UL!^>K%r&Pvbttkh#5+AmYCHf#={0CHc zVBnp-U1RoalWy)zdDITD`%jRXN>LD-ecCG0nCawu{ByV+6@&S6_&31 zmV2eUAQ++i=TAx&77W=m(<_PGf67@O5@HV?QwsIMBhDFao8q(^Eb5H=-~o&ogu~P8 zzS!(bG|$v;cb{5Wi5asfDk}@uG-R+}6GUz1bdFk)u#~3RP2C0K;F+>#nsj4?it)7f zB`qA^`$I{I_k_eowLN*m@>FjeTOg98#2MF@yrqCq@R)a|q{+kiXle? zk<1lK0((2X9w&6i~+I{N$ZPKrp z1A!DaII+8NpJ2Kk>s)ys=Bh0K;u%!YM30xZ+Wl$8SX&msP#8%?94GqcVYGp~u$>JU!+=^w% z)z!?v!|dmHkh-_$*f0O8^Lus&OZM1txg3_Ht7>TUU+sQrs3y08v zJq5O?^{nlleHCjTLj-!KeS0 z+`)mGh6W*iuy1ELnSjrg7TA5W{qY=BBCeS0asGHJxwM4z^uf;8=O4p#g}v%Qj3b<6 z3s1vjT0$HgZ*O7HqVgo~?XUaHS50n8N;n;DD%kO@&aW%pc zUlBb%t|%2xs-UY&6pl_}a-QgJrfo4%*MVU0WH(hZyi~G|5)olkGcO0QCMqf_SeU>I zDtpp;(0YES64lxEOuL_SLW*+I%J9fN9znV7?a}PZ!=Uhh8M}(1p^}dezsufeL0emv z&v^%4^RfbPgNcc$vANmhZqbs%eu;B$EPF6l(5Ln*qkI-;q+~R%in6kZzrRpxYb&rx zmvJYheQrP7cSVkOat7VulULUhK3H*OXY#qxw|ktIc6R1wWf7Xq;>h1kE(Wb*ph8_l zm*5q*6f&&|cU!9cH@=p>8%}|8#M6`a(a{kw3{Q^YmoHyDUoHm$xlmkP9oH9*v3=?J zun~;0(BZ@V-|C%rJ~KfJ*)>DlmY-|`@6_#OMnIQR zWKm*%euBZ-SqYSMbPt)uqM8+fJ1jgxV~l3FR@6P;>xGK`rStg;k*Sp_#-9C}URaO; z9uB~&qaz!Dt2(vn{WRzx1Qy22ib8pLN21B z@&QQKvonW*7=pe|zvut3!M&ZNd3h`hjR=5V2#nCsKXlW?ZdV~Uyy;P+kp6M)3G8#nT?FjjReHEcDbjkRYkZ65liEO{CrKRewE>gg-;b&)aj& zz)ui890pV~PxM*?eSYh$0;bZLvuQGESgh5l0w@kJmoK)q4FBcYY=y>yuh5DEx5vu2 z!RHWRa6$A@bP>!Xg5t1k` zpN?x9T=o@}lBi24#jl}&$9;Xe1%ttpGc)2^T3Q)U_3CgiaBo^{Ey>ltDP`rexnk$$ zG^3)TtZZxwi;H2)W@LeQH8(fs$r-nrz<`BTsdKFRTDxcmEF$@d;E(X-)6(|#Y-QR% zYD$9r#j?o@tuAE+?nGG_0GTww4Fjh9UW3rZ((UnbY)Guz9M@~y=OmvSW2D##*_iOZ z<5=^%*FSOR?+ZgKs;|ca(!;=j6j;^t5uDFY-rnAYg6no%(mtL2CXohO7&tG>j$JwY zXDt+!mXnUKSik5y-7fn>f<%G9sxJ(=RgZevOg^z#y`y=EDwGg}?r#ieVY+cFP=pcc3h@SIz?NC zD8F%EBC%<%c9NAWUKt0(dP4d$7wyo`v9ii7%OcP%OfZW!XxeeHd}u09cqhl`=7n4HWsJRc@pcz^9&_1)ztzwt3Pq3KY^(_Jv4 z&b|MF{~Xo`z$D+N!!kgNumNZW6x?roIbd8|fK1?+-aPtt7E9#~n?;2MYFxCpw*#at z@BL+85Au3DoMn*_Glr}Q*3s2{+=^zw=d{8EN@PD)4df=GfKekb*LL{oUkcvUC`3Ua zSr^hlPx75T%FSafvbhS)_8VaMT!8u<+G7XFAm8x}eLqyr5`KOHa!RxEGvS_s`Hi(C zggx32EXmw{!fr8^Rnnf5Ih24$9`z|2m#No`T@nVr$<1xxH;AMnUwHrWkgDjGuT*~X zO1~Y^%`KD(B5Kqhg|h-w$P9VvtgNgb)z#GasGp)f1;e9#!bJQd@zmI+d-|voZFu?$ z@G24V?jI^eo{zifY?-L1r>SYEXz<#qOsFcd;aUkuCvTwj1K%z?874~x2AKS&rc~RS z4qQnUD=Vw~U%$APRDdW0zvlftQ0u{f4`wr2KXu=}ex#+RH<>DwoT_QO!8^>x#PTf!?yeXfCpH4bxqA_0C<4Z1ZK6gqyvCdq4)an zEj8NplHD)-BBiDWeP+BMa#kQ<0k)awOZ9Zfy(w|V&#l~e#9wgAufb9Nk`YHk_R z(`;}a^AKP?ao5`~b0MRkSOZAY*a%J^k+im^1CWMIDsTNd5Y^JPPQ8?*-2~pB+|DKYwXk6`_F2 ze*?ezv~gW_Pdjd-v=XTL6GB>Ek|`BpnAX=iVxzt(8I55;*cc~;a9Wzy2a)zB@+M9e z8`!ePR4ypg>YR9pZ*Fe>e0{w%eLgCHGx+=j=#uThc+0bu*03eJl=>u4=1+gr@0Qcg zdFMWOhP1zDSE|Qj8{O;`b^RBAwXQUKnt**lK8wHnPj5j)+zvaX*$!fuu!KZJfL$S~ z0tV;>P(W#;u(r>vk7t8w!n`#4CIEeR{rIDz!lWk%4uIv9p$xhDq8hUyGzrX$XWx^x z4kDmd$XZ*k7eUI;2%tn#Oe{GN{^W)TfE*w+E<%|-S-<{_)piPQDdu?0mZ`;)3jW2> z(hFQaAKY|OHZ+U}U}@J`1z?S@HH3ag^Rrt_{wiQoM*mb}0CpWaAF0v9Dd>ilxNK@H zO)>DDVW?DgO41Dqi;98)h^X#-vibYTA%S$7;VBm0n~O}ZAG@+ssf^p5e+k-$fIIgN zKmEIN&gQR@LZFB=%^S!8Ob%e5sDzvmTU#bT@hkuFBfq7EXJ9p&Y+0*E3rr<-N;t%1mY7*PRHkQFl^1^-NuPci)InoD4aDElp<@Q*iT z8PB-s-Xn{0u3>f^DTV#xp%Ub8g+}Gj*jVh5A5iK6xg8rOI)3Lzm;~agOaEgeg!2vrWJKXm zbLTA+sn<^8&u*=K3_>bX7igT&o?rxk&QE@&_+#XFUU^_R`-HLgW=kz7sx{h_Rrqm5 z@K7>L%b&tj(EGe>TAIx7kCOjYat3P+Z4{t){wp=C`0|9?TET)xfD$sj=_K5(uh$FI$gLhJVqVo$}!>$`VI@ky35c=Vs4-_|;O zT8}C!F(nfLifvAGL5zhUKM6Y3B5gUK{4OIn|!AOJ(ltWjmI(wq^ze1sz~9m(399 z_m;p33yoH?2XKv6MnPr|p2r;h`%>uCOu9#4&Dba7qd!EDlaH_$7-2^DCw*fUf#CbE zcJ|3$Ww%(IK%7^tySnY_&3=kc_$BT7T=XDh3Pb$GGUgqc%Se{B`hIg|Gx23>Ro=I^ zwIxtS+E%xQJ)gC+AQ!HYt{8TZ#k2&5a7TDL_9J&bY@z&ThW4lHi5ZKEPX9)_HKTi4 z4IIz7-ug~|)_!5GSe^N!9lgV=@e!BBV3Y;Gs$#hoVsvR&B(0YPf8Pu(l;1WtaKWOA zg173qfT(Z$(D88dU>RW<0;L8;A6bBz=XXZ{7*7C^j&pOrWA6qFoXUmy$S^pzASY4^e!Xx<56^A;NdIg-EmWjEfc4nG`o%U z>Tq^6E+uH=`qA?$k{2Mj6_u6cH8o1@A#&jU;*CnVmPV<(<G8f09 zj%HzRVY51w9Y7-knYy#HQ@ld*2eJWr#$~45aaQr$3{{uUNYL134_wBZ>hig-rtDBM znR)VBaqWO}RKn>L`QVTjJ8nt}NE|vky2!|z65ZaC&kv2J&yc5?ybM-jF`bK8!a$fB zXin}FN+jDqRkBh!dH#QCvjHC{V8+~Wur4U8VyZ(#7U$EgHwo}`ZhKW; z+_%Am4Xm$s{`oukJNJy6~@KvKA{=z)Rs&iu(8HiRSoVdq+0c3qFT;2fXuY z)QjOZWHZTitZE?74SX*p8FA$r?`h0~oWz#g2gBE!{T%P*5BFd_Z7(ld)1b7N+JD5>ey%r2b-xknP z)MJXwpP{M!uE{@ZkoJP-KjL- zrYxhv24a$9S%jn{btk|ogG1BOaJfEnb9i1D4dl8Law)J5>8SQjlD!0^S>{I4(aiqi zczLqLHyMlM`pQDm;JBHQD0M_GiU~+u-!_ZSRqZ-}*R4i~n~<$0VIaD2s-B{}gW1>R z91+y3qbRt|)w%L3M8pVY-mfg!oIwG$-(adnzZY)v!=Gd&p;ixErIssvqa_GpcYP~{Cf7)ZGH+^)-?49hbUD8(*8A9Ttue9bcn+kT zu^c(ORhVyvMIIuW-aq8{Bb{IelbpkMr+>jN6c(Dy`LZ=!yG4FrKN&mCY1L!h&v&1H zT{LZ%Cjpz_BRvV?`Jbe5afHBBNxR_!3CwGiLr8js9nO*=fJi;@N4bdrg5YNbx=e)k{0sDiC{p?@xom=eH%CF6F~ z3g4TgOur6hCsKas5`M}df7}cwdz*>EVQ_~g9owW}gp=^L4}@h0BXecS|EE)75YPeZ zT=LtuZ!s!K%5&roezBy4iOksJLgA)^e7vp?faY?&^@EW`xM$GbeAG1KRGJi!k0 zDniXWpLr_EB%JaCl0G=sW`&Hx7T*wcjC;F z5q_Yzyaf=bUWevznwW@BNwgnFR6V6r5on2DLY*y~89J1T& zfDiD>xsbQVeFLAa+@XD;ZjGUb4;ww$51;M8;7I)YG=H{fj zw#xmhCBgkaBofTwv=b94kt8*tFgOBbF+UMWrzR(x#@~6KN`DOQA+PCrdtw0W(iI!; z9uF!1j+-Nh#tY4Xe`>wvq%}>bnVMk~Uj<4yW$|Y+&%AbA|KN8XN9Jup>Y1fWUPDs- z!4jHxnfup&_fxBN(AsqghCKA;yLt4nLzMr{s*7BS(P~6n4)@5M#zT;RE)R5|2q%~r zF{CG%@`LOdoIxrqbwg&!r=b}8SjDd0l*b`FjPMGf-F%mK!|A0l%9nL7$Ba{~pbRK|C4KU??@Hg2wJZ%Lo zG3Oe%_q(~W53n3jpDssf-g_X1^{l2O9Ubq2l(MDyp7J&$!sBSDp}vj3e0y?NmlMn* zqmDT6{hn6AsWB2s#WIcz6L+89C4aXWT0ztcz>1iF>?BI3B@w^C zHnU(q7&)=ZfF<9a=DP+MQ32K+F=F$bBqrtY(#u7A@&#e3?U%$1nCkb{UPH&bnZ}4P zkK+Ow!LcSdG_G$X(F{A3ipH|>#hiC&O;C5?aaSrxRQNIUHimbbnHUYv#HeObBnrBDP<^CS3@Ok3xgvWd8Tiur$^k*y2GR5wrS)K z=^vEmJq(wvd4DV-bMDUcYL;i)DQq`IYkd@F%dVw`X>|l<7l8+xeGd(wgv18o%^mt&XwU@j;o6>5>)b z#e5{K<8o3QqoW@bfT0}&_g{wqXn0AHy?7qId-Cn9A?VCXsQGuWcC_Wi z3&vVpX~)}A9ull~-_M*Jhw9GF_?0(7g#8$9(pYMJ&a;dM@PyKGKV09tAoawiX@@N$U~b*c?+>m@iYA#E_-(U#QO&g6WSMiKbp>4v z8Etv`aVl_xRMKu9)V5f0e=YyTVj)35!(;F>mXKWMUgL|D40 zp!M^3W*1T!EZD%y*!KY{3PzMy{Oxu{vhAZP^UXoLIb z-@X@{>1^hs7Jr0@QbD|H28Xv-r`F{AQ2Kd{1yW0~IS9693E*4!-FLJ*9s z)IP=wya1x`hz99NzodAdjl+Xem3-?t{10Xlt*WkW^rGGP5Xhir9_ai{2=Y6w3Am}V zkoww()EM?41fv=?FY|84a2%YR6t}ezO53l~cv;9TvTCy88*P{B3veDkulSv-WiZXb z)>W%kT(8}H)fI_BeXLM5a#B-UG9W{R2M%TN0P%cMHdn3R9?iNi4DbGQa#TXm$JK3~ zt{*mkOvS>YwC*viNbcO;{&3R3l%n;o%*~X8J(yZQE%gExU6LA%tosfJ_XytEFEbAS z0NuX*{q1?5n8ztU_|Gu$Ws!HA>9f=@E6h;6yn}-S529;OG5$01XUt zSjD}_ukf6^9V4!dJ_v2*CKitW_Fd81+FBJJ;JMS;fK78hNa|w;XIu*qon+po4I;vY z;V{Kff^2x};k~l2^}DhHfonLjpRt40rD@cV<=>Yf(lxs}C@ohh?HSI~@u=lNe>i7h z5-8S!SN@alYsLZaLfkzWCs%zC)!*J|!1bm_JlUDgh9pOO9D|DxJ3>Og<>`laH6?{lDJA{vRzZ z_}Xcv_(p?qB!(+)^ZH62?(U>T&4aeDKEibaxy0r7Eei_+Tc93t6kJA7eq|+tvGI_W z=;u&{QGs{fH2dsm!-iNpR?4ZVQ%RNl(n3&!1+`*g$9FlU+`!eO^x6D0`j1r6etWMLu4DY`38JthxFstZS>x` zHtAU{O8hbQhqb7T*N~ zogtR(iS@1Ds1>Q9FziM7+Js{h7f9OkkD10n^W)k1rTd{l-`;pF1dTydRI zFcm9E&`2`}PO?HjS%$xMeTgDRtnfx1%i;I%(b7s^n$ei533F}w^d2&KKP*Ge2=`M2 zy+d9ZaYuUEE~G5ZZwyp7txAbnpU1j!R6yvxw92c9=$99xjzj)oP6evhh#`cm8$RPP z3rd6oS)K*v$N6Z@V{H6Uj#Gz$96gK`uTJF4y<{~6&7ZOSTy6w*Nqn{7I0XE7Noj?S zy}9N)8*9OT-DoRv~83p|xbN1Z~ literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/gerbview.png b/resources/linux/mime/icons/hicolor/128x128/apps/gerbview.png new file mode 100644 index 0000000000000000000000000000000000000000..54b66cdf8ed394ab2a7cb21a5214c58a582a0c86 GIT binary patch literal 21775 zcmXtA1yGc2+kKW?x*McR>F(|>5$TdzkQPv4=~_TqLXcLG4yC(W8l*+KyZ`6?W`2g< z9oU)Wjw{bO7tuOe$~c&mm;eCasH!OFfv@oY9#Ay!xl77w2z){Dlv6c;f?xhn+bHm7 z3^$e6o&dnK^WOua!?bk`{*uB=(a1~R)y~Vu%Hs{-rjFc z0O$Z!1z7{%?EMzU6zV>2uIr_!{-U(cmQJAtVEpwPz&SC{*kF3fXnFiCtW3*`n&1x&4hLs$~YkegS_};Y=#G&vn*$pz+=neZ9*V_AIZ-V>m{ZkO@d&YctnaR&f3rIKVB07_yIY zY;^B4c6qQ^faO0@1RTazh(av6;fp_AS02x>{5}W4ygE=V_h!ndBL-YWyDBN+th}7W z+Ab@tEstX5eiZOrh>^%xUmI{df3Wyd+Pih@>8FNk7;rBI=SGHah^%D_!k!lt6f{^@ zZI22|wFaWB`jH3EVOVa3;z^0T!=3e~Y|jF|l$Djyhm9BMu?xfI@PPXXa6uNLvn%JW z8Ou<}9I7P|@F(c$0sZ}l4^69?2A(ZaQp)=|L04{Z0$7QY-w60*;nj!qe6W+aG|(3UI}_zGl+rD^N3? z|4yqE`3rV?!})LZ7Z$g_(?cfXaJqP?sj0Co-tRC!UT-99nftDVzbl$@q!B5*;IX?U z;O6NCR#AaH$m;TkBgqEelXVKMtzcwyN}na~S65XweIA@!rx1Uf_NM@@%zrBkW)6^F z&mv@h9n>#IyluHK6o34R(GGDlMpI{hQ)P61b+D+cEqP&Z`K=_!=}JO7d+qT#5Kl0H z#?YCya{upiOZw@?tu@#f)?z>Q?5Moy&{&6q^mAImcb?JR^uycOwjanwMRlXdmc}qN zIIN69;8(<3nT^z;S@S<4k?c2565!vz#)dCKRu7wwq^j&jGM(=yxXAk|kY!lm<=5QO zOAWO%jRwyJ@u)?<6_SqW!e2|=+Ji62CIePK^W$E8W@4e2KrG_-kx4WgV&!0aJ zL73#AMJWq)u=FRgpc68zC5_%jhXF#4*Nw#L>701n#?{1#L=p?Cad>Zbn;J|E?dw+F zN&d{OBEW6#Jzu13G1mM5`yEaS5BhD~Mg;_(7trD~l*78_mU1DV0_c&((HOC#WwO&c z0G-n5`vT>J!L8h&<%I@M`>T_UKK*~ASK_y~w}YUC!pyy=Umo_xQN8(~jGo2u>W@d+ zE?d#Jix2jex5I5azQA}?+dFLI6|C(e^BVWA0nglqAjw8cB)3UTVFI{2VD|9vus%2YCAh;j*JXydy&vz zuV4>dny)O@ayO42Ulm}n1A9O-Ht=)#{oZz;^y7&_3oVk{7jI{Ll=J(89Ek*HIbL=hIHh4i%w!@X-CBS{ewIs6vX*# z3@flsJh^S7>)~uYMc4zU!$aR}+s*e=)QrEv@NQIzS?__M^5#=DT024H_P_hVRN(qR zi>SE7q@UWIF72ymz1^Lla&4}<&bewXnOj_3-0epNl-9|BW44(0@igM~%;`h)xno`R z`Y(XdhW(=>vJBH4Zqs^%aw3Yru-Y{Iz<9MjREKx}Goa*zB8?OUAQ807BD@NT9Z~z) z2nQ3yGTz*D4jAm}*1P{6D%SeaBj9bKY42B@dDO3LxO(%~PgB7P1;-3N^Q0IWcs9YB zbMx@i?5?=21@0Npu`TBWcL4&4fXBz9r@+dV7D-58AR6*H0Ce&cD#ns7@kvQZDYx+V zo}X?GslPJ!+uX(x{=;*-eAVjXwe!1(e`>om;Eqe&dv}Ti;NjsJH?6Wp4LxD?!jCwe za+vrwO~p|)uK-UTkukz$K@KjcLvY=}u^HJY5@!gz0g?!)Kc3a3{G2l#w!A|bfg+(S zF+H@1G^4(%Go*->uR#KX>s0QT!=@VoPcDA*PPDE6qS`d_PpR;#Un?(8Z=^2@W$uFi zL)MkWM~pTZFM8Bfe0Z=a7n$U7JF>n4$|oI{t^F!IscXww$*Vs#c4Ik`SZL_zi(_8P zJ+Wlz!f*eG+|K-L_4WdtrYJf(x=c?kE6QynPFP4}iyG)cBnj>DSV zvz8gV7J-Zr(({QF5sX6LrGi!Ny)CR;VKCSI7o~Xc4MQ0ODa8QV1*ow*I80^H=c|NZ zG_4cA$Bpvesywju?)4U{q4+sQn2!T^Fc8A_tKvEE_SL?HuwfIG@J>-$pq$gTJ-B2w zWAU9-By&Xpf4n9&u^`yU?P;7QEK+9~V@#})@pIfFG&JJxh963TZcAE~pZVE#94*D8OmN|hQHsio(+6PsJx`^PCusz1gU-)f9ok$kw?~+-7ssbzG;m1%ss$TCsP9+ z`QbrR#v3)c5BsbCMzV-5+Ni25vpM79;$DOPopARo@Olk(OzLhwL_QMNTUl3t(Z%I8 z>3=Ea9~e$|{*Fg|V$(heyNTcH^r-P|$WA57D|H!_p)7optQd!xn5Km9OqHQw*brYJ z%IeVF3iPDOheH2lTao9Rm*$BWD^wXVjluF4j`P0g+s0aEOXF}3c(97UzuRkxO%r|B zTSVgATsG!p3){pLY_=mbdwW3T*%mU!;t#+G77gj|U#+E90DLH7zEWMu|2ebqEdPVJd z5G%YN_WxUKtSh)9Vb&v;#WdK=4~n+a!56W@%f1`iO)C$FRj(5Pr#j<9FfG3i!biDJ z&b?=fh>UcD3LE z6c}8)^Av^{f7KqSg6X=iHOQ%OC5yI=%@)sZA%_CI-X@W+`bv#BZdE$yF>wf zpqu2Csc!2=yg1=M9LOC1hx`AE2TpD2hx7HxW&A|*R)4>pm>1=2=3tf5T^<+xY2J{Jy*N4 z;Ls5g9?ViKk5NV;W2|3iTK_$jUHye7O3f~XePc@jKO6CaT=#c4U=92f;posHN#(_p zVG_JokyXzaG-6_=G?#z*eA=?ssBmJ9UL3)&ySvvH((}U2y*@+&W^LV!!Yr7gcYT8Q z^q9yPiTg;-l3op-_;n8|=O@tve+vLIhqOu$Tz_7<`J9L6zlyAC`N)SL`jn)Hd2nz* z`+eHUKFr^KlJn=E5bc0neK)gLW`RAa6Tyti2VpmFTXCL^?T|e;OCndEK{5!yL^t=7 z&6aQ{46hd=#&jV!sxuaEkQ<*dhf-+nd6TFvoc*bJi+}BZvL4$`j@<819RBd2+4~8t zxW-5bc>_D7hdhSmBP|6d)3k;xj}$6918G|6w1cg0nBcvRt=Rh?%X@wa6771@{QRd8yG`5dMVPzRB6aGbXTtt zYZ9}5sq2llQ9kK9%XoaZimh89xwsmm^Jc^ID`>yVPx=@PyFoyoN z!$imo51?ZpZfrW{?h|oxH!ysW13w*9k|eUkfGpPQxLo%Rx%y>zb|0BoBWA|xQgrl2 zKNFN$nNXLzP&CUpYNYM)2Vy}i5mpbk7e;e!_`IeN=F}fEdwQX=#;=$6X#UC8h3y)Q zC{T~QzsL)k*Fwj8=FIuYZ*47-cG=gE$U9!a81~gBBoR#yHbLh!nAt)dbJiU@ylH+$vUY+Yce$G2E*`5Mf$$C;YnqF+>- zEt^R9v&7gOXF&uYt}U0l)i$xw+WzZA0X8-^(R3WXplQU~3p#GesB*0)54hg1qD%Yk zGt%BbiR6Y&x@OgMiKC6nW=NiNHXC=lg0D zuzjG9KTd9BBt!3+!Gc7PRqk`eNEc&$p#dL3CFk##whRk~=;(gUBMAoR!~S(g^^1fn z6ef@3%TRYhgoIbnfufPR!r#QI;afh^35jb2rvV8nu%*dKqBOPS&We{C0=4U{NBlp> zDYSm6`@Tx}%UGh6SJ4F_55kN4xDTs84P8^8~}c4HA=Ha@%5&a_Iz4zADmFRY+v%rAX~lKBz2s zg*8Lp^gh$y%NHqUs|p!Cp#glq&SCJIXU?yhFABi#71l5n8EVv3gs*qOL_bFUrlpI+DHO;7_3ofBItuPuw6~kWKk(*7k-i0RYO1OCF#YW5PYJ;VK_jS zsv?)JMU^k$LlsU(ccj6hrQd7hm~7MW!_iJJRx?#s9XUmL*+_Pp{W7Fd1WCHP^%b67 zj_*3gi`KtPaABrsuVODZB9;tK6B2$kezxojv|e8%BC1*Vm!c$M)tC)AtvF*G7Z(>g1|9}Biagy6 zIpS-GB@*;3{o{}wd)7d|aanv`p8lT-4IRP7@Jn30o;6+pO3j&{s|-y>97yfuU?un$ zXJc5LlKi>nGArk zv4fXyBvWig$E(>NVXlKOwtCK?l@fafyipZ(oy=au~sxuzbYy z)U2$nk-#Ko`A1E?;)f}E!~-?Cut?d9FUZoCnibI+y{K&Q06u>T6b0uuB`JD;y>3FM zhR752+vBL=r##GCQVPP(aRI``4yaLfT;hge1g zaf(oDfu`-hm3q$I!~GE2i0|J%7=5s(DUjFvrG-Xj;m>Zq)OOWnQtv$9VAB_m#RWwh z5gYpGe08`)BmJ!+e% zjb5dye$RhQqY~fMGpJkEgP-Zoh6;M#DWXh1npT9OQunTWJ5|1z7z-1p9 zJE>FZ2dGeThTh<(AJlF7MDD4To{RxC-;?qY3`nKe@ zY>}$x_cL~{Mq|EDKmX4&t{-^<|XuaJAig+^>_h!LvqWd$uaWVjG2U zpRtB?${&U|lJBS0mk;>I}CmY6r>};l6j6N?KUtEkeYkHSA;qXQ8+iEmx zPJ6LtmL2gsq~eqh8@+MlTv{?O-gJst<*}oZ6FAF$)Z?R2Tn(|A=nN;Ppphkx_&QBV zPD$OZ7)#bw4MhO#kdMLdi0;Y+=~9K3#UOy|0y`}_0Jvj? zWAzAz-NzsP&uDFJ4-1Uh7w0v4kr7+%kW{58z+SGMRPH5nfn4vK-V0rwOb@US7lv^+ zA9r?t_E~PQ^!1$!j%Is&1<)>5zZrT0+_peyWVGS-x;YcoYyx3qPr1K*E@d zcZZ{+D?2mf$59E}9ko3@o|3g5hbPm}&!UAl;PBHiGw_dRqV)i&Ze0=K? z*Q=pRb;dY=;s9|5@aVy8GCQqwVVkZ!TyI!^#v9P*eNFTS;}M=84{v)Cdm-O=#rHx>rh zyXD&0Yq2vbIa19-GN5EEmB9x_exEorPn5d!=ba;Xd*?1@Epq*a zOqTU-=f?s`mpsQLu_VVfcmjxma96K{T$k_tfUD1%njP3UZbB37qo2j^KtSeZYKpkH zxERE6zZr2@dwHeA^f2T;P*PFZR5YJrn!k5ka38U&`E}U1_i=!Hyvmeo12k(HaxIet zIw;OIp?EMU>vfXsi}1A$*(6<*BJ0SpIcuY|_L$Tcmb?*}TLp%t8{3JDA9Yb;i8+SC z>`jj;GCI27=_fHp`Hfie#dH#it+fHGnE1j35r%pYomFi7KZS+K>6KuwmN;U^w1Lu# zQD;1@#%NoBq2ZL#A>*j_{5K{&FO>dEZHOV|#zyi7fLig#n{fR5e_5{Y0T-3Tc4C$lt zp?XT}XLW`jDJ$0v-Kb4!3Z)Q0AK&mRcmz@*wraOZfs2WZspcTG@)Iv)9#M+QU))}5 z=o~MC7rfKh#l)>!YG{cS&lZDyN}eS%1|x(A0MF1qUeYeqyF4sZJY-%J&^|bTn|eHE z(;Yk#1RinQYqEr01GjdrJK;62(M)&uEAqrZLPnXJx_Zi4d;Lv&0+jECdT$C(2!wzV5)& zTJk>|+2EWEWQL;)yDm4|fWGP4%OPgZs>ANRj06mG|LlS zwEExFR60|kv|nXK<@XD`W07XF%ia(%`uUENx=(58j8{x5ng`k;DqO0M&levY>7vf2 zRE(6ut8Zb+FDGRKn`Ia@qI*$)qbK`<&TR8Jwx(PZut=CmA4g_ z3Q9`sM@5PZoX`q?;~NL_M)-3{%LmkuE@`^!b3@$B&_RadMD69*4-f{$kH#g>m+OCx z8X@?Q4Eja451xN%D?xSgsB9 zE^`A?Sqda1xpdX%P<31M4BWo|d5~cZ9@JYe++Z^V16}g_G zKUTIe7=>YJVyXaTFRPuL61{u=+{RWVM}gV8wT@AUtQ+nqxkttiuP~huXzLaR+J>h` zNYqHgB_+-XMd&Ja`JM`X29FW!S9$E^bk*G2mN%C3~~d+>}&VK_7AqAw0Z|a;uS*21Mh<+ITWa-{f0# zl_$^eQ6L7yMVI2Ew}o4pF^a|Ep^mE9c_<+|JKwkT>lS3{4(YKOq4dAiz8LoM6D{cy zM-2b)3fY+ZhL?B^VR(n+M{V|Z@id7yWF`i+HJ9mypEaLTDj)N&V8&0*?^|_O)bh+L zHYRf)&*%RFN|tIv77Bk-Zt~|bb@Cmx|Ngk;ZflEu3@Knw_=e#T$-z##2 zeEf##tf9$Jx@TA9nP_*4O`$=DjVvI6H?4+R_Z~;K#!<}6l001`HjKRHXPDY2Qbl@o zG1)K-c0rizg)xQe0w!@`%tr-ixyzpI1q>-z#?;Nt3R{9vhxj_1pKpofgtr%g%7~;^ zgwfl;%lv)k%&q`~B&)r?{?Ffvr`w8z;hfURk7ztlBHn#@#3&&_z;P%o(_($HZ_{3O zXS=O_gm7lkuV==^S~>;hk(`pyUOH-FoV&2qVruM3DOtSdw?lOhipe zhni}LUN=-_-FuO+ca*Vink6nb+d?gb55TuaVX zNbts6ajZ^_A;Unv; zngGBUf?z1;>=0qJ<)82OrlH79OHOo|dQP(b=YG_biz;p}syPC^Oz`yPevch`5IX-E zXWFUmMmXq9n5?I-5Ar8qMr$=31Q26AL_I~7o}=Bb2V#>#fNNREd}tIF8XoFd_&jFP@jC3>jQeLPLSVm!^(CpvD9#8V7AOu-Ly6 z9K4W_P^<_xOmbd*@wtg<3WiI9uRV|2nwo@LEKXlFQmHA_!t&;$zPGg9m1D^z*zk9B~|xImLI+q-fs_!s`e8YO185pP9rI| zY588KHXU#pD{ABBOvR8jLAqr;7?saPN8$r84{MvNsbXd&F)#>d^nXLO?-q@m5t|0& zDr=?bS_oo7KOkQbutqowQHiQD^y4GTP*P5-rM`|wUUbjbtNKJiMWZg9VpMHAar5u) z&Z>T0{+q$+GvC$V!b}*n-GW8m=0PExJh|GY{?^d8Bu_0iWlAyy;?a69|Ry= zD1ZD-IG8>e9-L$;6+nti?%~&-i}fisi!uDa5i<2}K&YkPjxW<1!ecI$@anX-L}{F< zNP#$WFuxn3AwQ&mOvejBLtd>)n3$M&Rxi^|JVepSGu}yvhKmAcYwtEI3MO~aujO!U_5(`AvQU}Nqz=fQ}i2C-1xor zDo&%$IX@yyLPBi|*dA+}yUT-?lam7Dy${K<_Tk923s1A(w_83Muo2UR z@r09v=vjR6U1wVOTd11uVbk;54F?`GMV%M55m;ucP~8)15A4Q?sZ!{5`uCEfkPUwI zf%Mg{iQ#%w;aR!A^^Y^chfBuG)#<7&XJw3N#(pVHX@IjEP9kGvX& z30*IWKxP~oLQ#N#g%_nCx9f%aYjZ3PU&pO+-81sxWd2s~WikFLuqm;J2)|@As1Bq9zY>-cb?) zhAgg!&1a+KX=9HLt=!GU7!HbneY$I#`E$}(uoyj}$Wj1^-m|23$0^|y6o5v!e4;P! zU{KnRlDxfb{wX4SVCWtHs{5Gb@7np@aDw8Y`ow$b`W?-&u5TT(2H&SCRA}kZGK{UC z9O)b=KUoidf*aB@L_fDNNOY>B6!*So!abBmV%LNWClv*tmp9&?o<%>rVt6YPu$lU5 z7wk6G^kh!wK$mF@_-}P+D*J8o7@-zAcsy1T1Rd_#wS?PA>Ri#n9>ay3*2XYwT-?s9 z0BZ4f*r`q!H>TpXXz@ouB*=Rd3s{slo2BDFwCa7Yal){5f+ImOMMqs3F~mSm?uFyv za1@w=LvMpMV7|?g8jc=F@yx}G1Hb2TE#2Sh^`@LBXnpuBcRW{EoA#qSRDkUmc|XWc zT8mnoq%@E&%X{$WACI$VPO0TJj8yG0zUAk+AlZCXD{}dC;AN3X57aOQv&AS7D++yF zcoHqF>+SAFQ7a+@zyE8ppQnDT4e=ZuQR6`h8y(d;zqlx>h>yo@-qCdJOk`Ac+nr(u zdQXPzYPOG#av>6BuWQgE@o4}146SStG}u5-B?pxn1l@ctHrN}Ms82YLiaZ{OoSn?# zVDLufuig0ceDIIGVhqZatGYA-<`=abj0#rJUT7@oMS*T2RbFVSWz~9Mq z1#B*=+EXE_oc#EV3VhaTFO+NtMNYjYco`z+XV zehx;%zq1F8|);7R|-BZN(}&S|=uM!BK9AS)+9`Pq8} zZp(WVn}Usph&Rzq3%;ssDJMaXn z@oM?Qw`rKPg@+kg>8pxfi-&28H822ZCIsCruJnS9()j#5#!&U|vKe5tJs5fCK}i>0 zpnSQWRQD?1&_xDWr11zKEjynei#Tpdcl&1G6k5WfUh5{C>*X991xIXrAy71au3G0r zqD$PP@`YXhXxl+KPp7@RTZEjkUnOKcOeAp!32As4Rbg_C1%dok)W$YR=ASQl#Bizb z@!yTH_0`9&Pi$I+EqKVK-0#;~j+z{w{20ng@wrD?bN#DD1q4!@d#M zua&DBW7w3FVE40cV&1zct@y*}8oXciFtsHG&!YKsiLF2po`X=<*ItuK!_t=mS(*}H z2h2u{`Az=$8x$o?FR%I{!Sm3_`1S2zc?VotnUbN81DlieCu$ADwR~A(7^AvuwLA6x zTEbZ)6rUi_ud}vy{!k7*LmkBd=eTO{QC-y;?_S-u<9iULG%3*@x{PL0k z8`<>TK#$091do`Q<9*UPIZMojQKJ*8SkuDVJ1zxswMk6{CE@2T*|L50b$Y*l%%t_p zuv3V8sDv}PpA**d#N_XMEvTzu!{w*2aS)Jb@fjNu@OL6s#-&NPqrtvyyxL@~vffPU z5QNQSADtl^_NDiA;Ysf&BqXpZiU&Q+*(4l%DC5pG1o?3MuC6W=0CW<9Vyg|$IL)!x ze=sU-2Y-L+jg4Yrb7=75+a{cJQx6O@G*bF3Z}xbUb!{BxJ!O?N#h=AGM4g9z8^eAZQ>744YG|hyBEu2H@t>&`+PR?r#42o0LkP%O)H3 z;sg}R{CXG_KQcXu47!foin%Z9dG;S~RN&E&EuwyP_^07)dp>)$*2=OJV7hxk8h5(% zoL`n%XbX}C!}fnA6Mm^-S*DLEOL9lPVl0T1db1>BKrA;%$dsQpURVsfEZqj$^4m>o zBjRnWZK*YQILsQd8$ZM+X2Il-LKKaaG5;{|45>AR{;Cwfzw-Ra0ES^hlrsD<`uld} znzgmLwG9#PQ{rMZ60yK*Ca1a`5GJKQKRfvePM}hAHFOknIeCBQ?u5z~`qN1oZwP zqv1ZI5Df^MT3(I^i48$kR@TYN^B%I?uGRqVH5bl>ZF)d9NXzS=Qe4`(Sw#KI?!|Ust2j(16^-iRU3fVD9SXk9RQF7-^9P34T zUrJn_zq49;LuNCbnQECWt3=S*evPfiP8_>I&KVDDqtH?QARJFEnk1?Y*+v>C8dE*K zs0fm*dU)BLa42b#Ig74x`GgEVJbXLP$JLHX3NUp3hwAu0-K}URY z`;Se#{<9?|Y_ktCM{a-n3FLl!R#H*|<(=^VvP_Se&(fQ>6311pD_Fno4!MQuSSTf4 z6D!AK#~7E%j*Fq%Fcj(JWOq*eB{ojj^Szt+o0jw;D%Wp>mXkHSpjBs+qsi%~hohj}P96<`b2IQ} zBubEe`=NpX;M`X#*A=yB3_agp^L?NAad>}PQw>&?N`>rm-yWNf^#fEfc56Dle2 zW;?yjBtT85QOkQlDNu4)LLmDBR1W=lx9GCHx2KOMaSU(W&W}Csle(h zM>IBm&;+28FTYoNGz`YmO7}e2sSqop*mrsL20PDm+zxS1mU2;iiA_6~hZ0m=LJ==` ziU=YIaH+*2PNLa~7pk|9_PSNbSA)?HU+L>7TOxMAnS-2>g8dIxIfdX^{af)_35Z?a zAmY)MlV%kwK*N`FSga??qP5JCgma<1P79}c`wJrc#|=4++X8pidy52)0yJLCn7Hqi z{Y2tP?DW?x)+O$!ce(%P`dEBBY%jH;%FZh&s2GV$vo8K_zn@71)Dx#u3BPsb>vbE+ z;ttiLOjV0ZWqI+#{Hp&d>%6WaVC1*44R~U^X(n%@$r)(|KGH~fj5DD9X!R|tHjCx7 zsG|B~7At)L+3-3u{^v9A7pXoX>Eb4!aE63hACwu3$mjSD6K z(tna|i_Qyw3@a}dykE!zb8SIwpCugfTR4d{(*j^eIq5qwFrg5~IG&iwt4(CKcumWJ zq;R7K4}uW7ka?j`zrW|!r8~EmqF=6W&^}$mLWBU)P6=rWeSQ6rqky9i12yRpkUyTJ zE4lKXD0J9eQ@9tGm-U9ggB}Xq{0>Y=G@jp8QqIglv$lx9qoAh>-<_WH zP0(r=&mu&sUlaa0K=70f-%+hxEA6>HCjWQ#WJw|gq6RP=_BZd>U%xm^KV>`T!Jb@C zFx=hCI{Qiwr9bcP?#`YbBbS%TRetqq7CVoVH1;f_Qo(kz@&0_uGu5&E4Nq_FsnY^!8AD6(M_e0Ck z+pgHLHl)6))ka+B#gI4AzigtipboOaLZ*QfZC55MEB6JoPeY9z9cx0sai94x&g1Go zP30rA+uYEBhu1)?gjhVD5fq&M`=br+jwLy9-ua!9#~Uv%ujLP}Fj(l8 zpG5jl(FQs?dJm}jM4ZUtf7HVSWn-qjcev)DLF+-mMyk?zr}OxDP2YJwGyEf;&XvD6 znRMVjRO+6pEzW1<^o65Ym^)vvQ$xI39qyWu!;$lzxZAPA%Fx(BNs>@EIm&JkXSo>~Wa=!Qs4@sOVJtKZ>eSXqj! zTG=uz$r&B@I8;Kz;)ZIf4W?RxlyMXYCs)`1?ut52P)JBf82}k#ku6V3bFC82%n_El zoRYRSi+A4MI~#oo`dsO{RKn#G01q!OfrTeIs6`t&i%JPOLMIn{tGoC zlTaxYLoz~}&;8`-Mq-zWgaR3td6+6MnwFRTT3dE2!y_OtH3zCieqW#7@q#vHx>MT# z%zB%!G6BDl5`_zS>0T=u!}eFCm#`*gW`yjW^$_#ULVm8Uu5*LK=ratNBrJ!A9~Bkaq?o9ZqN1uG6AqG1?>jAkAJ4azVplr}M3 z>bn=eB7X+kF7Yh8k5A4fgd_($8PI==apnvw5eylm6F*O0sROkQI_sYf(0EGzciu?j zrvMu&9jK(6=fURwb?amH%EEI*@bQ~|$<)u*3(k7~n^OmHC?j9+>{aPBI3>g*U`uzn zwr2G>E|0fXJ;c?uxO`ck@*l;9WBr$}s3bQqa|LARXTVXRmt5)UKCGySnVtQ8jgg+3 zF{z>d3OeH#uga$+B_%y;@wT!-^uV`fo{26l3CM1+Hg=Sys+fm+rinu0aV8l%aEb`|IbS%}U#WBzV+uoU4D=EjR_SiPZjZwUU5}MYKsS2EKGj;DF5piEf`7Gkzxt1-yw6wJQ!s2ESyG~k6EHl0Z zoY9+@o=%O3`A<+|-+c#WF5kn3t;)cFTLnodJ?Qm`IcIe!0FOArJZV0zcS!2cq0@Z_ncUAvBDgrI-=j<_hmbu?Q>XO@9fg=`O zx#5pkv;cLVqxsiZmTuA6@;x+k++mksasO+cnKBbHBqciT#_fEEgN3>YP{6b`cM8sP zjQDaFffE?EAWEL)!$FW&P$183`fFUh(KRRN@kUfA=+X~j69}($zd#_lZFai*U%FIS z`fVKH&nQ9ctkxo~)?!v3dDBdvEZWb8fKsB$Mxbfve&rC!)09Wp=CPOi@Rvj6H%sFs z{hNcOAg$O@uY;N*zsU|xns&-6G&i%Ki9?%%JmFBXnEw)8^G$d-7FoVwX#=8a{3>9^ zLBQDkD1&u^@;{&8%fOC^i1?xRO`&8@NDZh+H-E1R-bf6{J{wnB24~|1Xkf|_ISEiM zTm>M(!S*G*?DOwbwVotBy*wQi^`-@9S{f~AS@7~3WF!9@!(Wnn9Y(-rLg>~j^4=jd zx;+^KbX#QG*$zE^{Gxw!R1y6u7;M{F4(CypUvE zsS27d`{XUZ{S5n@j4KGb927;i-8hDNOl2sIGZq~8`wM2~2c-gfaNX3|^tNRN&%xza zg2(>4eVAb?tg)$ym*=C<0)zV_unNlLHLDWA@ly~yd4WrmS2v@l*0AA*H*})$ z9iN2ZRQBZhIeLDGZ5m1zLMY%Om5DRV)9WRboBPu_GirQtGC7AMYeWfbU&Rh=)WD7~ zr)8|epVHFVd3c{sDi)lVTkZ2s0y!ANI^)k(bop%gU6FYD{87gV+!iR`f|Jn`@h>1t{f1#FCK9 zG|8`-ncxeHi<2RfhJ%|PYhdS`59$DE`gAiiSYoD6sBQbbmqf88i9nDu0!*TXDT!Nt zPGB}5nDCG0H7MoB@`{3j$>=#0soKRvTT5#+Iz9>tx*z~&On%+WSBITmPun*^mfOcX z%MCC0$+$_)MMYC@b(>4-u(G|;&P+|SlLp470NKo*Eh z$SRr@rZ+cuW$zom9IVA}Y+toKJ$U!IFW4>aYp@jXXCSiUw%^peUUCqyyG8;#%ClR7 zv$#4JDzGqUXHGXo8f^gKulEB<%(7CsTSa<~qef#{TGh-=p^_kNMgf3tilq z-hNk{fS|FlF{kVZvRpG!|VweOSea6QDjeh}KQh|4U0k$l7baF0Do1UYR{0$90|3)7Ij;>B_ z0)ke!kA(aIm7QW4@)=^^R1vXh@w3p-tbXqDCtgXu#-@tS{!7$|m!WoFwQ})xol_Fj zVb(qk(8#W^4G}wV`|>ROtrumkIHiZ_oF+oHPvEp4|9+KvIv0(#O?lhndlV!{n4Hsr zqN1(}54pgKdmoKF^{|7$cVbdf$_Z^nMa5nm%*d|7(o(DvtbFZ`yKW+gqYvOrL(kygpjGZJ zYmP6&rbh$91_U`{8$7pg#i{B&Hr3YOHN~|ZHJ@oU)EazNc7(wSgtA7APR>_D@vKk( z4p3|bUC!GNgJW_yByHe8=3j8gC+X{t9~uoyZ!@3KZ$HFC=?!4#GUO4wa=Y?oT-QC# z3ViJO2M%RUPZu59-;dD!0X$0&s*2k$R9U19jj!P^(;G&9*qm%LFJEGVFhar6N2cg# z1qO{*ugIFfnc8Bo1;dT(O7f>qI8YDKi$Qj}20u3N_wV0vFp2jU6&2~de3{DQ`A@^~-*Cn!zBib? z@iei6YIPXseeS(6^p7T02fha&2v|Y3{uubr84?!CocDNPpnz`ZRJG z>o=KODpE=q501~${$iko`h#|1&^lt5Y*RHya)C@qN!bsU1M~Oy!LXH|uDRdp>R#qE za+qA+J@gik;YMzPLu6ji=*ihxds0P$_o3?lwVu*@XE%kty-HfT#MxrrgHsa|d2>a?k!gEZGtU8gCmm|J;rA?r z?u+#<=aRE>r0UxzOY6zJ_olh}g$qGkRG7IqZu5?RWb7P14d7rmFDMecQ&cjDNG5DX zv7C1}e8TrGPn7&;@VrMR?h3~-Ua7!Y;u)>~V0y&TTV(;t@Z}J#zW=M>Dx;$6qUalP zWCj>Qq$G!uL5D7pMnH0;q(KQsrTJh)x&)C%2|+rfrKLq$L}?HaB&1VP;=8W(-dgi# zX5M}4o^$ry=j#r|$m&#I3AU78yygK2b?*RD9X8 z+*psk^XTSTOC<(TKF;FVU{2&&A|rI%${&4jI3cUUsof4NLN-Z-URDPlk8h>%iU+12wOTSB8$V> z+1WXhlaqd7Hf4oOCRO&7My00vNl9<73hnOg?Ep%elIo?X%hG42TuHBZbOcjbQ-7ks z-BV0|5SBMI?X;Pjo}{$EG!95Ey#=X9i$2(L48II`0d4M>b2Gf~L5TFzCk`od{KN&0=R>~hQ&>i%|aVtk( zCJFTL^mOfg5T9$ttOS2{i@mSEUmu4fDjwAY`CI*9>nC-X4boq6^Maxjv2N`qO8^(6zghpltdl%xA zQz%Zf0rrEno`>d4L>1T0$Q$JVoo?g0Yz)}sifi?nAGcM%&#>Y(FTkvaNMd$r~*Iw4r2j1wH(_M07b+^6MSpf z`(XXv%+{fMoH{y~fq^8@q}1qhnxt+k`BPhpoF`qC_)2&w)d&ad->9LF=AAnUpjhTR ziuI+o90bqz>c(9DbE5oA_Yv`K*LOwl#y-mJ$>93Hk8SF~^)PMJ?xK`wLI7rd z=SBv1Yw|u)F6@Q1y3uwg*8lwU%L_ISm{zLTC14Ff@4h}E{JqSgv4q|C4~?w<`Lm9> zcZG!tuCBuLs^HL$7D~A7{IlaFFMYbGL$pmT*iuO~zJCv*-1k$u-o3kK)0ZfaEbY$g z1P*qK0D#j)Mn>i;rs&7oF|}KxKqv5DXON2h%ld+Cb)yC%hmi3)UniZJH^lnarwCys zIAhgqkLd~-O@Xf_{#-9vmPxe_a$Sjo?GXg(=e;QmlqSe3Yhl-I7ksguYWpiroD}kU zgI!eg?+l+Z*Ms%RK=9|L3dCj1-%d40CO=GQEht{q!uxsh3A^K(yUY^ zW_8gSQm#a7G2v3IRN!sbobdMJ28?@-^pPDVS@xKkLkz^%`8r&P< zqm{#vMK`95s^OJH1OgE{Gc!ZG_Nh^$X`fqQ(xO{@bli4G%Q%cF^Uc*yxY%|ivuRro z3rcjC+$|4&!lPh-eYADgM4V;9Eo+BKyr=_bY_)lOUatY8b^#A60 z=weuKAI)=(Vm_9{eU2~ zE10`BVAP@Xk}Ka_;(4ze_Gm?4ky*UNxdff9pqVWM(+hg*_aWmM)}Q-oePQp%Pf^p9#xXiynbrwNrzt zf2zT=Mn^{}AOdh8DB+K+-)i}b0k_jH(A^RVX#-#t^xvQW5JA$c(IYOVBTjXK;BPv% zTpG*;G>h7qR|&+4IPc~eBy3iytgyOzWMpV4NUx9KqC~uV=jARNYq?xhO3TOoB{J4* zcsM^drrnOqo2G4-ru%-be^dw{%$G_mo1fY{J8!+-cek1kXUaTE zX{o|DehwHjC4AF0{lEVDPdC_jlb1d2sx zsz-Cu!X8Bay>&rDaNpef z%nu(-vl2y|n|8?J(3b!&a>YII`q9O>m4Eur3Y*~AgaqXpExe|{fVwh!V!S2A%V*h% zYSiqv%#{S%ozr%|`2C&|<|Nh}S>gSS|m;V3=HRad8bRXE+U9z*YbLj8u z`$AM$`F*1Sq+F}=l9JE6$Iq0>fJH!7b8~uiT^%vhk41ulnlwFa-nrlF2=FdcuY&3ge2PMLnSu*pOxXEIXEEh&+olJC3lys#xH~TrOh6;yjoE}ypCA0vm zH86`9qhO;Fwf_v46c^uUS`PGA`)6?>2e^UnY+#@9>1Ldoa-a&;o$;lE9cy~@q@c*& z2O<#%B) z2~}>6ch-Q0WZu)$GtYGOdlpDRKg<|e32v~Zlg6O3&Eal+7wr#Bdg8F&9?p@5!1^xA z2llj8I0~LI5}i<(=Jo=+pSqv)RzWrAS#i&O^ z*CO8Qjmq=qd31zPS|Szd`YrUT%8xEeyTg3mRWz6Guv(daLwcq3U0S9WUR>{Rgo(>o ze@?Lv(BzWRP>B$VSb{@ASNsmq$NSlDzp>3;q(IAG`6^7!%=*ntP4h1W%|Zdg1-js~ zsmp-)_$LsQv-?l)$)zRZu7xUba-!fp?bcseDMH`liV|(cc{I3yGOWZhM!o-Uj#FKl zRJzW@V~O-iu`=Z=B*mkvflJ(=<$?x3zwfpwOT{YXq9AL;8R9|2e=*8gJ-`$$Nn!3? z?RR1Gc|gcuqt%|wz$w!fC7GE*B%RSqxxBKF4Zn?3^QUn7Xe}DB1zlkXz(7gH);G^> z8Aa`p0FI^s3aJTfX40p72t`!Mz`%f*yu6i%6srpHw3B2(UMvsn&l&>vYe(v>I$BDf zo_x@ZPE|k3o+$*yEsd6I7<()s3q#YZ>Pt9H7aAYvVGCc$=T*N&(UQ#`O>R}}TfLsG zb-`MWx&6U*{g~~+5~oZnV7O(h7reumQ1L`l_k}~^-%su8v`79d;~A}MRk)B+f%x;D z4qRnRojNfK%gEl?@#ZQrxOPV{u-y2WxGEQo35G#6$JLY0n%G(#KpVgdi)F>~=evjXnAt`@`w*Yb!{QSNqx?!- zcx`RAJvfU=)c(kYhG@Z=G6V%kc{&i!D3W$AxDrG4;{xxSU$97JAqK=~yp$(wE>^l+ zfsj$qGzOU#7kye*%DSAGe{=8-Fk7jmL7Y6hrTWV5`1tqR!C?|LqV3Nv;~E0mG)f;} z^ewwStjPw?sVx{5)4I#^KAGpTtLGkeH-oW0)C}kAURS4IU2T zZ7Cwpw(!4i+xM+je**B(PPACXNrU(@vhguO{+u?VLPohSwJJ`|leE6_PAvVso*fb? zfhK(xVh*Uumwn7VWN z5($6XImOO*@v-pF{vBc7*f z18cjUMpqv5VYU#Cw7BPd97DNNwK0Zf6EAF$A+c6@{_V!&6sOK4;^N{ykMMXWvI<6p z=9UY|)?@9>(z+Sb-QE5hcPdRVzE@S*0ve#QPKC$@{Xs8Kvc*!gR_EsX!53fSP|Q$f z(W(wKVH7^L>DQzId$zN4i}ie-J@$D>+cty2XMbeXIJ?^9NA}=4LX@Q{Qh6kQ0;za& zj9QAWS54W_fYUWYTQ$hdI z0N1DaOK96m^EKT+L(9I@ngGbV@o(Z+jjp$td6uZKs3;4pr8Q)g?o4!~JrvWwFluZi zWLocHGXu;NV`IsB{K=+fiQ7O7bi#w$9PjqY{}l-?&!P($x*Dc898Kvf+;p+z^x>$~ z_w(*yt-r^?#9YqvZd%Z{k4G!U+3`mPd1Jy3YVAGVNdM3`Ha?s!E^m>^4PG{+#8POz zz4e1)R#Yz$Sul5gUTcj#nga*Rna%a}$C-&#D{5|r zPz%Ysckia5F__6qSS>XvDQPbtAG3=yGZjo){d^V8%+fOY4osdr*|3uyp9%`z(PNr| z3~q~Zi+*46*afVFpfzLI!-o&0f6mO5Ah3(ruxO-jU>M2c{tkvZJ!2s|auFyGYTV0$+)?1=&>YAFG zf^fMV3WYTMe@dl$i7ivI6dX@0cbcz3#wbHW!@CeB0AG9t)mnzlA12MdgHF@u@r*}a b(HF!}vs_;Qy+uASFNHKzbyUiftb_gsWkfam literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/kicad.png b/resources/linux/mime/icons/hicolor/128x128/apps/kicad.png new file mode 100644 index 0000000000000000000000000000000000000000..57d15ecadc79c761f3a922a6745a9a5ed40be08f GIT binary patch literal 5144 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 G(EkCXk`JH& literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/pcbnew.png b/resources/linux/mime/icons/hicolor/128x128/apps/pcbnew.png new file mode 100644 index 0000000000000000000000000000000000000000..d95d0b2573c2277ad3c15d147f383280d239bf54 GIT binary patch literal 12292 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 literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-cvpcb-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-cvpcb-project.png new file mode 100644 index 0000000000000000000000000000000000000000..9bd762ece5cb11f168adf362ed743290378e8508 GIT binary patch literal 12149 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 Y-quNVBmz$kw8(>GKPpPriW>#}4{wsC)&Kwi literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-eeschema-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-eeschema-project.png new file mode 100644 index 0000000000000000000000000000000000000000..9d85ac91c80429312a11c0e2c57f473141362268 GIT binary patch literal 11777 zcmX9^1yEbh*A4Dc2(B&eTHKxDUff-advJG`;#QzI6f0h=I23nxcmLk+|7GSSlQ)y> zzTJDzJ?GqwR92KmLncH9fk0@oG7_r5XTX0KB0TWkCuBSVe89Sh$*Lm)FJDBn2;e)C zlZ>_t2t;}K-vy;ix&Hv%Byg4d>Z;~w;p$=RY!349@L;xfuyrvtb~0ynbhgYs6CebE z$U(9aqUxUCPjkG!gJ)asyJnA1p642RY8gJ!!}sSGO2HTuZK1bH#b!y2Dmb-Mx6a!| zWQ|b~s;?a<{a}j-3cv!ZOHe_7LaqOc%8JU*F?z$*F?;nkr7t+^qcMT%ygt8wU9RMj z!?*vh{7GCOvptB>~9Qe?hz=Ldu-Ry&+lQq}~T#WRphg8BwANeaApF)N~-~WLd84v8yC1 zIU%o;x*?Zbu>#CMBTSj_&V(Dst@4D%q(8+w zkJHVfV+lfoNIg9SOBraH3e=3aCs|ZeP)SAwg?+-DrW!T9E~pCuZ7;cq6jpaw}eqB7Y7jbT5=RH*N!R(Kf7I zh*>o9*JyCT`cgguFvV5E9JP_7`C2QMC=LU**42YMQ;FQx{v_t8vu@w%C}0lMpf(%d z&Te7geC;?8;4f;RSM1Kp#i8-FpdtgPo6nRaHX5J=wxi#penShIeT6%*T_2gN(zUj; zGus(b$za-R`7O3|?ZiasM+Z{@f3qi>%pz1{JAx~~!~~8dRp_ZTHq4}#;a|%RL@ftN zCVD-eHnU~Qx2!tY*x1lAuxPm)U0gpay&s>Q`oRinR}drDZAuH9yuUn7HGUL+V=EJQ zaT=(H5%p`0|A=E$_qDvbT1o}{MNW>6o<2%%X6m>BibuB;q;v0a`DgvTf1e+nx~ho9 z^=!`Lac|DUWZGTu7ED$4U98WeE4%XosT3WUg=|vm(^2K&!9o7--^9_gQIxXZyq+y? z?{PeDpEznD0~KiYvd4!Eu?y`Vt)JfDP0TK(pSF#4Uq@j!=EpKN4JZhJSUP`?NQWf1_f^> zqNVdr(4aI#NrudIJ9dy8dI6(&1Rh`c0uA5uFhI$p>Q_a=JjBEAC01>nTTN&e{+HlF z_qa5fBzh?8{vP{N4?=nC&2)^p+bRdjUrjW8WvI*^C3xqqdvBO-8qH~h$?fXAn{F7? zu4CrkRaM{^;z(X@?nkDX<{#^{%g^|lP(4Nog6;Rpy2ttXwXA<(U#E}J+I-qFKFN{_ z=9l>uB>qn86^l~hXL9iuOsV3N5kBUpAW(&hD!ho-GAyaAjQ;!gOLfcD7i45)_&@Lm z+#56c^STB#JFLo`O1IWBkvLOfBSflD)X7L)>q?T6a3v)r5d{g%pA+Z>ZNKG(5>DC+ z;}0hn#1zzPCnaok8BCW$_X;kVv9q}yso$L}itFo>q-tnH6yh%jbh1lu9nzhNW*xsm zKWS}T-EtqI!iJJ5q?uP#RF*qhiD32r>{ddt5XOgu%?2c1`N8S!;|G5@%7)9AgQL`W zD^RAjCq1AElSwD_Pyp!#q8sSEqX?}&Q@i2G{Ijn2KPS6qo%{AdIOpM$W`iKZq+75n zF_pOCF_-yxVQZ#=sS;*Ayy0YaE^G#dj-iR$GSSeNEmSL6+(+Mce%}=?VI8j0@dbnk zQn5DQORrgRwLJ+flrX!1jBBUD&c>FTpFV!@)=opKS;f0`jWs^DUFCs4d&Z1v%qp5| zObq41>_CX^K_;{z{WksJclIO@7!_`@&71=PogjWJTfqVz!(nSDS*pzc(%QhH&Kfo9 z)3)j_$_duV{Ck83%F174$LwY>v{Aj`3|jT(Be-Fx1ihhn`VxwYiaOS{{;zyoepBDw zVD1}FOB)+e!;82|$nuUtU+(x>DE>m7X|oGix4N2h%&zXgyo&I@HD%sLzlUN%%gLh0 zsklWHTKzz3(*S{@dIw^O=xAxX_pdjd`G&`aWh;?a9GVMsDT|C)Gg4>sj&!;L)(itI ztyw#-U$9a>GyfgNpz-W(KgW!!d~RPS^?FP2aQA(noJ=eyedPif{rdVaz}?w1RU}n$F*Bk6+Kxv1{0f)j_7>If z(Z~V(!2Ll2Z4fyoR{z%vwHAx8>7-W6Z--`I;vId&3J1*PRG8OFxO)2kr)tUmwGs9_NiK6qz$*SL9E1swV`;H@ZV&$x@T`wx#ls+z#%Q26X+ zk4ad`y=xSUT$Y@6Pe41jg;NFmjW?&*)kyWmJGo!XTGl$IxnL|WmQV(18dMqR%8+|A z{qpV|=p04MOjr6%nZnD<>*3Ykj*AFxGqeCUSTv}IoVp>hM?4F_V@oZ$=)0M} z8ms%AkCNi-ob?dmDE!N>4*Q>akEB9|@de=;<&DKD$2Hk;{U*bTD`bwJMcxBQlpKYg zF#Za{Ex0)2v)j==K0X5fJ!-)b8XBsyAzq-oU==45n>39z5JNI@XTOZU?(~Gy)p9WY z4&|V6JWGN!%7xkJ_nh{#2jSr8tgyt*^2s3Y%gfP+>&)t7kg$RJJ$IGAEzaEOX5xHr z5cF>c4E>y(hyqMFI5?RkusI0FHx(G{5$^H3`;+z8pST0_d6%y~$xN*#`&p%HX+5aCiHteW{%T4Xqsu<$WSjoeGWM}&<4EeTH z`45m_Lv3M1(kRmy4Pu7PdUu^Sop^p!R4XF;7vsc``0(-a^0w%WqjG;a*|cZ~{Xik5 zs-v34=9njA$s$Zz+z~&xZG7~v+-|WxwC|UL!^>K%r&Pvbttkh#5+AmYCHf#={0CHc zVBnp-U1RoalWy)zdDITD`%jRXN>LD-ecCG0nCawu{ByV+6@&S6_&31 zmV2eUAQ++i=TAx&77W=m(<_PGf67@O5@HV?QwsIMBhDFao8q(^Eb5H=-~o&ogu~P8 zzS!(bG|$v;cb{5Wi5asfDk}@uG-R+}6GUz1bdFk)u#~3RP2C0K;F+>#nsj4?it)7f zB`qA^`$I{I_k_eowLN*m@>FjeTOg98#2MF@yrqCq@R)a|q{+kiXle? zk<1lK0((2X9w&6i~+I{N$ZPKrp z1A!DaII+8NpJ2Kk>s)ys=Bh0K;u%!YM30xZ+Wl$8SX&msP#8%?94GqcVYGp~u$>JU!+=^w% z)z!?v!|dmHkh-_$*f0O8^Lus&OZM1txg3_Ht7>TUU+sQrs3y08v zJq5O?^{nlleHCjTLj-!KeS0 z+`)mGh6W*iuy1ELnSjrg7TA5W{qY=BBCeS0asGHJxwM4z^uf;8=O4p#g}v%Qj3b<6 z3s1vjT0$HgZ*O7HqVgo~?XUaHS50n8N;n;DD%kO@&aW%pc zUlBb%t|%2xs-UY&6pl_}a-QgJrfo4%*MVU0WH(hZyi~G|5)olkGcO0QCMqf_SeU>I zDtpp;(0YES64lxEOuL_SLW*+I%J9fN9znV7?a}PZ!=Uhh8M}(1p^}dezsufeL0emv z&v^%4^RfbPgNcc$vANmhZqbs%eu;B$EPF6l(5Ln*qkI-;q+~R%in6kZzrRpxYb&rx zmvJYheQrP7cSVkOat7VulULUhK3H*OXY#qxw|ktIc6R1wWf7Xq;>h1kE(Wb*ph8_l zm*5q*6f&&|cU!9cH@=p>8%}|8#M6`a(a{kw3{Q^YmoHyDUoHm$xlmkP9oH9*v3=?J zun~;0(BZ@V-|C%rJ~KfJ*)>DlmY-|`@6_#OMnIQR zWKm*%euBZ-SqYSMbPt)uqM8+fJ1jgxV~l3FR@6P;>xGK`rStg;k*Sp_#-9C}URaO; z9uB~&qaz!Dt2(vn{WRzx1Qy22ib8pLN21B z@&QQKvonW*7=pe|zvut3!M&ZNd3h`hjR=5V2#nCsKXlW?ZdV~Uyy;P+kp6M)3G8#nT?FjjReHEcDbjkRYkZ65liEO{CrKRewE>gg-;b&)aj& zz)ui890pV~PxM*?eSYh$0;bZLvuQGESgh5l0w@kJmoK)q4FBcYY=y>yuh5DEx5vu2 z!RHWRa6$A@bP>!Xg5t1k` zpN?x9T=o@}lBi24#jl}&$9;Xe1%ttpGc)2^T3Q)U_3CgiaBo^{Ey>ltDP`rexnk$$ zG^3)TtZZxwi;H2)W@LeQH8(fs$r-nrz<`BTsdKFRTDxcmEF$@d;E(X-)6(|#Y-QR% zYD$9r#j?o@tuAE+?nGG_0GTww4Fjh9UW3rZ((UnbY)Guz9M@~y=OmvSW2D##*_iOZ z<5=^%*FSOR?+ZgKs;|ca(!;=j6j;^t5uDFY-rnAYg6no%(mtL2CXohO7&tG>j$JwY zXDt+!mXnUKSik5y-7fn>f<%G9sxJ(=RgZevOg^z#y`y=EDwGg}?r#ieVY+cFP=pcc3h@SIz?NC zD8F%EBC%<%c9NAWUKt0(dP4d$7wyo`v9ii7%OcP%OfZW!XxeeHd}u09cqhl`=7n4HWsJRc@pcz^9&_1)ztzwt3Pq3KY^(_Jv4 z&b|MF{~Xo`z$D+N!!kgNumNZW6x?roIbd8|fK1?+-aPtt7E9#~n?;2MYFxCpw*#at z@BL+85Au3DoMn*_Glr}Q*3s2{+=^zw=d{8EN@PD)4df=GfKekb*LL{oUkcvUC`3Ua zSr^hlPx75T%FSafvbhS)_8VaMT!8u<+G7XFAm8x}eLqyr5`KOHa!RxEGvS_s`Hi(C zggx32EXmw{!fr8^Rnnf5Ih24$9`z|2m#No`T@nVr$<1xxH;AMnUwHrWkgDjGuT*~X zO1~Y^%`KD(B5Kqhg|h-w$P9VvtgNgb)z#GasGp)f1;e9#!bJQd@zmI+d-|voZFu?$ z@G24V?jI^eo{zifY?-L1r>SYEXz<#qOsFcd;aUkuCvTwj1K%z?874~x2AKS&rc~RS z4qQnUD=Vw~U%$APRDdW0zvlftQ0u{f4`wr2KXu=}ex#+RH<>DwoT_QO!8^>x#PTf!?yeXfCpH4bxqA_0C<4Z1ZK6gqyvCdq4)an zEj8NplHD)-BBiDWeP+BMa#kQ<0k)awOZ9Zfy(w|V&#l~e#9wgAufb9Nk`YHk_R z(`;}a^AKP?ao5`~b0MRkSOZAY*a%J^k+im^1CWMIDsTNd5Y^JPPQ8?*-2~pB+|DKYwXk6`_F2 ze*?ezv~gW_Pdjd-v=XTL6GB>Ek|`BpnAX=iVxzt(8I55;*cc~;a9Wzy2a)zB@+M9e z8`!ePR4ypg>YR9pZ*Fe>e0{w%eLgCHGx+=j=#uThc+0bu*03eJl=>u4=1+gr@0Qcg zdFMWOhP1zDSE|Qj8{O;`b^RBAwXQUKnt**lK8wHnPj5j)+zvaX*$!fuu!KZJfL$S~ z0tV;>P(W#;u(r>vk7t8w!n`#4CIEeR{rIDz!lWk%4uIv9p$xhDq8hUyGzrX$XWx^x z4kDmd$XZ*k7eUI;2%tn#Oe{GN{^W)TfE*w+E<%|-S-<{_)piPQDdu?0mZ`;)3jW2> z(hFQaAKY|OHZ+U}U}@J`1z?S@HH3ag^Rrt_{wiQoM*mb}0CpWaAF0v9Dd>ilxNK@H zO)>DDVW?DgO41Dqi;98)h^X#-vibYTA%S$7;VBm0n~O}ZAG@+ssf^p5e+k-$fIIgN zKmEIN&gQR@LZFB=%^S!8Ob%e5sDzvmTU#bT@hkuFBfq7EXJ9p&Y+0*E3rr<-N;t%1mY7*PRHkQFl^1^-NuPci)InoD4aDElp<@Q*iT z8PB-s-Xn{0u3>f^DTV#xp%Ub8g+}Gj*jVh5A5iK6xg8rOI)3Lzm;~agOaEgeg!2vrWJKXm zbLTA+sn<^8&u*=K3_>bX7igT&o?rxk&QE@&_+#XFUU^_R`-HLgW=kz7sx{h_Rrqm5 z@K7>L%b&tj(EGe>TAIx7kCOjYat3P+Z4{t){wp=C`0|9?TET)xfD$sj=_K5(uh$FI$gLhJVqVo$}!>$`VI@ky35c=Vs4-_|;O zT8}C!F(nfLifvAGL5zhUKM6Y3B5gUK{4OIn|!AOJ(ltWjmI(wq^ze1sz~9m(399 z_m;p33yoH?2XKv6MnPr|p2r;h`%>uCOu9#4&Dba7qd!EDlaH_$7-2^DCw*fUf#CbE zcJ|3$Ww%(IK%7^tySnY_&3=kc_$BT7T=XDh3Pb$GGUgqc%Se{B`hIg|Gx23>Ro=I^ zwIxtS+E%xQJ)gC+AQ!HYt{8TZ#k2&5a7TDL_9J&bY@z&ThW4lHi5ZKEPX9)_HKTi4 z4IIz7-ug~|)_!5GSe^N!9lgV=@e!BBV3Y;Gs$#hoVsvR&B(0YPf8Pu(l;1WtaKWOA zg173qfT(Z$(D88dU>RW<0;L8;A6bBz=XXZ{7*7C^j&pOrWA6qFoXUmy$S^pzASY4^e!Xx<56^A;NdIg-EmWjEfc4nG`o%U z>Tq^6E+uH=`qA?$k{2Mj6_u6cH8o1@A#&jU;*CnVmPV<(<G8f09 zj%HzRVY51w9Y7-knYy#HQ@ld*2eJWr#$~45aaQr$3{{uUNYL134_wBZ>hig-rtDBM znR)VBaqWO}RKn>L`QVTjJ8nt}NE|vky2!|z65ZaC&kv2J&yc5?ybM-jF`bK8!a$fB zXin}FN+jDqRkBh!dH#QCvjHC{V8+~Wur4U8VyZ(#7U$EgHwo}`ZhKW; z+_%Am4Xm$s{`oukJNJy6~@KvKA{=z)Rs&iu(8HiRSoVdq+0c3qFT;2fXuY z)QjOZWHZTitZE?74SX*p8FA$r?`h0~oWz#g2gBE!{T%P*5BFd_Z7(ld)1b7N+JD5>ey%r2b-xknP z)MJXwpP{M!uE{@ZkoJP-KjL- zrYxhv24a$9S%jn{btk|ogG1BOaJfEnb9i1D4dl8Law)J5>8SQjlD!0^S>{I4(aiqi zczLqLHyMlM`pQDm;JBHQD0M_GiU~+u-!_ZSRqZ-}*R4i~n~<$0VIaD2s-B{}gW1>R z91+y3qbRt|)w%L3M8pVY-mfg!oIwG$-(adnzZY)v!=Gd&p;ixErIssvqa_GpcYP~{Cf7)ZGH+^)-?49hbUD8(*8A9Ttue9bcn+kT zu^c(ORhVyvMIIuW-aq8{Bb{IelbpkMr+>jN6c(Dy`LZ=!yG4FrKN&mCY1L!h&v&1H zT{LZ%Cjpz_BRvV?`Jbe5afHBBNxR_!3CwGiLr8js9nO*=fJi;@N4bdrg5YNbx=e)k{0sDiC{p?@xom=eH%CF6F~ z3g4TgOur6hCsKas5`M}df7}cwdz*>EVQ_~g9owW}gp=^L4}@h0BXecS|EE)75YPeZ zT=LtuZ!s!K%5&roezBy4iOksJLgA)^e7vp?faY?&^@EW`xM$GbeAG1KRGJi!k0 zDniXWpLr_EB%JaCl0G=sW`&Hx7T*wcjC;F z5q_Yzyaf=bUWevznwW@BNwgnFR6V6r5on2DLY*y~89J1T& zfDiD>xsbQVeFLAa+@XD;ZjGUb4;ww$51;M8;7I)YG=H{fj zw#xmhCBgkaBofTwv=b94kt8*tFgOBbF+UMWrzR(x#@~6KN`DOQA+PCrdtw0W(iI!; z9uF!1j+-Nh#tY4Xe`>wvq%}>bnVMk~Uj<4yW$|Y+&%AbA|KN8XN9Jup>Y1fWUPDs- z!4jHxnfup&_fxBN(AsqghCKA;yLt4nLzMr{s*7BS(P~6n4)@5M#zT;RE)R5|2q%~r zF{CG%@`LOdoIxrqbwg&!r=b}8SjDd0l*b`FjPMGf-F%mK!|A0l%9nL7$Ba{~pbRK|C4KU??@Hg2wJZ%Lo zG3Oe%_q(~W53n3jpDssf-g_X1^{l2O9Ubq2l(MDyp7J&$!sBSDp}vj3e0y?NmlMn* zqmDT6{hn6AsWB2s#WIcz6L+89C4aXWT0ztcz>1iF>?BI3B@w^C zHnU(q7&)=ZfF<9a=DP+MQ32K+F=F$bBqrtY(#u7A@&#e3?U%$1nCkb{UPH&bnZ}4P zkK+Ow!LcSdG_G$X(F{A3ipH|>#hiC&O;C5?aaSrxRQNIUHimbbnHUYv#HeObBnrBDP<^CS3@Ok3xgvWd8Tiur$^k*y2GR5wrS)K z=^vEmJq(wvd4DV-bMDUcYL;i)DQq`IYkd@F%dVw`X>|l<7l8+xeGd(wgv18o%^mt&XwU@j;o6>5>)b z#e5{K<8o3QqoW@bfT0}&_g{wqXn0AHy?7qId-Cn9A?VCXsQGuWcC_Wi z3&vVpX~)}A9ull~-_M*Jhw9GF_?0(7g#8$9(pYMJ&a;dM@PyKGKV09tAoawiX@@N$U~b*c?+>m@iYA#E_-(U#QO&g6WSMiKbp>4v z8Etv`aVl_xRMKu9)V5f0e=YyTVj)35!(;F>mXKWMUgL|D40 zp!M^3W*1T!EZD%y*!KY{3PzMy{Oxu{vhAZP^UXoLIb z-@X@{>1^hs7Jr0@QbD|H28Xv-r`F{AQ2Kd{1yW0~IS9693E*4!-FLJ*9s z)IP=wya1x`hz99NzodAdjl+Xem3-?t{10Xlt*WkW^rGGP5Xhir9_ai{2=Y6w3Am}V zkoww()EM?41fv=?FY|84a2%YR6t}ezO53l~cv;9TvTCy88*P{B3veDkulSv-WiZXb z)>W%kT(8}H)fI_BeXLM5a#B-UG9W{R2M%TN0P%cMHdn3R9?iNi4DbGQa#TXm$JK3~ zt{*mkOvS>YwC*viNbcO;{&3R3l%n;o%*~X8J(yZQE%gExU6LA%tosfJ_XytEFEbAS z0NuX*{q1?5n8ztU_|Gu$Ws!HA>9f=@E6h;6yn}-S529;OG5$01XUt zSjD}_ukf6^9V4!dJ_v2*CKitW_Fd81+FBJJ;JMS;fK78hNa|w;XIu*qon+po4I;vY z;V{Kff^2x};k~l2^}DhHfonLjpRt40rD@cV<=>Yf(lxs}C@ohh?HSI~@u=lNe>i7h z5-8S!SN@alYsLZaLfkzWCs%zC)!*J|!1bm_JlUDgh9pOO9D|DxJ3>Og<>`laH6?{lDJA{vRzZ z_}Xcv_(p?qB!(+)^ZH62?(U>T&4aeDKEibaxy0r7Eei_+Tc93t6kJA7eq|+tvGI_W z=;u&{QGs{fH2dsm!-iNpR?4ZVQ%RNl(n3&!1+`*g$9FlU+`!eO^x6D0`j1r6etWMLu4DY`38JthxFstZS>x` zHtAU{O8hbQhqb7T*N~ zogtR(iS@1Ds1>Q9FziM7+Js{h7f9OkkD10n^W)k1rTd{l-`;pF1dTydRI zFcm9E&`2`}PO?HjS%$xMeTgDRtnfx1%i;I%(b7s^n$ei533F}w^d2&KKP*Ge2=`M2 zy+d9ZaYuUEE~G5ZZwyp7txAbnpU1j!R6yvxw92c9=$99xjzj)oP6evhh#`cm8$RPP z3rd6oS)K*v$N6Z@V{H6Uj#Gz$96gK`uTJF4y<{~6&7ZOSTy6w*Nqn{7I0XE7Noj?S zy}9N)8*9OT-DoRv~83p|xbN1Z~ literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-gerbview-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-gerbview-project.png new file mode 100644 index 0000000000000000000000000000000000000000..54b66cdf8ed394ab2a7cb21a5214c58a582a0c86 GIT binary patch literal 21775 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 b(HF!}vs_;Qy+uASFNHKzbyUiftb_gsWkfam literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-kicad-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-kicad-project.png new file mode 100644 index 0000000000000000000000000000000000000000..57d15ecadc79c761f3a922a6745a9a5ed40be08f GIT binary patch literal 5144 zcmV+z6zA)SP)Gak{1f(k2_dB%9=dBvbcA%cMDMj=rGD)D)tBGA8H)Ad+BIS{@FpP47Ayv&5L`U=Qu%1_0Nn=g z#u{-6uo7@uCtJp_9yo9OOxY02MQ^MT>*>;(sixHtqGI%{aQQU{fp4r9!;q{*;zUB$ z@ogkq-&ieYzD;AT=g8;C%CJ}+@ayD;yVACXgBZMs0|fuwHN zt<--|VbkcPc zpK=EXyOHvkCgHU$E5#ta3Ya)sK1PVCKwUf)Xa!n>!04_YkU!{R%Ems3V)Gg)lVM2S z*s?;LrkCb~IdXFl#)l*yND0YwK^1ut&;Wg13v@ZRe=-#lp23z=VWeEjC2ZWhLi|v# z5losb{}oiImasLMgFrR3#sG2ScDqrOJ}9Qx+xWeNAH^i7iD=CN zqSEt(^d>hq7rQ;VdqrQow4UuXSBLpcUABqB(y=k8)zs8Pb8~Zqzv3#T;`FE3zilop z`(KHci~=m)biX(rxMI>Asiu9Ie2HuWap9&KK18r6W?^GQs8nor4A6_bygUjD3RJ@s zyNB|zzaxLp45R(-Yv#SEk$?tJ?`QiWLI{eA zituNd4g`9`vDkPzVTY${?)dF zvl<`{TrL+SB_*0OgrXC!#6NNgC=Mg#I6^}0U+)tm_41r@g?v^9nT%wgnSDU~YqouS z1`rLq-A+kKiRKK!TlIa)$NvH0C@@ktl@d0uzfXKuFV89S<(o>7@e+2KRR0!Aa?;dnh<=meD@@YzdoQzDHE)5R0(xD$=y5UKjop` z*IF_L=h&WD8OaJPNe+Tr4D? zAo=06`SMy-Sxvm}q|kO~FQ32ue3;+Cov%m4`)tc|9RA?#Fu!BIbpeiCovnEjvDs|+ z{eJ4|>S%3kjR?>6pF!2>kF&S-G6HR1MoZ2MJonNPG5^eka=BKdDObtez(pIEio(_q zgWU=rOj1?Z8AKHmtb z7!yF(456r~NL40#PAQeukCIzD+34VKfrMvXTp~<4q$BP@V0H`;8y=5`l9H|iLVA~( zu(>H8Q$xX^pBSAcehj^U zC<(PM+$~N?M5egS!ll%%>|{5BLDPOp#klZs!ACDVz`kvN4f9I}EoHacDJ?DK;K74* zbaX_77Yx1xySs{gf4>?TjCwN7Kw!%YcZnZO|GE52Jn~pF21qsq#}1|P^s~cUZjuLk zv0$^=C@n43+(Yn`Po=W@Arx1!k-Er}u=e@8#GH6!iF-HDia}a`LI^=gNeN!BSA9XL zU^oL#e*&lHI3rJ_4N1-Oi^W~fFE(bX+(X>EfmzIYo>jawGLyD2X(SDhgUVW(`wZTL=@XLOo421)Io?-WtbCDhdbx_M;#f75X|97k7D)ylT$ z_{%9h^$w$^X2(c)?YTR}G`&1^H9)-6fMj<8iF6g|<`hMttgK9PhLBfvHkISnV6zt( z(FFNO);{-JF*4C$SE34<%CDD=MFSdR}Kk}{pq(t)mkNcT6h}c z^ZD@ke5zqi&#?@c@F+QjBaA$;$0E5;EuHv{LQ|+F)n(3n^{?iE7IzoWvlXR-t^J{ zJ!K_B-G3z#i4Y2kUEQ+U?6FtIX#h{f5Kf+XYnb28HP?kT0K3~85yxyc%o#^#(_v&N zh?JqPRA!ehmZ1=$>p#_s)Vv;GIHW0r(0R4L%puPS89Zhv~Ha8Gx zX(G^igkW1U!S)t{?X845+X;5I6YA_B6zD_-1ISRwP%VWz+IaJpi}+&GQxRd>tA_F2 zhv^z19U4M9$dD+ZKquSpzMQuD0})-G7Fi9@*N~E34_wdt58v9$TSE&FeP5_I`s)CC z^_i_}RfM7dqU)~^2n13Hgb*ND!umF)W64Sc+IRYcI9bq~|gc!;mfzFTu0 zAgR;9bSBi%%BL^=-qJi2r@F;XLI}aoOK-tdSi<`c-xd+Pul7Y+5AUa9#_cFEehWd1 zjI;+x*Kvng_fz?e^Ktpqjd~k*Z=>$@XEbL5(Ohr(qz1@jnm&Dxw{HG1?R6?=1_;5B z3m4%WqkGiq=D>ji5#a{bmsCk>4Y05F1wMLqS(x9zvwlX^H_q3+&XHft#3dW7 z6phXECJ^fNZs*=D&svg~GtbAUYagQU#4#K>a>UfM(^>R zB$0U(d~GD-ezTU`iXl(>|3cJQ-^IOWyf#Z^>pX#*Yet0xF{ zwj+Zk}CttqUavAcE8)11KYG{{xvh4vhu)0yS?*EjxZaR;Eiq&sf z_1TI-45^c0@hlDk_0i_0u(vA_OPSxRHU=XZGo*{;MQs3=mgR|7KUsLiWG; zQi7@IBW!kt&bf<6q%kl52n9ztQ%^^Z|_;%TXtq)%Y_UO-QnWylasAMd2#gfZ6Tk;a(47huV5V9DsI zJmTriqPv50}=hg`S_kVXi9gT+=a^6)5=h5G>WH+$#n-?Z57{F)i{-D|b06uzh z37t)cIq{NTckR3PBeJ`J-F)`agZ%Tpd4vL;sTL(;NnkfHMPq^9;48VJ14Me>c!+%9v|V~ z@UQJ`zv)~$4)3!x|5RhPb#&Yxj7Sys+Y> z0mx90j{5z$dgaZZM5-0~ok4)nkBkAlTN-kx$;$jniJIJ}uq1 z`DsFd$j`><;T?1mzNwdLhR0JU31|R)v+ZP(?6FACZ{sH0=9Y)19Y!y2Be`ZZKmyou zJa`A4q#MUsRGK7dOw<60+5_|#@&*rQ%xy2mb?t-|+hQ+2q0rnBwB8~pi}l!4_B0I9 zVeWp1=_`T#T`O2kax8+fb=YsnczXc7{H&e@qzY9HK(eQSDdgSd^9eN9AN{TNp5Xgy z7GZORe?oV_^vft3HqjIrnm*b=Q?K1fHEb?7WfRYd8ZXe&$ong!A8l8D{akc={ z&j(1x({Xs`RyrHiKbz7+!~1VV`1!}1x20S6_gDD%>7`NQIr2*kHGoiOI|sL|iyF`W z{ROIhz3e)cI%YCb17wLL=`V()(-^=g@vdE{jR9gw$4FeW(=$ruy%_{JJo$t=+he}Y z=5U%PLtYVX184U*^VGlmX*T9A1SR-hfYpro?Rv}e2tz2iimL!{be}#}Iduk=Q)if# zh22xY(D^H(##1~+$d*GYAxX9e=ubHFiy60cv!!_`b|)ocbiectKz)6^s(;d_f!V!) z)Sy}J$({ygNiR~M0n*1FpueH60V3@Ij9|J3Xl-pJ5QxpxDT{I1+tp`3=4yc6DXV#F zJn4J(?1@8+EQw<3ZeUh}_aCx@z&=EFFQ7kT@-#4NQXMUOQ5>GwbIf81o$YmoCU3nx zv^2o3XeoWM<6Z+|Wl1w4>QzevY_MLIEJ>yj*xuVyO9Si#J_80=D^r#vvi_PH8FA3E z>I?MO)JQ4eClWe)#uizZ(QdDVD-8ACg8&Y~K*S1nNY2#rNG z@_t|j(6AUOzrYx$FZ)imjMmBD4pDPN0Ujr8b#iogu#VS4pmH^8Z)kxiq8AA== ztK2?U3;<3AhLGV0P4+Q5fOmnn7uCqfZ_sNHa|6iIxA=e31+%lZY=4;m0000TOH-22_{e)oM)fAyRQp9UWQ03u~21x?Hu_5TLN!5q;t7DJd5#6w({!5aAGxUNe#IV4)T6r=#qLN3*e6u`&O$_w*_; z+G#f0Iq+fh`g^ZYzN6Esr}J-5m-TeFXiG2s%-VYDVm;L?=*pThKk1KTTZ?&_H+I?D z&YvbAV8G1;X#2Z@x$zYo4#NQmo~^VK$_rq{1N}*wa|iP7e6%6qFmMN62lg{So4H{+ zA0Gu}0x{Y3Y&z&-eKBgKMQt)ZtPT_g--rdDsF~*a;EkTt>NPumB9eoZg`*R+Mx{@N zmme<8a$5ludpO&0O{eI0P*Mbm0IFk1puJ(gY~Sb3C3a0ZX)xZ}D8~0C$^s7SLc%mx z&F+u&;%;Z2KP-R%X5h#%$gBjs0>Eq?9UWzkwkwzYrE{(oScnZ0%e<$)67mjpZM(CQ z6Q0!#r~BigGyrCm?|JP98M`EtcWtz&f6F;{Gm$L}YJt*}A-o@et<;TpHqsBiLIY@ev=qw2hjf0){Fi8WTA`7Z=-8lK&`* z4K1?+sf(gA2ZdONgo^OO`pTl^6WP>RK<7`_?TEmXp`1u1yrLg2{R64rAu3#0J)9eP zEsQ^j8z`|+NKznH6aQ@NDGUOWk538}Kkb_-Q(@=921gk^34TPk&V&!vAL-Xr;VKe3 zUd?Ms9h?P5Wgn!O!+$G4)78j}&;mv6XPejfb`(R`**_80o+VFP?{AhL+6vm+WydBb zC)uF#6r4Y|!O-MRAzls+obg}3q4f(oI}Qyy?-i|}&&xWr^srRce;>r!|I11nPh|s| zeg54GgvbBG(of3%_NK+J>>)QM%gye(Nj~!1+)T4EzlewkN;B%UkacgcI>Ow5G8}Wj znX1*nUUZOTVNm;$8-`uTc45HvPWAiM&~rR+h5yBY5tLcryFkw7Rn1z6J_D+&NYk)I zr%8ta5T_$Pl+I0g>t&k+!a}WzBf^2#*D>Lv+569YW|)9HHya||as%79e9Qp)OKv1{ zzo_kZCkO6lSkgEEUEk`i*@L9L!?%%Y!}q%Hdo1A$483#JZ#1+_ob0hsApHnzjNu8j zDgxhR+uz5YJ1Z360Uh1GaG=6+J;2qtELjZ+$k&h}&#S+RqSo)C$WWU({v$7-SCT-f zaihCRx*0ELVID#Z%!Lgd0wCsJ69o2t4G@TXpVHC1nO^DT;{kxcR@Tcs1*+b@t_G*w z3}Y5~ayc97NgR1D?Bv%-jQ|M~xk{Rj@`=j;89p~lOR(nrPy4BvNAew2QAvYUS@oiu z)zqcOR+Vu1d>BgtbjN=9##QE;<7!&ZxRiLb~gJhpI+R&;>G&&IZEetcunE&AT`B;iu0@L|)YyZABP^JQ678tslXb5VV`XOsh3U24 zQhXNg0-~#E+%x8ZaLx!G0#v#TxWatgvElctVdnvHPfFe_?9?F(rOB`_Ish6G&kI4; zY1C6}0|XRMDL<+F{>Si?3|w**$8fjL;qu#J6p!q$p&iHv>wwEnJkPA1Ctv6=A1J)e>-_amO-;i|j-|qRz8qz+*E~_!>#BmHz$l z9Pi2a&j`~Wkrti_6AA|EW>916c0(cNvUEJB?^4j0@_9u;2BTh2slyR=56|R!UgGKN z+QktLAvJk@5ULzM3Bx%hqhTET=lw^JPJUijO(#G!klP6eG&u|I{g`kLQWYSo*onYa z;=opojy#$WILA^aM87@Btgy-c<9T(JMG!OZL{AMtBx3&PW6 zN(g5a6_OpX$kzxEk5azF@L$LRoKR-h=0KRD!uwELgL6;ZLUQ8B$NL_F9{yVD+WpX9 zR2ebu>1KAlSkop*?sNzzqS+4H_wmI$Vv08g48r!5VS_^5NgtZiG-nUnJ&E$(I10{p z5GMb^xpp{Z1^vfa2*v4JIk_4)Zl@7yYgqk5s1FTAxq1%&+ANO-vmpfo2QRS5Eukqx zw<_Y1MH|>5cEfMD6FT%+EDWYau?(s~zo_(S+H^JrEGt<+S78i1_)Y_Jh9Yx_J7>L3X^#G;dMhd9iX^{543Yw-m- zIud#q+Bj^YpQ3S^`WS=hOiWA~PDZ4TF!O&i*zVq=zWiDUB*yGI^ULxk?w&e7UoJZ5 zzmJ7d2Z|3zR`A_zxjq-u=bn~b#uHrFLMBLJl^t-DwI+LQ2orQgysTo|yu^Zu6P}{O z5;-|r@51=RWhpvN=DI&V=jA9Onz_Bbol$Jj^U;#uF5gqhs(&xuGcRv@vd|G1{P^)R z{uNYkOamZ}=xm9}bPZZw5WiWd@2ucWSq>zjdqri-LX3+>_J5w7=^A;Y|Lb+U0kn?c z)6ZL5ktj9UJM!GXbAynxWWLAY@CekJ`2K8z8|(bSLMP^}CfRCwB=Y9{xw64BId3

}NxaU@UC3_fr3mQ#hK3!@sTV+jfez54E^akFmZ-y^U zjnf&c1s%Id>y*X!n9~1lMRKN`MLg~Q6{%qzJNX{hUWiPHL^G4=h zXa=qfCeV_o>rfyO z4z|V%#QjcfNB-O0-6ix4XNUWUk8|R0EnYnFo7~R!H=QoC(5I|vTk3seOHZOsN}H$d zq&^_h>`77OxM;H2diUz~NG*5v736330)G4Go4jTBHJtv$_rAa9KkA#AMfUbm<22P> zowyFY%tQ#1)_gg+YSFLuD-7XZxjK_xF5pbzxYp*WbUY_*lFA9^QM7gTF2nX~3Px$*MZq;(=E(pu|` zM0=LPcsSZTzow@HMi9^NkP#6cn4aego}!9>1UF0}Qtyv=_z@n_Hcryt@kG7D-ki#+IPk~0Vlj{ zYIsEF@_%&%FH(7@Com=!bbspsOWb&Y2G#?AEC&cbS+40}(~ICW@&U15frh!weN1gL zl0Ckamy;a%8XIXUa=l}MzE@y@(+I_-j>h6oKMZL1ZdY%1RFyutKlhdPU42J|cr!xB z#|Ot}C5wUEs(A%C=lSn^|905@NNpv>$a#E%O}VSEO$KW*y?PU=)O%6kI42~1cRJ~N z*oj*!5@9j6VNUMy82O^^znN>dgbQOse-mP2Fw(jz4wK_g#9f$9Irp0mN=>KTU0DH ztsjpcaW7r3p7#P#_(0Ci&;JZ2^FZIpH(YIGdK|y7Zlf}cCF!BBl`osZO{L;eL_G#8bW2p0-I6&xNF<3{vC zwgdqE*vT3tX@c!;e?#?h9B#2fg)NtWEr?cc|;a)E*W;o#s9ZcC5SY|*`n zWPa!f!4eO=7UAIFAlxR#%@wBZc9RzWSZKj*nN&%4FsRK^)ZK&%pkbo?m|s^%DQq(g zlW}dxPOwfIlQ^MTnQto2!05HfI&6&^=ku< zb49GrwZoIO4C+onWD#;Q5}x&pHT~vqz@Hr&+cEg=rMq2b z&STN=uk9vYr4WHYpduNA#NP;g)|Se%-Pqkgx9{a8M?twg@n=_E%@2MzVmF5JEK+Rk z+DU?y3U=j5Oc!oAAmfI~0%lF{i-XyerC_zrWFD)_xQ7d#ZbuueOq-F8u;p8V!+?!5 zj!()hhZ79OZ9>km3iIwa(c_`RV-;e&p-9cN?DA;K3E(wHXce)+XBcE0 z7FBE2NBrA+!6^a*K}`6GAx}uv;AM?>=L@9x?+(YFl2A`rji-h1lYM|6yR;lBVxln4 z^3{kB;r2MMqy}u&TY{+1Oy<-Hax3#RgEJ~+v}FDOF%G66MGcsHSB@|V8AQJc9OH05I2*gC|%|Y}3`IFn9ZoGoWFt*Jc zzqsWQ7n$(&l4G*TTo`Fw~-8P2Pc_poIljzC){||1<@64P@)&@?eReopo7(*?g z+DPiOdG~IpD%yG|1@q>;<#w|dZ+_jBY<9!R=O-t_{tw6g=X-*9_){|mnwhCGXmD@VPKwu5xNm zC9i&paFSrP8_tN?*7!NqZ4ZIK-?0I0#g{K*LdxtmRAJzlvXbgYCfV6IlK4>@U(<0U zQ&tg@q>8ly)#A@g8o$UVv5A-LmR`A_%ao~cc1DNNC5B~k)6zOHv7yr~tB!DJPjWx! zm*JgL^IeI!f7|2}&YH%z94)4C`O#;)x~wY~^EmIOr>FDs+oq8-^Apk87mn3L_-EJB z`Yo>cA!WsbaiW>d-S2;9$bXv*dG?XA3k$LD?(X8P4PI=D6Fb+_zI^#I#dF?q#mj{Wp>qTv zEq=7ZE$p406+JwJBO)SP?{D7%9rLvgFZFP&b>Q8WH7rpq^HL+TIPXR>CqpByC7))f za{2LlvRXVMH`LM!`?~Qp>-4@Y2om6jQ-bAZivDdFBbKa@x;7<(vHU~;6O5`IZ?Pp#AK z$A|?q%(fHp8F^=H6#Ao%FYx&luvF`q^_8fvB{QygI1Ax441x-OZnvJxh7PTF9V|AF zr`L@GXBrBg82j?y3J;H;T2LFN~Iso2_OQYPzKML?Lv z8d%6aFp+)WGyLRX&)I1mP zpciRUla&cCNnh~CEeqmL;x^%J%Gr4A0oy^0n$0T#!^;E^ulBw^!p@ltU?6>vO2L_x7B72_r!N zVzabyIjB3tTPZd9E>xcjha>x&Eh=j!U%RJ^0IK>Kr>Oh&tMj*&QhtUbrkXwN3@=_2 zmb}Rg)ScA7wqEMbcx7$Ca8O!af45p+PRr`aPE9vNeFwB)@qc^jpJEj$1N4#I9*DJr zZ8?thybl$P2#`sV>IIaH)YHelV5T4RWP+*e!4un`qzHMR_zIgTrBNI6r`8cV4kWU> zX27jhUEx9PW_b0lf~AEvdtfWQL=K1J32ICO(;#NP>WtN+2BKoG&Ms{6({YRbCxRjT zakRlt&U94$tg+nXI99~ z!CZdnZ6(lmH29ps%QGmK>SC$YB)z^KWs|nEg&`x(i++*cTMI%F(L$PnCy>xQj1~-C z>-!|+Rm(dCnF~V6M}~SA)sV=Y!I~DG2}#a*%qHT8V>EDG7%EF6=UBN-Ai0?dk+Iei z6}lrdw2~1PtDYM{q9Tm+%!E+%O7$mtEP!kq&D2OS4qQdIJ9?Q#ljP5O)KXxBK@haY zsqtP9X3jA7aa|`f2lGkc^ggwKC+?@0A3g%1>S<;|sH|+djoiLh zM1{-G-#4)QWM2vxIM!sr9!l28IKtahm%#ww0+r0{n7K;89vn_;VW=&QN(Zr8{g^(%b zZ7;q$ouRF7FWBL>QBDz*mL`*ZTWoc^DbNxil&>aN*YxTK^c zCc(jo_F)%G{v>g-_YGWVh+IeEi>Q-=vNGbcv$K@-ubN!O4Xz+8EG*bF!A;@XxvoDa zSFpG~LH~0{XlUNA%?b9J;GmI;Y=rx?1N#pe5 zqM*K>s){#k^R2HmLrqOh8Zk)6FvXe_3Sk-&q9ew=4U)~s$biKt5{0n6`%K=+>D}F3 zHP~1B=mVswxfui`vSokhTibuD^fMmy`S^JrBAAZj2V+qMpslCJnxK@|WSCxRYirv< z>(T8mXmgtu4>` z*KNdBeQ~T(pYUJH2{C|7{BlxIP>@UR{{r9w1OYeBUbA>j`yEtK5g;Nex_Y&l+xPWl z=Ggpv6yyD2r0-s3UsNOZ=W))arly*=9`eut8AeR@t*)b^lhkamxR2iVeoFrAize6p z$3MCvx_0X7hz_=~BX?azIddfO8K&8@<4Q0aX+4`7S8?fKjlVsTMd01xB?0UE& z*K)m6;(I*sSi{ULcdbj*zcZ96@7p(AG#YIrz!+(Y3R#sM9 z2M5!RAWTO6`*$>kv}9%1>>n8QH`>Qj4&J)g$T3@-+GlVB(xbb(+!`v6&oaHWgs9Au z9~WZrL*&N3N;}xwk4;bG#Pw-uroS`iPAi|tZ2hL9mXI1&LJ5=SdL}|MHR#^Fm?j(C zR+p_R!TF@(%kSNgTT@Lbmo05V*&hh&+?dJD@1fmQyb2l$7-vf1Gr~!!J@?U5p(Won zx1$Fe;cf5+Bq<=)y_4NVz3fhMXu6Xm1=`Bv`QD(2*+3(_>@oi z!tSncx*)!)?CXx6#!(}n!iC{8H4SfDQAL#eh1#^A7QWxfBMeNQ3fghw3toQB^e$=u zqT}M$y0AT#-xY6g>6{Zq^{vgr1FLxNHHUZ$Ybh-c)5ncph7{>bqcW(V5Oouim?ZvF z<)2zcQiUq7%S}90xj1#+tB4Zr4-UOKQ={TjcY6-&bJK4NXbe&G!uif@T*b>u`O$89 zmD`L`9Fdub6J{Xw zd;!xl2)N_wWD_GPu76Q$ywGHZf-xWEC{`xIGr4w@S6Mk8pQ6Nu5Es`UGmgBkAKgSp zQwx8MsW29O9xr)>>8+sAjd@;BMC8lMo58B+`}-h_Fa8xMMP|ta(i9Z*Wg4b$0NZISi{!_TA8G&j*M+!wo1m^U7%Y(hFZw47Tc3Mj8bFcwsi zNKbJ({i@e^p`A<^A&5;mly?Y<6DIDu0z7e?MHLqhf18bJ)x)S29UVwHQWb7Yr{B(E zlzfIsTDoRtX0Hsc_SkYqBR4iS>?{DVwRkAUE(jyy#jA3xo?Kzl=y)Zz8d5Yy>Jaof zFy_^-V7lO0=J5Jyv)Aac>3Vg%p5^cZf~1f^9=VQWNoY0KwYFCJjN4-t75w@CS^)aB zwP~r~h0fixSG24ZoFI&FmZf$fxNi9_FP?JutWY@d|7*W99GV_D#YEXgXX z!|B3X+uNPz``AJ4dXX3yR#jCcF9rSD{T1(YMh&ccxa~aWB?wX3ytqj&|B&|A8iPf# zur77`i~Z4K^>_d#uMhmjI2+v#T~H%U$F^tJwx>OTm^LOS#OzpFlme#F7=ioKV>Qdy zuOXpKObl^UPA^NEV+wPXU(^dhp60`3?k}x?euLi=nEx?7tvlpDM;&IfX#;zffV;vH z{vPNafQ7e!kO)146O~r8LY?6A*5kr;|Hvxd zYQ%v;{_=VOODrkRb6DnxRX!9TKqa$%$S#Fz5B0?Fx$4RV#=d`T^Ov|YlaFAOXbwyQSM#H`h!unTtAoYHl?}@b3=|yI<_5&-Y(2G0H@#-5>rOuz$*Xv+5xN6l!yXZ@CoHSMh$V)Zf#|9lgkojU^8F=f8}u zb6RjrHKqzxA->?&(PiJ&=FFA3`upf50Fe{_Jva9j6qTR)lb}{3g~(F3@f3yzSrZoD zZ-Neg{`^T)qE~*;jz&z!9`14yBu&f~zDR9bTv`eO4>~h)aW)4+o0h+;*aBK0fY8}I z3{$V$+1V*ho$2;{j zfi(IWdD`c*ctA+P(R>yVJ%V>OMxw!~@?QL&OwMI{Tc=sFj;fkC`G|#l!^nlsn2k4G zfvYeab{BJs^WIQHc%vjS&;%&WcWKQ?h`E(#(oMWVGH?{$q>(c#m?Y@$lw*)QGMrp$d`cjO zi4}T1t>)TFz=<=r<`67#g)({b G(EkCXk`JH& literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbnew-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbnew-project.png new file mode 100644 index 0000000000000000000000000000000000000000..d95d0b2573c2277ad3c15d147f383280d239bf54 GIT binary patch literal 12292 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 diff --git a/resources/linux/mime/icons/hicolor/22x22/apps/kicad.png b/resources/linux/mime/icons/hicolor/22x22/apps/kicad.png new file mode 100644 index 0000000000000000000000000000000000000000..57daaa6c31437bc79a8633ee8b042f5a36ab77c9 GIT binary patch literal 1083 zcmV-B1jPG^P)CU98JS9w=@eC zHrnWgKID3FU~IHCR-1j;KA2Q9f)+$8LY#gG78Ft&Slh6o7V4B15riRagxK=PHraMO z9?#2l-+efGJjZjQy5Qn@xbN%!U;q1mUDs_~>ftB`ZvZy`ZV{~+bQm0}-K+l?J!R~} zpeIe-fHBm~f_-<5?J3>!A8<9dubv`&XZ!|=r!-tMOwI!pDQ1~> z@1SrUaVpLPg`*<8EP{xj&3G0Olu}9lRqIBg3-b)@UYE2Ekei!Jenl(!wI5@+?t0V| zMk5C2B2gX^VP{;5bimHcY@Rsl!Ns4s9cHTXxNSN;qY)$EJhvjDbCszRo-@d`Od#fogt%+eNa3&T{L>3u^ z8E$W+;e%o9+(H^o4kdxANQlARC0uAJ!F0JmLvhoyxYt%9*EAxr$xG^r*n|n8Th|%y z>%d#}aLP?^xY*wR7g{S&3au2T%VK(ZI)U+`6tkDkF#h?81Xf`}x2|*lp_8e=iDsDK zFyn<3Ss{hz!N;h4a)11)7uzj)8=s3>|DHk0%H+L>RHpW z$tWn{_tRahf8nio^>fOqsCu`zrt!_mor7))h~l;bQyH(+afv-f^7KJ$;ByAi5|;Fcg@{@Ll;5-<4z_ovuP22h&68 ziwCd}m_ZalXH+;gH#^ZeH#_0|$LRlOH?`)?zX9eLuR!C$>?{BP002ovPDHLkV1j=J B|6%|D literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/22x22/mimetypes/application-x-kicad-project.png b/resources/linux/mime/icons/hicolor/22x22/mimetypes/application-x-kicad-project.png index 7c9d4f031a9ef1eb2b64eb8af8aa6e03c57ac179..57daaa6c31437bc79a8633ee8b042f5a36ab77c9 100644 GIT binary patch delta 1062 zcmV+>1ljw`3%dxAB!2{RLP=Bz2nYy#2xN!=000SaNLh0L01egv01egwkZ*aM0000P zbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$TxJg7oR5*>5lx;|zWf;eQ z&vVXsp5xnDd7L_PX%5{SO~Wy_Gz%3r+USKoftB`ZvZy`ZV{~+bQm0}-K+l?J!R~}peIe-fHBm~f_-<5?J3>!A8<9dubv`&XZ!|= zr!-tMOwI!pD}N)MqOeVOF`j!{$jQk`fg0<2o~<3H0Z>YjS-1<|PY3hneP4fr<|R|H zO&Ro~X-+(6ihzhlYmH@DDL(t#Sp47ZcH>=L%$hBIiJ~_mycUSCOKG}A!IGPz zmVsA#aCQc*6v3Om_?pAvz?-+4g6dAZRqvp19dRno1b>C2BD^euh@j1Q77>(EN&Z#q zMxqPz4D4Q)v<{G)n@fI0EBUn_W4P{m)D%V|2InGC9ui?^T#9tS&dh9{IP1finVqty zl)~fjP*B>;n!0`*8S5$uGbRf6jwAZQB4dqGDn$x3u`i^RN^4D4Ru%=t4^UM9DVDd+ zi)L`(gn#jj8C4KX!$v7ZbYYI~4>uAG2GZ^?xoz7N7L`#{+ks(B4E-&*h{&6$vrex?#DywW93t{j6$k!?5xZMS*ikigdts-aV*DnC}j3P3dok z%d#}aLP?^xY*wR7g{S&3au2T%VK(Z zI)U+`6tkDkF#h?81Xf`}x2|*lp_8e=iDsDKFyn<3Ss{hz!N;h4a)11)7uz(l0N*X&%peF9?*KI2V}5Dk{hC_p_n%EP-H%P46!E zxXKOowdT#g0p=L5K;yyeEC2ui07*qoM6N<$f)_mip8x;= delta 1465 zcmV;q1xEV22+IqQB!2;OQb$4nuFf3k00006VoOIv0RI600RN!9r;`8x010qNS#tmY z4c7nw4c7reD4Tcy000McNliru*##65I2iRW`%C};1xrapK~y-)eU)8o99I>`fA`MJ z&d%(5*5373>?Amiy>^_WE^$aiN{uKFm0AIcM1zV*M4?jqQh!vSNJT;@2p&)+BoIh6 z6;)Ly53SpRh}6mxnsWPO6@#bMHO+-6P%q z|6IX_`28<_bEdA@SC2gPttSCao}7J1xL1TJGN;2704RtqH&R}F#aVv$=xi~>!fKVy zw)meO`SI}wH-ED9jf;Hh-$TAv{laTMKYmQc5BHRd$^E`|m)7J@irxNY048KUP_}#G zyH%D!XO`!Sy%gPAnA<|XedAy>#j0Cys>%A&ymjv+vdIM#1(byi`1mS6PzuB_yb=l@ zE9^8VrKAwTzr~S!ObkZmIOzRV>-Wy|>(mEwavP&!h<_=15g0Y4v_M+6^5@W38SL;d zTbz4Nop_-#{Nm`t>m5J$y<-XlQV7bG6hzi;^y_RPfvCR7mH80ki3Ap_^mR5Wr>p&f z#K-d8ehR48iKm|@J&+YeC#`mmu+}hgpmh@FrfLy7j_n%K&ygD1D zD;C4)dVpg1v#hQad*&Wwd=I^)_ql$3JMS-K80?IoZ3~P7c$H%G7FgU$9(&}mwzhEX z!)VB%W$$+ofi^xVoaNUUotP)!&-2sge8?QLyzZXFVVH@ z0rcEENz@sDQu&HW)_&4&k_iAZ>{U`e&NcAJHWzp&f2E^%$g_w zIJelnmh}?WinzW(d{+spP~rS+A8r2I1ayrfM-D9liz6dfqr=1h++k2_U4J~KlzjH7 zA9%|Z9nZQKCH`+pN6T|4{$Dal4PAPCn14^Ov|J!vC=j(xcHj5#s^=~{rm|_8Ld9I4mPAqQ45pIW;r{4neq7~`|kVNA9X1R>QWv3=2s%s zbY{vZB*wmR>~p_7dUPnU$K)hLBw)pS3VFLClFzD3#dl zp6!YCS5P5aoH+59W_S7z4MpWS>)?%+o_95ts{+D+5)5|c8SI3bD>(TfU`hP=q7bc@_Ll_ zf?rm?@I_q-n#dtg_%f)vyh$-5c5?ONMP{-!26t>LHTOUG=;5#5KeB070Drvn%oFv` zL>4p;$*R+Ud9w+9pA{h zW6)n%X<x!gQ2N_hnlipSCO!?Alw}8YHF^D7s^QR$Vmq#?Vj6n=cJTiIP_BN diff --git a/resources/linux/mime/icons/hicolor/24x24/apps/kicad.png b/resources/linux/mime/icons/hicolor/24x24/apps/kicad.png new file mode 100644 index 0000000000000000000000000000000000000000..667a6ce2c7dad8b0c3d40abf9ac4feab211e2c44 GIT binary patch literal 1160 zcmV;31b6$1P)AzGEV1UX*J< zp2OO1G0@I!)03aIU`fM$vz|M`)XmdaQ-9qBUafjde|524e1>3y(^~OE8`_;+#-iuG z!RPZi0{(tG(}k+|283Z&FXDe-!} zj(Yt}NBrC8^O2jkh=t`R$Si6QQO%zIR`I#?a8O36my&5IA*O~@pp<ME0F$T(J-~>L>x)m)gem>ZkWH(I{zu%9) z~m z4~rcDu2cqTqKVrb{WFr8nTc6YjHozD@bXS^``=S7Ump_Qcz{IONp0r#aJH@pgh=x% zD~whOt?f>6Ww=@R(8EOKZtmW?Ok`@b$(2}SrvT4)jBu{Dke9nh<0*^Dx%EpBLV#f) z3={VPKUMqB*oP$pshFR{AQd2qHY^2grzx|rn2J58xjAuX1`}K=VbD@j)LU1` z6Wcl|soxG}Mw-9o_#ci3lH-#&4w_S+kDmIJg0<@%XJj_Du{260wrM0x-^S9k(-hZ# zfa%Rjs*kcF%ys1|DHCci!$1jxHJ!gDE&RT|h(Jd(%ii2iZdp~_KKOPyzQtv;2AKEa zW;-j144k^i$Z+%c`<;xm)KS$oj4)jQcngbJ*?c0-#K`a@uoC=v{qOi~n$$(90oEM5 zin793cP3CWieZ>D9{Y+IdUrL-3RBtk3qLkhqO35k`B~&_>AKG#v5QgzC@YMPT7X?6 zt#!OrwbraU^c^m*{nD;(9ggn{1OmixU5O7)av{SEynesvgPT?WP~8W2l5KnhZ~ea>lN8Gj=TvZ&tZE_{p69@N@{N2)VT4oyfh=-r&{lq6de6Ao)jV an*Rs+_W3B7LfuyY0000A< zbW7c0m5?A6HgdC0)55a(!-2^m#mPC(x$nCl9?tQ+JTR~A+MegS?)!V)-|OYR#ZU*E z(HuaqfCm!^oL8D{m5ut>zGEV1UX*JugVS2^LmS$iUB;s4zQO17IRgHEbSKLNkViB8dnLBEc&Rw{y0 zfmAUD%4XmMKGM1sEiHaN*qCHDO%uQ0kH6#%@}E8eBC9-NF)R%tTJwxj3~0r9BNjz32#!wQJ)U&1t zpmf7_5P!z3Da_8!Ca>^G3aff>=RDq zHY^2grzx|rn2J58xjAuX1`}K=VbD@j)LU1`6Wcl|soxG}Mw-9o_#ci3lH-#&4w_S+ zkAI%}l!CSE9cN@VwXrlxCAMiKOy9=Rw9^#Ve}L)DN~({tBFuH=Dk&3cFvCCzgEgJM zB`y5EzKB3aGt1uGPi|RN+&=hrIljeZvj&*=;$}N5i42^&$jET>`1_rVwA4}6HjFS` z0C)?FS=oFd&cw*@B(M_vdHwJBZJN|Yseb|19J`9L!dQ1EP%?^Pm@^*xiWqu#HOdN8 z+4c)RHdUgmFs}Jo&mggjQUfR}jE-7>T_de^yj8WGJY#W$$|c)HDhCAsDv(>3{Qu0#;$cUb}bEW zR=GL&$(;W1bO@;kxwPV)$i2|s;MMM;2Zw(k`A2A){|EW@`6!q|-B$nr002ovPDHLk FV1muaE?EEo delta 1594 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 delta 1380 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 UXx%`rUH||907*qoM6N<$g6d8T*#H0l literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/48x48/apps/cvpcb.png b/resources/linux/mime/icons/hicolor/48x48/apps/cvpcb.png new file mode 100644 index 0000000000000000000000000000000000000000..56bba832ee7d1672d8569116bf3d094e3bafee7c GIT binary patch literal 2435 zcmV-}34Hd6P)?k%K~z|U?U{LS)zuZpKj+@xdoK%lP)E)i*#{;akOfQU>5{s^=K8-Xn(5-$KICflvRpMgcH+SIE6s(M8l-~cZGjX*hIFJfB) zd;lB*Zs=8h)d3 zcoZmA)j2)oV|tl<#`mWdPZ&EO=fMIS^GD)71{bzHb%EKnz>dzt3}gY%06RrwRtllE zCjIn?lt(~B#P4VDk8Upc)xf^SZ`w4bYQ7w{a8u>c@5+NoUk11vh>FO(WY>wE+>8L$ z0GoRV_bD)3L_(?>xhw>L!dHJh>7kM_#lxuHA8#~1f|>5~M~@N!z6Zns1xkT7;ErU! zqAq;p^$_b!(pL!h>I6i@4k=6@a8*gsT^WAI7ezHD!r=V&cU83!nAXX50dTvj&PaaG z=G+Q(8dwbM0{(fCkXHkzRrNqmxurS>LQl;Zb60WcpuTN;tK(wCsF((B-_+QJ?MEag zZfCf_a$uyYE(N?E;!OlTP9?EaODH@rKRC@VVqJ|3g1AU?H@2SvRzz-A)eS%uFkV%6 zbY+|zq4Pw*JmAwVbkN&u%0rz?9^jfu!Y;3W|mtE!*% z5aPmppoqjq*Vu&)E@2-C+;CO?sH||v@9z2B6~Txg4kr8L-zR5Ke9H;=ENHzJC38V#l{jC)d5hF&83G}i4oS|tkockn!K_5JM z&!|%9uVuY-T3nv)J5XnzyfsZTz_U`HI^9KLv=kwoG5XeE9}|g$v8q7;9&VNPSCl z(|{E#Rx|-T^2oy1e189I^}J0@O^tW1T)FZjz{2wK-PSs#@pyd8bH88G?YXGO5l~f6 zL~2jh9IX$H8(N@ePJ8OMfO?>cVlaU$Q1AzH%#h-uG1-&uz;#_JE4NMtHUNwmIbzo6 z(PIJ4e&erynMh*7Qc_wnd&0zPIaXUc`Q3l4AAMFnW5&$VK3Q2DuBn;Wb!I6NNTTjY zUEAj2eY3x1(*xqgj;ZIy!Hq*aj>ckWdlchKH%_c2Tv%90JRT=ABeTOnR#p~8MTGzu zb8hVjW@Ry;xEMDcKTn>YpGS6fh{lG7izb%RrCvm2%IKW0uU<5DeZP_YGh$VhXrB@x zf4rECASNRdNy~tiqpVuCf}gD3hcy=KSd1|^*1=kvXpT+j?73z-emB_8w$@^d#c?bm zhP=G+$-26_yVt$7wxYKq5PtZU!PDF8Be>P4RE#kux(_dshUbg}YY+udKlyzFWM^mN zb9^*4H{n=^>`*pMO-%%YL9F8tZEYnzBLmlUvBqE>2gf=XYj7MVN#YzC$4L+=$S(+= zIB}wkWSONzKtu}OTy)(p#t-ZJP=0#U4MCL%&C){Z~m2ZffF7OZ25#bX$2@%wzVMWa|JwE_?^ghJWmhI5%a_x#O2 zl~w4_8$ZA1dVO!(eV#gH0ly3jhtmfIi*hnE2lm4ePmJf`v~I&a<&oj{&{#8#?UERJ zYZG(8u{1R{VI7BfEQYl7r6_KBxkKga_9RYy5pRPOsEJ-#1wlAGDq`4*V z9X#KZ+DP-PuZ_r|U|^W$xFsWCX>IT6$e%jW)6-E;@y@*=^^QPS7(4&tgTS-Ev({R5 z9ESnnbPBT5s5=wGn$-WoJ)yR?mQW~!8;`Sd=T2tadP|3{SFaL1;b^3t?T5R&=Uh74 z+uPZ(V<*-7s;Ssmah~q761bAM;Ayf0ms~;@;Bi&O^OT#XfAgl2l9DnMwrt%}v3u9< z_dW5LRyJ+%z=4A>#<1?KwJ$d{x6}}i^NlGtE*>^)I1LRAeE8v}m!r|P8qZUvPoKFs zGc%J@r%thX^CyoR<8t`OVO-B!+by%p(Y9^h{#ezXy}I-BUAlb5ipS2%UwLJfR_)oV zReSd8{Q2|C&dOJI7oa3 zCw!&gbb4WONto+QGPxTrbAcY0PNtX8fywk}FY)yz&E?WbhBl?doiYc>g(IaY1%@=7 zjfs6GNPs2V*jQlMGKyqBw&kyue#mtSCFKlDRL=ghM_f* zl)o*XNV&adbH1i&6MOgW{qcbV2TlOPk|gP-F=ZSjJA}jGDd0SzlHuJn z-LF5o$gfWHU^Xd+-YbZNXgRPOSrasObz!Tn&KpLVXfwb|63DX5R~IkN0XBp+Fyv&1 zm$6`k`xVQG|Y zXvv*-v3Bi)QwF8oID1TF4>+R%m@c}mb70p`SoM_#;v$9+g2==KU%P#=$yw}d6ha)$ z2^(G4>1c1~{`>DoQWPX$`SJ$p>K05vCHt0@mSxPkIgZF`n+`a;ZP%_{giIk~HZzaZ zRCDFy3oNLpVNPKo6{Y2->f)-^Ii8ngna0M(jErJ7o0(fVcXnCF0LTb>IztW~Jjn3y zFzeT^r=g*N|GC(WTX(Z1W``gQT_4hh#)dCv);39qxTHY69$Ay)c}>^({mGN8Yi>rF2H426 zk|f~|4HJ#4*sX=L@^X5c-|uJZ)~#&ax|NcW5{8F|(KHRS*^J-s=c4N(XU`4!;*wZ( z1p~dRsx&nhzLq$c!F_+8bnLH<_w$U|> zRt`}fn)(LrTpC3SUmc)eZ}MZxKG(%;|D z2OoUEiWMtZv0_C}`gL-%jiRt=)25t|nGLukNsI(S^j;g~`G5UyLk~+rrQyeak32rg zrtWUqJsw(HTUoYj8T04Q$L)5bX&MfPgK#)ZUtb?fmMr1rmtSV*&Yc<8%AH|JtS_XN z5VB#X0SAbwg19QUIrh0ByrRa?LP7M239fm)JpTCO+s#nb7Dct2S?D z(V|6IEEaq|AETqASglrUHX9y~hiEj)*|TS{*=&Z6$bnZ(Cbn$Zl5yjFp4W7NEKM!* zB=j1IXP19+Dkv>w^P`VaP*8x`Y)030hK7bPo6R_#P6h@B@caF=wY5=GQ)37Y1_M?i zQCH;ib~?~6I$q?}-~V;0%{$t~K+$vPRX;bZWhQmIckjmI@!)hisjRG|udgpPKz%+R zLqkLC+_{r=>(-H69Ge9^AT*65M~)y=HKW!xg1Hs-6PUPxBuTvA^BT{;*GP4F6?-3T z&FMQShYug-;K73|T)2=$ix%PadI4}a90Y?w`uqD?y?Qk-yzl~=re$o|k~3gUio%wy zTTx^=Z()_LQ?(fTK754E!Qb%B`tR`AiWgC2Q_eX6moHyt$BrE|G&E3MU5(4-A{vci zx7!gy(BI!rMMVW|ZEYE_vw97j6h)R(Ue5+dmte%tRsVS$1?BA7@L$x=`-_bBjM_XH z!sRP&+-?^SJn#TzWo4L5CL)mtH*Va(VzJTXHF3s`;cv0H7@iBblHL1?M6`)ii?XG92~^w^RZ{o9+oa$iVz}akmf6` z4a3H4GJzy9F*HQ`GtU6Y8oz4J>U2V0sQ%@H7s9NPyeN>R95fiTHo*J zXg|&H^*Clr8DnE(T)TFSjT<-e>w72@x!N|${7bA_ID!Wn@08n@M+ftKI{4!tM9uDMVUi&?P|(zyB&CopSAhv z@4rrMZ7r>>t;FMTG)+U-bu>*wB===WA|8+9a=9+N_10VMK#1fA2E$2e-MV#{Os2#u z;481>t>1rl;}-tlAVQ0y$mX0UFn@kEdgyZ;&Ls#DM5sYzVl*|aXT*P)OD+@dzWXl0 zV31fmj!4G0s;Vf8iGl(Ps;bi4+uPmJ(((e}0mcCVIjh8p&1$vgJTjw(%;vI-|t5VL0MTD zPG>Q(Sd5;Yo(nB4EzbjPU>Jx_e%3LpZBCpxL3496<{8!!%L}VnShk#)I>8S=dV<=L zJ9+hyb5k)IIETqH50M0qgm2(>cO#qT(fo~HG7*vSdcCY!vj&^ZhNfwZkB<`w1Q;J5 zr@OnmbNlw~-vcfu!6Uk^rzU`$RH-Nmt5>haG|NR$tGST98;=9v@ptjvqfLZkw*dI( zAH2j>k4`)u;OgZ+aPc!Yi|fBf{mSnnD@DX&apJ0q&1ORgK_C!dY;24`Ai#wS7dp3Z z-~LZYk6#BS(!mYk0g8%>X0<3}H5Fo33b2|B*|Xt&HZFMv*Nsk|{*On|G{HN+{Df~k zwx6H=^yie%+s3MSXPd)Y2OVfcvp0GJ}>J*KQ zjWdSLUp2ghs>QLGY}`Gsk-O(L;ySmAp^kxUQra5EnAkFq_Vx8F7|?woOVTbAPJo! zM={>M0qpitR8>XOL@I|TydEPGjZjxtOCS&+5D0MO$dN<)_U(HcxC;2D12=>RC@n2D zQco+_0xmx3dyg^8B~)jG0qfUsL`x|wOc*#h@k|;JkE;~f?Sw)hMn}iA*49_|A3AjC z1HcQ6kjN@|!42Vs5Pbac$B98}GPKQ5daj@1{hohg!WKia>b%!;3ZbbiEM1Y)H(`Lr zoH=v2dGluU*=L{q&iV7_&jI};J`%_D=?eLzNJ#_c0r$jWv0n=z@cDeqojW&G$>p2= zr+b&3tidM>tn?x)8jTVThw=G*{+5=Oza1SN{ftC>2LW|9;4tNR9nd6MM$$B@s;XvO zmP?o9{}&9~WLZvyilQXG5DFm}7#QejZf@R5V%GElVbcCrFk5&Xhynh8`O%Ml5Rb>r zx~|WT`b&r`%i7@J;EmT`fBh`b2l$yqd>hGRMezUm$B9X2g_2wRi+DR-q(9PMR&+rB dGTu&?{{tBXeaPm1K9K+b002ovPDHLkV1j}=XGZ`4 literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/48x48/apps/gerbview.png b/resources/linux/mime/icons/hicolor/48x48/apps/gerbview.png new file mode 100644 index 0000000000000000000000000000000000000000..87c9ce38628f3f14e27db646342cae5df2d71dee GIT binary patch literal 5333 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 literal 3896 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 UXx%`rUH||907*qoM6N<$g6d8T*#H0l literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-cvpcb-project.png b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-cvpcb-project.png new file mode 100644 index 0000000000000000000000000000000000000000..56bba832ee7d1672d8569116bf3d094e3bafee7c GIT binary patch literal 2435 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 literal 0 HcmV?d00001 diff --git a/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-gerbview-project.png b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-gerbview-project.png new file mode 100644 index 0000000000000000000000000000000000000000..87c9ce38628f3f14e27db646342cae5df2d71dee GIT binary patch literal 5333 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 literal 3896 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 wQn^Ny6w8Ul=yM?v|4)DWPyqE_S + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + D + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/bitmap2component.svg b/resources/linux/mime/icons/hicolor/scalable/apps/bitmap2component.svg new file mode 100644 index 0000000000..fe6cb4d96a --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/apps/bitmap2component.svg @@ -0,0 +1,1125 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/cvpcb.svg b/resources/linux/mime/icons/hicolor/scalable/apps/cvpcb.svg new file mode 100644 index 0000000000..e4bc2f0a08 --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/apps/cvpcb.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/eeschema.svg b/resources/linux/mime/icons/hicolor/scalable/apps/eeschema.svg new file mode 100644 index 0000000000..b6599cb16e --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/apps/eeschema.svg @@ -0,0 +1,394 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/gerbview.svg b/resources/linux/mime/icons/hicolor/scalable/apps/gerbview.svg new file mode 100644 index 0000000000..a60a93064c --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/apps/gerbview.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/pcbcalculator.svg b/resources/linux/mime/icons/hicolor/scalable/apps/pcbcalculator.svg new file mode 100644 index 0000000000..c71e211da2 --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/apps/pcbcalculator.svg @@ -0,0 +1,1100 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/pcbnew.svg b/resources/linux/mime/icons/hicolor/scalable/apps/pcbnew.svg new file mode 100644 index 0000000000..800183067f --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/apps/pcbnew.svg @@ -0,0 +1,1554 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-3d-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-3d-project.svg new file mode 100644 index 0000000000..1d4f04c6ef --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-3d-project.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + D + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-bitmap2component-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-bitmap2component-project.svg new file mode 100644 index 0000000000..fe6cb4d96a --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-bitmap2component-project.svg @@ -0,0 +1,1125 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-cvpcb-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-cvpcb-project.svg new file mode 100644 index 0000000000..e4bc2f0a08 --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-cvpcb-project.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-eeschema-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-eeschema-project.svg new file mode 100644 index 0000000000..b6599cb16e --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-eeschema-project.svg @@ -0,0 +1,394 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-gerbview-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-gerbview-project.svg new file mode 100644 index 0000000000..a60a93064c --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-gerbview-project.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbcalculator-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbcalculator-project.svg new file mode 100644 index 0000000000..c71e211da2 --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbcalculator-project.svg @@ -0,0 +1,1100 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbnew-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbnew-project.svg new file mode 100644 index 0000000000..800183067f --- /dev/null +++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbnew-project.svg @@ -0,0 +1,1554 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 69b16484ec74604c48a126b0c0cd600c294b0ed8 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 24 Dec 2013 13:07:17 -0600 Subject: [PATCH 22/57] add mime type application/x-pcbnew-pcb, extend Desktop Exec to support a file to open on command line --- CMakeLists.txt | 4 ++-- resources/linux/mime/applications/eeschema.desktop | 2 +- resources/linux/mime/applications/kicad.desktop | 2 +- resources/linux/mime/applications/pcbnew.desktop | 4 ++-- .../linux/mime/mimelnk/application/x-kicad-pcb.desktop | 8 ++++++++ 5 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 resources/linux/mime/mimelnk/application/x-kicad-pcb.desktop diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e5fcde1a6..e0473081e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -484,7 +484,7 @@ if( UNIX ) set( UNIX_MIME_DIR resources/linux/mime ) set( UNIX_MIMELNK_FILES ${UNIX_MIME_DIR}/mimelnk ) set( UNIX_MIME_FILES ${UNIX_MIME_DIR}/mime ) - set( UNIX_ICONS_FILES ${UNIX_MIME_DIR}/icons ) + set( UNIX_ICON_FILES ${UNIX_MIME_DIR}/icons ) set( UNIX_APPLICATIONS_FILES ${UNIX_MIME_DIR}/applications ) # Install Mimelnk directory @@ -494,7 +494,7 @@ if( UNIX ) ) # Install Mime directory - install( DIRECTORY ${UNIX_ICONS_FILES} + install( DIRECTORY ${UNIX_ICON_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/share COMPONENT resources ) diff --git a/resources/linux/mime/applications/eeschema.desktop b/resources/linux/mime/applications/eeschema.desktop index 888598c5a6..0efd102ff5 100644 --- a/resources/linux/mime/applications/eeschema.desktop +++ b/resources/linux/mime/applications/eeschema.desktop @@ -2,7 +2,7 @@ Categories=Development;Electronics Comment=Design an electronic schematic Comment[fr]=Dessiner des schémas électroniques -Exec=eeschema +Exec=eeschema %f GenericName=Electronic schematic design GenericName[fr]=Saisie de schéma électronique Icon=eeschema diff --git a/resources/linux/mime/applications/kicad.desktop b/resources/linux/mime/applications/kicad.desktop index 1d7645ba72..bf813160f7 100644 --- a/resources/linux/mime/applications/kicad.desktop +++ b/resources/linux/mime/applications/kicad.desktop @@ -2,7 +2,7 @@ Categories=Development;Electronics Comment=Design a printed circuit board Comment[fr]=Concevoir un circuit imprimé -Exec=kicad +Exec=kicad %f GenericName=EDA Suite GenericName[fr]=Suite logicielle de conception électronique Icon=kicad diff --git a/resources/linux/mime/applications/pcbnew.desktop b/resources/linux/mime/applications/pcbnew.desktop index f1052326a6..57551ab8e5 100644 --- a/resources/linux/mime/applications/pcbnew.desktop +++ b/resources/linux/mime/applications/pcbnew.desktop @@ -2,10 +2,10 @@ [Desktop Entry] Categories=Development;Electronics Comment=Design a printed circuit board -Exec=pcbnew +Exec=pcbnew %f GenericName=EDA Suite Icon=pcbnew -MimeType=application/x-pcbnew-project; +MimeType=application/x-pcbnew-pcb; Name=pcbnew Type=Application Name[en_US]=pcbnew diff --git a/resources/linux/mime/mimelnk/application/x-kicad-pcb.desktop b/resources/linux/mime/mimelnk/application/x-kicad-pcb.desktop new file mode 100644 index 0000000000..7a5afb8b80 --- /dev/null +++ b/resources/linux/mime/mimelnk/application/x-kicad-pcb.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=MimeType +MimeType=application/x-kicad-pcb +Icon=pcbnew +Patterns=*.kicad_pcb + +Comment=KiCad Printed Circuit Board +#Comment[fr]=Schéma électronique KiCad From d160459f4a5ea3e559514df2669366e139261d7a Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 24 Dec 2013 13:09:41 -0600 Subject: [PATCH 23/57] FIX: avoid use of wxFileName::GetModificationTime() when it can fail. --- include/utf8.h | 2 +- pcbnew/eagle_plugin.cpp | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/utf8.h b/include/utf8.h index 4f5bf6d856..7f60173083 100644 --- a/include/utf8.h +++ b/include/utf8.h @@ -65,7 +65,7 @@ public: /// For use with _() function on wx 2.8: UTF8( const wchar_t* txt ); - explicit UTF8( const std::string& o ) : + UTF8( const std::string& o ) : std::string( o ) { } diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index 703bb57ddd..ef4f1e42a9 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -93,9 +93,6 @@ typedef boost::optional opt_double; typedef boost::optional opt_bool; -const wxChar* traceEaglePlugin = wxT( "KicadEaglePlugin" ); - - /// segment (element) of our XPATH into the Eagle XML document tree in PTREE form. struct TRIPLET { @@ -2741,6 +2738,18 @@ wxDateTime EAGLE_PLUGIN::getModificationTime( const wxString& aPath ) { wxFileName fn( aPath ); + // Do not call wxFileName::GetModificationTime() on a non-existent file, because + // if it fails, wx's implementation calls the crap wxLogSysError() which + // eventually infects our UI with an unwanted popup window, so don't let it fail. + if( !fn.IsFileReadable() ) + { + wxString msg = wxString::Format( + _( "File '%s' is not readable." ), + GetChars( aPath ) ); + + THROW_IO_ERROR( msg ); + } + /* // update the writable flag while we have a wxFileName, in a network this // is possibly quite dynamic anyway. @@ -2770,8 +2779,6 @@ void EAGLE_PLUGIN::cacheLib( const wxString& aLibPath ) if( aLibPath != m_lib_path || load ) { - wxLogTrace( traceEaglePlugin, wxT( "Loading '%s'" ), TO_UTF8( aLibPath ) ); - PTREE doc; LOCALE_IO toggle; // toggles on, then off, the C locale. From bf9db2148cf0a6aa9f4e898503ee950bf66960a3 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Thu, 26 Dec 2013 16:36:43 -0600 Subject: [PATCH 24/57] Hide m_galCanvas and m_galCanvasActive behind accessors. Fix DLIST concatonation API corner case. --- TODO.txt | 1 - common/dlist.cpp | 45 ++++++++++++----------- common/drawframe.cpp | 34 ++++++++--------- common/zoom.cpp | 10 ++--- include/wxstruct.h | 24 ++++++------ pcbnew/basepcbframe.cpp | 16 ++++---- pcbnew/class_pcb_layer_widget.cpp | 6 ++- pcbnew/dialogs/dialog_general_options.cpp | 6 +-- pcbnew/pcbframe.cpp | 44 +++++++++++----------- pcbnew/tools/pcb_tools.cpp | 2 +- 10 files changed, 97 insertions(+), 91 deletions(-) diff --git a/TODO.txt b/TODO.txt index 7a534122bb..f7c42ce854 100644 --- a/TODO.txt +++ b/TODO.txt @@ -62,7 +62,6 @@ PCBNew Dick's Final TODO List: ====================== -*) Apply Fabrizio and Alexander's linux desktop patches after unifying them. *) Get licensing cleaned up. *) Re-arrange the repo architecture. *) DLL-ization of pcbnew & eeschema diff --git a/common/dlist.cpp b/common/dlist.cpp index 55ed137fd3..512fd1ce7a 100644 --- a/common/dlist.cpp +++ b/common/dlist.cpp @@ -87,31 +87,32 @@ void DHEAD::append( EDA_ITEM* aNewElement ) void DHEAD::append( DHEAD& aList ) { - wxCHECK_RET( aList.GetCount() != 0, wxT( "Attempt to append empty list." ) ); - - // Change the item's list to me. - for( EDA_ITEM* item = aList.first; item != NULL; item = item->Next() ) - item->SetList( this ); - - if( first ) // list is not empty, set last item's next to the first item in aList + if( aList.first ) { - wxCHECK_RET( last != NULL, wxT( "Last list element not set." ) ); + // Change the item's list to me. + for( EDA_ITEM* item = aList.first; item; item = item->Next() ) + item->SetList( this ); - last->SetNext( aList.first ); - aList.first->SetBack( last ); - last = aList.last; + if( first ) // this list is not empty, set last item's next to the first item in aList + { + wxCHECK_RET( last != NULL, wxT( "Last list element not set." ) ); + + last->SetNext( aList.first ); + aList.first->SetBack( last ); + last = aList.last; + } + else // this list is empty, first and last are same as aList + { + first = aList.first; + last = aList.last; + } + + count += aList.count; + + aList.count = 0; + aList.first = NULL; + aList.last = NULL; } - else // list is empty, first and last are same as aList - { - first = aList.first; - last = aList.last; - } - - count += aList.count; - - aList.count = 0; - aList.first = NULL; - aList.last = NULL; } diff --git a/common/drawframe.cpp b/common/drawframe.cpp index 3aabc8bd48..3937a02286 100644 --- a/common/drawframe.cpp +++ b/common/drawframe.cpp @@ -228,10 +228,10 @@ void EDA_DRAW_FRAME::SkipNextLeftButtonReleaseEvent() void EDA_DRAW_FRAME::OnToggleGridState( wxCommandEvent& aEvent ) { SetGridVisibility( !IsGridVisible() ); - if( m_galCanvasActive ) + if( IsGalCanvasActive() ) { - m_galCanvas->GetGAL()->SetGridVisibility( IsGridVisible() ); - m_galCanvas->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED ); + GetGalCanvas()->GetGAL()->SetGridVisibility( IsGridVisible() ); + GetGalCanvas()->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED ); } m_canvas->Refresh(); @@ -387,11 +387,11 @@ void EDA_DRAW_FRAME::OnSelectGrid( wxCommandEvent& event ) screen->SetGrid( id ); SetCrossHairPosition( RefPos( true ) ); - if( m_galCanvasActive ) + if( IsGalCanvasActive() ) { - m_galCanvas->GetGAL()->SetGridSize( VECTOR2D( screen->GetGrid().m_Size.x, + GetGalCanvas()->GetGAL()->SetGridSize( VECTOR2D( screen->GetGrid().m_Size.x, screen->GetGrid().m_Size.y ) ); - m_galCanvas->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED ); + GetGalCanvas()->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED ); } m_canvas->Refresh(); @@ -422,17 +422,17 @@ void EDA_DRAW_FRAME::OnSelectZoom( wxCommandEvent& event ) GetScreen()->SetZoom( selectedZoom ); - if( m_galCanvasActive ) + if( IsGalCanvasActive() ) { // Apply computed view settings to GAL - KIGFX::VIEW* view = m_galCanvas->GetView(); - KIGFX::GAL* gal = m_galCanvas->GetGAL(); + KIGFX::VIEW* view = GetGalCanvas()->GetView(); + KIGFX::GAL* gal = GetGalCanvas()->GetGAL(); double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); double zoom = 1.0 / ( zoomFactor * GetZoom() ); view->SetScale( zoom ); - m_galCanvas->Refresh(); + GetGalCanvas()->Refresh(); } else RedrawScreen( GetScrollCenterPosition(), false ); @@ -954,8 +954,8 @@ void EDA_DRAW_FRAME::AdjustScrollBars( const wxPoint& aCenterPositionIU ) void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable ) { - KIGFX::VIEW* view = m_galCanvas->GetView(); - KIGFX::GAL* gal = m_galCanvas->GetGAL(); + KIGFX::VIEW* view = GetGalCanvas()->GetView(); + KIGFX::GAL* gal = GetGalCanvas()->GetGAL(); double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); @@ -965,7 +965,7 @@ void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable ) BASE_SCREEN* screen = GetScreen(); // Switch to GAL rendering - if( !m_galCanvasActive ) + if( !IsGalCanvasActive() ) { // Set up viewport double zoom = 1.0 / ( zoomFactor * m_canvas->GetZoom() ); @@ -981,7 +981,7 @@ void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable ) else { // Switch to standard rendering - if( m_galCanvasActive ) + if( IsGalCanvasActive() ) { // Change view settings only if GAL was active previously double zoom = 1.0 / ( zoomFactor * view->GetScale() ); @@ -993,17 +993,17 @@ void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable ) } m_canvas->SetEvtHandlerEnabled( !aEnable ); - m_galCanvas->SetEvtHandlerEnabled( aEnable ); + GetGalCanvas()->SetEvtHandlerEnabled( aEnable ); // Switch panes m_auimgr.GetPane( wxT( "DrawFrame" ) ).Show( !aEnable ); m_auimgr.GetPane( wxT( "DrawFrameGal" ) ).Show( aEnable ); m_auimgr.Update(); - m_galCanvasActive = aEnable; + SetGalCanvasActive( aEnable ); if( aEnable ) - m_galCanvas->SetFocus(); + GetGalCanvas()->SetFocus(); } //-----< BASE_SCREEN API moved here >-------------------------------------------- diff --git a/common/zoom.cpp b/common/zoom.cpp index f839684ae2..6add6bc2f6 100644 --- a/common/zoom.cpp +++ b/common/zoom.cpp @@ -84,7 +84,7 @@ void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer ) if( screen->m_FirstRedraw ) SetCrossHairPosition( GetScrollCenterPosition() ); - if( !m_galCanvasActive ) + if( !IsGalCanvasActive() ) RedrawScreen( GetScrollCenterPosition(), aWarpPointer ); } @@ -194,18 +194,18 @@ void EDA_DRAW_FRAME::OnZoom( wxCommandEvent& event ) RedrawScreen( center, true ); } - if( m_galCanvasActive ) + if( IsGalCanvasActive() ) { // Apply computed view settings to GAL - KIGFX::VIEW* view = m_galCanvas->GetView(); - KIGFX::GAL* gal = m_galCanvas->GetGAL(); + KIGFX::VIEW* view = GetGalCanvas()->GetView(); + KIGFX::GAL* gal = GetGalCanvas()->GetGAL(); double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); double zoom = 1.0 / ( zoomFactor * GetZoom() ); view->SetScale( zoom ); view->SetCenter( VECTOR2D( center ) ); - m_galCanvas->Refresh(); + GetGalCanvas()->Refresh(); } UpdateStatusBar(); diff --git a/include/wxstruct.h b/include/wxstruct.h index b26dfeeca2..002b396938 100644 --- a/include/wxstruct.h +++ b/include/wxstruct.h @@ -390,24 +390,24 @@ class EDA_DRAW_FRAME : public EDA_BASE_FRAME friend class EDA_DRAW_PANEL; ///< Id of active button on the vertical toolbar. - int m_toolId; + int m_toolId; - BASE_SCREEN* m_currentScreen; ///< current used SCREEN - bool m_snapToGrid; ///< Indicates if cursor should be snapped to grid. + BASE_SCREEN* m_currentScreen; ///< current used SCREEN + + bool m_snapToGrid; ///< Indicates if cursor should be snapped to grid. + bool m_galCanvasActive; ///< whether to use new GAL engine + + EDA_DRAW_PANEL_GAL* m_galCanvas; protected: EDA_HOTKEY_CONFIG* m_HotkeysZoomAndGridList; int m_LastGridSizeId; bool m_DrawGrid; // hide/Show grid - bool m_galCanvasActive; // whether to use new GAL engine EDA_COLOR_T m_GridColor; // Grid color /// The area to draw on. EDA_DRAW_PANEL* m_canvas; - /// New type of area (GAL-based) to draw on. - EDA_DRAW_PANEL_GAL* m_galCanvas; - /// Tool ID of previously active draw tool bar button. int m_lastDrawToolId; @@ -979,20 +979,22 @@ public: virtual void UseGalCanvas( bool aEnable ); /** - * Function IsNewCanvasActive + * Function IsGalCanvasActive * is used to check which canvas (GAL-based or standard) is currently in use. * * @return True for GAL-based canvas, false for standard canvas. */ - bool IsGalCanvasActive() { return m_galCanvasActive; } + bool IsGalCanvasActive() const { return m_galCanvasActive; } + void SetGalCanvasActive( bool aState ) { m_galCanvasActive = aState; } /** - * Function GetCalCanvas + * Function GetGalCanvas * returns a pointer to GAL-based canvas of given EDA draw frame. * * @return Pointer to GAL-based canvas. */ - EDA_DRAW_PANEL_GAL* GetGalCanvas() { return m_galCanvas; } + EDA_DRAW_PANEL_GAL* GetGalCanvas() const { return m_galCanvas; } + void SetGalCanvas( EDA_DRAW_PANEL_GAL* aPanel ) { m_galCanvas = aPanel; } DECLARE_EVENT_TABLE() }; diff --git a/pcbnew/basepcbframe.cpp b/pcbnew/basepcbframe.cpp index 7b3f66df58..858aa40c0e 100644 --- a/pcbnew/basepcbframe.cpp +++ b/pcbnew/basepcbframe.cpp @@ -152,10 +152,12 @@ PCB_BASE_FRAME::PCB_BASE_FRAME( wxWindow* aParent, ID_DRAWFRAME_TYPE aFrameType, m_FastGrid1 = 0; m_FastGrid2 = 0; - m_galCanvas = new EDA_DRAW_PANEL_GAL( this, -1, wxPoint( 0, 0 ), m_FrameSize, - EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ); + SetGalCanvas( new EDA_DRAW_PANEL_GAL( + this, -1, wxPoint( 0, 0 ), m_FrameSize, + EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ) ); + // Hide by default, it has to be explicitly shown - m_galCanvas->Hide(); + GetGalCanvas()->Hide(); m_auxiliaryToolBar = NULL; } @@ -166,7 +168,7 @@ PCB_BASE_FRAME::~PCB_BASE_FRAME() delete m_Collector; delete m_Pcb; // is already NULL for FOOTPRINT_EDIT_FRAME - delete m_galCanvas; + delete GetGalCanvas(); } @@ -437,11 +439,11 @@ void PCB_BASE_FRAME::OnTogglePadDrawMode( wxCommandEvent& aEvent ) // Apply new display options to the GAL canvas KIGFX::PCB_PAINTER* painter = - static_cast ( m_galCanvas->GetView()->GetPainter() ); + static_cast ( GetGalCanvas()->GetView()->GetPainter() ); KIGFX::PCB_RENDER_SETTINGS* settings = static_cast ( painter->GetSettings() ); settings->LoadDisplayOptions( DisplayOpt ); - m_galCanvas->GetView()->RecacheAllItems( true ); + GetGalCanvas()->GetView()->RecacheAllItems( true ); m_canvas->Refresh(); } @@ -764,7 +766,7 @@ void PCB_BASE_FRAME::LoadSettings() m_DisplayModText = FILLED; // Apply display settings for GAL - KIGFX::VIEW* view = m_galCanvas->GetView(); + KIGFX::VIEW* view = GetGalCanvas()->GetView(); // Set rendering order and properties of layers for( LAYER_NUM i = 0; (unsigned) i < sizeof(GAL_LAYER_ORDER) / sizeof(LAYER_NUM); ++i ) diff --git a/pcbnew/class_pcb_layer_widget.cpp b/pcbnew/class_pcb_layer_widget.cpp index 68811af01d..00dcd3fad4 100644 --- a/pcbnew/class_pcb_layer_widget.cpp +++ b/pcbnew/class_pcb_layer_widget.cpp @@ -404,25 +404,27 @@ void PCB_LAYER_WIDGET::OnLayerVisible( LAYER_NUM aLayer, bool isVisible, bool is myframe->GetCanvas()->Refresh(); } + void PCB_LAYER_WIDGET::OnRenderColorChange( int aId, EDA_COLOR_T aColor ) { myframe->GetBoard()->SetVisibleElementColor( aId, aColor ); myframe->GetCanvas()->Refresh(); } + void PCB_LAYER_WIDGET::OnRenderEnable( int aId, bool isEnabled ) { BOARD* brd = myframe->GetBoard(); brd->SetElementVisibility( aId, isEnabled ); - EDA_DRAW_PANEL_GAL *galCanvas = myframe->GetGalCanvas(); + EDA_DRAW_PANEL_GAL* galCanvas = myframe->GetGalCanvas(); if( galCanvas ) { KIGFX::VIEW* view = galCanvas->GetView(); view->SetLayerVisible( ITEM_GAL_LAYER( aId ), isEnabled ); } - if( myframe->IsGalCanvasActive() ) + if( galCanvas && myframe->IsGalCanvasActive() ) galCanvas->Refresh(); else myframe->GetCanvas()->Refresh(); diff --git a/pcbnew/dialogs/dialog_general_options.cpp b/pcbnew/dialogs/dialog_general_options.cpp index 3470426f2c..237b7b18a1 100644 --- a/pcbnew/dialogs/dialog_general_options.cpp +++ b/pcbnew/dialogs/dialog_general_options.cpp @@ -153,7 +153,7 @@ void PCB_EDIT_FRAME::OnSelectOptionToolbar( wxCommandEvent& event ) int id = event.GetId(); bool state = event.IsChecked(); KIGFX::PCB_PAINTER* painter = - static_cast ( m_galCanvas->GetView()->GetPainter() ); + static_cast ( GetGalCanvas()->GetView()->GetPainter() ); KIGFX::PCB_RENDER_SETTINGS* settings = static_cast ( painter->GetSettings() ); bool recache = false; @@ -259,9 +259,9 @@ void PCB_EDIT_FRAME::OnSelectOptionToolbar( wxCommandEvent& event ) { // Apply new display options to the GAL canvas settings->LoadDisplayOptions( DisplayOpt ); - m_galCanvas->GetView()->RecacheAllItems( true ); + GetGalCanvas()->GetView()->RecacheAllItems( true ); } if( IsGalCanvasActive() ) - m_galCanvas->Refresh(); + GetGalCanvas()->Refresh(); } diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index 3145afec51..58c1692d31 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -335,14 +335,14 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( wxWindow* parent, const wxString& title, SetBoard( new BOARD() ); - if( m_galCanvas ) + if( GetGalCanvas() ) { ViewReloadBoard( m_Pcb ); // update the tool manager with the new board and its view. if( m_toolManager ) - m_toolManager->SetEnvironment( m_Pcb, m_galCanvas->GetView(), - m_galCanvas->GetViewControls(), this ); + m_toolManager->SetEnvironment( m_Pcb, GetGalCanvas()->GetView(), + GetGalCanvas()->GetViewControls(), this ); } // Create the PCB_LAYER_WIDGET *after* SetBoard(): @@ -356,7 +356,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( wxWindow* parent, const wxString& title, if( screenHeight <= 900 ) pointSize = (pointSize * 8) / 10; - m_Layers = new PCB_LAYER_WIDGET( this, m_galCanvas, pointSize ); + m_Layers = new PCB_LAYER_WIDGET( this, GetGalCanvas(), pointSize ); m_drc = new DRC( this ); // these 2 objects point to each other @@ -454,8 +454,8 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( wxWindow* parent, const wxString& title, m_auimgr.AddPane( m_canvas, wxAuiPaneInfo().Name( wxT( "DrawFrame" ) ).CentrePane() ); - if( m_galCanvas ) - m_auimgr.AddPane( (wxWindow*) m_galCanvas, + if( GetGalCanvas() ) + m_auimgr.AddPane( (wxWindow*) GetGalCanvas(), wxAuiPaneInfo().Name( wxT( "DrawFrameGal" ) ).CentrePane().Hide() ); if( m_messagePanel ) @@ -538,21 +538,21 @@ void PCB_EDIT_FRAME::SetBoard( BOARD* aBoard ) { PCB_BASE_FRAME::SetBoard( aBoard ); - if( m_galCanvas ) + if( GetGalCanvas() ) { ViewReloadBoard( aBoard ); // update the tool manager with the new board and its view. if( m_toolManager ) - m_toolManager->SetEnvironment( aBoard, m_galCanvas->GetView(), - m_galCanvas->GetViewControls(), this ); + m_toolManager->SetEnvironment( aBoard, GetGalCanvas()->GetView(), + GetGalCanvas()->GetViewControls(), this ); } } void PCB_EDIT_FRAME::ViewReloadBoard( const BOARD* aBoard ) const { - KIGFX::VIEW* view = m_galCanvas->GetView(); + KIGFX::VIEW* view = GetGalCanvas()->GetView(); view->Clear(); // All of PCB drawing elements should be added to the VIEW @@ -629,8 +629,8 @@ void PCB_EDIT_FRAME::ViewReloadBoard( const BOARD* aBoard ) const view->SetPanBoundary( worksheet->ViewBBox() ); view->RecacheAllItems( true ); - if( m_galCanvasActive ) - m_galCanvas->Refresh(); + if( IsGalCanvasActive() ) + GetGalCanvas()->Refresh(); } @@ -668,7 +668,7 @@ void PCB_EDIT_FRAME::OnQuit( wxCommandEvent& event ) void PCB_EDIT_FRAME::OnCloseWindow( wxCloseEvent& Event ) { m_canvas->SetAbortRequest( true ); - m_galCanvas->StopDrawing(); + GetGalCanvas()->StopDrawing(); if( GetScreen()->IsModify() ) { @@ -750,8 +750,8 @@ void PCB_EDIT_FRAME::UseGalCanvas( bool aEnable ) { EDA_DRAW_FRAME::UseGalCanvas( aEnable ); - m_toolManager->SetEnvironment( m_Pcb, m_galCanvas->GetView(), - m_galCanvas->GetViewControls(), this ); + m_toolManager->SetEnvironment( m_Pcb, GetGalCanvas()->GetView(), + GetGalCanvas()->GetViewControls(), this ); ViewReloadBoard( m_Pcb ); } @@ -768,12 +768,12 @@ void PCB_EDIT_FRAME::SwitchCanvas( wxCommandEvent& aEvent ) break; case ID_MENU_CANVAS_CAIRO: - m_galCanvas->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO ); + GetGalCanvas()->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO ); UseGalCanvas( true ); break; case ID_MENU_CANVAS_OPENGL: - m_galCanvas->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ); + GetGalCanvas()->SwitchBackend( EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ); UseGalCanvas( true ); break; } @@ -902,7 +902,7 @@ bool PCB_EDIT_FRAME::IsMicroViaAcceptable( void ) void PCB_EDIT_FRAME::setHighContrastLayer( LAYER_NUM aLayer ) { // Set display settings for high contrast mode - KIGFX::VIEW* view = m_galCanvas->GetView(); + KIGFX::VIEW* view = GetGalCanvas()->GetView(); KIGFX::RENDER_SETTINGS* rSettings = view->GetPainter()->GetSettings(); setTopLayer( aLayer ); @@ -945,7 +945,7 @@ void PCB_EDIT_FRAME::setHighContrastLayer( LAYER_NUM aLayer ) void PCB_EDIT_FRAME::setTopLayer( LAYER_NUM aLayer ) { // Set display settings for high contrast mode - KIGFX::VIEW* view = m_galCanvas->GetView(); + KIGFX::VIEW* view = GetGalCanvas()->GetView(); view->ClearTopLayers(); view->SetTopLayer( aLayer ); @@ -993,8 +993,8 @@ void PCB_EDIT_FRAME::setActiveLayer( LAYER_NUM aLayer, bool doLayerWidgetUpdate if( doLayerWidgetUpdate ) syncLayerWidgetLayer(); - if( m_galCanvasActive ) - m_galCanvas->Refresh(); + if( IsGalCanvasActive() ) + GetGalCanvas()->Refresh(); } @@ -1015,7 +1015,7 @@ void PCB_EDIT_FRAME::syncLayerVisibilities() { m_Layers->SyncLayerVisibilities(); - KIGFX::VIEW* view = m_galCanvas->GetView(); + KIGFX::VIEW* view = GetGalCanvas()->GetView(); // Load layer & elements visibility settings for( LAYER_NUM i = 0; i < NB_LAYERS; ++i ) { diff --git a/pcbnew/tools/pcb_tools.cpp b/pcbnew/tools/pcb_tools.cpp index f3749a0c94..e8e0568587 100644 --- a/pcbnew/tools/pcb_tools.cpp +++ b/pcbnew/tools/pcb_tools.cpp @@ -44,7 +44,7 @@ void PCB_EDIT_FRAME::setupTools() // Create the manager and dispatcher & route draw panel events to the dispatcher m_toolManager = new TOOL_MANAGER; m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager, this ); - m_galCanvas->SetEventDispatcher( m_toolDispatcher ); + GetGalCanvas()->SetEventDispatcher( m_toolDispatcher ); // Register tool actions m_toolManager->RegisterAction( &COMMON_ACTIONS::moveActivate ); From 0d52d9aa5283c138d5c9869208ca129e28e86cf4 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Fri, 27 Dec 2013 08:24:36 +0100 Subject: [PATCH 25/57] Pcbnew, 3D viewer: fix incorrect position of multiline texts. --- common/eda_text.cpp | 2 +- include/eda_text.h | 2 +- pcbnew/board_items_to_polygon_shape_transform.cpp | 15 ++++++--------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/common/eda_text.cpp b/common/eda_text.cpp index fdadf86c82..35983951af 100644 --- a/common/eda_text.cpp +++ b/common/eda_text.cpp @@ -295,7 +295,7 @@ void EDA_TEXT::Draw( EDA_RECT* aClipBox, wxDC* aDC, const wxPoint& aOffset, void EDA_TEXT::GetPositionsOfLinesOfMultilineText( - std::vector& aPositions, int aLineCount ) + std::vector& aPositions, int aLineCount ) const { wxPoint pos = m_Pos; // Position of first line of the // multiline text according to diff --git a/include/eda_text.h b/include/eda_text.h index 7de9dbd2c7..a114e56f1a 100644 --- a/include/eda_text.h +++ b/include/eda_text.h @@ -275,7 +275,7 @@ public: * for efficiency reasons */ void GetPositionsOfLinesOfMultilineText( - std::vector& aPositions, int aLineCount ); + std::vector& aPositions, int aLineCount ) const; /** * Function Format * outputs the object to \a aFormatter in s-expression form. diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index c18dbc3d19..66c5c7afa6 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -290,22 +290,19 @@ void TEXTE_PCB::TransformShapeWithClearanceToPolygonSet( if( IsMultilineAllowed() ) { - wxPoint pos = GetTextPosition(); wxArrayString* list = wxStringSplit( GetText(), '\n' ); - wxPoint offset; + std::vector positions; + positions.reserve( list->Count() ); + GetPositionsOfLinesOfMultilineText( positions, list->Count() ); - offset.y = GetInterline(); - RotatePoint( &offset, GetOrientation() ); - - for( unsigned i = 0; iCount(); i++ ) + for( unsigned ii = 0; ii < list->Count(); ii++ ) { - wxString txt = list->Item( i ); - DrawGraphicText( NULL, NULL, pos, color, + wxString txt = list->Item( ii ); + DrawGraphicText( NULL, NULL, positions[ii], color, txt, GetOrientation(), size, GetHorizJustify(), GetVertJustify(), GetThickness(), IsItalic(), true, addTextSegmToPoly ); - pos += offset; } delete list; From e18828bed7227ab68df0f1a811966f277a9749d7 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Fri, 27 Dec 2013 12:44:40 +0100 Subject: [PATCH 26/57] Dialog fp plugin option: Fix compil issue under gcc 4.4 (does not happen with 4.6 and later) about an overloaded function. --- pcbnew/dialogs/dialog_fp_plugin_options.cpp | 2 +- pcbnew/dialogs/dialog_fp_plugin_options_base.cpp | 6 +++--- pcbnew/dialogs/dialog_fp_plugin_options_base.fbp | 2 +- pcbnew/dialogs/dialog_fp_plugin_options_base.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pcbnew/dialogs/dialog_fp_plugin_options.cpp b/pcbnew/dialogs/dialog_fp_plugin_options.cpp index edbfedb6ec..dc3a0b5127 100644 --- a/pcbnew/dialogs/dialog_fp_plugin_options.cpp +++ b/pcbnew/dialogs/dialog_fp_plugin_options.cpp @@ -375,7 +375,7 @@ private: abort(); } - void onCancelButtonClick( wxCloseEvent& event ) + void onCancelCaptionButtonClick( wxCloseEvent& event ) { abort(); } diff --git a/pcbnew/dialogs/dialog_fp_plugin_options_base.cpp b/pcbnew/dialogs/dialog_fp_plugin_options_base.cpp index 324d98a634..2cd0e86e96 100644 --- a/pcbnew/dialogs/dialog_fp_plugin_options_base.cpp +++ b/pcbnew/dialogs/dialog_fp_plugin_options_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Nov 5 2013) +// C++ code generated with wxFormBuilder (version Nov 6 2013) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! @@ -130,7 +130,7 @@ DIALOG_FP_PLUGIN_OPTIONS_BASE::DIALOG_FP_PLUGIN_OPTIONS_BASE( wxWindow* parent, this->Centre( wxBOTH ); // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onCancelButtonClick ) ); + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onCancelCaptionButtonClick ) ); m_add_row->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onAppendRow ), NULL, this ); m_delete_row->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onDeleteRow ), NULL, this ); m_move_up->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onMoveUp ), NULL, this ); @@ -145,7 +145,7 @@ DIALOG_FP_PLUGIN_OPTIONS_BASE::DIALOG_FP_PLUGIN_OPTIONS_BASE( wxWindow* parent, DIALOG_FP_PLUGIN_OPTIONS_BASE::~DIALOG_FP_PLUGIN_OPTIONS_BASE() { // Disconnect Events - this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onCancelButtonClick ) ); + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onCancelCaptionButtonClick ) ); m_add_row->Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onAppendRow ), NULL, this ); m_delete_row->Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onDeleteRow ), NULL, this ); m_move_up->Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( DIALOG_FP_PLUGIN_OPTIONS_BASE::onMoveUp ), NULL, this ); diff --git a/pcbnew/dialogs/dialog_fp_plugin_options_base.fbp b/pcbnew/dialogs/dialog_fp_plugin_options_base.fbp index c0e3ceb544..a48a01c5bf 100644 --- a/pcbnew/dialogs/dialog_fp_plugin_options_base.fbp +++ b/pcbnew/dialogs/dialog_fp_plugin_options_base.fbp @@ -61,7 +61,7 @@ - onCancelButtonClick + onCancelCaptionButtonClick diff --git a/pcbnew/dialogs/dialog_fp_plugin_options_base.h b/pcbnew/dialogs/dialog_fp_plugin_options_base.h index f71dbaae6c..1cf55510c0 100644 --- a/pcbnew/dialogs/dialog_fp_plugin_options_base.h +++ b/pcbnew/dialogs/dialog_fp_plugin_options_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Nov 5 2013) +// C++ code generated with wxFormBuilder (version Nov 6 2013) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! @@ -53,7 +53,7 @@ class DIALOG_FP_PLUGIN_OPTIONS_BASE : public DIALOG_SHIM wxButton* m_sdbSizer1Cancel; // Virtual event handlers, overide them in your derived class - virtual void onCancelButtonClick( wxCloseEvent& event ) = 0; + virtual void onCancelCaptionButtonClick( wxCloseEvent& event ) = 0; virtual void onAppendRow( wxMouseEvent& event ) = 0; virtual void onDeleteRow( wxMouseEvent& event ) = 0; virtual void onMoveUp( wxMouseEvent& event ) = 0; From 514c6c11ae0039303617b3df8ae0e45e38199730 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Sat, 28 Dec 2013 12:29:20 +0100 Subject: [PATCH 27/57] =?UTF-8?q?Shows=20the=20current=20BZR=20version=20i?= =?UTF-8?q?n=20version.h,=20not=20the=20last=20in=20the=20repository=20(Ni?= =?UTF-8?q?ck=20=C3=98stergaard)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeModules/CreateBzrVersionHeader.cmake | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CMakeModules/CreateBzrVersionHeader.cmake b/CMakeModules/CreateBzrVersionHeader.cmake index a17a48ebda..5cafcf60bb 100644 --- a/CMakeModules/CreateBzrVersionHeader.cmake +++ b/CMakeModules/CreateBzrVersionHeader.cmake @@ -30,8 +30,15 @@ macro( create_bzr_version_header ) set( _Bazaar_SAVED_LC_ALL "$ENV{LC_ALL}" ) set( ENV{LC_ALL} C ) + # Get the tree revison execute_process( COMMAND - ${Bazaar_EXECUTABLE} log -r-1 ${PROJECT_SOURCE_DIR} + ${Bazaar_EXECUTABLE} revno --tree ${PROJECT_SOURCE_DIR} + OUTPUT_VARIABLE _bazaar_TREE_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Get more info about that revision + execute_process( COMMAND + ${Bazaar_EXECUTABLE} log -r${_bazaar_TREE_DATE} ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE _bazaar_LAST_CHANGE_LOG ERROR_VARIABLE _bazaar_log_error RESULT_VARIABLE _bazaar_log_result From 7689d48632e6496b7b46b42d54df114b7c89f3cd Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 29 Dec 2013 11:15:06 +0100 Subject: [PATCH 28/57] Move HitTestForCorner and HitTestForEdge code from class_zone to polygon/PolyLine.cpp, to avoid redundant code. Fix bug 1264248. Fix a very minor issue in RemoveTrailingZeros, for countries where the separator in floating numbers is a comma. --- common/string.cpp | 2 +- pcbnew/class_zone.cpp | 66 +++-------------------------------- pcbnew/edit_track_width.cpp | 4 +-- pcbnew/tr_modif.cpp | 4 ++- polygon/PolyLine.cpp | 69 +++++++++++++++++++++++++++++++++++++ polygon/PolyLine.h | 22 ++++++++++-- 6 files changed, 100 insertions(+), 67 deletions(-) diff --git a/common/string.cpp b/common/string.cpp index 86b60d938f..91f2099b36 100644 --- a/common/string.cpp +++ b/common/string.cpp @@ -487,7 +487,7 @@ wxString RemoveTrailingZeros( const wxString& aString ) while( --i > 0 && retv[i] == wxChar( '0' ) ) retv.RemoveLast(); - if( retv[i] == wxChar( '.' ) ) + if( retv[i] == wxChar( '.' ) || retv[i] == wxChar( ',' ) ) retv.RemoveLast(); return retv; diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index 642023de9d..b1d8405c81 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -480,32 +480,12 @@ bool ZONE_CONTAINER::HitTest( const wxPoint& aPosition ) // Zones outlines have no thickness, so it Hit Test functions // we must have a default distance between the test point // and a corner or a zone edge: -#define MIN_DIST_IN_MILS 10 +#define MAX_DIST_IN_MM 0.25 bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos ) { - m_CornerSelection = -1; // Set to not found - - // distance (in internal units) to detect a corner in a zone outline. - int min_dist = MIN_DIST_IN_MILS*IU_PER_MILS; - - wxPoint delta; - unsigned lim = m_Poly->m_CornersList.GetCornersCount(); - - for( unsigned item_pos = 0; item_pos < lim; item_pos++ ) - { - delta.x = refPos.x - m_Poly->m_CornersList.GetX( item_pos ); - delta.y = refPos.y - m_Poly->m_CornersList.GetY( item_pos ); - - // Calculate a distance: - int dist = std::max( abs( delta.x ), abs( delta.y ) ); - - if( dist < min_dist ) // this corner is a candidate: - { - m_CornerSelection = item_pos; - min_dist = dist; - } - } + int distmax = Millimeter2iu( MAX_DIST_IN_MM ); + m_CornerSelection = m_Poly->HitTestForCorner( refPos, distmax ); return m_CornerSelection >= 0; } @@ -513,44 +493,8 @@ bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos ) bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos ) { - unsigned lim = m_Poly->m_CornersList.GetCornersCount(); - - m_CornerSelection = -1; // Set to not found - - // distance (in internal units) to detect a zone outline - int min_dist = MIN_DIST_IN_MILS*IU_PER_MILS; - - unsigned first_corner_pos = 0; - - for( unsigned item_pos = 0; item_pos < lim; item_pos++ ) - { - unsigned end_segm = item_pos + 1; - - /* the last corner of the current outline is tested - * the last segment of the current outline starts at current corner, and ends - * at the first corner of the outline - */ - if( m_Poly->m_CornersList.IsEndContour ( item_pos ) || end_segm >= lim ) - { - unsigned tmp = first_corner_pos; - first_corner_pos = end_segm; // first_corner_pos is now the beginning of the next outline - end_segm = tmp; // end_segm is the beginning of the current outline - } - - // test the dist between segment and ref point - int dist = KiROUND( GetPointToLineSegmentDistance( - refPos.x, refPos.y, - m_Poly->m_CornersList.GetX( item_pos ), - m_Poly->m_CornersList.GetY( item_pos ), - m_Poly->m_CornersList.GetX( end_segm ), - m_Poly->m_CornersList.GetY( end_segm ) ) ); - - if( dist < min_dist ) - { - m_CornerSelection = item_pos; - min_dist = dist; - } - } + int distmax = Millimeter2iu( MAX_DIST_IN_MM ); + m_CornerSelection = m_Poly->HitTestForEdge( refPos, distmax ); return m_CornerSelection >= 0; } diff --git a/pcbnew/edit_track_width.cpp b/pcbnew/edit_track_width.cpp index 869afdaede..6698f2be1a 100644 --- a/pcbnew/edit_track_width.cpp +++ b/pcbnew/edit_track_width.cpp @@ -63,9 +63,9 @@ bool PCB_EDIT_FRAME::SetTrackSegmentWidth( TRACK* aTrackItem, if( aTrackItem->GetShape() == VIA_MICROVIA ) { if( net ) - new_width = net->GetViaSize(); - else new_width = net->GetMicroViaSize(); + else + new_width = GetBoard()->GetCurrentMicroViaSize(); } } diff --git a/pcbnew/tr_modif.cpp b/pcbnew/tr_modif.cpp index 11fa841a96..d4aa7eb5a6 100644 --- a/pcbnew/tr_modif.cpp +++ b/pcbnew/tr_modif.cpp @@ -234,7 +234,9 @@ int PCB_EDIT_FRAME::EraseRedundantTrack( wxDC* aDC, } nbconnect--; - pt_del->SetState( IS_LINKED, false ); + + if( pt_del ) + pt_del->SetState( IS_LINKED, false ); pt_del = GetBoard()->MarkTrace( pt_del, &nb_segm, NULL, NULL, true ); diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp index 41a562a154..de50c23a0c 100644 --- a/polygon/PolyLine.cpp +++ b/polygon/PolyLine.cpp @@ -1149,6 +1149,75 @@ int CPolyLine::Distance( const wxPoint& aPoint ) } +/* test is the point aPos is near (< aDistMax ) a vertex + * return int = the index of the first corner of the vertex, or -1 if not found. + */ +int CPolyLine::HitTestForEdge( const wxPoint& aPos, int aDistMax ) const +{ + unsigned lim = m_CornersList.GetCornersCount(); + int corner = -1; // Set to not found + unsigned first_corner_pos = 0; + + for( unsigned item_pos = 0; item_pos < lim; item_pos++ ) + { + unsigned end_segm = item_pos + 1; + + /* the last corner of the current outline is tested + * the last segment of the current outline starts at current corner, and ends + * at the first corner of the outline + */ + if( m_CornersList.IsEndContour ( item_pos ) || end_segm >= lim ) + { + unsigned tmp = first_corner_pos; + first_corner_pos = end_segm; // first_corner_pos is now the beginning of the next outline + end_segm = tmp; // end_segm is the beginning of the current outline + } + + // test the dist between segment and ref point + int dist = KiROUND( GetPointToLineSegmentDistance( + aPos.x, aPos.y, + m_CornersList.GetX( item_pos ), + m_CornersList.GetY( item_pos ), + m_CornersList.GetX( end_segm ), + m_CornersList.GetY( end_segm ) ) ); + + if( dist < aDistMax ) + { + corner = item_pos; + aDistMax = dist; + } + } + + return corner; +} + +/* test is the point aPos is near (< aDistMax ) a corner + * return int = the index of corner of the, or -1 if not found. + */ +int CPolyLine::HitTestForCorner( const wxPoint& aPos, int aDistMax ) const +{ + int corner = -1; // Set to not found + wxPoint delta; + unsigned lim = m_CornersList.GetCornersCount(); + + for( unsigned item_pos = 0; item_pos < lim; item_pos++ ) + { + delta.x = aPos.x - m_CornersList.GetX( item_pos ); + delta.y = aPos.y - m_CornersList.GetY( item_pos ); + + // Calculate a distance: + int dist = std::max( abs( delta.x ), abs( delta.y ) ); + + if( dist < aDistMax ) // this corner is a candidate: + { + corner = item_pos; + aDistMax = dist; + } + } + + return corner; +} + /* * Copy the contours to a KI_POLYGON_WITH_HOLES * The first contour is the main outline, others are holes diff --git a/polygon/PolyLine.h b/polygon/PolyLine.h index 1eb4a83e85..2610a5d765 100644 --- a/polygon/PolyLine.h +++ b/polygon/PolyLine.h @@ -409,13 +409,31 @@ public: */ int Distance( wxPoint aStart, wxPoint aEnd, int aWidth ); + /** + * Function HitTestForEdge + * test is the point aPos is near (< aDistMax ) a vertex + * @param aPos = the reference point + * @param aDistMax = the max distance between a vertex and the reference point + * @return int = the index of the first corner of the vertex, or -1 if not found. + */ + int HitTestForEdge( const wxPoint& aPos, int aDistMax ) const; + + /** + * Function HitTestForCorner + * test is the point aPos is near (< aDistMax ) a corner + * @param aPos = the reference point + * @param aDistMax = the max distance between a vertex and the corner + * @return int = the index of corner of the, or -1 if not found. + */ + int HitTestForCorner( const wxPoint& aPos, int aDistMax ) const; + private: LAYER_NUM m_layer; // layer to draw on enum HATCH_STYLE m_hatchStyle; // hatch style, see enum above - int m_hatchPitch; // for DIAGONAL_EDGE hatched outlines, basic distance between 2 hatch lines + int m_hatchPitch; // for DIAGONAL_EDGE hatched outlines, basic distance between 2 hatch lines // and the len of eacvh segment // for DIAGONAL_FULL, the pitch is twice this value - int m_utility; // a flag used in some calculations + int m_utility; // a flag used in some calculations public: CPOLYGONS_LIST m_CornersList; // array of points for corners std::vector m_HatchLines; // hatch lines showing the polygon area From 7e884e1eb1eaa248a68464629ef61993e7d286a2 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 29 Dec 2013 12:01:54 +0100 Subject: [PATCH 29/57] Fix bugs 1264236, 1264238, 1264254 --- common/drawframe.cpp | 2 +- gerbview/class_am_param.cpp | 18 +++++++++++++----- pcbnew/class_track.cpp | 12 +++++++++--- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/common/drawframe.cpp b/common/drawframe.cpp index 3937a02286..7f170a3d5e 100644 --- a/common/drawframe.cpp +++ b/common/drawframe.cpp @@ -636,7 +636,7 @@ void EDA_DRAW_FRAME::ClearMsgPanel( void ) void EDA_DRAW_FRAME::SetMsgPanel( const MSG_PANEL_ITEMS& aList ) { - if( m_messagePanel == NULL && !aList.empty() ) + if( m_messagePanel == NULL ) return; ClearMsgPanel(); diff --git a/gerbview/class_am_param.cpp b/gerbview/class_am_param.cpp index b722a48151..63306a4251 100644 --- a/gerbview/class_am_param.cpp +++ b/gerbview/class_am_param.cpp @@ -71,6 +71,7 @@ double AM_PARAM::GetValue( const D_CODE* aDcode ) const double paramvalue = 0.0; double curr_value = 0.0; parm_item_type state = POPVALUE; + for( unsigned ii = 0; ii < m_paramStack.size(); ii++ ) { AM_PARAM_ITEM item = m_paramStack[ii]; @@ -85,12 +86,19 @@ double AM_PARAM::GetValue( const D_CODE* aDcode ) const case PUSHPARM: // get the parameter from the aDcode - if( aDcode && item.GetIndex() <= aDcode->GetParamCount() ) - curr_value = aDcode->GetParam( item.GetIndex() ); - else // Get parameter from local param definition + if( aDcode ) // should be always true here { - const APERTURE_MACRO * am_parent = aDcode->GetMacro(); - curr_value = am_parent->GetLocalParam( aDcode, item.GetIndex() ); + if( item.GetIndex() <= aDcode->GetParamCount() ) + curr_value = aDcode->GetParam( item.GetIndex() ); + else // Get parameter from local param definition + { + const APERTURE_MACRO * am_parent = aDcode->GetMacro(); + curr_value = am_parent->GetLocalParam( aDcode, item.GetIndex() ); + } + } + else + { + wxLogDebug( wxT( "AM_PARAM::GetValue(): NULL param aDcode\n" ) ); } // Fall through case PUSHVALUE: // a value is on the stack: diff --git a/pcbnew/class_track.cpp b/pcbnew/class_track.cpp index 5b48a8eb0a..21cca65517 100644 --- a/pcbnew/class_track.cpp +++ b/pcbnew/class_track.cpp @@ -1154,12 +1154,18 @@ void TRACK::GetMsgPanelInfoBase( std::vector< MSG_PANEL_ITEM >& aList ) LAYER_NUM top_layer, bottom_layer; Via->ReturnLayerPair( &top_layer, &bottom_layer ); - msg = board->GetLayerName( top_layer ) + wxT( "/" ) - + board->GetLayerName( bottom_layer ); + if( board ) + msg = board->GetLayerName( top_layer ) + wxT( "/" ) + + board->GetLayerName( bottom_layer ); + else + msg.Printf(wxT("%d/%d"), top_layer, bottom_layer ); } else { - msg = board->GetLayerName( m_Layer ); + if( board ) + msg = board->GetLayerName( m_Layer ); + else + msg.Printf(wxT("%d"), m_Layer ); } aList.push_back( MSG_PANEL_ITEM( _( "Layer" ), msg, BROWN ) ); From 7ec2c004a2c4c9069acdec08ea436b90b1cdcf8b Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 29 Dec 2013 16:12:27 +0100 Subject: [PATCH 30/57] fix bugs 1264240, 1264247, 1264239, 1264233 (clang report errors) --- common/basicframe.cpp | 3 ++- common/fp_lib_table.cpp | 12 ++++++++---- eeschema/schematic_undo_redo.cpp | 8 ++------ pcbnew/drc.cpp | 3 ++- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/common/basicframe.cpp b/common/basicframe.cpp index 702a4d2654..e04663c91e 100644 --- a/common/basicframe.cpp +++ b/common/basicframe.cpp @@ -221,7 +221,8 @@ void EDA_BASE_FRAME::LoadSettings() // Once this is fully implemented, wxAuiManager will be used to maintain the persistance of // the main frame and all it's managed windows and all of the legacy frame persistence // position code can be removed. - config->Read( m_FrameName + entryPerspective, &m_perspective ); + if( config ) + config->Read( m_FrameName + entryPerspective, &m_perspective ); } diff --git a/common/fp_lib_table.cpp b/common/fp_lib_table.cpp index 09bcf826e9..5ee2c3a2bf 100644 --- a/common/fp_lib_table.cpp +++ b/common/fp_lib_table.cpp @@ -828,10 +828,14 @@ bool FP_LIB_TABLE::ConvertFromLegacy( NETLIST& aNetList, const wxArrayString& aL if( !newFPID.IsValid() ) { - msg.Printf( _( "Component `%s` FPID <%s> is not valid.\n" ), - GetChars( component->GetReference() ), - GetChars( FROM_UTF8( newFPID.Format().c_str() ) ) ); - aReporter->Report( msg ); + if( aReporter ) + { + msg.Printf( _( "Component `%s` FPID <%s> is not valid.\n" ), + GetChars( component->GetReference() ), + GetChars( FROM_UTF8( newFPID.Format().c_str() ) ) ); + aReporter->Report( msg ); + } + retv = false; } else diff --git a/eeschema/schematic_undo_redo.cpp b/eeschema/schematic_undo_redo.cpp index 4481e8cce0..2d6dbba449 100644 --- a/eeschema/schematic_undo_redo.cpp +++ b/eeschema/schematic_undo_redo.cpp @@ -269,19 +269,15 @@ void SCH_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool aRed for( int ii = aList->GetCount() - 1; ii >= 0; ii-- ) { item = (SCH_ITEM*) aList->GetPickedItem( ii ); + wxAssert( item ); - if( item ) - item->ClearFlags(); + item->ClearFlags(); SCH_ITEM* image = (SCH_ITEM*) aList->GetPickedItemLink( ii ); switch( aList->GetPickedItemStatus( ii ) ) { case UR_CHANGED: /* Exchange old and new data for each item */ - // tmp = item->Clone(); - // *item = *image; - // *image = *tmp; - // delete tmp; item->SwapData( image ); break; diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index bd08a55e20..49961cc581 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -224,7 +224,8 @@ void DRC::RunTests( wxTextCtrl* aMessages ) wxSafeYield(); } - m_mainWindow->Fill_All_Zones( aMessages->GetParent(), false ); + m_mainWindow->Fill_All_Zones( aMessages ? aMessages->GetParent() : m_mainWindow, + false ); // test zone clearances to other zones if( aMessages ) From 70b7131ae7901933fa2aecfa7397df502ea60986 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Sun, 29 Dec 2013 20:36:30 +0100 Subject: [PATCH 31/57] [MacOSX] fixes a missing feature in boost and fixes static build with x86/i386 involved (https://svn.boost.org/trac/boost/ticket/8266) --- CMakeModules/download_boost.cmake | 21 ++++---- patches/boost_macosx_x86.patch | 72 ++++++++++++++++++++++++++++ patches/boost_macosx_x86_build.patch | 13 +++++ 3 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 patches/boost_macosx_x86.patch create mode 100644 patches/boost_macosx_x86_build.patch diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 1548ab5a6f..bf43e685d6 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -40,8 +40,9 @@ set( BOOST_ROOT "${PROJECT_SOURCE_DIR}/boost_root" ) # Space separated list which indicates the subset of boost libraries to compile. # Chosen libraries are based on AVHTTP requirements, and possibly # unit_test_framework for its own worth. +# tool_manager.cpp -> coroutine -> context (_jump_fcontext) set( BOOST_LIBS_BUILT - #context + context #coroutine date_time #exception @@ -55,7 +56,6 @@ set( BOOST_LIBS_BUILT thread #unit_test_framework ) - #-------------------------------------------------------------------- find_package( BZip2 REQUIRED ) @@ -104,14 +104,14 @@ if( MINGW ) foreach( lib ${boost_libs_list} ) set( b2_libs ${b2_libs} --with-${lib} ) endforeach() - unset( PIC_STUFF ) + unset( BOOST_CFLAGS ) else() string( REGEX REPLACE "\\;" "," libs_csv "${boost_libs_list}" ) #message( STATUS "libs_csv:${libs_csv}" ) set( bootstrap ./bootstrap.sh --with-libraries=${libs_csv} ) # pass to *both* C and C++ compilers - set( PIC_STUFF "cflags=${PIC_FLAG}" ) + set( BOOST_CFLAGS "cflags=${PIC_FLAG}" ) set( BOOST_INCLUDE "${BOOST_ROOT}/include" ) unset( b2_libs ) endif() @@ -121,8 +121,8 @@ if( APPLE ) # I set this to being compatible with wxWidgets # wxWidgets still using libstdc++ (gcc), meanwhile OSX # has switched to libc++ (llvm) by default - set(BOOST_CXXFLAGS "cxxflags=-mmacosx-version-min=10.5" ) - set(BOOST_LINKFLAGS "linkflags=-mmacosx-version-min=10.5" ) + set(BOOST_CXXFLAGS "cxxflags=-mmacosx-version-min=10.5 -fno-common -fno-lto" ) + set(BOOST_LINKFLAGS "linkflags=-mmacosx-version-min=10.5 -fno-common -fno-lto" ) set(BOOST_TOOLSET "toolset=darwin" ) if( CMAKE_OSX_ARCHITECTURES ) @@ -141,6 +141,9 @@ if( APPLE ) set(BOOST_ARCHITECTURE "architecture=combined") endif() + set(BOOST_CFLAGS "${BOOST_CFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) + set(BOOST_CXXFLAGS "${BOOST_CXXFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) + set(BOOST_LINKFLAGS "${BOOST_LINKFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) endif() endif() @@ -159,6 +162,8 @@ ExternalProject_Add( boost # PATCH_COMMAND continuation (any *_COMMAND here can be continued with COMMAND): COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_minkowski.patch" COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_cstdint.patch" + COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_macosx_x86.patch" #https://svn.boost.org/trac/boost/ticket/8266 + COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_macosx_x86_build.patch" #https://svn.boost.org/trac/boost/ticket/8266 # [Mis-]use this step to erase all the boost headers and libraries before # replacing them below. @@ -170,7 +175,7 @@ ExternalProject_Add( boost BUILD_COMMAND ./b2 variant=release threading=multi - ${PIC_STUFF} + ${BOOST_CFLAGS} ${BOOST_TOOLSET} ${BOOST_CXXFLAGS} ${BOOST_LINKFLAGS} @@ -229,7 +234,7 @@ ExternalProject_Add_Step( boost bzr_commit_boost ExternalProject_Add_Step( boost bzr_add_boost # add only the headers to the scratch repo, repo = "../.bzr" from ${headers_src} - COMMAND bzr add -q ${headers_src} + COMMAND bzr add -q ${PREFIX}/src/boost COMMENT "adding pristine boost files to 'boost scratch repo'" DEPENDERS bzr_commit_boost ) diff --git a/patches/boost_macosx_x86.patch b/patches/boost_macosx_x86.patch new file mode 100644 index 0000000000..f14c5f49f8 --- /dev/null +++ b/patches/boost_macosx_x86.patch @@ -0,0 +1,72 @@ +=== modified file 'libs/context/build/Jamfile.v2' +--- libs/context/build/Jamfile.v2 2013-12-29 11:28:14 +0000 ++++ libs/context/build/Jamfile.v2 2013-12-29 11:31:05 +0000 +@@ -414,6 +414,25 @@ + ; + + alias asm_context_sources ++ : asm/make_i386_x86_64_sysv_macho_gas.S ++ asm/jump_i386_x86_64_sysv_macho_gas.S ++ : 32_64 ++ x86 ++ mach-o ++ darwin ++ darwin ++ ; ++ ++alias asm_context_sources ++ : [ make asm/make_i386_x86_64_sysv_macho_gas.o : asm/make_i386_x86_64_sysv_macho_gas.S : @gas ] ++ [ make asm/jump_i386_x86_64_sysv_macho_gas.o : asm/jump_i386_x86_64_sysv_macho_gas.S : @gas ] ++ : 32_64 ++ x86 ++ mach-o ++ darwin ++ ; ++ ++alias asm_context_sources + : asm/make_x86_64_ms_pe_masm.asm + asm/jump_x86_64_ms_pe_masm.asm + dummy.cpp + +=== added file 'libs/context/src/asm/jump_i386_x86_64_sysv_macho_gas.S' +--- libs/context/src/asm/jump_i386_x86_64_sysv_macho_gas.S 1970-01-01 00:00:00 +0000 ++++ libs/context/src/asm/jump_i386_x86_64_sysv_macho_gas.S 2013-12-29 11:33:46 +0000 +@@ -0,0 +1,16 @@ ++/* ++ Copyright Sergue E. Leontiev 2013. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++// Stub file for universal binary ++ ++#if defined(__i386__) ++ #include "jump_i386_sysv_macho_gas.S" ++#elif defined(__x86_64__) ++ #include "jump_x86_64_sysv_macho_gas.S" ++#else ++ #error "No arch's" ++#endif + +=== added file 'libs/context/src/asm/make_i386_x86_64_sysv_macho_gas.S' +--- libs/context/src/asm/make_i386_x86_64_sysv_macho_gas.S 1970-01-01 00:00:00 +0000 ++++ libs/context/src/asm/make_i386_x86_64_sysv_macho_gas.S 2013-12-29 11:32:49 +0000 +@@ -0,0 +1,16 @@ ++/* ++ Copyright Sergue E. Leontiev 2013. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++// Stub file for universal binary ++ ++#if defined(__i386__) ++ #include "make_i386_sysv_macho_gas.S" ++#elif defined(__x86_64__) ++ #include "make_x86_64_sysv_macho_gas.S" ++#else ++ #error "No arch's" ++#endif + diff --git a/patches/boost_macosx_x86_build.patch b/patches/boost_macosx_x86_build.patch new file mode 100644 index 0000000000..4beb68444b --- /dev/null +++ b/patches/boost_macosx_x86_build.patch @@ -0,0 +1,13 @@ +=== modified file 'tools/build/v2/tools/gcc.jam' +--- tools/build/v2/tools/gcc.jam 2013-12-29 13:13:00 +0000 ++++ tools/build/v2/tools/gcc.jam 2013-12-29 14:36:31 +0000 +@@ -635,7 +635,7 @@ + + actions compile.asm + { +- "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" ++ "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) -D$(DEFINES) $(USER_OPTIONS) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" + } + + # Class checking that we do not try to use the static property + From b9e9480e5c7ba25ecea45c8a49a2b5bf9d31892e Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Sun, 29 Dec 2013 20:38:12 +0100 Subject: [PATCH 32/57] [MacOSX]/All purpose - Starting support to static linking --- CMakeModules/download_cairo.cmake | 81 +++++++++++++++++++++++++++ CMakeModules/download_glew.cmake | 72 ++++++++++++++++++++++++ CMakeModules/download_libpng.cmake | 69 +++++++++++++++++++++++ CMakeModules/download_pixman.cmake | 69 +++++++++++++++++++++++ CMakeModules/download_pkgconfig.cmake | 63 +++++++++++++++++++++ 5 files changed, 354 insertions(+) create mode 100644 CMakeModules/download_cairo.cmake create mode 100644 CMakeModules/download_glew.cmake create mode 100644 CMakeModules/download_libpng.cmake create mode 100644 CMakeModules/download_pixman.cmake create mode 100644 CMakeModules/download_pkgconfig.cmake diff --git a/CMakeModules/download_cairo.cmake b/CMakeModules/download_cairo.cmake new file mode 100644 index 0000000000..c8c6fa7db5 --- /dev/null +++ b/CMakeModules/download_cairo.cmake @@ -0,0 +1,81 @@ +# This program source code file is part of KICAD, a free EDA CAD application. +# +# Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck +# Copyright (C) 2013 Kicad Developers, see AUTHORS.txt for contributors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Downloads and builds CAIRO + +#--------------------------------------------------------------------- + +set( CAIRO_RELEASE 1.12.0 ) +set( CAIRO_MD5 e6c85575ba7094f88b637bdfd835a751 ) # re-calc this on every RELEASE change + +# The boost headers [and static libs if built] go here, at the top of KiCad +# source tree in boost_root. +set( CAIRO_ROOT "${PROJECT_SOURCE_DIR}/cairo_root" ) + +#-------------------------------------------------------------------- + +find_package( BZip2 REQUIRED ) + +set( PREFIX ${DOWNLOAD_DIR}/cairo ) + +if (APPLE) + if( CMAKE_OSX_ARCHITECTURES ) + set( CAIRO_CFLAGS "CFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -isysroot ${CMAKE_OSX_SYSROOT} -fno-lto" ) + set( CAIRO_LDFLAGS "LDFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -framework CoreGraphics -framework CoreServices" ) + set( CAIRO_OPTS "--enable-quartz-image" ) + endif( CMAKE_OSX_ARCHITECTURES ) +endif(APPLE) + +# = ${PREFIX}/src/glew +# There is a Bazaar 'boost scratch repo' in /boost and after committing pristine +# download, the patch is applied. This lets you regenerate a new patch at any time +# easily, simply by editing the working tree in and doing "bzr diff" in there. + +ExternalProject_Add( cairo + PREFIX "${PREFIX}" + DOWNLOAD_DIR "${DOWNLOAD_DIR}" + URL http://cairographics.org/releases/cairo-${CAIRO_RELEASE}.tar.gz + URL_MD5 ${CAIRO_MD5} + STAMP_DIR "${PREFIX}" + + DEPENDS pkgconfig pixman libpng + + BUILD_IN_SOURCE 1 + #SOURCE_DIR "${PREFIX}" + #PATCH_COMMAND "" + + CONFIGURE_COMMAND ./configure --prefix=${CAIRO_ROOT} --enable-static --disable-shared + PKG_CONFIG=${PROJECT_SOURCE_DIR}/pkgconfig_root/bin/pkg-config + PKG_CONFIG_PATH=${PROJECT_SOURCE_DIR}/pixman_root/lib/pkgconfig:${PROJECT_SOURCE_DIR}/libpng_root/lib/pkgconfig + --enable-png=yes --enable-svg=yes + --disable-silent-rules + ${CAIRO_CFLAGS} + ${CAIRO_LDFLAGS} + CC=${CMAKE_C_COMPILER} + + #BINARY_DIR "${PREFIX}" + + BUILD_COMMAND make + + INSTALL_DIR "${CAIRO_ROOT}" + INSTALL_COMMAND make install + ) diff --git a/CMakeModules/download_glew.cmake b/CMakeModules/download_glew.cmake new file mode 100644 index 0000000000..6e02a5b79e --- /dev/null +++ b/CMakeModules/download_glew.cmake @@ -0,0 +1,72 @@ +# This program source code file is part of KICAD, a free EDA CAD application. +# +# Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck +# Copyright (C) 2013 Kicad Developers, see AUTHORS.txt for contributors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Downloads and builds GLEW + +#--------------------------------------------------------------------- + +set( GLEW_RELEASE 1.10.0 ) +set( GLEW_MD5 2f09e5e6cb1b9f3611bcac79bc9c2d5d ) # re-calc this on every RELEASE change + +# The boost headers [and static libs if built] go here, at the top of KiCad +# source tree in boost_root. +set( GLEW_ROOT "${PROJECT_SOURCE_DIR}/glew_root" ) + +#-------------------------------------------------------------------- + +find_package( BZip2 REQUIRED ) + +set( PREFIX ${DOWNLOAD_DIR}/glew ) + +if (APPLE) + if( CMAKE_OSX_ARCHITECTURES ) + set( GLEW_CFLAGS "CFLAGS.EXTRA=-arch ${CMAKE_OSX_ARCHITECTURES} -mmacosx-version-min=10.5" ) + set( GLEW_LDFLAGS "LDFLAGS.EXTRA=-arch ${CMAKE_OSX_ARCHITECTURES} -mmacosx-version-min=10.5" ) + set( GLEW_STRIP "STRIP=") + endif( CMAKE_OSX_ARCHITECTURES ) +endif(APPLE) + +# = ${PREFIX}/src/glew +# There is a Bazaar 'boost scratch repo' in /boost and after committing pristine +# download, the patch is applied. This lets you regenerate a new patch at any time +# easily, simply by editing the working tree in and doing "bzr diff" in there. + +ExternalProject_Add( glew + PREFIX "${PREFIX}" + DOWNLOAD_DIR "${DOWNLOAD_DIR}" + URL http://sourceforge.net/projects/glew/files/glew/1.10.0/glew-${GLEW_RELEASE}.tgz + URL_MD5 ${GLEW_MD5} + STAMP_DIR "${PREFIX}" + + #SOURCE_DIR "${PREFIX}" + BUILD_IN_SOURCE 1 + + #PATCH_COMMAND "true" + CONFIGURE_COMMAND "" + + #BINARY_DIR "${PREFIX}" + + BUILD_COMMAND make ${GLEW_CFLAGS} ${GLEW_LDFLAGS} ${GLEW_STRIP} + + INSTALL_DIR "${GLEW_ROOT}" + INSTALL_COMMAND make GLEW_DEST="${GLEW_ROOT}" install && ranlib "${GLEW_ROOT}/lib/libGLEW.a" + ) diff --git a/CMakeModules/download_libpng.cmake b/CMakeModules/download_libpng.cmake new file mode 100644 index 0000000000..93074f6848 --- /dev/null +++ b/CMakeModules/download_libpng.cmake @@ -0,0 +1,69 @@ +# This program source code file is part of KICAD, a free EDA CAD application. +# +# Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck +# Copyright (C) 2013 Kicad Developers, see AUTHORS.txt for contributors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Downloads and builds LIBPNG + +#--------------------------------------------------------------------- + +set( LIBPNG_RELEASE 1.4.12 ) +set( LIBPNG_MD5 849b14d88e1240a1b9f2cb39dd39701e ) # re-calc this on every RELEASE change + +# The boost headers [and static libs if built] go here, at the top of KiCad +# source tree in boost_root. +set( LIBPNG_ROOT "${PROJECT_SOURCE_DIR}/libpng_root" ) + +#-------------------------------------------------------------------- + +find_package( BZip2 REQUIRED ) + +set( PREFIX ${DOWNLOAD_DIR}/libpng ) + +if (APPLE) + if( CMAKE_OSX_ARCHITECTURES ) + SET( LIBPNG_CFLAGS "CFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES}") + endif( CMAKE_OSX_ARCHITECTURES ) +endif(APPLE) + +# = ${PREFIX}/src/glew +# There is a Bazaar 'boost scratch repo' in /boost and after committing pristine +# download, the patch is applied. This lets you regenerate a new patch at any time +# easily, simply by editing the working tree in and doing "bzr diff" in there. + +ExternalProject_Add( libpng + PREFIX "${PREFIX}" + DOWNLOAD_DIR "${DOWNLOAD_DIR}" + URL http://sourceforge.net/projects/libpng/files/libpng14/${LIBPNG_RELEASE}/libpng-${LIBPNG_RELEASE}.tar.bz2 + URL_MD5 ${LIBPNG_MD5} + STAMP_DIR "${PREFIX}" + + #SOURCE_DIR "${PREFIX}" + BUILD_IN_SOURCE 1 + + #PATCH_COMMAND "true" + CONFIGURE_COMMAND ./configure --prefix=${LIBPNG_ROOT} --enable-static --disable-shared ${LIBPNG_CFLAGS} + #BINARY_DIR "${PREFIX}" + + BUILD_COMMAND make + + INSTALL_DIR "${LIBPNG_ROOT}" + INSTALL_COMMAND make install + ) diff --git a/CMakeModules/download_pixman.cmake b/CMakeModules/download_pixman.cmake new file mode 100644 index 0000000000..34981d44d9 --- /dev/null +++ b/CMakeModules/download_pixman.cmake @@ -0,0 +1,69 @@ +# This program source code file is part of KICAD, a free EDA CAD application. +# +# Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck +# Copyright (C) 2013 Kicad Developers, see AUTHORS.txt for contributors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Downloads and builds PIXMAN + +#--------------------------------------------------------------------- + +set( PIXMAN_RELEASE 0.32.4 ) +set( PIXMAN_MD5 eba449138b972fbf4547a8c152fea162 ) # re-calc this on every RELEASE change + +# The boost headers [and static libs if built] go here, at the top of KiCad +# source tree in boost_root. +set( PIXMAN_ROOT "${PROJECT_SOURCE_DIR}/pixman_root" ) + +#-------------------------------------------------------------------- + +find_package( BZip2 REQUIRED ) + +set( PREFIX ${DOWNLOAD_DIR}/pixman ) + +if (APPLE) + if( CMAKE_OSX_ARCHITECTURES ) + set(PIXMAN_CPPFLAGS "CFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -fno-common -mmacosx-version-min=10.5") + endif( CMAKE_OSX_ARCHITECTURES ) +endif(APPLE) + +# = ${PREFIX}/src/glew +# There is a Bazaar 'boost scratch repo' in /boost and after committing pristine +# download, the patch is applied. This lets you regenerate a new patch at any time +# easily, simply by editing the working tree in and doing "bzr diff" in there. + +ExternalProject_Add( pixman + PREFIX "${PREFIX}" + DOWNLOAD_DIR "${DOWNLOAD_DIR}" + URL http://cairographics.org/releases/pixman-${PIXMAN_RELEASE}.tar.gz + URL_MD5 ${PIXMAN_MD5} + STAMP_DIR "${PREFIX}" + + #SOURCE_DIR "${PREFIX}" + BUILD_IN_SOURCE 1 + + #PATCH_COMMAND "true" + CONFIGURE_COMMAND ./configure --prefix=${PIXMAN_ROOT} --enable-static=yes --enable-shared=no ${PIXMAN_CPPFLAGS} CC=${CMAKE_C_COMPILER} + #BINARY_DIR "${PREFIX}" + + BUILD_COMMAND make + + INSTALL_DIR "${PIXMAN_ROOT}" + INSTALL_COMMAND make install + ) diff --git a/CMakeModules/download_pkgconfig.cmake b/CMakeModules/download_pkgconfig.cmake new file mode 100644 index 0000000000..251a499e90 --- /dev/null +++ b/CMakeModules/download_pkgconfig.cmake @@ -0,0 +1,63 @@ +# This program source code file is part of KICAD, a free EDA CAD application. +# +# Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck +# Copyright (C) 2013 Kicad Developers, see AUTHORS.txt for contributors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Downloads and builds CAIRO + +#--------------------------------------------------------------------- + +set( PKGCONFIG_RELEASE 0.28 ) +set( PKGCONFIG_MD5 aa3c86e67551adc3ac865160e34a2a0d ) # re-calc this on every RELEASE change + +# The boost headers [and static libs if built] go here, at the top of KiCad +# source tree in boost_root. +set( PKGCONFIG_ROOT "${PROJECT_SOURCE_DIR}/pkgconfig_root" ) + +#-------------------------------------------------------------------- + +find_package( BZip2 REQUIRED ) + +set( PREFIX ${DOWNLOAD_DIR}/pkgconfig ) + +# = ${PREFIX}/src/glew +# There is a Bazaar 'boost scratch repo' in /boost and after committing pristine +# download, the patch is applied. This lets you regenerate a new patch at any time +# easily, simply by editing the working tree in and doing "bzr diff" in there. + +ExternalProject_Add( pkgconfig + PREFIX "${PREFIX}" + DOWNLOAD_DIR "${DOWNLOAD_DIR}" + URL http://pkgconfig.freedesktop.org/releases/pkg-config-${PKGCONFIG_RELEASE}.tar.gz + URL_MD5 ${PKGCONFIG_MD5} + STAMP_DIR "${PREFIX}" + + #SOURCE_DIR "${PREFIX}" + BUILD_IN_SOURCE 1 + + #PATCH_COMMAND "true" + CONFIGURE_COMMAND ./configure --prefix=${PKGCONFIG_ROOT} --with-internal-glib --enable-shared=no --enable-static=yes --disable-silent-rules + #BINARY_DIR "${PREFIX}" + + BUILD_COMMAND make + + INSTALL_DIR "${PKGCONFIG_ROOT}" + INSTALL_COMMAND make install + ) From 19ebe3feb8a83fe3b51e5592a35e185088624a1e Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 29 Dec 2013 23:05:06 +0100 Subject: [PATCH 33/57] eeschema: fix compil issue. Boost build: boost lib context built only on OSX (does not build on mingw, and not needed) --- CMakeModules/download_boost.cmake | 10 ++++++++-- eeschema/schematic_undo_redo.cpp | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index bf43e685d6..7745ded765 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -40,9 +40,15 @@ set( BOOST_ROOT "${PROJECT_SOURCE_DIR}/boost_root" ) # Space separated list which indicates the subset of boost libraries to compile. # Chosen libraries are based on AVHTTP requirements, and possibly # unit_test_framework for its own worth. -# tool_manager.cpp -> coroutine -> context (_jump_fcontext) +# tool_manager.cpp -> coroutine -> context (_jump_fcontext) (on OSX) +if( APPLE ) + set( BOOST_EXTRA_LIBS "context" ) +else() + set( BOOST_EXTRA_LIBS "" ) +endif() + set( BOOST_LIBS_BUILT - context + ${BOOST_EXTRA_LIBS} #coroutine date_time #exception diff --git a/eeschema/schematic_undo_redo.cpp b/eeschema/schematic_undo_redo.cpp index 2d6dbba449..2312cae2b1 100644 --- a/eeschema/schematic_undo_redo.cpp +++ b/eeschema/schematic_undo_redo.cpp @@ -269,7 +269,7 @@ void SCH_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool aRed for( int ii = aList->GetCount() - 1; ii >= 0; ii-- ) { item = (SCH_ITEM*) aList->GetPickedItem( ii ); - wxAssert( item ); + wxASSERT( item ); item->ClearFlags(); From e0eb666f6c98d2ed3f1e50be99c7beda8486de7a Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Mon, 30 Dec 2013 01:27:03 +0100 Subject: [PATCH 34/57] [MacOSX]/All purpose - Support for static linking --- CMakeLists.txt | 42 +++++++++++++++++++++++++++++-- CMakeModules/download_cairo.cmake | 7 +++++- cvpcb/CMakeLists.txt | 1 + pcbnew/CMakeLists.txt | 4 ++- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0473081e3..cb7fc24c38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,10 @@ option( KICAD_SCRIPTING_WXPYTHON "set this option ON to build wxpython implementation for wx interface building in python and py.shell" ) +option( KICAD_BUILD_STATIC + "Builds Kicad and all libraries static (except wx-widgets)" + ) + # when option KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES is enabled: # PYTHON_EXECUTABLE can be defined when invoking cmake # ( use -DPYTHON_EXECUTABLE=/python.exe or python2 ) @@ -156,7 +160,12 @@ if( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) if( GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8 ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-local-typedefs" ) endif() - + + if( APPLE ) + set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__ASSERTMACROS__" ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__ASSERTMACROS__" ) + endif() + endif( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") if( KICAD_KEEPCASE ) @@ -242,6 +251,19 @@ include( Functions ) include( ExternalProject ) +if ( APPLE ) + set( KICAD_BUILD_STATIC ON) + + if( NOT CMAKE_CXX_COMPILER ) + EXEC_PROGRAM( wx-config ARGS --cc OUTPUT_VARIABLE CMAKE_C_COMPILER ) + endif( NOT CMAKE_CXX_COMPILER ) + + if( NOT CMAKE_CXX_COMPILER ) + EXEC_PROGRAM( wx-config ARGS --cxx OUTPUT_VARIABLE CMAKE_CXX_COMPILER ) + endif( NOT CMAKE_CXX_COMPILER ) + +endif( APPLE ) + #================================================ # Find libraries that are needed to build KiCad. #================================================ @@ -256,6 +278,23 @@ add_definitions(-DWX_COMPATIBILITY) find_package( OpenGL QUIET ) check_find_package_result( OPENGL_FOUND "OpenGL" ) +if ( KICAD_BUILD_STATIC ) + + #set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so;.dylib;.dll") + + include( download_pkgconfig ) + set( PKG_CONFIG_EXECUTABLE "${PKGCONFIG_ROOT}/bin/pkg-config") + include( download_glew ) + set( GLEW_GLEW_LIBRARY "${GLEW_ROOT}/lib/libGLEW.a") + set( GLEW_INCLUDE_DIR "${GLEW_ROOT}/include") + include( download_pixman ) + set( PIXMAN_LIBRARY "${PIXMAN_ROOT}/lib/libpixman-1.a") + include( download_libpng ) + include( download_cairo ) + set( CAIRO_INCLUDE_DIR "${CAIRO_ROOT}/include/cairo") + set( CAIRO_LIBRARY "${CAIRO_ROOT}/lib/libcairo.a") +endif( KICAD_BUILD_STATIC ) + ##################### # Find GLEW library # ##################### @@ -272,7 +311,6 @@ check_find_package_result(CAIRO_FOUND "Cairo") ################################################# include( download_boost ) - ########################## # Find wxWidgets library # ########################## diff --git a/CMakeModules/download_cairo.cmake b/CMakeModules/download_cairo.cmake index c8c6fa7db5..7720428d2d 100644 --- a/CMakeModules/download_cairo.cmake +++ b/CMakeModules/download_cairo.cmake @@ -39,10 +39,15 @@ set( PREFIX ${DOWNLOAD_DIR}/cairo ) if (APPLE) if( CMAKE_OSX_ARCHITECTURES ) - set( CAIRO_CFLAGS "CFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -isysroot ${CMAKE_OSX_SYSROOT} -fno-lto" ) + set( CAIRO_CFLAGS "CFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -fno-lto" ) set( CAIRO_LDFLAGS "LDFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -framework CoreGraphics -framework CoreServices" ) set( CAIRO_OPTS "--enable-quartz-image" ) endif( CMAKE_OSX_ARCHITECTURES ) + + if( CMAKE_OSX_SYSROOT ) + set( CAIRO_CFLAGS "${CAIRO_CFLAGS} -isysroot ${CMAKE_OSX_SYSROOT}") + endif( CMAKE_OSX_SYSROOT) + endif(APPLE) # = ${PREFIX}/src/glew diff --git a/cvpcb/CMakeLists.txt b/cvpcb/CMakeLists.txt index 8498688eaf..53f39ada5e 100644 --- a/cvpcb/CMakeLists.txt +++ b/cvpcb/CMakeLists.txt @@ -107,6 +107,7 @@ target_link_libraries( cvpcb ${GDI_PLUS_LIBRARIES} ${GLEW_LIBRARIES} ${CAIRO_LIBRARIES} + ${PIXMAN_LIBRARY} ) # Only for win32 cross compilation using MXE diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 24c30ccea8..575cc744bf 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -367,7 +367,7 @@ if( KICAD_SCRIPTING_MODULES ) 3d-viewer pcbcommon pnsrouter - common + c${ommon pcad2kicadpcb lib_dxf ${GITHUB_PLUGIN_LIBRARIES} @@ -376,6 +376,7 @@ if( KICAD_SCRIPTING_MODULES ) gal ${GLEW_LIBRARIES} ${CAIRO_LIBRARIES} + ${PIXMAN_LIBRARY} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${GDI_PLUS_LIBRARIES} @@ -553,6 +554,7 @@ target_link_libraries( pcbnew ${PCBNEW_EXTRA_LIBS} ${GLEW_LIBRARIES} ${CAIRO_LIBRARIES} + ${PIXMAN_LIBRARY} ${Boost_LIBRARIES} # must follow GITHUB ) From 5777523b35fc1001623aee08c9b62c261e7316d8 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Mon, 30 Dec 2013 01:36:57 +0100 Subject: [PATCH 35/57] Fixing typo --- pcbnew/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 575cc744bf..06cec9d8e1 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -367,7 +367,7 @@ if( KICAD_SCRIPTING_MODULES ) 3d-viewer pcbcommon pnsrouter - c${ommon + common pcad2kicadpcb lib_dxf ${GITHUB_PLUGIN_LIBRARIES} From 4d3ac11467fda9f69f62b1ba9e3b6e73f8a9a5a5 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Mon, 30 Dec 2013 16:53:09 +0100 Subject: [PATCH 36/57] [MacOSX] fixes issues with previous OSX compilers gcc-4.2 and makes a better general beaviour of KICAD_BUILD_STATIC --- CMakeModules/download_boost.cmake | 29 ++++++++++++++++++++++++----- CMakeModules/download_cairo.cmake | 14 +++++++++----- CMakeModules/download_libpng.cmake | 2 +- CMakeModules/download_pixman.cmake | 2 +- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 7745ded765..9c6041ecef 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -97,6 +97,13 @@ string( REPLACE "unit_test_framework" "test" boost_libs_list "${BOOST_LIBS_BUILT # Default Toolset set( BOOST_TOOLSET "toolset=gcc" ) +if( KICAD_BUILD_STATIC ) + set(BOOST_LINKTYPE "link=static") +else() + set( BOOST_LINKTYPE "#link=static") +endif() + + if( MINGW ) if( MSYS ) # The Boost system does not build properly on MSYS using bootstrap.sh. Running @@ -122,14 +129,18 @@ else() unset( b2_libs ) endif() - if( APPLE ) # I set this to being compatible with wxWidgets # wxWidgets still using libstdc++ (gcc), meanwhile OSX # has switched to libc++ (llvm) by default - set(BOOST_CXXFLAGS "cxxflags=-mmacosx-version-min=10.5 -fno-common -fno-lto" ) - set(BOOST_LINKFLAGS "linkflags=-mmacosx-version-min=10.5 -fno-common -fno-lto" ) + set(BOOST_CXXFLAGS "cxxflags=-mmacosx-version-min=10.5 -fno-common" ) + set(BOOST_LINKFLAGS "linkflags=-mmacosx-version-min=10.5 -fno-common" ) set(BOOST_TOOLSET "toolset=darwin" ) + + if( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) + set(BOOST_CXXFLAGS "${BOOST_CXXFLAGS} -fno-lto" ) + set(BOOST_LINKFLAGS "${BOOST_LINKFLAGS} -fno-lto" ) + endif() if( CMAKE_OSX_ARCHITECTURES ) @@ -142,9 +153,17 @@ if( APPLE ) if( (CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" OR CMAKE_OSX_ARCHITECTURES MATCHES "386") AND (CMAKE_OSX_ARCHITECTURES MATCHES "ppc")) - message("-- BOOST found ppc/intel Architecture") + message("-- BOOST found ppc/x86 Architecture") set(BOOST_ARCHITECTURE "architecture=combined") + elseif( (CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" OR CMAKE_OSX_ARCHITECTURES MATCHES "386") ) + message("-- BOOST found x86 Architecture") + + set(BOOST_ARCHITECTURE "architecture=x86") + elseif( (CMAKE_OSX_ARCHITECTURES MATCHES "ppc64" OR CMAKE_OSX_ARCHITECTURES MATCHES "ppc") ) + message("-- BOOST found ppc Architecture") + + set(BOOST_ARCHITECTURE "architecture=ppc") endif() set(BOOST_CFLAGS "${BOOST_CFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) @@ -188,7 +207,7 @@ ExternalProject_Add( boost ${BOOST_ADDRESSMODEL} ${BOOST_ARCHITECTURE} ${b2_libs} - #link=static + ${BOOST_LINKTYPE} --prefix= install diff --git a/CMakeModules/download_cairo.cmake b/CMakeModules/download_cairo.cmake index 7720428d2d..82e9b86b4d 100644 --- a/CMakeModules/download_cairo.cmake +++ b/CMakeModules/download_cairo.cmake @@ -39,13 +39,18 @@ set( PREFIX ${DOWNLOAD_DIR}/cairo ) if (APPLE) if( CMAKE_OSX_ARCHITECTURES ) - set( CAIRO_CFLAGS "CFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -fno-lto" ) - set( CAIRO_LDFLAGS "LDFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -framework CoreGraphics -framework CoreServices" ) + set( CAIRO_CFLAGS "CFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES}" ) + set( CAIRO_LDFLAGS "LDFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -framework CoreServices -framework Cocoa" ) set( CAIRO_OPTS "--enable-quartz-image" ) endif( CMAKE_OSX_ARCHITECTURES ) + if( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) + set(CAIRO_CFLAGS "${CAIRO_CFLAGS} -fno-lto" ) + endif() + if( CMAKE_OSX_SYSROOT ) - set( CAIRO_CFLAGS "${CAIRO_CFLAGS} -isysroot ${CMAKE_OSX_SYSROOT}") + #set( CAIRO_CFLAGS "${CAIRO_CFLAGS} -isysroot ${CMAKE_OSX_SYSROOT}") + set( CAIRO_LDFLAGS "${CAIRO_LDFLAGS} -isysroot ${CMAKE_OSX_SYSROOT}") endif( CMAKE_OSX_SYSROOT) endif(APPLE) @@ -72,10 +77,9 @@ ExternalProject_Add( cairo PKG_CONFIG=${PROJECT_SOURCE_DIR}/pkgconfig_root/bin/pkg-config PKG_CONFIG_PATH=${PROJECT_SOURCE_DIR}/pixman_root/lib/pkgconfig:${PROJECT_SOURCE_DIR}/libpng_root/lib/pkgconfig --enable-png=yes --enable-svg=yes - --disable-silent-rules + --disable-silent-rules --disable-dependency-tracking ${CAIRO_CFLAGS} ${CAIRO_LDFLAGS} - CC=${CMAKE_C_COMPILER} #BINARY_DIR "${PREFIX}" diff --git a/CMakeModules/download_libpng.cmake b/CMakeModules/download_libpng.cmake index 93074f6848..043d1d259d 100644 --- a/CMakeModules/download_libpng.cmake +++ b/CMakeModules/download_libpng.cmake @@ -59,7 +59,7 @@ ExternalProject_Add( libpng BUILD_IN_SOURCE 1 #PATCH_COMMAND "true" - CONFIGURE_COMMAND ./configure --prefix=${LIBPNG_ROOT} --enable-static --disable-shared ${LIBPNG_CFLAGS} + CONFIGURE_COMMAND ./configure --prefix=${LIBPNG_ROOT} --enable-static --disable-shared ${LIBPNG_CFLAGS} --disable-dependency-tracking #BINARY_DIR "${PREFIX}" BUILD_COMMAND make diff --git a/CMakeModules/download_pixman.cmake b/CMakeModules/download_pixman.cmake index 34981d44d9..74716c8cfa 100644 --- a/CMakeModules/download_pixman.cmake +++ b/CMakeModules/download_pixman.cmake @@ -59,7 +59,7 @@ ExternalProject_Add( pixman BUILD_IN_SOURCE 1 #PATCH_COMMAND "true" - CONFIGURE_COMMAND ./configure --prefix=${PIXMAN_ROOT} --enable-static=yes --enable-shared=no ${PIXMAN_CPPFLAGS} CC=${CMAKE_C_COMPILER} + CONFIGURE_COMMAND ./configure --prefix=${PIXMAN_ROOT} --enable-static=yes --enable-shared=no ${PIXMAN_CPPFLAGS} --disable-dependency-tracking #BINARY_DIR "${PREFIX}" BUILD_COMMAND make From 5ae8d9b2202841dd5ea8fead8b67cdddc23c11b1 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Mon, 30 Dec 2013 11:53:12 -0600 Subject: [PATCH 37/57] Add -fvisibility options to significantly reduce binary sizes. Switch to boost::context for all platforms, almost. Please try your platform by commenting out fcontext.s in common/CMakeLists.txt. --- CMakeLists.txt | 26 +- CMakeModules/PerformFeatureChecks.cmake | 61 +-- CMakeModules/download_boost.cmake | 46 +- common/CMakeLists.txt | 31 +- demos/CMakeLists.txt | 13 +- patches/boost_mingw.patch | 594 ++++++++++++++++++++++++ 6 files changed, 697 insertions(+), 74 deletions(-) create mode 100644 patches/boost_mingw.patch diff --git a/CMakeLists.txt b/CMakeLists.txt index cb7fc24c38..7d200af8f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,14 @@ if( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) set( CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG" ) set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG" ) + if( GXX_HAS_VISIBILITY_FLAG ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden" ) + endif() + + if( GXX_HAS_VISIBILITY_INLINES_FLAG ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden" ) + endif() + if( MINGW ) set( CMAKE_EXE_LINKER_FLAGS_RELEASE "-s" ) @@ -132,8 +140,8 @@ if( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) # Disable this response file for includes ( See Windows-GNU.cmake module ) if( WIN32 AND MSYS AND NOT CMAKE_CROSSCOMPILING ) # fixme: it is needed only with MSYS+MINGW32? or always under MINGW - if (${CMAKE_SIZEOF_VOID_P} MATCHES 4) - set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 0) + if( ${CMAKE_SIZEOF_VOID_P} MATCHES 4 ) + set( CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 0 ) endif() endif() @@ -160,13 +168,13 @@ if( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) if( GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8 ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-local-typedefs" ) endif() - - if( APPLE ) + + if( APPLE ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__ASSERTMACROS__" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__ASSERTMACROS__" ) endif() - -endif( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + +endif( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) if( KICAD_KEEPCASE ) add_definitions( -DKICAD_KEEPCASE ) @@ -251,7 +259,7 @@ include( Functions ) include( ExternalProject ) -if ( APPLE ) +if ( APPLE ) set( KICAD_BUILD_STATIC ON) if( NOT CMAKE_CXX_COMPILER ) @@ -286,13 +294,13 @@ if ( KICAD_BUILD_STATIC ) set( PKG_CONFIG_EXECUTABLE "${PKGCONFIG_ROOT}/bin/pkg-config") include( download_glew ) set( GLEW_GLEW_LIBRARY "${GLEW_ROOT}/lib/libGLEW.a") - set( GLEW_INCLUDE_DIR "${GLEW_ROOT}/include") + set( GLEW_INCLUDE_DIR "${GLEW_ROOT}/include") include( download_pixman ) set( PIXMAN_LIBRARY "${PIXMAN_ROOT}/lib/libpixman-1.a") include( download_libpng ) include( download_cairo ) set( CAIRO_INCLUDE_DIR "${CAIRO_ROOT}/include/cairo") - set( CAIRO_LIBRARY "${CAIRO_ROOT}/lib/libcairo.a") + set( CAIRO_LIBRARY "${CAIRO_ROOT}/lib/libcairo.a") endif( KICAD_BUILD_STATIC ) ##################### diff --git a/CMakeModules/PerformFeatureChecks.cmake b/CMakeModules/PerformFeatureChecks.cmake index 6f8bc8586b..5c9d092957 100644 --- a/CMakeModules/PerformFeatureChecks.cmake +++ b/CMakeModules/PerformFeatureChecks.cmake @@ -36,17 +36,21 @@ # Consider it a benchmark when writing your own feature tests. # -macro(perform_feature_checks) +macro( perform_feature_checks ) - include(CheckIncludeFile) - #include(CheckFunctionExists) - include(CheckLibraryExists) - include(CheckSymbolExists) - include(CheckIncludeFileCXX) - include(CheckCXXSymbolExists) - include(CheckCXXSourceCompiles) + include( CheckIncludeFile ) + #include( CheckFunctionExists ) + include( CheckLibraryExists ) + include( CheckSymbolExists ) + include( CheckIncludeFileCXX ) + include( CheckCXXSymbolExists ) + include( CheckCXXSourceCompiles ) + include( CheckCXXCompilerFlag ) - check_include_file("malloc.h" HAVE_MALLOC_H) + check_cxx_compiler_flag( -fvisibility=hidden GXX_HAS_VISIBILITY_FLAG ) + check_cxx_compiler_flag( -fvisibility-inlines-hidden GXX_HAS_VISIBILITY_INLINES_FLAG ) + + check_include_file( "malloc.h" HAVE_MALLOC_H ) # FIXME: Visual C++ does not support the "not" keyword natively. It is # defined as a macro in . There should be a cmake macro @@ -54,7 +58,7 @@ macro(perform_feature_checks) # then check for and include it. Although it doesn't # appear to cause any problems with other compilers, that doesn't # mean won't fail somewhere down the line. - check_include_file("iso646.h" HAVE_ISO646_H) + check_include_file( "iso646.h" HAVE_ISO646_H ) # The STDINT header file test is required because MinGW under Windows # doesn't define HAVE_STDINT_H even though it does have it. @@ -62,7 +66,7 @@ macro(perform_feature_checks) # We need to add it to the global compiler definitions as config.h is not # included in pyport.h which is where the problem ocurrs without this # fix. - check_include_file("stdint.h" HAVE_STDINT_H) + check_include_file( "stdint.h" HAVE_STDINT_H ) if( HAVE_STDINT_H ) add_definitions( -DHAVE_STDINT_H ) @@ -73,40 +77,41 @@ macro(perform_feature_checks) # re-introduce this. # check_include_file("strings.h" HAVE_STRINGS_H) - check_symbol_exists(strcasecmp "string.h" HAVE_STRCASECMP) - check_symbol_exists(strcasecmp "strings.h" HAVE_STRCASECMP) - check_symbol_exists(strncasecmp "string.h" HAVE_STRNCASECMP) - check_symbol_exists(strncasecmp "strings.h" HAVE_STRNCASECMP) + check_symbol_exists( strcasecmp "string.h" HAVE_STRCASECMP ) + check_symbol_exists( strcasecmp "strings.h" HAVE_STRCASECMP ) + check_symbol_exists( strncasecmp "string.h" HAVE_STRNCASECMP ) + check_symbol_exists( strncasecmp "strings.h" HAVE_STRNCASECMP ) check_symbol_exists( strtok_r "string.h" HAVE_STRTOKR ) # Some platforms define malloc and free in malloc.h instead of stdlib.h. - check_symbol_exists(malloc "stdlib.h" MALLOC_IN_STDLIB_H) + check_symbol_exists( malloc "stdlib.h" MALLOC_IN_STDLIB_H ) # Check for functions in math.h. - check_include_file("math.h" HAVE_MATH_H) + check_include_file( "math.h" HAVE_MATH_H ) # Check for functions in C++ cmath. - check_include_file_cxx(cmath HAVE_CXX_CMATH) - check_cxx_symbol_exists(asinh cmath HAVE_CMATH_ASINH ) - check_cxx_symbol_exists(acosh cmath HAVE_CMATH_ACOSH ) - check_cxx_symbol_exists(atanh cmath HAVE_CMATH_ATANH ) + check_include_file_cxx( cmath HAVE_CXX_CMATH ) + check_cxx_symbol_exists( asinh cmath HAVE_CMATH_ASINH ) + check_cxx_symbol_exists( acosh cmath HAVE_CMATH_ACOSH ) + check_cxx_symbol_exists( atanh cmath HAVE_CMATH_ATANH ) # CMakes check_cxx_symbol_exists() doesn't work for templates so we must create a # small program to verify isinf() exists in cmath. check_cxx_source_compiles( "#include \nint main(int argc, char** argv)\n{\n (void)argv;\n std::isinf(1.0); (void)argc;\n return 0;\n}\n" HAVE_CMATH_ISINF ) - #check_symbol_exists(clock_gettime "time.h" HAVE_CLOCK_GETTIME) non-standard library, does not work - check_library_exists(rt clock_gettime "" HAVE_CLOCK_GETTIME) + #check_symbol_exists( clock_gettime "time.h" HAVE_CLOCK_GETTIME ) non-standard library, does not work + check_library_exists( rt clock_gettime "" HAVE_CLOCK_GETTIME ) # HAVE_GETTIMEOFDAY is already in use within 2.9 wxWidgets, so use HAVE_GETTIMEOFDAY_FUNC - check_symbol_exists(gettimeofday "sys/time.h" HAVE_GETTIMEOFDAY_FUNC) + check_symbol_exists( gettimeofday "sys/time.h" HAVE_GETTIMEOFDAY_FUNC ) # Check for Posix getc_unlocked() for improved performance over getc(). Fall back to # getc() on platforms where getc_unlocked() doesn't exist. - check_symbol_exists(getc_unlocked "stdio.h" HAVE_FGETC_NOLOCK) + check_symbol_exists( getc_unlocked "stdio.h" HAVE_FGETC_NOLOCK ) # Generate config.h. - configure_file(${PROJECT_SOURCE_DIR}/CMakeModules/config.h.cmake - ${CMAKE_BINARY_DIR}/config.h) + configure_file( ${PROJECT_SOURCE_DIR}/CMakeModules/config.h.cmake + ${CMAKE_BINARY_DIR}/config.h + ) -endmacro(perform_feature_checks) +endmacro( perform_feature_checks ) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 9c6041ecef..9ab0c571d3 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -41,14 +41,9 @@ set( BOOST_ROOT "${PROJECT_SOURCE_DIR}/boost_root" ) # Chosen libraries are based on AVHTTP requirements, and possibly # unit_test_framework for its own worth. # tool_manager.cpp -> coroutine -> context (_jump_fcontext) (on OSX) -if( APPLE ) - set( BOOST_EXTRA_LIBS "context" ) -else() - set( BOOST_EXTRA_LIBS "" ) -endif() set( BOOST_LIBS_BUILT - ${BOOST_EXTRA_LIBS} + context #coroutine date_time #exception @@ -98,9 +93,9 @@ string( REPLACE "unit_test_framework" "test" boost_libs_list "${BOOST_LIBS_BUILT set( BOOST_TOOLSET "toolset=gcc" ) if( KICAD_BUILD_STATIC ) - set(BOOST_LINKTYPE "link=static") + set( BOOST_LINKTYPE "link=static" ) else() - set( BOOST_LINKTYPE "#link=static") + unset( BOOST_LINKTYPE ) endif() @@ -129,14 +124,15 @@ else() unset( b2_libs ) endif() + if( APPLE ) # I set this to being compatible with wxWidgets # wxWidgets still using libstdc++ (gcc), meanwhile OSX # has switched to libc++ (llvm) by default - set(BOOST_CXXFLAGS "cxxflags=-mmacosx-version-min=10.5 -fno-common" ) - set(BOOST_LINKFLAGS "linkflags=-mmacosx-version-min=10.5 -fno-common" ) - set(BOOST_TOOLSET "toolset=darwin" ) - + set( BOOST_CXXFLAGS "cxxflags=-mmacosx-version-min=10.5 -fno-common" ) + set( BOOST_LINKFLAGS "linkflags=-mmacosx-version-min=10.5 -fno-common" ) + set( BOOST_TOOLSET "toolset=darwin" ) + if( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) set(BOOST_CXXFLAGS "${BOOST_CXXFLAGS} -fno-lto" ) set(BOOST_LINKFLAGS "${BOOST_LINKFLAGS} -fno-lto" ) @@ -146,9 +142,9 @@ if( APPLE ) if( (CMAKE_OSX_ARCHITECTURES MATCHES "386" OR CMAKE_OSX_ARCHITECTURES MATCHES "ppc ") AND (CMAKE_OSX_ARCHITECTURES MATCHES "64")) - message("-- BOOST found 32/64 Address Model") + message( "-- BOOST found 32/64 Address Model" ) - set(BOOST_ADDRESSMODEL "address-model=32_64") + set( BOOST_ADDRESSMODEL "address-model=32_64" ) endif() if( (CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" OR CMAKE_OSX_ARCHITECTURES MATCHES "386") AND @@ -166,9 +162,9 @@ if( APPLE ) set(BOOST_ARCHITECTURE "architecture=ppc") endif() - set(BOOST_CFLAGS "${BOOST_CFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) - set(BOOST_CXXFLAGS "${BOOST_CXXFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) - set(BOOST_LINKFLAGS "${BOOST_LINKFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) + set( BOOST_CFLAGS "${BOOST_CFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) + set( BOOST_CXXFLAGS "${BOOST_CXXFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) + set( BOOST_LINKFLAGS "${BOOST_LINKFLAGS} -arch ${CMAKE_OSX_ARCHITECTURES}" ) endif() endif() @@ -187,9 +183,21 @@ ExternalProject_Add( boost # PATCH_COMMAND continuation (any *_COMMAND here can be continued with COMMAND): COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_minkowski.patch" COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_cstdint.patch" + COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_macosx_x86.patch" #https://svn.boost.org/trac/boost/ticket/8266 + # tell bzr about new files added by last patch so above "bzr revert" works: + COMMAND bzr add libs/context/src/asm/jump_i386_x86_64_sysv_macho_gas.S + COMMAND bzr add libs/context/src/asm/make_i386_x86_64_sysv_macho_gas.S + COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_macosx_x86_build.patch" #https://svn.boost.org/trac/boost/ticket/8266 + COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_mingw.patch" #https://svn.boost.org/trac/boost/ticket/7262 + # tell bzr about new files added by last patch so above "bzr revert" works: + COMMAND bzr add libs/context/src/asm/make_i386_ms_pe_gas.S + COMMAND bzr add libs/context/src/asm/jump_i386_ms_pe_gas.S + COMMAND bzr add libs/context/src/asm/make_x86_64_ms_pe_gas.S + COMMAND bzr add libs/context/src/asm/jump_x86_64_ms_pe_gas.S + # [Mis-]use this step to erase all the boost headers and libraries before # replacing them below. UPDATE_COMMAND ${CMAKE_COMMAND} -E remove_directory "${BOOST_ROOT}" @@ -240,8 +248,8 @@ endif() set( boost_libs "" ) set_boost_lib_names( "${BOOST_LIBS_BUILT}" boost_libs ) -set( Boost_LIBRARIES ${boost_libs} CACHE FILEPATH "Boost libraries directory" ) -set( Boost_INCLUDE_DIR "${BOOST_INCLUDE}" CACHE FILEPATH "Boost include directory" ) +set( Boost_LIBRARIES ${boost_libs} ) +set( Boost_INCLUDE_DIR "${BOOST_INCLUDE}" ) mark_as_advanced( Boost_LIBRARIES Boost_INCLUDE_DIR ) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index dc0697f357..20101747ad 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories(BEFORE ${INC_BEFORE}) +include_directories( BEFORE ${INC_BEFORE} ) include_directories( ./dialogs ./dialog_about @@ -62,7 +62,7 @@ if(WIN32 AND MSYS) add_definitions(-DGLEW_STATIC) endif(WIN32 AND MSYS) -set(COMMON_ABOUT_DLG_SRCS +set( COMMON_ABOUT_DLG_SRCS dialog_about/AboutDialog_main.cpp dialog_about/dialog_about.cpp dialog_about/dialog_about_base.cpp @@ -78,7 +78,7 @@ set(COMMON_ABOUT_DLG_SRCS dialogs/dialog_page_settings_base.cpp ) -set(COMMON_PAGE_LAYOUT_SRCS +set( COMMON_PAGE_LAYOUT_SRCS page_layout/title_block_shapes.cpp page_layout/class_worksheet_dataitem.cpp page_layout/class_worksheet_layout.cpp @@ -88,7 +88,7 @@ set(COMMON_PAGE_LAYOUT_SRCS page_layout/page_layout_reader.cpp ) -set(COMMON_SRCS +set( COMMON_SRCS ${COMMON_ABOUT_DLG_SRCS} ${COMMON_PAGE_LAYOUT_SRCS} base_struct.cpp @@ -153,17 +153,14 @@ if( NOT HAVE_STRTOKR ) set( COMMON_SRCS ${COMMON_SRCS} strtok_r.c ) endif() -enable_language(C CXX ASM) -set_source_files_properties(system/fcontext.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp") -set(COMMON_SRCS +set( COMMON_SRCS ${COMMON_SRCS} view/view.cpp view/view_item.cpp view/view_group.cpp math/math_util.cpp - system/fcontext.s tool/tool_base.cpp tool/tool_manager.cpp @@ -179,9 +176,19 @@ set(COMMON_SRCS geometry/shape_index.cpp ) -add_library(common STATIC ${COMMON_SRCS}) +# TODO: remove this whole if test and remove the sytem/*.s sources once every platform is tested with +# boost::context library +if( UNIX AND NOT APPLE ) +else() + enable_language( C CXX ASM ) + set_source_files_properties( system/fcontext.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp" ) + list( APPEND COMMON_SRCS system/fcontext.s ) +endif() -set(PCB_COMMON_SRCS + +add_library( common STATIC ${COMMON_SRCS} ) + +set( PCB_COMMON_SRCS base_screen.cpp eda_text.cpp class_page_info.cpp @@ -235,7 +242,7 @@ set(PCB_COMMON_SRCS fp_lib_table.cpp ) -set(PCB_COMMON_SRCS +set( PCB_COMMON_SRCS ${PCB_COMMON_SRCS} ../pcbnew/pcb_painter.cpp ) @@ -245,7 +252,7 @@ set_source_files_properties( ${PCB_COMMON_SRCS} PROPERTIES COMPILE_DEFINITIONS "PCBNEW" ) -add_library(pcbcommon STATIC ${PCB_COMMON_SRCS}) +add_library( pcbcommon STATIC ${PCB_COMMON_SRCS} ) # auto-generate specctra_lexer.h and specctra_keywords.cpp make_lexer( diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt index aec5809bf7..5e25018f91 100644 --- a/demos/CMakeLists.txt +++ b/demos/CMakeLists.txt @@ -1,6 +1,7 @@ -install(DIRECTORY complex_hierarchy ecc83 electric flat_hierarchy - kit-dev-coldfire-xilinx_5213 interf_u microwave - pic_programmer pspice "sonde xilinx" test_xil_95108 video - DESTINATION ${KICAD_DEMOS} - COMPONENT resources - PATTERN ".svn" EXCLUDE) +install( DIRECTORY complex_hierarchy + ecc83 electric flat_hierarchy + kit-dev-coldfire-xilinx_5213 interf_u microwave + pic_programmer pspice "sonde xilinx" test_xil_95108 video + DESTINATION ${KICAD_DEMOS} + COMPONENT resources + ) diff --git a/patches/boost_mingw.patch b/patches/boost_mingw.patch new file mode 100644 index 0000000000..8d9dfa6fe4 --- /dev/null +++ b/patches/boost_mingw.patch @@ -0,0 +1,594 @@ +--- libs/context/build/Jamfile.v2.orig 2013-03-03 13:39:59.684868916 +0100 ++++ libs/context/build/Jamfile.v2 2013-03-03 15:09:09.893232829 +0100 +@@ -282,6 +284,17 @@ + ; + + alias asm_context_sources ++ : asm/make_i386_ms_pe_gas.S ++ asm/jump_i386_ms_pe_gas.S ++ dummy.cpp ++ : 32 ++ x86 ++ pe ++ windows ++ gcc ++ ; ++ ++alias asm_context_sources + : asm/make_i386_ms_pe_masm.asm + asm/jump_i386_ms_pe_masm.asm + dummy.cpp +@@ -379,6 +392,17 @@ + ; + + alias asm_context_sources ++ : asm/make_x86_64_ms_pe_gas.S ++ asm/jump_x86_64_ms_pe_gas.S ++ dummy.cpp ++ : 64 ++ x86 ++ pe ++ windows ++ gcc ++ ; ++ ++alias asm_context_sources + : asm/make_x86_64_ms_pe_masm.asm + asm/jump_x86_64_ms_pe_masm.asm + dummy.cpp +--- libs/context/src/asm/make_i386_ms_pe_gas.S.orig 2013-03-03 13:41:28.645502113 +0100 ++++ libs/context/src/asm/make_i386_ms_pe_gas.S 2013-03-03 14:39:27.590477410 +0100 +@@ -0,0 +1,115 @@ ++/* ++ Copyright Oliver Kowalke 2009. ++ Copyright Thomas Sailer 2013. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++/******************************************************************** ++ * * ++ * -------------------------------------------------------------- * ++ * | 0 | 1 | 2 | 3 | 4 | 5 | * ++ * -------------------------------------------------------------- * ++ * | 0h | 04h | 08h | 0ch | 010h | 014h | * ++ * -------------------------------------------------------------- * ++ * | EDI | ESI | EBX | EBP | ESP | EIP | * ++ * -------------------------------------------------------------- * ++ * -------------------------------------------------------------- * ++ * | 6 | 7 | 8 | | * ++ * -------------------------------------------------------------- * ++ * | 018h | 01ch | 020h | | * ++ * -------------------------------------------------------------- * ++ * | sp | size | limit | | * ++ * -------------------------------------------------------------- * ++ * -------------------------------------------------------------- * ++ * | 9 | | * ++ * -------------------------------------------------------------- * ++ * | 024h | | * ++ * -------------------------------------------------------------- * ++ * |fc_execpt| | * ++ * -------------------------------------------------------------- * ++ * -------------------------------------------------------------- * ++ * | 10 | | * ++ * -------------------------------------------------------------- * ++ * | 028h | | * ++ * -------------------------------------------------------------- * ++ * |fc_strage| | * ++ * -------------------------------------------------------------- * ++ * -------------------------------------------------------------- * ++ * | 11 | 12 | | * ++ * -------------------------------------------------------------- * ++ * | 02ch | 030h | | * ++ * -------------------------------------------------------------- * ++ * | fc_mxcsr|fc_x87_cw| | * ++ * -------------------------------------------------------------- * ++ * * ++ * *****************************************************************/ ++ ++.file "make_i386_ms_pe_gas.S" ++.text ++.p2align 4,,15 ++.globl _make_fcontext ++.def _make_fcontext; .scl 2; .type 32; .endef ++_make_fcontext: ++ movl 0x04(%esp), %eax /* load 1. arg of make_fcontext, pointer to context stack (base) */ ++ leal -0x34(%eax),%eax /* reserve space for fcontext_t at top of context stack */ ++ ++ /* shift address in EAX to lower 16 byte boundary */ ++ /* == pointer to fcontext_t and address of context stack */ ++ andl $-16, %eax ++ ++ movl 0x04(%esp), %ecx /* load 1. arg of make_fcontext, pointer to context stack (base) */ ++ movl %ecx, 0x18(%eax) /* save address of context stack (base) in fcontext_t */ ++ movl 0x08(%esp), %edx /* load 2. arg of make_fcontext, context stack size */ ++ movl %edx, 0x1c(%eax) /* save context stack size in fcontext_t */ ++ negl %edx /* negate stack size for LEA instruction (== substraction) */ ++ leal (%ecx,%edx),%ecx /* compute bottom address of context stack (limit) */ ++ movl %ecx, 0x20(%eax) /* save address of context stack (limit) in fcontext_t */ ++ movl 0x0c(%esp), %ecx /* load 3. arg of make_fcontext, pointer to context function */ ++ movl %ecx, 0x14(%eax) /* save address of context function in fcontext_t */ ++ ++ stmxcsr 0x02c(%eax) /* save MMX control word */ ++ fnstcw 0x030(%eax) /* save x87 control word */ ++ ++ leal -0x1c(%eax),%edx /* reserve space for last frame and seh on context stack, (ESP - 0x4) % 16 == 0 */ ++ movl %edx, 0x10(%eax) /* save address in EDX as stack pointer for context function */ ++ ++ movl $finish, %ecx /* abs address of finish */ ++ movl %ecx, (%edx) /* save address of finish as return address for context function */ ++ /* entered after context function returns */ ++ ++ /* traverse current seh chain to get the last exception handler installed by Windows */ ++ /* note that on Windows Server 2008 and 2008 R2, SEHOP is activated by default */ ++ /* the exception handler chain is tested for the presence of ntdll.dll!FinalExceptionHandler */ ++ /* at its end by RaiseException all seh andlers are disregarded if not present and the */ ++ /* program is aborted */ ++ movl %fs:(0x18), %ecx /* load NT_TIB into ECX */ ++ ++walk: ++ movl (%ecx), %edx /* load 'next' member of current SEH into EDX */ ++ incl %edx /* test if 'next' of current SEH is last (== 0xffffffff) */ ++ jz found ++ decl %edx ++ xchgl %ecx, %edx /* exchange content; ECX contains address of next SEH */ ++ jmp walk /* inspect next SEH */ ++ ++found: ++ movl 0x04(%ecx), %ecx /* load 'handler' member of SEH == address of last SEH handler installed by Windows */ ++ movl 0x10(%eax), %edx /* load address of stack pointer for context function */ ++ movl %ecx, 0x18(%edx) /* save address in ECX as SEH handler for context */ ++ movl $0xffffffff,%ecx /* set ECX to -1 */ ++ movl %ecx, 0x14(%edx) /* save ECX as next SEH item */ ++ leal 0x14(%edx), %ecx /* load address of next SEH item */ ++ movl %ecx, 0x24(%eax) /* save next SEH */ ++ ++ ret ++ ++finish: ++ /* ESP points to same address as ESP on entry of context function + 0x4 */ ++ xorl %eax, %eax ++ movl %eax, (%esp) /* exit code is zero */ ++ call __exit /* exit application */ ++ hlt ++ ++.def __exit; .scl 2; .type 32; .endef /* standard C library function */ +--- libs/context/src/asm/jump_i386_ms_pe_gas.S.orig 2013-03-03 13:41:34.332670479 +0100 ++++ libs/context/src/asm/jump_i386_ms_pe_gas.S 2013-03-03 14:35:35.634611625 +0100 +@@ -0,0 +1,108 @@ ++/* ++ Copyright Oliver Kowalke 2009. ++ Copyright Thomas Sailer 2013. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++/******************************************************************** ++ * * ++ * -------------------------------------------------------------- * ++ * | 0 | 1 | 2 | 3 | 4 | 5 | * ++ * -------------------------------------------------------------- * ++ * | 0h | 04h | 08h | 0ch | 010h | 014h | * ++ * -------------------------------------------------------------- * ++ * | EDI | ESI | EBX | EBP | ESP | EIP | * ++ * -------------------------------------------------------------- * ++ * -------------------------------------------------------------- * ++ * | 6 | 7 | 8 | | * ++ * -------------------------------------------------------------- * ++ * | 018h | 01ch | 020h | | * ++ * -------------------------------------------------------------- * ++ * | sp | size | limit | | * ++ * -------------------------------------------------------------- * ++ * -------------------------------------------------------------- * ++ * | 9 | | * ++ * -------------------------------------------------------------- * ++ * | 024h | | * ++ * -------------------------------------------------------------- * ++ * |fc_execpt| | * ++ * -------------------------------------------------------------- * ++ * -------------------------------------------------------------- * ++ * | 10 | | * ++ * -------------------------------------------------------------- * ++ * | 028h | | * ++ * -------------------------------------------------------------- * ++ * |fc_strage| | * ++ * -------------------------------------------------------------- * ++ * -------------------------------------------------------------- * ++ * | 11 | 12 | | * ++ * -------------------------------------------------------------- * ++ * | 02ch | 030h | | * ++ * -------------------------------------------------------------- * ++ * | fc_mxcsr|fc_x87_cw| | * ++ * -------------------------------------------------------------- * ++ * * ++ * *****************************************************************/ ++ ++.file "jump_i386_ms_pe_gas.S" ++.text ++.p2align 4,,15 ++.globl _jump_fcontext ++.def _jump_fcontext; .scl 2; .type 32; .endef ++_jump_fcontext: ++ movl 0x04(%esp), %ecx /* load address of the first fcontext_t arg */ ++ movl %edi, (%ecx) /* save EDI */ ++ movl %esi, 0x04(%ecx) /* save ESI */ ++ movl %ebx, 0x08(%ecx) /* save EBX */ ++ movl %ebp, 0x0c(%ecx) /* save EBP */ ++ ++ movl %fs:(0x18), %edx /* load NT_TIB */ ++ movl (%edx), %eax /* load current SEH exception list */ ++ movl %eax, 0x24(%ecx) /* save current exception list */ ++ movl 0x04(%edx), %eax /* load current stack base */ ++ movl %eax, 0x18(%ecx) /* save current stack base */ ++ movl 0x08(%edx), %eax /* load current stack limit */ ++ movl %eax, 0x20(%ecx) /* save current stack limit */ ++ movl 0x10(%edx), %eax /* load fiber local storage */ ++ movl %eax, 0x28(%ecx) /* save fiber local storage */ ++ ++ leal 0x04(%esp), %eax /* exclude the return address */ ++ movl %eax, 0x10(%ecx) /* save as stack pointer */ ++ movl (%esp), %eax /* load return address */ ++ movl %eax, 0x14(%ecx) /* save return address */ ++ ++ movl 0x08(%esp), %edx /* load address of the second fcontext_t arg */ ++ movl (%edx), %edi /* restore EDI */ ++ movl 0x04(%edx), %esi /* restore ESI */ ++ movl 0x08(%edx), %ebx /* restore EBX */ ++ movl 0x0c(%edx), %ebp /* restore EBP */ ++ ++ movl 0x10(%esp), %eax /* check if fpu enve preserving was requested */ ++ testl %eax, %eax ++ je 1f ++ ++ stmxcsr 0x2c(%ecx) /* save MMX control word */ ++ fnstcw 0x30(%ecx) /* save x87 control word */ ++ ldmxcsr 0x2c(%edx) /* restore MMX control word */ ++ fldcw 0x30(%edx) /* restore x87 control word */ ++1: ++ movl %edx, %ecx ++ movl %fs:(0x18), %edx /* load NT_TIB */ ++ movl 0x24(%ecx), %eax /* load SEH exception list */ ++ movl %eax, (%edx) /* restore next SEH item */ ++ movl 0x18(%ecx), %eax /* load stack base */ ++ movl %eax, 0x04(%edx) /* restore stack base */ ++ movl 0x20(%ecx), %eax /* load stack limit */ ++ movl %eax, 0x08(%edx) /* restore stack limit */ ++ movl 0x28(%ecx), %eax /* load fiber local storage */ ++ movl %eax, 0x10(%edx) /* restore fiber local storage */ ++ ++ movl 0x0c(%esp), %eax /* use third arg as return value after jump */ ++ ++ movl 0x10(%ecx), %esp /* restore ESP */ ++ movl %eax, 0x04(%esp) /* use third arg as first arg in context function */ ++ movl 0x14(%ecx), %ecx /* fetch the address to return to */ ++ ++ jmp *%ecx /* indirect jump to context */ +--- libs/context/src/asm/make_x86_64_ms_pe_gas.S.orig 2013-03-03 13:43:03.137299031 +0100 ++++ libs/context/src/asm/make_x86_64_ms_pe_gas.S 2013-03-03 14:54:16.036775106 +0100 +@@ -0,0 +1,132 @@ ++/* ++ Copyright Oliver Kowalke 2009. ++ Copyright Thomas Sailer 2013. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++/**************************************************************************************** ++ * * ++ * ---------------------------------------------------------------------------------- * ++ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * ++ * ---------------------------------------------------------------------------------- * ++ * | R12 | R13 | R14 | R15 | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * ++ * ---------------------------------------------------------------------------------- * ++ * | RDI | RSI | RBX | RBP | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 16 | 17 | 18 | 19 | | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x40 | 0x44 | 0x48 | 0x4c | | * ++ * ---------------------------------------------------------------------------------- * ++ * | RSP | RIP | | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 20 | 21 | 22 | 23 | 24 | 25 | | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | | * ++ * ---------------------------------------------------------------------------------- * ++ * | sp | size | limit | | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 26 | 27 | | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x68 | 0x6c | | * ++ * ---------------------------------------------------------------------------------- * ++ * | fbr_strg | | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | 0x88 | 0x8c | * ++ * ---------------------------------------------------------------------------------- * ++ * | fc_mxcsr|fc_x87_cw| fc_xmm | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | 0xa8 | 0xac | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | 0xc8 | 0xcc | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | 0xe8 | 0xec | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | 0x108 | 0x10c | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | 0x128 | 0x12c | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * * ++ * *************************************************************************************/ ++ ++.file "make_x86_64_ms_pe_gas.S" ++.text ++.p2align 4,,15 ++.globl make_fcontext ++.def make_fcontext; .scl 2; .type 32; .endef ++.seh_proc make_fcontext ++make_fcontext: ++.seh_endprologue ++ leaq -0x130(%rcx),%rax /* reserve space for fcontext_t at top of context stack */ ++ ++ /* shift address in RAX to lower 16 byte boundary */ ++ /* == pointer to fcontext_t and address of context stack */ ++ andq $-16, %rax ++ ++ movq %r8, 0x48(%rax) /* save address of context function in fcontext_t */ ++ movq %rdx, 0x58(%rax) /* save context stack size in fcontext_t */ ++ movq %rcx, 0x50(%rax) /* save address of context stack pointer (base) in fcontext_t */ ++ ++ negq %rdx /* negate stack size for LEA instruction (== substraction) */ ++ leaq (%rcx,%rdx),%rcx /* compute bottom address of context stack (limit) */ ++ movq %rcx, 0x60(%rax) /* save bottom address of context stack (limit) in fcontext_t */ ++ ++ stmxcsr 0x70(%rax) /* save MMX control and status word */ ++ fnstcw 0x74(%rax) /* save x87 control word */ ++ ++ leaq -0x28(%rax),%rdx /* reserve 32byte shadow space + return address on stack, (RSP - 0x8) % 16 == 0 */ ++ movq %rdx, 0x40(%rax) /* save address in RDX as stack pointer for context function */ ++ ++ leaq finish(%rip),%rcx /* compute abs address of label finish */ ++ movq %rcx,(%rdx) /* save address of finish as return address for context function */ ++ /* entered after context function returns */ ++ ++ ret ++ ++finish: ++ /* RSP points to same address as RSP on entry of context function + 0x8 */ ++ xorq %rcx, %rcx /* exit code is zero */ ++ call _exit /* exit application */ ++ hlt ++.seh_endproc ++ ++.def _exit; .scl 2; .type 32; .endef /* standard C library function */ +--- libs/context/src/asm/jump_x86_64_ms_pe_gas.S.orig 2013-03-03 13:42:57.753139784 +0100 ++++ libs/context/src/asm/jump_x86_64_ms_pe_gas.S 2013-03-03 15:06:08.269856857 +0100 +@@ -0,0 +1,189 @@ ++/* ++ Copyright Oliver Kowalke 2009. ++ Copyright Thomas Sailer 2013. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++/**************************************************************************************** ++ * * ++ * ---------------------------------------------------------------------------------- * ++ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * ++ * ---------------------------------------------------------------------------------- * ++ * | R12 | R13 | R14 | R15 | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * ++ * ---------------------------------------------------------------------------------- * ++ * | RDI | RSI | RBX | RBP | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 16 | 17 | 18 | 19 | | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x40 | 0x44 | 0x48 | 0x4c | | * ++ * ---------------------------------------------------------------------------------- * ++ * | RSP | RIP | | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 20 | 21 | 22 | 23 | 24 | 25 | | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | | * ++ * ---------------------------------------------------------------------------------- * ++ * | sp | size | limit | | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 26 | 27 | | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x68 | 0x6c | | * ++ * ---------------------------------------------------------------------------------- * ++ * | fbr_strg | | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | 0x88 | 0x8c | * ++ * ---------------------------------------------------------------------------------- * ++ * | fc_mxcsr|fc_x87_cw| fc_xmm | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | 0xa8 | 0xac | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | 0xc8 | 0xcc | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | 0xe8 | 0xec | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | 0x108 | 0x10c | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * ---------------------------------------------------------------------------------- * ++ * | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | * ++ * ---------------------------------------------------------------------------------- * ++ * | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | 0x128 | 0x12c | * ++ * ---------------------------------------------------------------------------------- * ++ * | SEE registers (XMM6-XMM15) | * ++ * ---------------------------------------------------------------------------------- * ++ * * ++ * *************************************************************************************/ ++ ++.file "jump_x86_64_ms_pe_gas.S" ++.text ++.p2align 4,,15 ++.globl jump_fcontext ++.def jump_fcontext; .scl 2; .type 32; .endef ++.seh_proc jump_fcontext ++jump_fcontext: ++.seh_endprologue ++ movq %r12, (%rcx) /* save R12 */ ++ movq %r13, 0x08(%rcx) /* save R13 */ ++ movq %r14, 0x10(%rcx) /* save R14 */ ++ movq %r15, 0x18(%rcx) /* save R15 */ ++ movq %rdi, 0x20(%rcx) /* save RDI */ ++ movq %rsi, 0x28(%rcx) /* save RSI */ ++ movq %rbx, 0x30(%rcx) /* save RBX */ ++ movq %rbp, 0x38(%rcx) /* save RBP */ ++ ++ movq %gs:(0x30), %r10 /* load NT_TIB */ ++ movq 0x08(%r10), %rax /* load current stack base */ ++ movq %rax, 0x50(%rcx) /* save current stack base */ ++ movq 0x10(%r10), %rax /* load current stack limit */ ++ movq %rax, 0x60(%rcx) /* save current stack limit */ ++ movq 0x18(%r10), %rax /* load fiber local storage */ ++ movq %rax, 0x68(%rcx) /* save fiber local storage */ ++ ++ testq %r9, %r9 ++ je 1f ++ ++ stmxcsr 0x70(%rcx) /* save MMX control and status word */ ++ fnstcw 0x74(%rcx) /* save x87 control word */ ++ /* save XMM storage */ ++ /* save start address of SSE register block in R10 */ ++ leaq 0x90(%rcx), %r10 ++ /* shift address in R10 to lower 16 byte boundary */ ++ /* == pointer to SEE register block */ ++ andq $-16, %r10 ++ ++ movaps %xmm6, (%r10) ++ movaps %xmm7, 0x10(%r10) ++ movaps %xmm8, 0x20(%r10) ++ movaps %xmm9, 0x30(%r10) ++ movaps %xmm10, 0x40(%r10) ++ movaps %xmm11, 0x50(%r10) ++ movaps %xmm12, 0x60(%r10) ++ movaps %xmm13, 0x70(%r10) ++ movaps %xmm14, 0x80(%r10) ++ movaps %xmm15, 0x90(%r10) ++ ++ ldmxcsr 0x70(%rdx) /* restore MMX control and status word */ ++ fldcw 0x74(%rdx) /* restore x87 control word */ ++ /* restore XMM storage */ ++ /* save start address of SSE register block in R10 */ ++ leaq 0x90(%rdx), %r10 ++ /* shift address in R10 to lower 16 byte boundary */ ++ /* == pointer to SEE register block */ ++ andq $-16, %r10 ++ ++ movaps (%r10), %xmm6 ++ movaps 0x10(%r10), %xmm7 ++ movaps 0x20(%r10), %xmm8 ++ movaps 0x30(%r10), %xmm9 ++ movaps 0x40(%r10), %xmm10 ++ movaps 0x50(%r10), %xmm11 ++ movaps 0x60(%r10), %xmm12 ++ movaps 0x70(%r10), %xmm13 ++ movaps 0x80(%r10), %xmm14 ++ movaps 0x90(%r10), %xmm15 ++ ++1: ++ leaq 0x08(%rsp), %rax /* exclude the return address */ ++ movq %rax, 0x40(%rcx) /* save as stack pointer */ ++ movq (%rsp), %rax /* load return address */ ++ movq %rax, 0x48(%rcx) /* save return address */ ++ ++ movq (%rdx), %r12 /* restore R12 */ ++ movq 0x08(%rdx), %r13 /* restore R13 */ ++ movq 0x10(%rdx), %r14 /* restore R14 */ ++ movq 0x18(%rdx), %r15 /* restore R15 */ ++ movq 0x20(%rdx), %rdi /* restore RDI */ ++ movq 0x28(%rdx), %rsi /* restore RSI */ ++ movq 0x30(%rdx), %rbx /* restore RBX */ ++ movq 0x38(%rdx), %rbp /* restore RBP */ ++ ++ movq %gs:(0x30), %r10 /* load NT_TIB */ ++ movq 0x50(%rdx), %rax /* load stack base */ ++ movq %rax, 0x08(%r10) /* restore stack base */ ++ movq 0x60(%rdx), %rax /* load stack limit */ ++ movq %rax, 0x10(%r10) /* restore stack limit */ ++ movq 0x68(%rdx), %rax /* load fiber local storage */ ++ movq %rax, 0x18(%r10) /* restore fiber local storage */ ++ ++ movq 0x40(%rdx), %rsp /* restore RSP */ ++ movq 0x48(%rdx), %r10 /* fetch the address to returned to */ ++ ++ movq %r8, %rax /* use third arg as return value after jump */ ++ movq %r8, %rcx /* use third arg as first arg in context function */ ++ ++ jmp *%r10 /* indirect jump to caller */ ++.seh_endproc From c28fbe566d4c2244340a29806399516d4a08ba3e Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 31 Dec 2013 00:25:53 -0600 Subject: [PATCH 38/57] minor tidying --- common/edaappl.cpp | 162 ++++++++++++++++++++++----------------------- include/macros.h | 26 ++++---- 2 files changed, 91 insertions(+), 97 deletions(-) diff --git a/common/edaappl.cpp b/common/edaappl.cpp index afc4d7644c..fc402e51c5 100644 --- a/common/edaappl.cpp +++ b/common/edaappl.cpp @@ -61,54 +61,49 @@ static const wxChar* CommonConfigPath = wxT( "kicad_common" ); # define TMP_FILE "/tmp/kicad.tmp" #endif -/* Just add new languages to the list. This macro will properly recalculate - * the size of the array. */ -#define LANGUAGE_DESCR_COUNT ( sizeof( s_Language_List ) / sizeof( struct LANGUAGE_DESCR ) ) - // some key strings used to store parameters in config -static wxString backgroundColorKey( wxT( "BackgroundColor" ) ); -static wxString showPageLimitsKey( wxT( "ShowPageLimits" ) ); -static wxString workingDirKey( wxT( "WorkingDir" ) ) ; -static wxString languageCfgKey( wxT( "LanguageID" ) ); -static wxString kicadFpLibPath( wxT( "KicadFootprintLibraryPath" ) ); +static const wxChar backgroundColorKey[] = wxT( "BackgroundColor" ); +static const wxChar showPageLimitsKey[] = wxT( "ShowPageLimits" ); +static const wxChar workingDirKey[] = wxT( "WorkingDir" ); +static const wxChar languageCfgKey[] = wxT( "LanguageID" ); +static const wxChar kicadFpLibPath[] = wxT( "KicadFootprintLibraryPath" ); /** - * A small class to handle the list on existing translations. - * the locale translation is automatic. - * the selection of languages is mainly for maintainer's convenience + * A small class to handle the list of existing translations. + * The locale translation is automatic. + * The selection of languages is mainly for maintainer's convenience * To add a support to a new translation: * create a new icon (flag of the country) (see Lang_Fr.xpm as an example) - * add a new item to s_Language_List[LANGUAGE_DESCR_COUNT] - * and set LANGUAGE_DESCR_COUNT to the new value + * add a new item to s_Languages[]. */ struct LANGUAGE_DESCR { /// wxWidgets locale identifier (See wxWidgets doc) - int m_WX_Lang_Identifier; + int m_WX_Lang_Identifier; /// KiCad identifier used in menu selection (See id.h) - int m_KI_Lang_Identifier; + int m_KI_Lang_Identifier; /// The menu language icons - BITMAP_DEF m_Lang_Icon; + BITMAP_DEF m_Lang_Icon; /// Labels used in menus const wxChar* m_Lang_Label; /// Set to true if the m_Lang_Label must not be translated - bool m_DoNotTranslate; + bool m_DoNotTranslate; }; /** - * Language list struct + * Variable s_Languages * Note: because this list is not created on the fly, wxTranslation * must be called when a language name must be displayed after translation. - * Do don change this behavior, because m_Lang_Label is also used as key in config + * Do not change this behavior, because m_Lang_Label is also used as key in config */ -static struct LANGUAGE_DESCR s_Language_List[] = +static LANGUAGE_DESCR s_Languages[] = { // Default language { @@ -262,6 +257,7 @@ static struct LANGUAGE_DESCR s_Language_List[] = lang_jp_xpm, _( "Japanese" ) }, + // Bulgarian language { wxLANGUAGE_BULGARIAN, @@ -290,28 +286,17 @@ EDA_APP::~EDA_APP() SaveSettings(); // delete user datas - if( m_projectSettings ) - delete m_projectSettings; - - if( m_commonSettings ) - delete m_commonSettings; - + delete m_projectSettings; + delete m_commonSettings; delete m_settings; - - if( m_Checker ) - delete m_Checker; - - if( m_oneInstancePerFileChecker ) - delete m_oneInstancePerFileChecker; - + delete m_Checker; + delete m_oneInstancePerFileChecker; delete m_Locale; } void EDA_APP::InitEDA_Appl( const wxString& aName, EDA_APP_T aId ) { - wxString EnvLang; - m_Id = aId; m_Checker = new wxSingleInstanceChecker( aName.Lower() + wxT( "-" ) + wxGetUserId() ); @@ -342,8 +327,11 @@ void EDA_APP::InitEDA_Appl( const wxString& aName, EDA_APP_T aId ) SetVendorName( wxT( "KiCad" ) ); SetAppName( aName.Lower() ); SetTitle( aName ); + m_settings = new wxConfig(); + wxASSERT( m_settings != NULL ); + m_commonSettings = new wxConfig( CommonConfigPath ); wxASSERT( m_commonSettings != NULL ); @@ -365,11 +353,11 @@ void EDA_APP::InitEDA_Appl( const wxString& aName, EDA_APP_T aId ) m_LanguageId = wxLANGUAGE_DEFAULT; // Search for the current selection - for( unsigned int ii = 0; ii < LANGUAGE_DESCR_COUNT; ii++ ) + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) { - if( s_Language_List[ii].m_Lang_Label == languageSel ) + if( s_Languages[ii].m_Lang_Label == languageSel ) { - m_LanguageId = s_Language_List[ii].m_WX_Lang_Identifier; + m_LanguageId = s_Languages[ii].m_WX_Lang_Identifier; break; } } @@ -387,8 +375,7 @@ void EDA_APP::InitEDA_Appl( const wxString& aName, EDA_APP_T aId ) void EDA_APP::SetHtmlHelpController( wxHtmlHelpController* aController ) { - if( m_HtmlCtrl ) - delete m_HtmlCtrl; + delete m_HtmlCtrl; m_HtmlCtrl = aController; } @@ -459,8 +446,8 @@ bool EDA_APP::SetBinDir() #endif // Use unix notation for paths. I am not sure this is a good idea, - // but it simplify compatibility between Windows and Unices - // However it is a potential problem in path handling under Windows + // but it simplifies compatibility between Windows and Unices. + // However it is a potential problem in path handling under Windows. m_BinDir.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP ); // Remove file name form command line: @@ -471,9 +458,8 @@ bool EDA_APP::SetBinDir() } -void EDA_APP::SetDefaultSearchPaths( void ) +void EDA_APP::SetDefaultSearchPaths() { - size_t i; wxString path = m_BinDir; wxPathList tmp; @@ -529,7 +515,7 @@ void EDA_APP::SetDefaultSearchPaths( void ) tmp.Add( wxT( DEFAULT_INSTALL_PATH ) ); // Add kicad, kicad/share, share, and share/kicad to each possible base path. - for( i = 0; i < tmp.GetCount(); i++ ) + for( unsigned i = 0; i < tmp.GetCount(); i++ ) { fn = wxFileName( tmp[i], wxEmptyString ); @@ -550,7 +536,7 @@ void EDA_APP::SetDefaultSearchPaths( void ) } // Remove all non-existent paths from the list. - for( i = 0; i < m_searchPaths.GetCount(); i++ ) + for( unsigned i = 0; i < m_searchPaths.GetCount(); i++ ) { if( !wxFileName::IsDirReadable( m_searchPaths[i] ) ) { @@ -587,7 +573,7 @@ void EDA_APP::SetDefaultSearchPaths( void ) } // Add PCB library file path to search path list. - if( ( m_Id == APP_PCBNEW_T ) || ( m_Id == APP_CVPCB_T ) ) + if( m_Id == APP_PCBNEW_T || m_Id == APP_CVPCB_T ) { fn.AppendDir( wxT( "modules" ) ); @@ -633,21 +619,20 @@ void EDA_APP::GetSettings( bool aReopenLastUsedDirectory ) { wxASSERT( m_settings != NULL && m_commonSettings != NULL ); - wxString Line; - m_HelpSize.x = 500; m_HelpSize.y = 400; wxString languageSel; + m_commonSettings->Read( languageCfgKey, &languageSel ); m_LanguageId = wxLANGUAGE_DEFAULT; // Search for the current selection - for( unsigned int ii = 0; ii < LANGUAGE_DESCR_COUNT; ii++ ) + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) { - if( s_Language_List[ii].m_Lang_Label == languageSel ) + if( s_Languages[ii].m_Lang_Label == languageSel ) { - m_LanguageId = s_Language_List[ii].m_WX_Lang_Identifier; + m_LanguageId = s_Languages[ii].m_WX_Lang_Identifier; break; } } @@ -660,9 +645,11 @@ void EDA_APP::GetSettings( bool aReopenLastUsedDirectory ) if( aReopenLastUsedDirectory ) { - if( m_settings->Read( workingDirKey, &Line ) && wxDirExists( Line ) ) + wxString dir; + + if( m_settings->Read( workingDirKey, &dir ) && wxDirExists( dir ) ) { - wxSetWorkingDirectory( Line ); + wxSetWorkingDirectory( dir ); } } @@ -685,14 +672,14 @@ void EDA_APP::GetSettings( bool aReopenLastUsedDirectory ) while( 1 ) { - upath = m_commonSettings->Read( wxString::Format( wxT( "LibraryPath%d" ), i ), - wxT( "" ) ); + upath = m_commonSettings->Read( + wxString::Format( wxT( "LibraryPath%d" ), i ), wxT( "" ) ); if( upath.IsSameAs( wxT( "" ) ) ) break; m_libSearchPaths.Add( upath ); - i ++; + i++; } } @@ -700,6 +687,7 @@ void EDA_APP::GetSettings( bool aReopenLastUsedDirectory ) void EDA_APP::SaveSettings() { wxASSERT( m_settings != NULL ); + m_settings->Write( showPageLimitsKey, g_ShowPageLimits ); m_settings->Write( workingDirKey, wxGetCwd() ); m_settings->Write( backgroundColorKey, (long) g_DrawBgColor ); @@ -731,6 +719,7 @@ bool EDA_APP::SetLanguage( bool first_time ) m_LanguageId = wxLANGUAGE_DEFAULT; delete m_Locale; + m_Locale = new wxLocale; m_Locale->Init(); retv = false; @@ -746,11 +735,11 @@ bool EDA_APP::SetLanguage( bool first_time ) wxString languageSel; // Search for the current selection - for( unsigned int ii = 0; ii < LANGUAGE_DESCR_COUNT; ii++ ) + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) { - if( s_Language_List[ii].m_WX_Lang_Identifier == m_LanguageId ) + if( s_Languages[ii].m_WX_Lang_Identifier == m_LanguageId ) { - languageSel = s_Language_List[ii].m_Lang_Label; + languageSel = s_Languages[ii].m_Lang_Label; break; } } @@ -762,11 +751,14 @@ bool EDA_APP::SetLanguage( bool first_time ) // Make a conversion double <=> string double dtst = 0.5; wxString msg; + extern bool g_DisableFloatingPointLocalNotation; // See common.cpp + g_DisableFloatingPointLocalNotation = false; + msg << dtst; double result; - msg.ToDouble(&result); + msg.ToDouble( &result ); if( result != dtst ) // string to double encode/decode does not work! Bug detected { @@ -788,27 +780,25 @@ bool EDA_APP::SetLanguage( bool first_time ) void EDA_APP::SetLanguageIdentifier( int menu_id ) { wxLogDebug( wxT( "Select language ID %d from %zd possible languages." ), - menu_id, LANGUAGE_DESCR_COUNT ); + menu_id, DIM( s_Languages ) ); - for( unsigned int ii = 0; ii < LANGUAGE_DESCR_COUNT; ii++ ) + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) { - if( menu_id == s_Language_List[ii].m_KI_Lang_Identifier ) + if( menu_id == s_Languages[ii].m_KI_Lang_Identifier ) { - m_LanguageId = s_Language_List[ii].m_WX_Lang_Identifier; + m_LanguageId = s_Languages[ii].m_WX_Lang_Identifier; break; } } } -void EDA_APP::SetLanguagePath( void ) +void EDA_APP::SetLanguagePath() { - size_t i; - // Add defined search paths to locale paths if( !m_searchPaths.IsEmpty() ) { - for( i = 0; i < m_searchPaths.GetCount(); i++ ) + for( unsigned i = 0; i < m_searchPaths.GetCount(); i++ ) { wxFileName fn( m_searchPaths[i], wxEmptyString ); @@ -843,7 +833,6 @@ void EDA_APP::AddMenuLanguageList( wxMenu* MasterMenu ) { wxMenu* menu = NULL; wxMenuItem* item; - unsigned int ii; item = MasterMenu->FindItem( ID_LANGUAGE_CHOICE ); @@ -852,17 +841,17 @@ void EDA_APP::AddMenuLanguageList( wxMenu* MasterMenu ) menu = new wxMenu; - for( ii = 0; ii < LANGUAGE_DESCR_COUNT; ii++ ) + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) { wxString label; - if( s_Language_List[ii].m_DoNotTranslate ) - label = s_Language_List[ii].m_Lang_Label; + if( s_Languages[ii].m_DoNotTranslate ) + label = s_Languages[ii].m_Lang_Label; else - label = wxGetTranslation( s_Language_List[ii].m_Lang_Label ); + label = wxGetTranslation( s_Languages[ii].m_Lang_Label ); - AddMenuItem( menu, s_Language_List[ii].m_KI_Lang_Identifier, - label, KiBitmap(s_Language_List[ii].m_Lang_Icon ), + AddMenuItem( menu, s_Languages[ii].m_KI_Lang_Identifier, + label, KiBitmap(s_Languages[ii].m_Lang_Icon ), wxITEM_CHECK ); } @@ -873,18 +862,18 @@ void EDA_APP::AddMenuLanguageList( wxMenu* MasterMenu ) KiBitmap( language_xpm ) ); // Set Check mark on current selected language - for( ii = 0; ii < LANGUAGE_DESCR_COUNT; ii++ ) + for( unsigned ii = 0; ii < DIM( s_Languages ); ii++ ) { - if( m_LanguageId == s_Language_List[ii].m_WX_Lang_Identifier ) - menu->Check( s_Language_List[ii].m_KI_Lang_Identifier, true ); + if( m_LanguageId == s_Languages[ii].m_WX_Lang_Identifier ) + menu->Check( s_Languages[ii].m_KI_Lang_Identifier, true ); else - menu->Check( s_Language_List[ii].m_KI_Lang_Identifier, false ); + menu->Check( s_Languages[ii].m_KI_Lang_Identifier, false ); } } -wxString EDA_APP::FindFileInSearchPaths( const wxString& filename, - const wxArrayString* subdirs ) +wxString EDA_APP::FindFileInSearchPaths( + const wxString& filename, const wxArrayString* subdirs ) { size_t i, j; wxFileName fn; @@ -909,7 +898,7 @@ wxString EDA_APP::FindFileInSearchPaths( const wxString& filename, } -wxString EDA_APP::GetHelpFile( void ) +wxString EDA_APP::GetHelpFile() { wxString fn; wxArrayString subdirs, altsubdirs; @@ -950,6 +939,7 @@ wxString EDA_APP::GetHelpFile( void ) // Step 1 : Try to find help file in help/ subdirs.Add( m_Locale->GetCanonicalName() ); altsubdirs.Add( m_Locale->GetCanonicalName() ); + fn = FindFileInSearchPaths( m_HelpFileName, &altsubdirs ); if( !fn ) @@ -964,6 +954,7 @@ wxString EDA_APP::GetHelpFile( void ) // wxLocale::GetName() does not return always the short name subdirs.Add( m_Locale->GetName().BeforeLast( '_' ) ); altsubdirs.Add( m_Locale->GetName().BeforeLast( '_' ) ); + fn = FindFileInSearchPaths( m_HelpFileName, &altsubdirs ); if( !fn ) @@ -977,6 +968,7 @@ wxString EDA_APP::GetHelpFile( void ) altsubdirs.RemoveAt( altsubdirs.GetCount() - 1 ); subdirs.Add( _T( "en" ) ); altsubdirs.Add( _T( "en" ) ); + fn = FindFileInSearchPaths( m_HelpFileName, &altsubdirs ); if( !fn ) @@ -1002,6 +994,7 @@ wxString EDA_APP::ReturnLastVisitedLibraryPath( const wxString& aSubPathToSearch if( pcount ) { unsigned ipath = 0; + if( m_libSearchPaths[0] == wxGetCwd() ) ipath = 1; @@ -1207,3 +1200,4 @@ bool EDA_APP::SetFootprintLibTablePath() return false; } + diff --git a/include/macros.h b/include/macros.h index 0c5fc9dcf2..19ab02607f 100644 --- a/include/macros.h +++ b/include/macros.h @@ -34,22 +34,22 @@ static inline wxString FROM_UTF8( const char* cstring ) /** * Function GetChars - * returns a wxChar* to the actual character data within a wxString, and is - * helpful for passing strings to wxString::Printf(wxT("%s"), GetChars(wxString) ) + * returns a wxChar* to the actual wxChar* data within a wxString, and is + * helpful for passing strings to wxString::Printf() and wxString::Format(). + * It can also be passed a UTF8 parameter which will be converted to wxString + * by the compiler. *

- * wxChar is defined to be + * Example: wxString::Format( wxT( "%s" ), GetChars( UTF( "some text" ) ) ); + *

+ * When wxWidgets is properly built for KiCad, a const wxChar* points to either: *

- * i.e. it depends on how the wxWidgets library was compiled. - * ( wxUSE_UNICODE is defined in wxWidgets, inside setup.h. - * for version >= 2.9 wxUSE_UNICODE is always defined to 1 ) - * There was a period - * during the development of wxWidgets 2.9 when GetData() was missing, so this - * function was used to provide insulation from that design change. It may - * no longer be needed, and is harmless. GetData() seems to be an acceptable - * alternative in all cases now. + * Note that you cannot pass 8 bit strings to wxString::Format() or Printf() so this + * is a useful conversion function to wxChar*, which is needed by wxString::Format(). + * + * @return const wxChar* - a pointer to the UNICODE or UTF16 (on windows) text. */ static inline const wxChar* GetChars( const wxString& s ) { From 7ae4bc208afca54c93647abffe9db20efa8d8cd1 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Tue, 31 Dec 2013 11:42:25 +0100 Subject: [PATCH 39/57] [MacOSX] disable -fvisibility due compiler blames.. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d200af8f1..bb3090d4c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,11 +114,11 @@ if( CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) set( CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG" ) set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG" ) - if( GXX_HAS_VISIBILITY_FLAG ) + if( GXX_HAS_VISIBILITY_FLAG AND NOT APPLE ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden" ) endif() - if( GXX_HAS_VISIBILITY_INLINES_FLAG ) + if( GXX_HAS_VISIBILITY_INLINES_FLAG AND NOT APPLE ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden" ) endif() From 0b62184c510fa83daaaf973fe18c38b6cdd8a981 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Tue, 31 Dec 2013 11:44:16 +0100 Subject: [PATCH 40/57] [MacOSX] Removing freetype dependecy for Cairo (uses cocoa font management) --- CMakeModules/download_cairo.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeModules/download_cairo.cmake b/CMakeModules/download_cairo.cmake index 82e9b86b4d..5e81f3c931 100644 --- a/CMakeModules/download_cairo.cmake +++ b/CMakeModules/download_cairo.cmake @@ -41,7 +41,7 @@ if (APPLE) if( CMAKE_OSX_ARCHITECTURES ) set( CAIRO_CFLAGS "CFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES}" ) set( CAIRO_LDFLAGS "LDFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES} -framework CoreServices -framework Cocoa" ) - set( CAIRO_OPTS "--enable-quartz-image" ) + set( CAIRO_OPTS --enable-ft=no ) endif( CMAKE_OSX_ARCHITECTURES ) if( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) @@ -80,7 +80,8 @@ ExternalProject_Add( cairo --disable-silent-rules --disable-dependency-tracking ${CAIRO_CFLAGS} ${CAIRO_LDFLAGS} - + ${CAIRO_OPTS} + #BINARY_DIR "${PREFIX}" BUILD_COMMAND make From d1bd519e7cba98e1b7a714e33f33bc106363c289 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Tue, 31 Dec 2013 11:45:17 +0100 Subject: [PATCH 41/57] =?UTF-8?q?[MacOSX]=C2=A0Manage=20older=20OpenSSL=20?= =?UTF-8?q?API=20(needed=20for=20OSX=2010.6)=20and=20probably=20older=20li?= =?UTF-8?q?nuxes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeModules/download_boost.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 9ab0c571d3..9568fc16ad 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -190,6 +190,7 @@ ExternalProject_Add( boost COMMAND bzr add libs/context/src/asm/make_i386_x86_64_sysv_macho_gas.S COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_macosx_x86_build.patch" #https://svn.boost.org/trac/boost/ticket/8266 + COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_macosx_older_openssl.patch" #https://svn.boost.org/trac/boost/ticket/9273 COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_mingw.patch" #https://svn.boost.org/trac/boost/ticket/7262 # tell bzr about new files added by last patch so above "bzr revert" works: From 3eab6c5af27dda0bf7c1c214616ddd0714d7819d Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Tue, 31 Dec 2013 11:48:16 +0100 Subject: [PATCH 42/57] [MacOSX] Manage older OpenSSL API (needed for OSX 10.6) and probably older linuxes --- patches/boost_macosx_older_openssl.patch | 55 ++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 patches/boost_macosx_older_openssl.patch diff --git a/patches/boost_macosx_older_openssl.patch b/patches/boost_macosx_older_openssl.patch new file mode 100644 index 0000000000..29f2ca80d0 --- /dev/null +++ b/patches/boost_macosx_older_openssl.patch @@ -0,0 +1,55 @@ +=== modified file 'boost/asio/ssl/impl/context.ipp' +--- boost/asio/ssl/impl/context.ipp 2013-12-30 14:53:15 +0000 ++++ boost/asio/ssl/impl/context.ipp 2013-12-30 19:08:01 +0000 +@@ -236,19 +236,27 @@ + boost::system::error_code context::clear_options( + context::options o, boost::system::error_code& ec) + { +-#if !defined(SSL_OP_NO_COMPRESSION) ++#if (OPENSSL_VERSION_NUMBER >= 0x009080DFL) \ ++ && (OPENSSL_VERSION_NUMBER != 0x00909000L) ++# if !defined(SSL_OP_NO_COMPRESSION) + if ((o & context::no_compression) != 0) + { +-#if (OPENSSL_VERSION_NUMBER >= 0x00908000L) ++# if (OPENSSL_VERSION_NUMBER >= 0x00908000L) + handle_->comp_methods = SSL_COMP_get_compression_methods(); +-#endif // (OPENSSL_VERSION_NUMBER >= 0x00908000L) ++# endif // (OPENSSL_VERSION_NUMBER >= 0x00908000L) + o ^= context::no_compression; + } +-#endif // !defined(SSL_OP_NO_COMPRESSION) ++# endif // !defined(SSL_OP_NO_COMPRESSION) + + ::SSL_CTX_clear_options(handle_, o); + + ec = boost::system::error_code(); ++#else // (OPENSSL_VERSION_NUMBER >= 0x009080DFL) ++ // && (OPENSSL_VERSION_NUMBER != 0x00909000L) ++ (void)o; ++ ec = boost::asio::error::operation_not_supported; ++#endif // (OPENSSL_VERSION_NUMBER >= 0x009080DFL) ++ // && (OPENSSL_VERSION_NUMBER != 0x00909000L) + return ec; + } + +@@ -428,7 +436,8 @@ + + if (format == context_base::asn1) + { +- if (::SSL_CTX_use_certificate_ASN1(handle_, buffer_size(certificate), ++ if (::SSL_CTX_use_certificate_ASN1(handle_, ++ static_cast(buffer_size(certificate)), + buffer_cast(certificate)) == 1) + { + ec = boost::system::error_code(); +@@ -929,7 +938,7 @@ + { + return ::BIO_new_mem_buf( + const_cast(buffer_cast(b)), +- buffer_size(b)); ++ static_cast(buffer_size(b))); + } + + #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL) + From 2fc8a05aec64729e50ed223869063973fa8a7401 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Tue, 31 Dec 2013 12:03:02 +0100 Subject: [PATCH 43/57] [MacOSX] Adding better support for Retina Display (NSHighResolutionCapable) --- bitmap2component/Info.plist | 2 ++ cvpcb/Info.plist | 2 ++ eeschema/Info.plist | 2 ++ gerbview/Info.plist | 2 ++ kicad/Info.plist | 2 ++ pagelayout_editor/Info.plist | 2 ++ pcb_calculator/Info.plist | 2 ++ pcbnew/Info.plist | 2 ++ 8 files changed, 16 insertions(+) diff --git a/bitmap2component/Info.plist b/bitmap2component/Info.plist index e470e183e7..f39f5a5dff 100644 --- a/bitmap2component/Info.plist +++ b/bitmap2component/Info.plist @@ -34,5 +34,7 @@ NSHumanReadableCopyright + NSHighResolutionCapable + True diff --git a/cvpcb/Info.plist b/cvpcb/Info.plist index 3885457278..9248420e5f 100644 --- a/cvpcb/Info.plist +++ b/cvpcb/Info.plist @@ -48,5 +48,7 @@ NSHumanReadableCopyright + NSHighResolutionCapable + True diff --git a/eeschema/Info.plist b/eeschema/Info.plist index abb6d796d7..80d58bfc7d 100644 --- a/eeschema/Info.plist +++ b/eeschema/Info.plist @@ -49,5 +49,7 @@ NSHumanReadableCopyright + NSHighResolutionCapable + True diff --git a/gerbview/Info.plist b/gerbview/Info.plist index 4cf3b724c9..a9d27ad02e 100644 --- a/gerbview/Info.plist +++ b/gerbview/Info.plist @@ -67,5 +67,7 @@ NSHumanReadableCopyright + NSHighResolutionCapable + True diff --git a/kicad/Info.plist b/kicad/Info.plist index 23fa6f97cb..fa990ed54d 100644 --- a/kicad/Info.plist +++ b/kicad/Info.plist @@ -49,5 +49,7 @@ NSHumanReadableCopyright + NSHighResolutionCapable + True diff --git a/pagelayout_editor/Info.plist b/pagelayout_editor/Info.plist index 99bd82ca0c..64d9326613 100644 --- a/pagelayout_editor/Info.plist +++ b/pagelayout_editor/Info.plist @@ -49,5 +49,7 @@ NSHumanReadableCopyright + NSHighResolutionCapable + True diff --git a/pcb_calculator/Info.plist b/pcb_calculator/Info.plist index 3d2a9f6f0c..d54598b872 100644 --- a/pcb_calculator/Info.plist +++ b/pcb_calculator/Info.plist @@ -32,5 +32,7 @@ NSHumanReadableCopyright + NSHighResolutionCapable + True diff --git a/pcbnew/Info.plist b/pcbnew/Info.plist index 8fef4f79de..1f4b5bfdd5 100644 --- a/pcbnew/Info.plist +++ b/pcbnew/Info.plist @@ -48,5 +48,7 @@ NSHumanReadableCopyright + NSHighResolutionCapable + True From 6c0c2b4365d579d903859ac5c74e8525fb736609 Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Tue, 31 Dec 2013 15:28:34 +0100 Subject: [PATCH 44/57] [MacOSX] Reorganize OSX Library PATH, first the USER's one then the SYSTEM's one --- common/edaappl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/edaappl.cpp b/common/edaappl.cpp index fc402e51c5..a8fa09ae83 100644 --- a/common/edaappl.cpp +++ b/common/edaappl.cpp @@ -505,8 +505,8 @@ void EDA_APP::SetDefaultSearchPaths() #ifdef __WXMSW__ tmp.AddEnvList( wxT( "PROGRAMFILES" ) ); #elif __WXMAC__ - tmp.Add( wxT( "/Library/Application Support" ) ); tmp.Add( wxString( wxGetenv( wxT( "HOME" ) ) ) + wxT( "/Library/Application Support" ) ); + tmp.Add( wxT( "/Library/Application Support" ) ); #else tmp.AddEnvList( wxT( "PATH" ) ); #endif From ce3943446c63edc86b3bee982634d7d1f8e727f8 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 31 Dec 2013 10:58:05 -0600 Subject: [PATCH 45/57] Add bzr clean-tree to download_boost.cmake --- CMakeModules/download_boost.cmake | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 9568fc16ad..74395f11cb 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -180,12 +180,14 @@ ExternalProject_Add( boost # fails when applying a patch to the branch twice and doesn't have a switch # to ignore previously applied patches PATCH_COMMAND bzr revert - # PATCH_COMMAND continuation (any *_COMMAND here can be continued with COMMAND): + # bzr revert is insufficient to remove "added" files: + COMMAND bzr clean-tree -q + COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_minkowski.patch" COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_cstdint.patch" COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_macosx_x86.patch" #https://svn.boost.org/trac/boost/ticket/8266 - # tell bzr about new files added by last patch so above "bzr revert" works: + # tell bzr about "added" files by last patch: COMMAND bzr add libs/context/src/asm/jump_i386_x86_64_sysv_macho_gas.S COMMAND bzr add libs/context/src/asm/make_i386_x86_64_sysv_macho_gas.S @@ -193,7 +195,7 @@ ExternalProject_Add( boost COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_macosx_older_openssl.patch" #https://svn.boost.org/trac/boost/ticket/9273 COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_mingw.patch" #https://svn.boost.org/trac/boost/ticket/7262 - # tell bzr about new files added by last patch so above "bzr revert" works: + # tell bzr about "added" files by last patch: COMMAND bzr add libs/context/src/asm/make_i386_ms_pe_gas.S COMMAND bzr add libs/context/src/asm/jump_i386_ms_pe_gas.S COMMAND bzr add libs/context/src/asm/make_x86_64_ms_pe_gas.S From 074a265166c7a43554c9945707233d98e38a5c5f Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 31 Dec 2013 10:59:33 -0600 Subject: [PATCH 46/57] FIX: only invoke fp-lib-table options editor when table not empty. --- pcbnew/dialogs/dialog_fp_lib_table.cpp | 37 ++++++++++++++------------ 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/pcbnew/dialogs/dialog_fp_lib_table.cpp b/pcbnew/dialogs/dialog_fp_lib_table.cpp index 57d33b6076..0a4d383ea2 100644 --- a/pcbnew/dialogs/dialog_fp_lib_table.cpp +++ b/pcbnew/dialogs/dialog_fp_lib_table.cpp @@ -583,28 +583,31 @@ private: { FP_TBL_MODEL* tbl = cur_model(); - int curRow = getCursorRow(); - ROW& row = tbl->rows[curRow]; - - wxString result; - const wxString& options = row.GetOptions(); - - InvokePluginOptionsEditor( this, row.GetNickName(), row.GetType(), options, &result ); - - if( options != result ) + if( tbl->GetNumberRows() ) { - row.SetOptions( result ); + int curRow = getCursorRow(); + ROW& row = tbl->rows[curRow]; - // all but options: - m_cur_grid->AutoSizeColumn( COL_NICKNAME, false ); - m_cur_grid->AutoSizeColumn( COL_URI, false ); - m_cur_grid->AutoSizeColumn( COL_TYPE, false ); + wxString result; + const wxString& options = row.GetOptions(); - // On Windows, the grid is not refresh, - // so force resfresh after a change + InvokePluginOptionsEditor( this, row.GetNickName(), row.GetType(), options, &result ); + + if( options != result ) + { + row.SetOptions( result ); + + // all but options: + m_cur_grid->AutoSizeColumn( COL_NICKNAME, false ); + m_cur_grid->AutoSizeColumn( COL_URI, false ); + m_cur_grid->AutoSizeColumn( COL_TYPE, false ); + + // On Windows, the grid is not refresh, + // so force resfresh after a change #ifdef __WINDOWS__ - Refresh(); + Refresh(); #endif + } } } From 3142172cff9bdad4a167195ea30c0e56b6cdc381 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Wed, 1 Jan 2014 20:17:07 -0600 Subject: [PATCH 47/57] 8 bit string relief via class UTF8 --- common/footprint_info.cpp | 4 +-- common/fp_lib_table.cpp | 36 +++++++++---------- common/fpid.cpp | 12 +++---- common/gal/stroke_font.cpp | 33 ++++++++--------- common/utf8.cpp | 4 +-- include/fp_lib_table.h | 2 +- include/fpid.h | 26 +++++++------- include/gal/stroke_font.h | 19 +++++----- include/richio.h | 2 +- include/utf8.h | 17 +++++++++ pcbnew/class_board.cpp | 14 ++++---- .../dialog_edit_module_for_Modedit.cpp | 2 +- pcbnew/dialogs/dialog_fp_plugin_options.cpp | 12 +++---- pcbnew/eagle_plugin.cpp | 8 ++--- pcbnew/gen_modules_placefile.cpp | 4 ++- pcbnew/github/github_plugin.cpp | 16 ++++----- pcbnew/io_mgr.cpp | 2 +- pcbnew/io_mgr.h | 4 +-- pcbnew/kicad_plugin.cpp | 3 +- pcbnew/legacy_plugin.cpp | 9 ++--- pcbnew/librairi.cpp | 6 ++-- pcbnew/loadcmp.cpp | 4 +-- pcbnew/plugin.cpp | 24 ++++++------- pcbnew/xchgmod.cpp | 4 +-- 24 files changed, 142 insertions(+), 125 deletions(-) diff --git a/common/footprint_info.cpp b/common/footprint_info.cpp index 4fe00f4108..4396ec66c1 100644 --- a/common/footprint_info.cpp +++ b/common/footprint_info.cpp @@ -273,8 +273,8 @@ FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aFootprintName ) wxString::Format( wxT( "'%s' is not a valid FPID." ), GetChars( aFootprintName ) ) ); - wxString libNickname = FROM_UTF8( fpid.GetLibNickname().c_str() ); - wxString footprintName = FROM_UTF8( fpid.GetFootprintName().c_str() ); + wxString libNickname = fpid.GetLibNickname(); + wxString footprintName = fpid.GetFootprintName(); if( libNickname == fp.GetNickname() && footprintName == fp.GetFootprintName() ) return &fp; diff --git a/common/fp_lib_table.cpp b/common/fp_lib_table.cpp index 5ee2c3a2bf..fe8aeb92cc 100644 --- a/common/fp_lib_table.cpp +++ b/common/fp_lib_table.cpp @@ -173,7 +173,7 @@ MODULE* FP_LIB_TABLE::FootprintLoad( const wxString& aNickname, const wxString& FPID& fpid = (FPID&) ret->GetFPID(); // Catch any misbehaving plugin, which should be setting internal footprint name properly: - wxASSERT( aFootprintName == FROM_UTF8( fpid.GetFootprintName().c_str() ) ); + wxASSERT( aFootprintName == (wxString) fpid.GetFootprintName() ); // and clearing nickname wxASSERT( !fpid.GetLibNickname().size() ); @@ -195,7 +195,7 @@ FP_LIB_TABLE::SAVE_T FP_LIB_TABLE::FootprintSave( const wxString& aNickname, con // Try loading the footprint to see if it already exists, caller wants overwrite // protection, which is atypical, not the default. - wxString fpname = FROM_UTF8( aFootprint->GetFPID().GetFootprintName().c_str() ); + wxString fpname = aFootprint->GetFPID().GetFootprintName(); std::auto_ptr m( row->plugin->FootprintLoad( row->GetFullURI( true ), fpname, row->GetProperties() ) ); @@ -480,16 +480,17 @@ PROPERTIES* FP_LIB_TABLE::ParseOptions( const std::string& aOptionsList ) } -std::string FP_LIB_TABLE::FormatOptions( const PROPERTIES* aProperties ) +UTF8 FP_LIB_TABLE::FormatOptions( const PROPERTIES* aProperties ) { - std::string ret; + UTF8 ret; if( aProperties ) { for( PROPERTIES::const_iterator it = aProperties->begin(); it != aProperties->end(); ++it ) { const std::string& name = it->first; - const std::string& value = it->second; + + const UTF8& value = it->second; if( ret.size() ) ret += OPT_SEP; @@ -741,7 +742,7 @@ bool FP_LIB_TABLE::ConvertFromLegacy( NETLIST& aNetList, const wxArrayString& aL { if( aReporter ) { - msg.Printf( _( "Cannot find footprint library file \"%s\" in any of the " + msg.Printf( _( "Cannot find footprint library file '%s' in any of the " "KiCad legacy library search paths.\n" ), GetChars( fn.GetFullPath() ) ); aReporter->Report( msg ); @@ -751,8 +752,7 @@ bool FP_LIB_TABLE::ConvertFromLegacy( NETLIST& aNetList, const wxArrayString& aL continue; } - module = pi->FootprintLoad( libPath, - FROM_UTF8( component->GetFPID().GetFootprintName().c_str() ) ); + module = pi->FootprintLoad( libPath, component->GetFPID().GetFootprintName() ); if( module ) { @@ -766,10 +766,10 @@ bool FP_LIB_TABLE::ConvertFromLegacy( NETLIST& aNetList, const wxArrayString& aL { if( aReporter ) { - msg.Printf( _( "Component `%s` footprint <%s> was not found in any legacy " + msg.Printf( _( "Component `%s` footprint '%s' was not found in any legacy " "library.\n" ), GetChars( component->GetReference() ), - GetChars( FROM_UTF8( component->GetFPID().Format().c_str() ) ) ); + GetChars( component->GetFPID().Format() ) ); aReporter->Report( msg ); } @@ -811,10 +811,10 @@ bool FP_LIB_TABLE::ConvertFromLegacy( NETLIST& aNetList, const wxArrayString& aL { if( aReporter ) { - msg.Printf( _( "Component `%s` footprint <%s> legacy library path <%s > " + msg.Printf( _( "Component `%s` footprint '%s' legacy library path <%s > " "was not found in the footprint library table.\n" ), GetChars( component->GetReference() ), - GetChars( FROM_UTF8( component->GetFPID().Format().c_str() ) ) ); + GetChars( component->GetFPID().Format() ) ); aReporter->Report( msg ); } @@ -830,9 +830,9 @@ bool FP_LIB_TABLE::ConvertFromLegacy( NETLIST& aNetList, const wxArrayString& aL { if( aReporter ) { - msg.Printf( _( "Component `%s` FPID <%s> is not valid.\n" ), + msg.Printf( _( "Component `%s` FPID '%s' is not valid.\n" ), GetChars( component->GetReference() ), - GetChars( FROM_UTF8( newFPID.Format().c_str() ) ) ); + GetChars( newFPID.Format() ) ); aReporter->Report( msg ); } @@ -860,7 +860,7 @@ void FP_LIB_TABLE::SetProjectPathEnvVariable( const wxFileName& aPath ) else path = aPath.GetPath(); - wxLogTrace( traceFpLibTable, wxT( "Setting env %s to <%s>." ), + wxLogTrace( traceFpLibTable, wxT( "Setting env %s to '%s'." ), GetChars( ProjectPathEnvVariableName() ), GetChars( path ) ); wxSetEnv( ProjectPathEnvVariableName(), path ); } @@ -899,7 +899,7 @@ wxString FP_LIB_TABLE::GetProjectFileName( const wxFileName& aPath ) fn.SetName( defaultFileName ); } - wxLogTrace( traceFpLibTable, wxT( "Project specific footprint library table file <%s>." ), + wxLogTrace( traceFpLibTable, wxT( "Project specific footprint library table file '%s'." ), GetChars( fn.GetFullPath() ) ); return fn.GetFullPath(); @@ -917,7 +917,7 @@ bool FP_LIB_TABLE::LoadGlobalTable( FP_LIB_TABLE& aTable ) throw (IO_ERROR, PARS if( !fn.DirExists() && !fn.Mkdir( 0x777, wxPATH_MKDIR_FULL ) ) { - THROW_IO_ERROR( wxString::Format( _( "Cannot create global library table path <%s>." ), + THROW_IO_ERROR( wxString::Format( _( "Cannot create global library table path '%s'." ), GetChars( fn.GetPath() ) ) ); } @@ -954,7 +954,7 @@ wxString FP_LIB_TABLE::GetGlobalTableFileName() fn.SetName( GetFileName() ); - wxLogTrace( traceFpLibTable, wxT( "Global footprint library table file <%s>." ), + wxLogTrace( traceFpLibTable, wxT( "Global footprint library table file '%s'." ), GetChars( fn.GetFullPath() ) ); return fn.GetFullPath(); diff --git a/common/fpid.cpp b/common/fpid.cpp index 92e1ee2786..ef9f35b89f 100644 --- a/common/fpid.cpp +++ b/common/fpid.cpp @@ -265,9 +265,9 @@ int FPID::SetRevision( const std::string& aRevision ) } -std::string FPID::Format() const +UTF8 FPID::Format() const { - std::string ret; + UTF8 ret; if( nickname.size() ) { @@ -287,9 +287,9 @@ std::string FPID::Format() const } -std::string FPID::GetFootprintNameAndRev() const +UTF8 FPID::GetFootprintNameAndRev() const { - std::string ret; + UTF8 ret; if( revision.size() ) { @@ -301,11 +301,11 @@ std::string FPID::GetFootprintNameAndRev() const } -std::string FPID::Format( const std::string& aLogicalLib, const std::string& aFootprintName, +UTF8 FPID::Format( const std::string& aLogicalLib, const std::string& aFootprintName, const std::string& aRevision ) throw( PARSE_ERROR ) { - std::string ret; + UTF8 ret; int offset; if( aLogicalLib.size() ) diff --git a/common/gal/stroke_font.cpp b/common/gal/stroke_font.cpp index bd536dca7e..2020954d61 100644 --- a/common/gal/stroke_font.cpp +++ b/common/gal/stroke_font.cpp @@ -147,7 +147,7 @@ BOX2D STROKE_FONT::computeBoundingBox( const GLYPH& aGLYPH, const VECTOR2D& aGLY } -void STROKE_FONT::Draw( const wxString& aText, const VECTOR2D& aPosition, double aRotationAngle ) +void STROKE_FONT::Draw( const UTF8& aText, const VECTOR2D& aPosition, double aRotationAngle ) { // Context needs to be saved before any transformations m_gal->Save(); @@ -192,7 +192,7 @@ void STROKE_FONT::Draw( const wxString& aText, const VECTOR2D& aPosition, double { size_t length = newlinePos - begin; - drawSingleLineText( aText.Mid( begin, length ) ); + drawSingleLineText( aText.substr( begin, length ) ); m_gal->Translate( VECTOR2D( 0.0, lineHeight ) ); begin = newlinePos + 1; @@ -200,20 +200,20 @@ void STROKE_FONT::Draw( const wxString& aText, const VECTOR2D& aPosition, double } // Draw the last (or the only one) line - if( !aText.IsEmpty() ) - drawSingleLineText( aText.Mid( begin ) ); + if( !aText.empty() ) + drawSingleLineText( aText.substr( begin ) ); m_gal->Restore(); } -void STROKE_FONT::drawSingleLineText( const wxString& aText ) +void STROKE_FONT::drawSingleLineText( const UTF8& aText ) { // By default the overbar is turned off m_overbar = false; - double xOffset; - VECTOR2D glyphSize( m_glyphSize ); + double xOffset; + VECTOR2D glyphSize( m_glyphSize ); // Compute the text size VECTOR2D textSize = computeTextSize( aText ); @@ -254,12 +254,12 @@ void STROKE_FONT::drawSingleLineText( const wxString& aText ) xOffset = 0.0; } - for( wxString::const_iterator chIt = aText.begin(); chIt != aText.end(); ++chIt ) + for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt ) { // Toggle overbar if( *chIt == '~' ) { - if( ++chIt == aText.end() ) + if( ++chIt >= end ) break; if( *chIt != '~' ) // It was a single tilda, it toggles overbar @@ -274,13 +274,14 @@ void STROKE_FONT::drawSingleLineText( const wxString& aText ) dd = '?' - ' '; GLYPH& glyph = m_glyphs[dd]; - BOX2D& bbox = m_glyphBoundingBoxes[dd]; + BOX2D& bbox = m_glyphBoundingBoxes[dd]; if( m_overbar ) { VECTOR2D startOverbar( xOffset, -getInterline() * OVERBAR_HEIGHT ); VECTOR2D endOverbar( xOffset + glyphSize.x * bbox.GetEnd().x, -getInterline() * OVERBAR_HEIGHT ); + m_gal->DrawLine( startOverbar, endOverbar ); } @@ -317,25 +318,25 @@ void STROKE_FONT::drawSingleLineText( const wxString& aText ) } -VECTOR2D STROKE_FONT::computeTextSize( const wxString& aText ) const +VECTOR2D STROKE_FONT::computeTextSize( const UTF8& aText ) const { VECTOR2D result = VECTOR2D( 0.0, m_glyphSize.y ); - for( wxString::const_iterator chIt = aText.begin(); chIt != aText.end(); ++chIt ) + for( UTF8::uni_iter it = aText.ubegin(), end = aText.uend(); it < end; ++it ) { - wxASSERT_MSG( *chIt != '\n', + wxASSERT_MSG( *it != '\n', wxT( "This function is intended to work with single line strings" ) ); // If it is double tilda, then it is displayed as a single tilda // If it is single tilda, then it is toggling overbar, so we need to skip it - if( *chIt == '~' ) + if( *it == '~' ) { - if( ++chIt == aText.end() ) + if( ++it >= end ) break; } // Index in the bounding boxes table - unsigned dd = *chIt - ' '; + unsigned dd = *it - ' '; if( dd >= m_glyphBoundingBoxes.size() || dd < 0 ) dd = '?' - ' '; diff --git a/common/utf8.cpp b/common/utf8.cpp index 48478bb15b..637d9fc8e1 100644 --- a/common/utf8.cpp +++ b/common/utf8.cpp @@ -24,7 +24,7 @@ #include -/* THROW_IO_ERROR needs this, but it will soon be including this file, so until some +/* THROW_IO_ERROR needs this, but it includes this file, so until some factoring of THROW_IO_ERROR into a separate header, defer and use the asserts. #include */ @@ -33,7 +33,7 @@ /* These are not inlined so that code space is saved by encapsulating the - creation of intermediate objects and referencing wxConvUTF8. + creation of intermediate objects and the referencing of wxConvUTF8. */ diff --git a/include/fp_lib_table.h b/include/fp_lib_table.h index 43dcf7e62a..5ce7d5236f 100644 --- a/include/fp_lib_table.h +++ b/include/fp_lib_table.h @@ -339,7 +339,7 @@ public: * @param aProperties is the PROPERTIES to format or NULL. If NULL the returned * string will be empty. */ - static std::string FormatOptions( const PROPERTIES* aProperties ); + static UTF8 FormatOptions( const PROPERTIES* aProperties ); /** * Function Format diff --git a/include/fpid.h b/include/fpid.h index 6417cfbc6f..17155858b8 100644 --- a/include/fpid.h +++ b/include/fpid.h @@ -27,7 +27,7 @@ #define _FPID_H_ #include - +#include /** * Class FPID @@ -53,7 +53,7 @@ * * @author Dick Hollenbeck */ -class FPID // aka GUID +class FPID { public: @@ -88,7 +88,7 @@ public: * Function GetLibNickname * returns the logical library name portion of a FPID. */ - const std::string& GetLibNickname() const + const UTF8& GetLibNickname() const { return nickname; } @@ -108,7 +108,7 @@ public: * Function GetFootprintName * returns the footprint name, i.e. footprintName. */ - const std::string& GetFootprintName() const { return footprint; } + const UTF8& GetFootprintName() const { return footprint; } /** * Function SetFootprintName @@ -120,15 +120,15 @@ public: int SetRevision( const std::string& aRevision ); - const std::string& GetRevision() const { return revision; } + const UTF8& GetRevision() const { return revision; } - std::string GetFootprintNameAndRev() const; + UTF8 GetFootprintNameAndRev() const; /** * Function Format * returns the fully formatted text of the FPID. */ - std::string Format() const; + UTF8 Format() const; /** * Function Format @@ -137,7 +137,7 @@ public: * * @throw PARSE_ERROR if any of the pieces are illegal. */ - static std::string Format( const std::string& aLibNickname, const std::string& aFootprintName, + static UTF8 Format( const std::string& aLibNickname, const std::string& aFootprintName, const std::string& aRevision ) throw( PARSE_ERROR ); @@ -182,8 +182,8 @@ public: */ int compare( const FPID& aFPID ) const; - bool operator <( const FPID& aFPID ) const { return this->compare( aFPID ) < 0; } - bool operator >( const FPID& aFPID ) const { return this->compare( aFPID ) > 0; } + bool operator < ( const FPID& aFPID ) const { return this->compare( aFPID ) < 0; } + bool operator > ( const FPID& aFPID ) const { return this->compare( aFPID ) > 0; } bool operator ==( const FPID& aFPID ) const { return this->compare( aFPID ) == 0; } bool operator !=( const FPID& aFPID ) const { return !(*this == aFPID); } @@ -192,9 +192,9 @@ public: #endif protected: - std::string nickname; ///< The nickname of the footprint library or empty. - std::string footprint; ///< The name of the footprint in the logical library. - std::string revision; ///< The footprint revision. + UTF8 nickname; ///< The nickname of the footprint library or empty. + UTF8 footprint; ///< The name of the footprint in the logical library. + UTF8 revision; ///< The footprint revision. }; diff --git a/include/gal/stroke_font.h b/include/gal/stroke_font.h index afa8d7f61c..71b2d421ed 100644 --- a/include/gal/stroke_font.h +++ b/include/gal/stroke_font.h @@ -29,8 +29,8 @@ #ifndef STROKE_FONT_H_ #define STROKE_FONT_H_ -#include #include +#include #include @@ -70,7 +70,7 @@ public: * @param aPosition is the text position in world coordinates. * @param aRotationAngle is the text rotation angle. */ - void Draw( const wxString& aText, const VECTOR2D& aPosition, double aRotationAngle ); + void Draw( const UTF8& aText, const VECTOR2D& aPosition, double aRotationAngle ); /** * @brief Set the glyph size. @@ -173,7 +173,7 @@ private: * * @param aText is the text to be drawn. */ - void drawSingleLineText( const wxString& aText ); + void drawSingleLineText( const UTF8& aText ); /** * @brief Compute the size of a given text. @@ -181,20 +181,19 @@ private: * @param aText is the text string. * @return is the text size. */ - VECTOR2D computeTextSize( const wxString& aText ) const; + VECTOR2D computeTextSize( const UTF8& aText ) const; /** * @brief Returns number of lines for a given text. * * @param aText is the text to be checked. - * @return Number of lines of aText. + * @return unsigned - The number of lines in aText. */ - unsigned int linesCount( const wxString& aText ) const + unsigned linesCount( const UTF8& aText ) const { - wxString::const_iterator it, itEnd; - unsigned int lines = 1; + unsigned lines = 1; - for( it = aText.begin(), itEnd = aText.end(); it != itEnd; ++it ) + for( UTF8::const_iterator it = aText.begin(), itEnd = aText.end(); it != itEnd; ++it ) { if( *it == '\n' ) ++lines; @@ -214,4 +213,4 @@ private: }; } // namespace KIGFX -#endif /* STROKE_FONT_H_ */ +#endif // STROKE_FONT_H_ diff --git a/include/richio.h b/include/richio.h index dc66d6c82b..6440fda7b0 100644 --- a/include/richio.h +++ b/include/richio.h @@ -29,8 +29,8 @@ // "richio" after its author, Richard Hollenbeck, aka Dick Hollenbeck. -#include #include +#include // I really did not want to be dependent on wxWidgets in richio // but the errorText needs to be wide char so wxString rules. diff --git a/include/utf8.h b/include/utf8.h index 7f60173083..c1bf94a971 100644 --- a/include/utf8.h +++ b/include/utf8.h @@ -83,6 +83,23 @@ public: return *this; } + UTF8& operator=( const char* s ) + { + std::string::operator=( s ); + return *this; + } + + UTF8& operator=( char c ) + { + std::string::operator=( c ); + return *this; + } + + UTF8 substr( size_t pos = 0, size_t len = npos ) const + { + return std::string::substr( pos, len ); + } + operator wxString () const; /// This one is not in std::string, and one wonders why... might be a solid diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index 22b082fc9d..a56c226d63 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -2447,7 +2447,7 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets, msg.Printf( _( "Checking netlist component footprint \"%s:%s:%s\".\n" ), GetChars( component->GetReference() ), GetChars( component->GetTimeStamp() ), - GetChars( FROM_UTF8( component->GetFPID().Format().c_str() ) ) ); + GetChars( component->GetFPID().Format() ) ); aReporter->Report( msg ); } @@ -2465,7 +2465,7 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets, msg.Printf( _( "Adding new component \"%s:%s\" footprint \"%s\".\n" ), GetChars( component->GetReference() ), GetChars( component->GetTimeStamp() ), - GetChars( FROM_UTF8( component->GetFPID().Format().c_str() ) ) ); + GetChars( component->GetFPID().Format() ) ); if( aReporter->ReportWarnings() ) aReporter->Report( msg ); @@ -2476,7 +2476,7 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets, "footprint \"%s\".\n" ), GetChars( component->GetReference() ), GetChars( component->GetTimeStamp() ), - GetChars( FROM_UTF8( component->GetFPID().Format().c_str() ) ) ); + GetChars( component->GetFPID().Format() ) ); if( aReporter->ReportErrors() ) aReporter->Report( msg ); @@ -2509,8 +2509,8 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets, "\"%s\".\n" ), GetChars( footprint->GetReference() ), GetChars( footprint->GetPath() ), - GetChars( FROM_UTF8( footprint->GetFPID().Format().c_str() ) ), - GetChars( FROM_UTF8( component->GetFPID().Format().c_str() ) ) ); + GetChars( footprint->GetFPID().Format() ), + GetChars( component->GetFPID().Format() ) ); if( aReporter->ReportWarnings() ) aReporter->Report( msg ); @@ -2521,7 +2521,7 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets, "footprint \"%s\".\n" ), GetChars( footprint->GetReference() ), GetChars( footprint->GetPath() ), - GetChars( FROM_UTF8( component->GetFPID().Format().c_str() ) ) ); + GetChars( component->GetFPID().Format() ) ); if( aReporter->ReportErrors() ) aReporter->Report( msg ); @@ -2755,7 +2755,7 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets, msg.Printf( _( "** Error: Component \"%s\" pad '%s' not found in footprint \"%s\" **\n" ), GetChars( component->GetReference() ), GetChars( padname ), - footprint->GetFPID().Format().c_str() ); + GetChars( footprint->GetFPID().Format() ) ); aReporter->Report( msg ); } } diff --git a/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp b/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp index d2c81102fe..cb1aeb12dc 100644 --- a/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp +++ b/pcbnew/dialogs/dialog_edit_module_for_Modedit.cpp @@ -117,7 +117,7 @@ void DIALOG_MODULE_MODULE_EDITOR::initModeditProperties() m_ReferenceCtrl->SetValue( m_referenceCopy->GetText() ); m_ValueCtrl->SetValue( m_valueCopy->GetText() ); m_ValueCtrl->SetValue( m_valueCopy->GetText() ); - m_FootprintNameCtrl->SetValue( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) ); + m_FootprintNameCtrl->SetValue( m_currentModule->GetFPID().Format() ); m_AttributsCtrl->SetItemToolTip( 0, _( "Use this attribute for most non SMD components" ) ); m_AttributsCtrl->SetItemToolTip( 1, diff --git a/pcbnew/dialogs/dialog_fp_plugin_options.cpp b/pcbnew/dialogs/dialog_fp_plugin_options.cpp index dc3a0b5127..316ce5afa1 100644 --- a/pcbnew/dialogs/dialog_fp_plugin_options.cpp +++ b/pcbnew/dialogs/dialog_fp_plugin_options.cpp @@ -84,7 +84,7 @@ public: for( PROPERTIES::const_iterator it = props->begin(); it != props->end(); ++it, ++row ) { m_grid->SetCellValue( row, 0, FROM_UTF8( it->first.c_str() ) ); - m_grid->SetCellValue( row, 1, FROM_UTF8( it->second.c_str() ) ); + m_grid->SetCellValue( row, 1, it->second ); } delete props; @@ -184,7 +184,7 @@ private: for( int row = 0; rowGetCellValue( row, 0 ).Trim( false ).Trim() ); - string value = TO_UTF8( m_grid->GetCellValue( row, 1 ).Trim( false ).Trim() ); + UTF8 value = m_grid->GetCellValue( row, 1 ).Trim( false ).Trim(); if( name.size() ) { @@ -192,9 +192,7 @@ private: } } - string options = FP_LIB_TABLE::FormatOptions( &props ); - - return FROM_UTF8( options.c_str() ); + return FP_LIB_TABLE::FormatOptions( &props ); } void saveColSizes() @@ -261,11 +259,11 @@ private: if( event.IsSelection() ) { string option = TO_UTF8( event.GetString() ); - string help_text; + UTF8 help_text; if( m_choices.Value( option.c_str(), &help_text ) ) { - wxString page = FROM_UTF8( help_text.c_str() ); + wxString page = help_text; m_html->SetPage( page ); } diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index ef4f1e42a9..80b2fe78c5 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -2711,8 +2711,8 @@ void EAGLE_PLUGIN::centerBoard() { if( m_props ) { - string page_width; - string page_height; + UTF8 page_width; + UTF8 page_height; if( m_props->Value( "page_width", &page_width ) && m_props->Value( "page_height", &page_height ) ) @@ -2873,10 +2873,10 @@ void EAGLE_PLUGIN::FootprintLibOptions( PROPERTIES* aListToAppendTo ) const PLUGIN::FootprintLibOptions( aListToAppendTo ); /* - (*aListToAppendTo)["ignore_duplicates"] = wxString( _( + (*aListToAppendTo)["ignore_duplicates"] = UTF8( _( "Ignore duplicately named footprints within the same Eagle library. " "Only the first similarly named footprint will be loaded." - )).utf8_str(); + )); */ } diff --git a/pcbnew/gen_modules_placefile.cpp b/pcbnew/gen_modules_placefile.cpp index 6d4301df6b..c5d1fc7073 100644 --- a/pcbnew/gen_modules_placefile.cpp +++ b/pcbnew/gen_modules_placefile.cpp @@ -479,9 +479,11 @@ int PCB_EDIT_FRAME::DoGenFootprintsPositionFile( const wxString& aFullFileName, for( int ii = 0; ii < moduleCount; ii++ ) { wxPoint module_pos; + const wxString& ref = list[ii].m_Reference; const wxString& val = list[ii].m_Value; - const wxString& pkg = FROM_UTF8( list[ii].m_Module->GetFPID().Format().c_str() ); + const wxString& pkg = list[ii].m_Module->GetFPID().Format(); + sprintf( line, "%-8.8s %-16.16s %-16.16s", TO_UTF8( ref ), TO_UTF8( val ), TO_UTF8( pkg ) ); diff --git a/pcbnew/github/github_plugin.cpp b/pcbnew/github/github_plugin.cpp index b552669381..7099a83a88 100644 --- a/pcbnew/github/github_plugin.cpp +++ b/pcbnew/github/github_plugin.cpp @@ -166,7 +166,7 @@ MODULE* GITHUB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, } } - string fp_name = TO_UTF8( aFootprintName ); + UTF8 fp_name = aFootprintName; MODULE_CITER it = m_gh_cache->find( fp_name ); @@ -321,20 +321,20 @@ void GITHUB_PLUGIN::FootprintLibOptions( PROPERTIES* aListToAppendTo ) const // inherit options supported by all PLUGINs. PLUGIN::FootprintLibOptions( aListToAppendTo ); - (*aListToAppendTo)[ PRETTY_DIR ] = wxString( _( + (*aListToAppendTo)[ PRETTY_DIR ] = UTF8( _( "Set this property to a directory where footprints are to be written as pretty " "footprints when saving to this library. Anything saved will take precedence over " "footprints by the same name in the github repo. These saved footprints can then " "be sent to the library maintainer as updates. " "

The directory must have a .pretty file extension because the " "format of the save is pretty.

" - )).utf8_str(); + )); /* - (*aListToAppendTo)["cache_github_zip_in_this_dir"] = wxString( _( + (*aListToAppendTo)["cache_github_zip_in_this_dir"] = UTF8( _( "Set this property to a directory where the github *.zip file will be cached. " "This should speed up subsequent visits to this library." - )).utf8_str(); + )); */ } @@ -356,11 +356,11 @@ void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath, const PROPERTIES* aP if( aProperties ) { - string pretty_dir; + UTF8 pretty_dir; if( aProperties->Value( PRETTY_DIR, &pretty_dir ) ) { - wxString wx_pretty_dir = FROM_UTF8( pretty_dir.c_str() ); + wxString wx_pretty_dir = pretty_dir; wx_pretty_dir = FP_LIB_TABLE::ExpandSubstitutions( wx_pretty_dir ); @@ -409,7 +409,7 @@ void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath, const PROPERTIES* aP if( fn.GetExt() == kicad_mod ) { - string fp_name = TO_UTF8( fn.GetName() ); // omit extension & path + UTF8 fp_name = fn.GetName(); // omit extension & path m_gh_cache->insert( fp_name, entry ); } diff --git a/pcbnew/io_mgr.cpp b/pcbnew/io_mgr.cpp index 6baa48513a..f29fe6fe23 100644 --- a/pcbnew/io_mgr.cpp +++ b/pcbnew/io_mgr.cpp @@ -44,7 +44,7 @@ // is there a better place for this function? -bool PROPERTIES::Value( const char* aName, std::string* aFetchedValue ) const +bool PROPERTIES::Value( const char* aName, UTF8* aFetchedValue ) const { PROPERTIES::const_iterator it = find( aName ); diff --git a/pcbnew/io_mgr.h b/pcbnew/io_mgr.h index 58f6459d17..6ac100ff7f 100644 --- a/pcbnew/io_mgr.h +++ b/pcbnew/io_mgr.h @@ -38,7 +38,7 @@ class MODULE; * is a name/value tuple with unique names and optional values. The names * may be iterated alphabetically. */ -class PROPERTIES : public std::map< std::string, std::string > +class PROPERTIES : public std::map< std::string, UTF8 > { // alphabetical tuple of name and value hereby defined. @@ -53,7 +53,7 @@ public: * exists and aFetchedValue is not NULL. * @return bool - true if property is found, else false. */ - bool Value( const char* aName, std::string* aFetchedValue = NULL ) const; + bool Value( const char* aName, UTF8* aFetchedValue = NULL ) const; }; diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 5a2caa377b..d18c32edaa 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -1782,8 +1782,7 @@ void PCB_IO::FootprintSave( const wxString& aLibraryPath, const MODULE* aFootpri MODULE_MAP& mods = m_cache->GetModules(); // Quietly overwrite module and delete module file from path for any by same name. - wxFileName fn( aLibraryPath, FROM_UTF8( aFootprint->GetFPID().GetFootprintName().c_str() ), - KiCadFootprintFileExtension ); + wxFileName fn( aLibraryPath, aFootprint->GetFPID().GetFootprintName(), KiCadFootprintFileExtension ); if( !fn.IsOk() ) { diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index 11afe8aa0f..4bc6e4550a 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -1144,8 +1144,9 @@ void LEGACY_PLUGIN::LoadMODULE( MODULE* aModule ) } } - wxString msg = wxString::Format( wxT( "Missing '$EndMODULE' for MODULE '%s'" ), - aModule->GetFPID().GetFootprintName().c_str() ); + wxString msg = wxString::Format( + wxT( "Missing '$EndMODULE' for MODULE '%s'" ), + GetChars( aModule->GetFPID().GetFootprintName() ) ); THROW_IO_ERROR( msg ); } @@ -1200,7 +1201,7 @@ void LEGACY_PLUGIN::loadPAD( MODULE* aModule ) padchar, padchar, m_reader->LineNumber(), - aModule->GetFPID().GetFootprintName().c_str() + GetChars( aModule->GetFPID().GetFootprintName() ) ); THROW_IO_ERROR( m_error ); } @@ -1401,7 +1402,7 @@ void LEGACY_PLUGIN::loadMODULE_EDGE( MODULE* aModule ) (unsigned char) line[1], (unsigned char) line[1], m_reader->LineNumber(), - aModule->GetFPID().GetFootprintName().c_str() + GetChars( aModule->GetFPID().GetFootprintName() ) ); THROW_IO_ERROR( m_error ); } diff --git a/pcbnew/librairi.cpp b/pcbnew/librairi.cpp index e767664afd..7fd65174f3 100644 --- a/pcbnew/librairi.cpp +++ b/pcbnew/librairi.cpp @@ -285,7 +285,7 @@ void FOOTPRINT_EDIT_FRAME::Export_Module( MODULE* aModule ) if( aModule == NULL ) return; - fn.SetName( FROM_UTF8( aModule->GetFPID().GetFootprintName().c_str() ) ); + fn.SetName( aModule->GetFPID().GetFootprintName() ); wxString wildcard = wxGetTranslation( KiCadFootprintLibFileWildcard ); @@ -485,7 +485,7 @@ bool FOOTPRINT_EDIT_FRAME::DeleteModuleFromCurrentLibrary() return false; FPID fpid( fpid_txt ); - wxString fpname = FROM_UTF8( fpid.GetFootprintName().c_str() ); + wxString fpname = fpid.GetFootprintName(); // Confirmation wxString msg = wxString::Format( FMT_OK_DELETE, fpname.GetData(), nickname.GetData() ); @@ -581,7 +581,7 @@ bool PCB_BASE_FRAME::Save_Module_In_Library( const wxString& aLibrary, SetMsgPanel( aModule ); // Ask what to use as the footprint name in the library - wxString footprintName = FROM_UTF8( aModule->GetFPID().GetFootprintName().c_str() ); + wxString footprintName = aModule->GetFPID().GetFootprintName(); if( aDisplayDialog ) { diff --git a/pcbnew/loadcmp.cpp b/pcbnew/loadcmp.cpp index 85ae9335f8..f435f867c8 100644 --- a/pcbnew/loadcmp.cpp +++ b/pcbnew/loadcmp.cpp @@ -451,8 +451,8 @@ MODULE* PCB_BASE_FRAME::loadFootprint( const FPID& aFootprintId ) wxCHECK_MSG( m_footprintLibTable != NULL, NULL, wxT( "Cannot look up FPID in NULL FP_LIB_TABLE." ) ); - wxString nickname = FROM_UTF8( aFootprintId.GetLibNickname().c_str() ); - wxString fpname = FROM_UTF8( aFootprintId.GetFootprintName().c_str() ); + wxString nickname = aFootprintId.GetLibNickname(); + wxString fpname = aFootprintId.GetFootprintName(); if( nickname.size() ) { diff --git a/pcbnew/plugin.cpp b/pcbnew/plugin.cpp index 65e032cd95..fd6359f4c5 100644 --- a/pcbnew/plugin.cpp +++ b/pcbnew/plugin.cpp @@ -114,34 +114,34 @@ void PLUGIN::FootprintLibOptions( PROPERTIES* aListToAppendTo ) const { // disable all these in another couple of months, after everyone has seen them: #if 1 - (*aListToAppendTo)["debug_level"] = wxString( _( + (*aListToAppendTo)["debug_level"] = UTF8( _( "Enable debug logging for Footprint*() functions in this PLUGIN." - )).utf8_str(); + )); - (*aListToAppendTo)["read_filter_regex"] = wxString( _( + (*aListToAppendTo)["read_filter_regex"] = UTF8( _( "Regular expression footprint name filter." - )).utf8_str(); + )); - (*aListToAppendTo)["enable_transaction_logging"] = wxString( _( + (*aListToAppendTo)["enable_transaction_logging"] = UTF8( _( "Enable transaction logging. The mere presence of this option turns on the " " logging, no need to set a Value." - )).utf8_str(); + )); - (*aListToAppendTo)["username"] = wxString( _( + (*aListToAppendTo)["username"] = UTF8( _( "User name for login to some special library server." - )).utf8_str(); + )); - (*aListToAppendTo)["password"] = wxString( _( + (*aListToAppendTo)["password"] = UTF8( _( "Password for login to some special library server." - )).utf8_str(); + )); #endif #if 1 // Suitable for a C++ to python PLUGIN::Footprint*() adapter, move it to the adapter // if and when implemented. - (*aListToAppendTo)["python_footprint_plugin"] = wxString( _( + (*aListToAppendTo)["python_footprint_plugin"] = UTF8( _( "Enter the python module which implements the PLUGIN::Footprint*() functions." - )).utf8_str(); + )); #endif } diff --git a/pcbnew/xchgmod.cpp b/pcbnew/xchgmod.cpp index 184d650e3f..a4ce86869f 100644 --- a/pcbnew/xchgmod.cpp +++ b/pcbnew/xchgmod.cpp @@ -394,8 +394,8 @@ bool DIALOG_EXCHANGE_MODULE::Change_1_Module( MODULE* aModule, aNewFootprintFPID.Format().c_str() ); m_WinMessages->AppendText( line ); - wxString moduleName = FROM_UTF8( aNewFootprintFPID.GetFootprintName().c_str() ); - wxString libName = FROM_UTF8( aNewFootprintFPID.GetLibNickname().c_str() ); + wxString moduleName = aNewFootprintFPID.GetFootprintName(); + wxString libName = aNewFootprintFPID.GetLibNickname(); newModule = m_parent->LoadFootprint( aNewFootprintFPID ); From 95bb3d6bb156a3be8cb4a9b32c95ef461127155c Mon Sep 17 00:00:00 2001 From: Cirilo Bernardo Date: Thu, 2 Jan 2014 10:26:03 +0100 Subject: [PATCH 48/57] Adds basic IDF3 export (board and cutouts / holes only) --- include/wxPcbStruct.h | 6 + pcbnew/CMakeLists.txt | 4 + pcbnew/dialogs/dialog_export_idf.cpp | 121 +++ pcbnew/dialogs/dialog_export_idf_base.cpp | 49 ++ pcbnew/dialogs/dialog_export_idf_base.fbp | 383 +++++++++ pcbnew/dialogs/dialog_export_idf_base.h | 52 ++ pcbnew/export_idf.cpp | 357 ++++++++ pcbnew/idf.cpp | 968 ++++++++++++++++++++++ pcbnew/idf.h | 454 ++++++++++ pcbnew/menubar_pcbframe.cpp | 5 + pcbnew/pcbframe.cpp | 1 + pcbnew/pcbnew_id.h | 1 + 12 files changed, 2401 insertions(+) create mode 100644 pcbnew/dialogs/dialog_export_idf.cpp create mode 100644 pcbnew/dialogs/dialog_export_idf_base.cpp create mode 100644 pcbnew/dialogs/dialog_export_idf_base.fbp create mode 100644 pcbnew/dialogs/dialog_export_idf_base.h create mode 100644 pcbnew/export_idf.cpp create mode 100644 pcbnew/idf.cpp create mode 100644 pcbnew/idf.h diff --git a/include/wxPcbStruct.h b/include/wxPcbStruct.h index 05e4e0dbfc..576c4a5038 100644 --- a/include/wxPcbStruct.h +++ b/include/wxPcbStruct.h @@ -977,6 +977,12 @@ public: bool ExportVRML_File( const wxString & aFullFileName, double aMMtoWRMLunit, bool aExport3DFiles, const wxString & a3D_Subdir ); + /** + * Function ExportToIDF3 + * will export the current BOARD to a IDFv3 board and lib files. + */ + void ExportToIDF3( wxCommandEvent& event ); + /** * Function ExporttoSPECCTRA * will export the current BOARD to a specctra dsn file. See diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 06cec9d8e1..1816bf42c9 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -55,6 +55,8 @@ set( PCBNEW_DIALOGS dialogs/dialog_edit_module_text.cpp dialogs/dialog_edit_module_text_base.cpp dialogs/dialog_exchange_modules_base.cpp + dialogs/dialog_export_idf.cpp + dialogs/dialog_export_idf_base.cpp dialogs/dialog_export_vrml_base.cpp dialogs/dialog_export_vrml.cpp dialogs/dialog_find_base.cpp @@ -173,6 +175,7 @@ set( PCBNEW_CLASS_SRCS event_handlers_tracks_vias_sizes.cpp export_d356.cpp export_gencad.cpp + export_idf.cpp export_vrml.cpp files.cpp gen_drill_report_files.cpp @@ -183,6 +186,7 @@ set( PCBNEW_CLASS_SRCS hotkeys.cpp hotkeys_board_editor.cpp hotkeys_module_editor.cpp + idf.cpp initpcb.cpp layer_widget.cpp librairi.cpp diff --git a/pcbnew/dialogs/dialog_export_idf.cpp b/pcbnew/dialogs/dialog_export_idf.cpp new file mode 100644 index 0000000000..a87710a6ef --- /dev/null +++ b/pcbnew/dialogs/dialog_export_idf.cpp @@ -0,0 +1,121 @@ +/** + * @file dialog_export_idf.cpp + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Cirilo Bernardo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include + +// IDF export header generated by wxFormBuilder +#include + +#define OPTKEY_IDF_THOU wxT( "IDFExportThou" ) + + +bool Export_IDF3( BOARD *aPcb, const wxString & aFullFileName, double aUseThou ); + + +class DIALOG_EXPORT_IDF3: public DIALOG_EXPORT_IDF3_BASE +{ +private: + PCB_EDIT_FRAME* m_parent; + wxConfig* m_config; + bool m_idfThouOpt; // remember last preference for units in THOU + + void OnCancelClick( wxCommandEvent& event ) + { + EndModal( wxID_CANCEL ); + } + void OnOkClick( wxCommandEvent& event ) + { + EndModal( wxID_OK ); + } + +public: + DIALOG_EXPORT_IDF3( PCB_EDIT_FRAME* parent ) : + DIALOG_EXPORT_IDF3_BASE( parent ) + { + m_parent = parent; + m_config = wxGetApp().GetSettings(); + SetFocus(); + m_idfThouOpt = false; + m_config->Read( OPTKEY_IDF_THOU, &m_idfThouOpt ); + m_chkThou->SetValue( m_idfThouOpt ); + + GetSizer()->SetSizeHints( this ); + Centre(); + } + + ~DIALOG_EXPORT_IDF3() + { + m_idfThouOpt = m_chkThou->GetValue(); + m_config->Write( OPTKEY_IDF_THOU, m_idfThouOpt ); + } + + bool GetThouOption() + { + return m_chkThou->GetValue(); + } + + wxFilePickerCtrl* FilePicker() + { + return m_filePickerIDF; + } +}; + + +/** + * Function OnExportIDF3 + * will export the current BOARD to IDF board and lib files. + */ +void PCB_EDIT_FRAME::ExportToIDF3( wxCommandEvent& event ) +{ + wxFileName fn; + + // Build default file name + fn = GetBoard()->GetFileName(); + fn.SetExt( wxT( "emn" ) ); + + DIALOG_EXPORT_IDF3 dlg( this ); + dlg.FilePicker()->SetPath( fn.GetFullPath() ); + + if ( dlg.ShowModal() != wxID_OK ) + return; + + bool thou = dlg.GetThouOption(); + + wxBusyCursor dummy; + + wxString fullFilename = dlg.FilePicker()->GetPath(); + + if ( !Export_IDF3( GetBoard(), fullFilename, thou ) ) + { + wxString msg = _("Unable to create ") + fullFilename; + wxMessageBox( msg ); + return; + } +} diff --git a/pcbnew/dialogs/dialog_export_idf_base.cpp b/pcbnew/dialogs/dialog_export_idf_base.cpp new file mode 100644 index 0000000000..8d79b41733 --- /dev/null +++ b/pcbnew/dialogs/dialog_export_idf_base.cpp @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_export_idf_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_EXPORT_IDF3_BASE::DIALOG_EXPORT_IDF3_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bSizerIDFFile; + bSizerIDFFile = new wxBoxSizer( wxVERTICAL ); + + m_txtBrdFile = new wxStaticText( this, wxID_ANY, wxT("IDF Board file"), wxDefaultPosition, wxDefaultSize, 0 ); + m_txtBrdFile->Wrap( -1 ); + bSizerIDFFile->Add( m_txtBrdFile, 0, wxALL, 5 ); + + m_filePickerIDF = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString, wxT("Select a board file"), wxT("*.emn"), wxDefaultPosition, wxDefaultSize, wxFLP_OVERWRITE_PROMPT|wxFLP_SAVE|wxFLP_USE_TEXTCTRL ); + m_filePickerIDF->SetMinSize( wxSize( 420,30 ) ); + + bSizerIDFFile->Add( m_filePickerIDF, 0, wxALL, 5 ); + + m_chkThou = new wxCheckBox( this, wxID_ANY, wxT("unit: THOU"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerIDFFile->Add( m_chkThou, 0, wxALL, 5 ); + + m_sdbSizer1 = new wxStdDialogButtonSizer(); + m_sdbSizer1OK = new wxButton( this, wxID_OK ); + m_sdbSizer1->AddButton( m_sdbSizer1OK ); + m_sdbSizer1Cancel = new wxButton( this, wxID_CANCEL ); + m_sdbSizer1->AddButton( m_sdbSizer1Cancel ); + m_sdbSizer1->Realize(); + + bSizerIDFFile->Add( m_sdbSizer1, 1, wxEXPAND, 5 ); + + + this->SetSizer( bSizerIDFFile ); + this->Layout(); + + this->Centre( wxBOTH ); +} + +DIALOG_EXPORT_IDF3_BASE::~DIALOG_EXPORT_IDF3_BASE() +{ +} diff --git a/pcbnew/dialogs/dialog_export_idf_base.fbp b/pcbnew/dialogs/dialog_export_idf_base.fbp new file mode 100644 index 0000000000..1199e64b6a --- /dev/null +++ b/pcbnew/dialogs/dialog_export_idf_base.fbp @@ -0,0 +1,383 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + dialog_export_idf_base + 1000 + none + 0 + dialog_export_idf3_base + + . + + 1 + 1 + 1 + 1 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + DIALOG_EXPORT_IDF3_BASE + + 458,177 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + DIALOG_SHIM; dialog_shim.h + Export IDFv3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizerIDFFile + wxVERTICAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + IDF Board file + + 0 + + + 0 + + 1 + m_txtBrdFile + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + Select a board file + + 0 + 420,30 + 1 + m_filePickerIDF + 1 + + + protected + 1 + + Resizable + 1 + + wxFLP_OVERWRITE_PROMPT|wxFLP_SAVE|wxFLP_USE_TEXTCTRL + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + *.emn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + unit: THOU + + 0 + + + 0 + + 1 + m_chkThou + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_sdbSizer1 + protected + + + + + + + + + + + + + + diff --git a/pcbnew/dialogs/dialog_export_idf_base.h b/pcbnew/dialogs/dialog_export_idf_base.h new file mode 100644 index 0000000000..c581798507 --- /dev/null +++ b/pcbnew/dialogs/dialog_export_idf_base.h @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_EXPORT_IDF_BASE_H__ +#define __DIALOG_EXPORT_IDF_BASE_H__ + +#include +#include +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_EXPORT_IDF3_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_EXPORT_IDF3_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_txtBrdFile; + wxFilePickerCtrl* m_filePickerIDF; + wxCheckBox* m_chkThou; + wxStdDialogButtonSizer* m_sdbSizer1; + wxButton* m_sdbSizer1OK; + wxButton* m_sdbSizer1Cancel; + + public: + + DIALOG_EXPORT_IDF3_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Export IDFv3"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 458,177 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_EXPORT_IDF3_BASE(); + +}; + +#endif //__DIALOG_EXPORT_IDF_BASE_H__ diff --git a/pcbnew/export_idf.cpp b/pcbnew/export_idf.cpp new file mode 100644 index 0000000000..e3a568d4a6 --- /dev/null +++ b/pcbnew/export_idf.cpp @@ -0,0 +1,357 @@ +/** + * @file export_idf.cpp + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Cirilo Bernardo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +// assumed default graphical line thickness: 10000 IU == 0.1mm +#define LINE_WIDTH (100000) + +/** + * Function idf_export_outline + * retrieves line segment information from the edge layer and compiles + * the data into a form which can be output as an IDFv3 compliant + * BOARD_OUTLINE section. + */ +static void idf_export_outline( BOARD* aPcb, IDF_BOARD& aIDFBoard ) +{ + double scale = aIDFBoard.GetScale(); + + DRAWSEGMENT* graphic; // KiCad graphical item + IDF_POINT sp, ep; // start and end points from KiCad item + + std::list< IDF_SEGMENT* > lines; // IDF intermediate form of KiCad graphical item + IDF_OUTLINE outline; // graphical items forming an outline or cutout + + // NOTE: IMPLEMENTATION + // If/when component cutouts are allowed, we must implement them separately. Cutouts + // must be added to the board outline section and not to the Other Outline section. + // The module cutouts should be handled via the idf_export_module() routine. + + double offX, offY; + aIDFBoard.GetOffset( offX, offY ); + + // Retrieve segments and arcs from the board + for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) + { + if( item->Type() != PCB_LINE_T || item->GetLayer() != EDGE_N ) + continue; + + graphic = (DRAWSEGMENT*) item; + + switch( graphic->GetShape() ) + { + case S_SEGMENT: + { + sp.x = graphic->GetStart().x * scale + offX; + sp.y = -graphic->GetStart().y * scale + offY; + ep.x = graphic->GetEnd().x * scale + offX; + ep.y = -graphic->GetEnd().y * scale + offY; + IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep ); + + if( seg ) + lines.push_back( seg ); + } + break; + + case S_ARC: + { + sp.x = graphic->GetCenter().x * scale + offX; + sp.y = -graphic->GetCenter().y * scale + offY; + ep.x = graphic->GetArcStart().x * scale + offX; + ep.y = -graphic->GetArcStart().y * scale + offY; + IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, -graphic->GetAngle() / 10.0, true ); + + if( seg ) + lines.push_back( seg ); + } + break; + + case S_CIRCLE: + { + sp.x = graphic->GetCenter().x * scale + offX; + sp.y = -graphic->GetCenter().y * scale + offY; + ep.x = sp.x - graphic->GetRadius() * scale; + ep.y = sp.y; + // Circles must always have an angle of +360 deg. to appease + // quirky MCAD implementations of IDF. + IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, 360.0, true ); + + if( seg ) + lines.push_back( seg ); + } + break; + + default: + break; + } + } + + // if there is no outline then use the bounding box + if( lines.empty() ) + { + goto UseBoundingBox; + } + + // get the board outline and write it out + // note: we do not use a try/catch block here since we intend + // to simply ignore unclosed loops and continue processing + // until we're out of segments to process + IDF3::GetOutline( lines, outline ); + + if( outline.empty() ) + goto UseBoundingBox; + + aIDFBoard.AddOutline( outline ); + + // get all cutouts and write them out + while( !lines.empty() ) + { + IDF3::GetOutline( lines, outline ); + + if( outline.empty() ) + continue; + + aIDFBoard.AddOutline( outline ); + } + + return; + +UseBoundingBox: + + // clean up if necessary + while( !lines.empty() ) + { + delete lines.front(); + lines.pop_front(); + } + + outline.Clear(); + + // fetch a rectangular bounding box for the board; + // there is always some uncertainty in the board dimensions + // computed via ComputeBoundingBox() since this depends on the + // individual module entities. + EDA_RECT bbbox = aPcb->ComputeBoundingBox( true ); + + // convert to mm and compensate for an assumed LINE_WIDTH line thickness + double x = ( bbbox.GetOrigin().x + LINE_WIDTH / 2 ) * scale + offX; + double y = ( bbbox.GetOrigin().y + LINE_WIDTH / 2 ) * scale + offY; + double dx = ( bbbox.GetSize().x - LINE_WIDTH ) * scale; + double dy = ( bbbox.GetSize().y - LINE_WIDTH ) * scale; + + double px[4], py[4]; + px[0] = x; + py[0] = y; + + px[1] = x; + py[1] = y + dy; + + px[2] = x + dx; + py[2] = y + dy; + + px[3] = x + dx; + py[3] = y; + + IDF_POINT p1, p2; + + p1.x = px[3]; + p1.y = py[3]; + p2.x = px[0]; + p2.y = py[0]; + + outline.push( new IDF_SEGMENT( p1, p2 ) ); + + for( int i = 1; i < 4; ++i ) + { + p1.x = px[i - 1]; + p1.y = py[i - 1]; + p2.x = px[i]; + p2.y = py[i]; + + outline.push( new IDF_SEGMENT( p1, p2 ) ); + } + + aIDFBoard.AddOutline( outline ); +} + + +/** + * Function idf_export_module + * retrieves information from all board modules, adds drill holes to + * the DRILLED_HOLES or BOARD_OUTLINE section as appropriate, + * compiles data for the PLACEMENT section and compiles data for + * the library ELECTRICAL section. + */ +static void idf_export_module( BOARD* aPcb, MODULE* aModule, + IDF_BOARD& aIDFBoard ) +{ + // Reference Designator + std::string crefdes = TO_UTF8( aModule->GetReference() ); + + if( crefdes.empty() || !crefdes.compare( "~" ) ) + { + std::string cvalue = TO_UTF8( aModule->GetValue() ); + + // if both the RefDes and Value are empty or set to '~' the board owns the part, + // otherwise associated parts of the module must be marked NOREFDES. + if( cvalue.empty() || !cvalue.compare( "~" ) ) + crefdes = "BOARD"; + else + crefdes = "NOREFDES"; + } + + // TODO: If module cutouts are supported we must add code here + // for( EDA_ITEM* item = aModule->GraphicalItems(); item != NULL; item = item->Next() ) + // { + // if( ( item->Type() != PCB_MODULE_EDGE_T ) + // || (item->GetLayer() != EDGE_N ) ) continue; + // code to export cutouts + // } + + // Export pads + double drill, x, y; + double scale = aIDFBoard.GetScale(); + IDF3::KEY_PLATING kplate; + std::string pintype; + std::string tstr; + + double dx, dy; + + aIDFBoard.GetOffset( dx, dy ); + + for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) + { + drill = (double) pad->GetDrillSize().x * scale; + x = pad->GetPosition().x * scale + dx; + y = -pad->GetPosition().y * scale + dy; + + // Export the hole on the edge layer + if( drill > 0.0 ) + { + // plating + if( pad->GetAttribute() == PAD_HOLE_NOT_PLATED ) + kplate = IDF3::NPTH; + else + kplate = IDF3::PTH; + + // hole type + tstr = TO_UTF8( pad->GetPadName() ); + + if( tstr.empty() || !tstr.compare( "0" ) || !tstr.compare( "~" ) + || ( kplate == IDF3::NPTH ) || ( pad->GetDrillShape() == PAD_OVAL ) ) + pintype = "MTG"; + else + pintype = "PIN"; + + // fields: + // 1. hole dia. : float + // 2. X coord : float + // 3. Y coord : float + // 4. plating : PTH | NPTH + // 5. Assoc. part : BOARD | NOREFDES | PANEL | {"refdes"} + // 6. type : PIN | VIA | MTG | TOOL | { "other" } + // 7. owner : MCAD | ECAD | UNOWNED + if( ( pad->GetDrillShape() == PAD_OVAL ) + && ( pad->GetDrillSize().x != pad->GetDrillSize().y ) ) + { + // NOTE: IDF does not have direct support for slots; + // slots are implemented as a board cutout and we + // cannot represent plating or reference designators + + double dlength = pad->GetDrillSize().y * scale; + + // NOTE: The orientation of modules and pads have + // the opposite sense due to KiCad drawing on a + // screen with a LH coordinate system + double angle = pad->GetOrientation() / 10.0; + + if( dlength < drill ) + { + std::swap( drill, dlength ); + angle += M_PI2; + } + + // NOTE: KiCad measures a slot's length from end to end + // rather than between the centers of the arcs + dlength -= drill; + + aIDFBoard.AddSlot( drill, dlength, angle, x, y ); + } + else + { + aIDFBoard.AddDrill( drill, x, y, kplate, crefdes, pintype, IDF3::ECAD ); + } + } + } + + // TODO + // add to the library item list +} + + +/** + * Function Export_IDF3 + * generates IDFv3 compliant board (*.emn) and library (*.emp) + * files representing the user's PCB design. + */ +bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, double aUseThou ) +{ + IDF_BOARD idfBoard; + + SetLocaleTo_C_standard(); + + // NOTE: + // XXX We may enclose all this in a TRY .. CATCH block + idfBoard.Setup( aPcb->GetFileName(), aFullFileName, aUseThou, + aPcb->GetDesignSettings().GetBoardThickness() ); + + // set up the global offsets + EDA_RECT bbox = aPcb->ComputeBoundingBox( true ); + idfBoard.SetOffset( bbox.Centre().x * idfBoard.GetScale(), + bbox.Centre().y * idfBoard.GetScale() ); + + // Export the board outline + idf_export_outline( aPcb, idfBoard ); + + // Output the drill holes and module (library) data. + for( MODULE* module = aPcb->m_Modules; module != 0; module = module->Next() ) + idf_export_module( aPcb, module, idfBoard ); + + idfBoard.Finish(); + + SetLocaleTo_Default(); + + return true; +} diff --git a/pcbnew/idf.cpp b/pcbnew/idf.cpp new file mode 100644 index 0000000000..6bbddfdabc --- /dev/null +++ b/pcbnew/idf.cpp @@ -0,0 +1,968 @@ +/** + * file: idf.cpp + * + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Cirilo Bernardo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +// TODO: Consider using different precision formats for THOU vs MM output +// Keep in mind that THOU cannot represent MM very well although MM can +// represent 1 THOU with 4 decimal places. For modern manufacturing we +// are interested in a resolution of about 0.1 THOU. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// differences in angle smaller than MIN_ANG are considered equal +#define MIN_ANG (0.01) +// minimum drill diameter (nanometers) - 10000 is a 0.01mm drill +#define IDF_MIN_DIA ( 10000.0 ) + +// minimum board thickness; this is about 0.012mm (0.5 mils) +// which is about the thickness of a single kapton layer typically +// used in a flexible design. +#define IDF_MIN_BRD_THICKNESS (12000) + +bool IDF_POINT::Matches( const IDF_POINT& aPoint, double aRadius ) +{ + double dx = x - aPoint.x; + double dy = y - aPoint.y; + + double d2 = dx * dx + dy * dy; + + if( d2 <= aRadius * aRadius ) + return true; + + return false; +} + + +double IDF_POINT::CalcDistance( const IDF_POINT& aPoint ) const +{ + double dx = aPoint.x - x; + double dy = aPoint.y - y; + double dist = sqrt( dx * dx + dy * dy ); + + return dist; +} + + +double IDF3::CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ) +{ + return atan2( aEndPoint.y - aStartPoint.y, aEndPoint.x - aStartPoint.x ); +} + + +double IDF3::CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ) +{ + double ang = CalcAngleRad( aStartPoint, aEndPoint ); + + // round to thousandths of a degree + int iang = int (ang / M_PI * 1800000.0); + + ang = iang / 10000.0; + + return ang; +} + + +IDF_SEGMENT::IDF_SEGMENT() +{ + angle = 0.0; + offsetAngle = 0.0; + radius = 0.0; +} + + +IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ) +{ + angle = 0.0; + offsetAngle = 0.0; + radius = 0.0; + startPoint = aStartPoint; + endPoint = aEndPoint; +} + + +IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, + const IDF_POINT& aEndPoint, + double aAngle, + bool aFromKicad ) +{ + double diff = abs( aAngle ) - 360.0; + + if( ( diff < MIN_ANG + && diff > -MIN_ANG ) || ( aAngle < MIN_ANG && aAngle > -MIN_ANG ) || (!aFromKicad) ) + { + angle = 0.0; + startPoint = aStartPoint; + endPoint = aEndPoint; + + if( diff < MIN_ANG && diff > -MIN_ANG ) + { + angle = 360.0; + center = aStartPoint; + offsetAngle = 0.0; + radius = aStartPoint.CalcDistance( aEndPoint ); + } + else if( aAngle < MIN_ANG && aAngle > -MIN_ANG ) + { + CalcCenterAndRadius(); + } + + return; + } + + // we need to convert from the KiCad arc convention + angle = aAngle; + + center = aStartPoint; + + offsetAngle = IDF3::CalcAngleDeg( aStartPoint, aEndPoint ); + + radius = aStartPoint.CalcDistance( aEndPoint ); + + startPoint = aEndPoint; + + double ang = offsetAngle + aAngle; + ang = (ang / 180.0) * M_PI; + + endPoint.x = ( radius * cos( ang ) ) + center.x; + endPoint.y = ( radius * sin( ang ) ) + center.y; +} + + +bool IDF_SEGMENT::MatchesStart( const IDF_POINT& aPoint, double aRadius ) +{ + return startPoint.Matches( aPoint, aRadius ); +} + + +bool IDF_SEGMENT::MatchesEnd( const IDF_POINT& aPoint, double aRadius ) +{ + return endPoint.Matches( aPoint, aRadius ); +} + + +void IDF_SEGMENT::CalcCenterAndRadius( void ) +{ + // NOTE: this routine does not check if the points are the same + // or too close to be sensible in a production setting. + + double offAng = IDF3::CalcAngleRad( startPoint, endPoint ); + double d = startPoint.CalcDistance( endPoint ) / 2.0; + double xm = ( startPoint.x + endPoint.x ) * 0.5; + double ym = ( startPoint.y + endPoint.y ) * 0.5; + + radius = d / sin( angle * M_PI / 180.0 ); + + if( radius < 0.0 ) + { + radius = -radius; + } + + // calculate the height of the triangle with base d and hypotenuse r + double dh2 = radius * radius - d * d; + + if( dh2 < 0 ) + { + // this should only ever happen due to rounding errors when r == d + dh2 = 0; + } + + double h = sqrt( dh2 ); + + if( angle > 0.0 ) + offAng += M_PI2; + else + offAng -= M_PI2; + + if( ( angle > M_PI ) || ( angle < -M_PI ) ) + offAng += M_PI; + + center.x = h * cos( offAng ) + xm; + center.y = h * sin( offAng ) + ym; + + offsetAngle = IDF3::CalcAngleDeg( center, startPoint ); +} + + +bool IDF_SEGMENT::IsCircle( void ) +{ + double diff = abs( angle ) - 360.0; + + if( ( diff < MIN_ANG ) && ( diff > -MIN_ANG ) ) + return true; + + return false; +} + + +double IDF_SEGMENT::GetMinX( void ) +{ + if( angle == 0.0 ) + return std::min( startPoint.x, endPoint.x ); + + // Calculate the leftmost point of the circle or arc + + if( IsCircle() ) + { + // if only everything were this easy + return center.x - radius; + } + + // cases: + // 1. CCW arc: if offset + included angle >= 180 deg then + // MinX = center.x - radius, otherwise MinX is the + // same as for the case of a line. + // 2. CW arc: if offset + included angle <= -180 deg then + // MinX = center.x - radius, otherwise MinX is the + // same as for the case of a line. + + if( angle > 0 ) + { + // CCW case + if( ( offsetAngle + angle ) >= 180.0 ) + { + return center.x - radius; + } + else + { + return std::min( startPoint.x, endPoint.x ); + } + } + + // CW case + if( ( offsetAngle + angle ) <= -180.0 ) + { + return center.x - radius; + } + + return std::min( startPoint.x, endPoint.x ); +} + + +void IDF_SEGMENT::SwapEnds( void ) +{ + if( IsCircle() ) + { + // reverse the direction + angle = -angle; + return; + } + + IDF_POINT tmp = startPoint; + startPoint = endPoint; + endPoint = tmp; + + if( ( angle < MIN_ANG ) && ( angle > -MIN_ANG ) ) + return; // nothing more to do + + // change the direction of the arc + angle = -angle; + // calculate the new offset angle + offsetAngle = IDF3::CalcAngleDeg( center, startPoint ); +} + + +IDF_DRILL_DATA::IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY, + IDF3::KEY_PLATING aPlating, + const std::string aRefDes, + const std::string aHoleType, + IDF3::KEY_OWNER aOwner ) +{ + if( aDrillDia < 0.3 ) + dia = 0.3; + else + dia = aDrillDia; + + x = aPosX; + y = aPosY; + plating = aPlating; + + if( !aRefDes.compare( "BOARD" ) ) + { + kref = IDF3::BOARD; + } + else if( aRefDes.empty() || !aRefDes.compare( "NOREFDES" ) ) + { + kref = IDF3::NOREFDES; + } + else if( !aRefDes.compare( "PANEL" ) ) + { + kref = IDF3::PANEL; + } + else + { + kref = IDF3::REFDES; + refdes = aRefDes; + } + + if( !aHoleType.compare( "PIN" ) ) + { + khole = IDF3::PIN; + } + else if( !aHoleType.compare( "VIA" ) ) + { + khole = IDF3::VIA; + } + else if( aHoleType.empty() || !aHoleType.compare( "MTG" ) ) + { + khole = IDF3::MTG; + } + else if( !aHoleType.compare( "TOOL" ) ) + { + khole = IDF3::TOOL; + } + else + { + khole = IDF3::OTHER; + holetype = aHoleType; + } + + owner = aOwner; +} // IDF_DRILL_DATA::IDF_DRILL_DATA( ... ) + + +bool IDF_DRILL_DATA::Write( FILE* aLayoutFile ) +{ + // TODO: check stream integrity and return 'false' as appropriate + + if( !aLayoutFile ) + return false; + + std::string holestr; + std::string refstr; + std::string ownstr; + std::string pltstr; + + switch( khole ) + { + case IDF3::PIN: + holestr = "PIN"; + break; + + case IDF3::VIA: + holestr = "VIA"; + break; + + case IDF3::TOOL: + holestr = "TOOL"; + break; + + case IDF3::OTHER: + holestr = "\"" + holetype + "\""; + break; + + default: + holestr = "MTG"; + break; + } + + switch( kref ) + { + case IDF3::BOARD: + refstr = "BOARD"; + break; + + case IDF3::PANEL: + refstr = "PANEL"; + break; + + case IDF3::REFDES: + refstr = "\"" + refdes + "\""; + break; + + default: + refstr = "NOREFDES"; + break; + } + + if( plating == IDF3::PTH ) + pltstr = "PTH"; + else + pltstr = "NPTH"; + + switch( owner ) + { + case IDF3::MCAD: + ownstr = "MCAD"; + break; + + case IDF3::ECAD: + ownstr = "ECAD"; + break; + + default: + ownstr = "UNOWNED"; + } + + fprintf( aLayoutFile, "%.3f %.5f %.5f %s %s %s %s\n", + dia, x, y, pltstr.c_str(), refstr.c_str(), holestr.c_str(), ownstr.c_str() ); + + return true; +} // IDF_DRILL_DATA::Write( aLayoutFile ) + + +IDF_BOARD::IDF_BOARD() +{ + outlineIndex = 0; + scale = 1e-6; + boardThickness = 1.6; // default to 1.6mm thick boards + + useThou = false; // by default we want mm output + hasBrdOutlineHdr = false; + + layoutFile = NULL; + libFile = NULL; +} + + +IDF_BOARD::~IDF_BOARD() +{ + Finish(); +} + + +bool IDF_BOARD::Setup( wxString aBoardName, + wxString aFullFileName, + bool aUseThou, + int aBoardThickness ) +{ + if( aBoardThickness < IDF_MIN_BRD_THICKNESS ) + return false; + + if( aUseThou ) + { + useThou = true; + scale = 1e-3 / 25.4; + } + else + { + useThou = false; + scale = 1e-6; + } + + boardThickness = aBoardThickness * scale; + + wxFileName brdname( aBoardName ); + wxFileName idfname( aFullFileName ); + + // open the layout file + idfname.SetExt( wxT( "emn" ) ); + layoutFile = wxFopen( aFullFileName, wxT( "wt" ) ); + + if( layoutFile == NULL ) + return false; + + // open the library file + idfname.SetExt( wxT( "emp" ) ); + libFile = wxFopen( idfname.GetFullPath(), wxT( "wt" ) ); + + if( libFile == NULL ) + { + fclose( layoutFile ); + layoutFile = NULL; + return false; + } + + + time_t date; + time( &date ); + struct tm tdate; + + time( &date ); + localtime_r( &date, &tdate ); + + fprintf( layoutFile, ".HEADER\n" + "BOARD_FILE 3.0 \"Created by KiCad %s\"" + " %.4d/%.2d/%.2d.%.2d:%.2d:%.2d 1\n" + "\"%s\" %s\n" + ".END_HEADER\n\n", + TO_UTF8( GetBuildVersion() ), + tdate.tm_year + 1900, tdate.tm_mon + 1, tdate.tm_mday, + tdate.tm_hour, tdate.tm_min, tdate.tm_sec, + TO_UTF8( brdname.GetFullName() ), useThou ? "THOU" : "MM" ); + + fprintf( libFile, ".HEADER\n" + "BOARD_FILE 3.0 \"Created by KiCad %s\" %.4d/%.2d/%.2d.%.2d:%.2d:%.2d 1\n" + ".END_HEADER\n\n", + TO_UTF8( GetBuildVersion() ), + tdate.tm_year + 1900, tdate.tm_mon + 1, tdate.tm_mday, + tdate.tm_hour, tdate.tm_min, tdate.tm_sec ); + + return true; +} + + +bool IDF_BOARD::Finish( void ) +{ + // Steps to finalize the board and library files: + // 1. (emp) finalize the library file + // 2. (emn) close the BOARD_OUTLINE section + // 3. (emn) write out the DRILLED_HOLES section + // 4. (emn) write out the COMPONENT_PLACEMENT section + + // TODO: + // idfLib.Finish(); + if( libFile != NULL ) + { + fclose( libFile ); + libFile = NULL; + } + + if( layoutFile == NULL ) + return false; + + // Finalize the board outline section + fprintf( layoutFile, ".END_BOARD_OUTLINE\n\n" ); + + // Write out the drill section + if( WriteDrills() ) + { + fclose( layoutFile ); + layoutFile = NULL; + return false; + } + + // TODO: Write out the component placement section + // IDF3::export_placement(); + + fclose( layoutFile ); + layoutFile = NULL; + + return true; +} + + +bool IDF_BOARD::AddOutline( IDF_OUTLINE& aOutline ) +{ + if( !layoutFile ) + return false; + + // TODO: check the stream integrity + + std::list::iterator bo; + std::list::iterator eo; + + if( !hasBrdOutlineHdr ) + { + fprintf( layoutFile, ".BOARD_OUTLINE ECAD\n%.5f\n", boardThickness ); + hasBrdOutlineHdr = true; + } + + if( aOutline.size() == 1 ) + { + if( !aOutline.front()->IsCircle() ) + return false; // this is a bad outline + + // NOTE: a circle always has an angle of 360, never -360, + // otherwise SolidWorks chokes on the file. + fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, + aOutline.front()->startPoint.x, aOutline.front()->startPoint.y ); + fprintf( layoutFile, "%d %.5f %.5f 360\n", outlineIndex, + aOutline.front()->endPoint.x, aOutline.front()->endPoint.y ); + + ++outlineIndex; + return true; + } + + // ensure that the very last point is the same as the very first point + aOutline.back()-> endPoint = aOutline.front()->startPoint; + + // check if we must reverse things + if( ( aOutline.IsCCW() && ( outlineIndex > 0 ) ) + || ( ( !aOutline.IsCCW() ) && ( outlineIndex == 0 ) ) ) + { + eo = aOutline.begin(); + bo = aOutline.end(); + --bo; + + // for the first item we write out both points + if( aOutline.front()->angle < MIN_ANG && aOutline.front()->angle > -MIN_ANG ) + { + fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, + aOutline.front()->endPoint.x, aOutline.front()->endPoint.y ); + fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, + aOutline.front()->startPoint.x, aOutline.front()->startPoint.y ); + } + else + { + fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, + aOutline.front()->endPoint.x, aOutline.front()->endPoint.y ); + fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex, + aOutline.front()->startPoint.x, aOutline.front()->startPoint.y, + -aOutline.front()->angle ); + } + + // for all other segments we only write out the start point + while( bo != eo ) + { + if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG ) + { + fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, + (*bo)->startPoint.x, (*bo)->startPoint.y ); + } + else + { + fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex, + (*bo)->startPoint.x, (*bo)->startPoint.y, -(*bo)->angle ); + } + + --bo; + } + } + else + { + bo = aOutline.begin(); + eo = aOutline.end(); + + // for the first item we write out both points + if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG ) + { + fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, + (*bo)->startPoint.x, (*bo)->startPoint.y ); + fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, + (*bo)->endPoint.x, (*bo)->endPoint.y ); + } + else + { + fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, + (*bo)->startPoint.x, (*bo)->startPoint.y ); + fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex, + (*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle ); + } + + ++bo; + + // for all other segments we only write out the last point + while( bo != eo ) + { + if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG ) + { + fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, + (*bo)->endPoint.x, (*bo)->endPoint.y ); + } + else + { + fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex, + (*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle ); + } + + ++bo; + } + } + + ++outlineIndex; + + return true; +} + + +bool IDF_BOARD::AddDrill( double dia, double x, double y, + IDF3::KEY_PLATING plating, + const std::string refdes, + const std::string holeType, + IDF3::KEY_OWNER owner ) +{ + if( dia < IDF_MIN_DIA * scale ) + return false; + + IDF_DRILL_DATA* dp = new IDF_DRILL_DATA( dia, x, y, plating, refdes, holeType, owner ); + drills.push_back( dp ); + + return true; +} + + +bool IDF_BOARD::AddSlot( double aWidth, double aLength, double aOrientation, + double aX, double aY ) +{ + if( aWidth < IDF_MIN_DIA * scale ) + return false; + + if( aLength < IDF_MIN_DIA * scale ) + return false; + + IDF_POINT c[2]; // centers + IDF_POINT pt[4]; + + double a1 = aOrientation / 180.0 * M_PI; + double a2 = a1 + M_PI2; + double d1 = aLength / 2.0; + double d2 = aWidth / 2.0; + double sa1 = sin( a1 ); + double ca1 = cos( a1 ); + double dsa2 = d2 * sin( a2 ); + double dca2 = d2 * cos( a2 ); + + c[0].x = aX + d1 * ca1; + c[0].y = aY + d1 * sa1; + + c[1].x = aX - d1 * ca1; + c[1].y = aY - d1 * sa1; + + pt[0].x = c[0].x - dca2; + pt[0].y = c[0].y - dsa2; + + pt[1].x = c[1].x - dca2; + pt[1].y = c[1].y - dsa2; + + pt[2].x = c[1].x + dca2; + pt[2].y = c[1].y + dsa2; + + pt[3].x = c[0].x + dca2; + pt[3].y = c[0].y + dsa2; + + IDF_OUTLINE outline; + + // first straight run + IDF_SEGMENT* seg = new IDF_SEGMENT( pt[0], pt[1] ); + outline.push( seg ); + // first 180 degree cap + seg = new IDF_SEGMENT( c[1], pt[1], -180.0, true ); + outline.push( seg ); + // final straight run + seg = new IDF_SEGMENT( pt[2], pt[3] ); + outline.push( seg ); + // final 180 degree cap + seg = new IDF_SEGMENT( c[0], pt[3], -180.0, true ); + outline.push( seg ); + + return AddOutline( outline ); +} + + +bool IDF_BOARD::WriteDrills( void ) +{ + if( !layoutFile ) + return false; + + // TODO: check the stream integrity and return false as appropriate + if( drills.empty() ) + return true; + + fprintf( layoutFile, ".DRILLED_HOLES\n" ); + + std::list::iterator ds = drills.begin(); + std::list::iterator de = drills.end(); + + while( ds != de ) + { + if( !(*ds)->Write( layoutFile ) ) + return false; + + ++ds; + } + + fprintf( layoutFile, ".END_DRILLED_HOLES\n" ); + + return true; +} + + +double IDF_BOARD::GetScale( void ) +{ + return scale; +} + + +void IDF_BOARD::SetOffset( double x, double y ) +{ + offsetX = x; + offsetY = y; +} + + +void IDF_BOARD::GetOffset( double& x, double& y ) +{ + x = offsetX; + y = offsetY; +} + + +void IDF3::GetOutline( std::list& aLines, + IDF_OUTLINE& aOutline ) +{ + aOutline.Clear(); + + // NOTE: To tell if the point order is CCW or CW, + // sum all: (endPoint.X[n] - startPoint.X[n])*(endPoint[n] + startPoint.Y[n]) + // If the result is >0, the direction is CW, otherwise + // it is CCW. Note that the result cannot be 0 unless + // we have a bounded area of 0. + + // First we find the segment with the leftmost point + std::list::iterator bl = aLines.begin(); + std::list::iterator el = aLines.end(); + std::list::iterator idx = bl++; // iterator for the object with minX + + double minx = (*idx)->GetMinX(); + double curx; + + while( bl != el ) + { + curx = (*bl)->GetMinX(); + + if( curx < minx ) + { + minx = curx; + idx = bl; + } + + ++bl; + } + + aOutline.push( *idx ); + aLines.erase( idx ); + + // If the item is a circle then we're done + if( aOutline.front()->IsCircle() ) + return; + + // Assemble the loop + bool complete = false; // set if loop is complete + bool matched; // set if a segment's end point was matched + + while( !complete ) + { + matched = false; + bl = aLines.begin(); + el = aLines.end(); + + while( bl != el && !matched ) + { + if( (*bl)->MatchesStart( aOutline.back()->endPoint ) ) + { + if( (*bl)->IsCircle() ) + { + // a circle on the perimeter is pathological but we just ignore it + ++bl; + } + else + { + matched = true; + aOutline.push( *bl ); + aLines.erase( bl ); + } + + continue; + } + + ++bl; + } + + if( !matched ) + { + // attempt to match the end points + bl = aLines.begin(); + el = aLines.end(); + + while( bl != el && !matched ) + { + if( (*bl)->MatchesEnd( aOutline.back()->endPoint ) ) + { + if( (*bl)->IsCircle() ) + { + // a circle on the perimeter is pathological but we just ignore it + ++bl; + } + else + { + matched = true; + (*bl)->SwapEnds(); + aOutline.push( *bl ); + aLines.erase( bl ); + } + + continue; + } + + ++bl; + } + } + + if( !matched ) + { + // still no match - attempt to close the loop + if( (aOutline.size() > 1) || ( aOutline.front()->angle < -MIN_ANG ) + || ( aOutline.front()->angle > MIN_ANG ) ) + { + // close the loop + IDF_SEGMENT* seg = new IDF_SEGMENT( aOutline.back()->endPoint, + aOutline.front()->startPoint ); + + if( seg ) + { + complete = true; + aOutline.push( seg ); + break; + } + } + + // the outline is bad; drop the segments + aOutline.Clear(); + + return; + } + + // check if the loop is complete + if( aOutline.front()->MatchesStart( aOutline.back()->endPoint ) ) + { + complete = true; + break; + } + } +} + + +bool IDF_LIB::WriteLib( FILE* aLibFile ) +{ + if( !aLibFile ) + return false; + + // TODO: check stream integrity and return false as appropriate + + // TODO: export models + + return true; +} + + +bool IDF_LIB::WriteBrd( FILE* aLayoutFile ) +{ + if( !aLayoutFile ) + return false; + + // TODO: check stream integrity and return false as appropriate + + // TODO: write out the board placement information + + return true; +} diff --git a/pcbnew/idf.h b/pcbnew/idf.h new file mode 100644 index 0000000000..926f9dce9d --- /dev/null +++ b/pcbnew/idf.h @@ -0,0 +1,454 @@ +/** + * @file idf.h + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Cirilo Bernardo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef IDF_H +#define IDF_H + +#include + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795028841 +#endif + +#ifndef M_PI2 +#define M_PI2 ( M_PI / 2.0 ) +#endif + +#ifndef M_PI4 +#define M_PI4 ( M_PI / 4.0 ) +#endif + +class IDF_POINT; +class IDF_SEGMENT; +class IDF_DRILL_DATA; +class IDF_OUTLINE; + +namespace IDF3 { +enum KEY_OWNER +{ + UNOWNED = 0, // < either MCAD or ECAD may modify a feature + MCAD, // < only MCAD may modify a feature + ECAD // < only ECAD may modify a feature +}; + +enum KEY_HOLETYPE +{ + PIN = 0, // < drill hole is for a pin + VIA, // < drill hole is for a via + MTG, // < drill hole is for mounting + TOOL, // < drill hole is for tooling + OTHER // < user has specified a custom type +}; + +enum KEY_PLATING +{ + PTH = 0, // < Plate-Through Hole + NPTH // < Non-Plate-Through Hole +}; + +enum KEY_REFDES +{ + BOARD = 0, // < feature is associated with the board + NOREFDES, // < feature is associated with a component with no RefDes + PANEL, // < feature is associated with an IDF panel + REFDES // < reference designator as assigned by the CAD software +}; + +// calculate the angle between the horizon and the segment aStartPoint to aEndPoint +double CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ); +double CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ); + +// take contiguous elements from 'lines' and stuff them into 'outline' +void GetOutline( std::list& aLines, + IDF_OUTLINE& aOutline ); +} + + +/** + * @Class IDF_POINT + * represents a point + */ +class IDF_POINT +{ +public: + double x; // < X coordinate + double y; // < Y coordinate + + IDF_POINT() + { + x = 0.0; + y = 0.0; + } + + /** + * Function Matches() + * returns true if the given coordinate point is within the given radius + * of the point. + * @param aPoint : coordinates of the point being compared + * @param aRadius : radius within which the points are considered the same + */ + bool Matches( const IDF_POINT& aPoint, double aRadius = 1e-5 ); + double CalcDistance( const IDF_POINT& aPoint ) const; +}; + + +/** + * @Class IDF_SEGMENT + * represents a geometry segment as used in IDFv3 outlines + */ +class IDF_SEGMENT +{ +private: + /** + * Function CalcCenterAndRadius() + * Calculates the center, radius, and angle between center and start point given the + * IDF compliant points and included angle. + * @var startPoint, @var endPoint, and @var angle must be set prior as per IDFv3 + */ + void CalcCenterAndRadius( void ); + +public: + IDF_POINT startPoint; // starting point in IDF coordinates + IDF_POINT endPoint; // end point in IDF coordinates + IDF_POINT center; // center of an arc or circle; used primarily for calculating min X + double angle; // included angle (degrees) according to IDFv3 specification + double offsetAngle; // angle between center and start of arc; used to speed up some calcs. + double radius; // radius of the arc or circle; used to speed up some calcs. + + /** + * Function IDF_SEGMENT() + * initializes the internal variables + */ + IDF_SEGMENT(); + + /** + * Function IDF_SEGMENT( start, end ) + * creates a straight segment + */ + IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ); + + /** + * Function IDF_SEGMENT( start, end ) + * creates a straight segment, arc, or circle depending on the angle + * @param aStartPoint : start point (center if using KiCad convention, otherwise IDF convention) + * @param aEndPoint : end point (start of arc if using KiCad convention, otherwise IDF convention) + * @param aAngle : included angle; the KiCad convention is equivalent to the IDF convention + * @param fromKicad : set true if we need to convert from KiCad to IDF convention + */ + IDF_SEGMENT( const IDF_POINT& aStartPoint, + const IDF_POINT& aEndPoint, + double aAngle, + bool aFromKicad ); + + /** + * Function MatchesStart() + * returns true if the given coordinate is within a radius 'rad' + * of the start point. + * @param aPoint : coordinates of the point being compared + * @param aRadius : radius within which the points are considered the same + */ + bool MatchesStart( const IDF_POINT& aPoint, double aRadius = 1e-3 ); + + /** + * Function MatchesEnd() + * returns true if the given coordinate is within a radius 'rad' + * of the end point. + * @param aPoint : coordinates of the point being compared + * @param aRadius : radius within which the points are considered the same + */ + bool MatchesEnd( const IDF_POINT& aPoint, double aRadius = 1e-3 ); + + /** + * Function IsCircle() + * returns true if this segment is a circle + */ + bool IsCircle( void ); + + /** + * Function GetMinX() + * returns the minimum X coordinate of this segment + */ + double GetMinX( void ); + + /** + * Function SwapEnds() + * Swaps the start and end points and alters internal + * variables as necessary for arcs + */ + void SwapEnds( void ); +}; + + +/** + * @Class IDF_OUTLINE + * contains segment and winding information for an IDF outline + */ +class IDF_OUTLINE +{ +private: + double dir; + std::list outline; + +public: + IDF_OUTLINE() { dir = 0.0; } + ~IDF_OUTLINE() { Clear(); } + + // returns true if the current list of points represents a counterclockwise winding + bool IsCCW( void ) + { + if( dir > 0.0 ) + return false; + + return true; + } + + // clears the internal list of outline segments + void Clear( void ) + { + dir = 0.0; + + while( !outline.empty() ) + { + delete outline.front(); + outline.pop_front(); + } + } + + // returns the size of the internal segment list + size_t size( void ) + { + return outline.size(); + } + + // returns true if the internal segment list is empty + bool empty( void ) + { + return outline.empty(); + } + + // return the front() of the internal segment list + IDF_SEGMENT*& front( void ) + { + return outline.front(); + } + + // return the back() of the internal segment list + IDF_SEGMENT*& back( void ) + { + return outline.back(); + } + + // return the begin() iterator of the internal segment list + std::list::iterator begin( void ) + { + return outline.begin(); + } + + // return the end() iterator of the internal segment list + std::list::iterator end( void ) + { + return outline.end(); + } + + // push a segment onto the internal list + void push( IDF_SEGMENT* item ) + { + // XXX - check that startPoint[N] == endPoint[N -1], otherwise THROW + // XXX - a Circle must stand alone; if we add to a circle or add a + // circle to an existing list, we should throw an exception. + outline.push_back( item ); + dir += ( outline.back()->endPoint.x - outline.back()->startPoint.x ) + * ( outline.back()->endPoint.y + outline.back()->startPoint.y ); + } +}; + + +/** + * @Class IDF_BOARD + * contains objects necessary for the maintenance of the IDF board and library files. + */ +class IDF_BOARD +{ +private: + std::list drills; ///< IDF drill data + int outlineIndex; ///< next outline index to use + bool useThou; ///< true if output is THOU + double scale; ///< scale from KiCad IU to IDF output units + double boardThickness; ///< total thickness of the PCB + bool hasBrdOutlineHdr; ///< true when a board outline header has been written + + double offsetX; ///< offset to roughly center the board on the world origin + double offsetY; + + FILE* layoutFile; ///< IDF board file (*.emn) + FILE* libFile; ///< IDF library file (*.emp) + + /** + * Function Write + * outputs a .DRILLED_HOLES section compliant with the + * IDFv3 specification. + * @param aLayoutFile : open file (*.emn) for output + */ + bool WriteDrills( void ); + +public: + IDF_BOARD(); + + ~IDF_BOARD(); + + // Set up the output files and scale factor; + // return TRUE if everything is OK + bool Setup( wxString aBoardName, wxString aFullFileName, bool aUseThou, int aBoardThickness ); + + // Finish a board + // Write out all current data and close files. + // Return true for success + bool Finish( void ); + + /** + * Function GetScale + * returns the output scaling factor + */ + double GetScale( void ); + + /** + * Function SetOffset + * sets the global coordinate offsets + */ + void SetOffset( double x, double y ); + + /** + * Function GetOffset + * returns the global coordinate offsets + */ + void GetOffset( double& x, double& y ); + + // Add an outline; the very first outline is the board perimeter; + // all additional outlines are cutouts. + bool AddOutline( IDF_OUTLINE& aOutline ); + + /** + * Function AddDrill + * creates a drill entry and adds it to the list of PCB holes + * @param dia : drill diameter + * @param x : X coordinate of the drill center + * @param y : Y coordinate of the drill center + * @param plating : flag, PTH or NPTH + * @param refdes : component Reference Designator + * @param holetype : purpose of hole + * @param owner : one of MCAD, ECAD, UNOWNED + */ + bool AddDrill( double dia, double x, double y, + IDF3::KEY_PLATING plating, + const std::string refdes, + const std::string holeType, + IDF3::KEY_OWNER owner ); + + /** + * Function AddSlot + * creates a slot cutout within the IDF BOARD section; this is a deficient representation + * of a KiCad 'oval' drill; IDF is unable to represent a plated slot and unable to + * represent the Reference Designator association with a slot. + */ + bool AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY ); +}; + + +/** + * @Class IDF_DRILL_DATA + * contains information describing a drilled hole and is responsible for + * writing this information to a file in compliance with the IDFv3 specification. + */ +class IDF_DRILL_DATA +{ +private: + double dia; + double x; + double y; + IDF3::KEY_PLATING plating; + IDF3::KEY_REFDES kref; + IDF3::KEY_HOLETYPE khole; + std::string refdes; + std::string holetype; + IDF3::KEY_OWNER owner; + +public: + /** + * Constructor IDF_DRILL_DATA + * creates a drill entry with information compliant with the + * IDFv3 specifications. + * @param aDrillDia : drill diameter + * @param aPosX : X coordinate of the drill center + * @param aPosY : Y coordinate of the drill center + * @param aPlating : flag, PTH or NPTH + * @param aRefDes : component Reference Designator + * @param aHoleType : purpose of hole + * @param aOwner : one of MCAD, ECAD, UNOWNED + */ + IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY, + IDF3::KEY_PLATING aPlating, + const std::string aRefDes, + const std::string aHoleType, + IDF3::KEY_OWNER aOwner ); + + /** + * Function Write + * writes a single line representing the hole within a .DRILLED_HOLES section + */ + bool Write( FILE* aLayoutFile ); +}; + + +/** + * @Class IDF_LIB + * stores information on IDF models ( also has an inbuilt NOMODEL model ) + * and is responsible for writing the ELECTRICAL sections of the library file + * (*.emp) and the PLACEMENT section of the board file. + */ +class IDF_LIB +{ + // TODO: IMPLEMENT + +public: + /** + * Function WriteLib + * writes all current library information to the output file + */ + bool WriteLib( FILE* aLibFile ); + + // write placement information to the board file + bool WriteBrd( FILE* aLayoutFile ); + + // bool Finish( void ) + // { + // TODO: Write out the library (*.emp) file + // idf_lib.Write( lib_file ); + // TODO: fclose( lib_file ); + // } +}; + +#endif // IDF_H diff --git a/pcbnew/menubar_pcbframe.cpp b/pcbnew/menubar_pcbframe.cpp index 0070d7d837..386dfe522d 100644 --- a/pcbnew/menubar_pcbframe.cpp +++ b/pcbnew/menubar_pcbframe.cpp @@ -204,6 +204,11 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() _( "Export a VRML board representation" ), KiBitmap( three_d_xpm ) ); + // IDF3 + AddMenuItem( submenuexport, ID_GEN_EXPORT_FILE_IDF3, + _( "I&DFv3 Board Shape Export" ), _( "Basci export of board shape only IDFv3 format" ), + KiBitmap( export_xpm ) ); + AddMenuItem( filesMenu, submenuexport, ID_GEN_EXPORT_FILE, _( "E&xport" ), _( "Export board" ), KiBitmap( export_xpm ) ); diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index 58c1692d31..47d0218b3d 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -115,6 +115,7 @@ BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME ) EVT_MENU( ID_GEN_EXPORT_FILE_GENCADFORMAT, PCB_EDIT_FRAME::ExportToGenCAD ) EVT_MENU( ID_GEN_EXPORT_FILE_MODULE_REPORT, PCB_EDIT_FRAME::GenFootprintsReport ) EVT_MENU( ID_GEN_EXPORT_FILE_VRML, PCB_EDIT_FRAME::OnExportVRML ) + EVT_MENU( ID_GEN_EXPORT_FILE_IDF3, PCB_EDIT_FRAME::ExportToIDF3 ) EVT_MENU( ID_GEN_IMPORT_SPECCTRA_SESSION,PCB_EDIT_FRAME::ImportSpecctraSession ) EVT_MENU( ID_GEN_IMPORT_SPECCTRA_DESIGN, PCB_EDIT_FRAME::ImportSpecctraDesign ) diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h index 1a03a3764c..d4acbfedf8 100644 --- a/pcbnew/pcbnew_id.h +++ b/pcbnew/pcbnew_id.h @@ -246,6 +246,7 @@ enum pcbnew_ids ID_MENU_PCB_SWAP_LAYERS, ID_MENU_PCB_RESET_TEXTMODULE_FIELDS_SIZES, + ID_GEN_EXPORT_FILE_IDF3, ID_GEN_EXPORT_FILE_VRML, ID_GEN_EXPORT_SPECCTRA, ID_GEN_EXPORT_FILE_GENCADFORMAT, From 8c540c8b83a100f3a665b36127f1ddc65a57da0d Mon Sep 17 00:00:00 2001 From: Cirilo Bernardo Date: Fri, 3 Jan 2014 08:31:50 -0500 Subject: [PATCH 49/57] IDF3 wxDateTime patch to fix MinGW builds for missing localtime_r(). --- pcbnew/idf.cpp | 18 ++++++------------ pcbnew/menubar_pcbframe.cpp | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/pcbnew/idf.cpp b/pcbnew/idf.cpp index 6bbddfdabc..0e3066ccc8 100644 --- a/pcbnew/idf.cpp +++ b/pcbnew/idf.cpp @@ -492,30 +492,24 @@ bool IDF_BOARD::Setup( wxString aBoardName, return false; } - - time_t date; - time( &date ); - struct tm tdate; - - time( &date ); - localtime_r( &date, &tdate ); + wxDateTime tdate( time( NULL ) ); fprintf( layoutFile, ".HEADER\n" "BOARD_FILE 3.0 \"Created by KiCad %s\"" - " %.4d/%.2d/%.2d.%.2d:%.2d:%.2d 1\n" + " %.4u/%.2u/%.2u.%.2u:%.2u:%.2u 1\n" "\"%s\" %s\n" ".END_HEADER\n\n", TO_UTF8( GetBuildVersion() ), - tdate.tm_year + 1900, tdate.tm_mon + 1, tdate.tm_mday, - tdate.tm_hour, tdate.tm_min, tdate.tm_sec, + tdate.GetYear(), tdate.GetMonth() + 1, tdate.GetDay(), + tdate.GetHour(), tdate.GetMinute(), tdate.GetSecond(), TO_UTF8( brdname.GetFullName() ), useThou ? "THOU" : "MM" ); fprintf( libFile, ".HEADER\n" "BOARD_FILE 3.0 \"Created by KiCad %s\" %.4d/%.2d/%.2d.%.2d:%.2d:%.2d 1\n" ".END_HEADER\n\n", TO_UTF8( GetBuildVersion() ), - tdate.tm_year + 1900, tdate.tm_mon + 1, tdate.tm_mday, - tdate.tm_hour, tdate.tm_min, tdate.tm_sec ); + tdate.GetYear(), tdate.GetMonth() + 1, tdate.GetDay(), + tdate.GetHour(), tdate.GetMinute(), tdate.GetSecond() ); return true; } diff --git a/pcbnew/menubar_pcbframe.cpp b/pcbnew/menubar_pcbframe.cpp index 386dfe522d..7dacb03986 100644 --- a/pcbnew/menubar_pcbframe.cpp +++ b/pcbnew/menubar_pcbframe.cpp @@ -206,7 +206,7 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() // IDF3 AddMenuItem( submenuexport, ID_GEN_EXPORT_FILE_IDF3, - _( "I&DFv3 Board Shape Export" ), _( "Basci export of board shape only IDFv3 format" ), + _( "I&DFv3 Board Shape Export" ), _( "Basic export of board shape only IDFv3 format" ), KiBitmap( export_xpm ) ); AddMenuItem( filesMenu, submenuexport, From 77defa69c67752cd536a1ee8538da91644be922d Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Fri, 3 Jan 2014 18:39:28 +0100 Subject: [PATCH 50/57] Pcbnew: very minor fixes and update: update clipper version. uncrustify polytri/* and fix some warning compil. --- lib_dxf/drw_base.h | 2 +- lib_dxf/libdxfrw.h | 4 +- polygon/PolyLine.cpp | 8 +- polygon/clipper.cpp | 4701 ++++++++++++++++++----------- polygon/clipper.hpp | 351 ++- polygon/poly2tri/common/shapes.cc | 555 ++-- polygon/poly2tri/common/shapes.h | 362 +-- polygon/poly2tri/common/utils.h | 128 +- 8 files changed, 3680 insertions(+), 2431 deletions(-) diff --git a/lib_dxf/drw_base.h b/lib_dxf/drw_base.h index eb640516c9..db700be133 100644 --- a/lib_dxf/drw_base.h +++ b/lib_dxf/drw_base.h @@ -13,7 +13,7 @@ #ifndef DRW_BASE_H #define DRW_BASE_H -#define DRW_VERSION "0.5.10" +#define DRW_VERSION "0.5.11" #include #include diff --git a/lib_dxf/libdxfrw.h b/lib_dxf/libdxfrw.h index 574cfa88f9..d7119dce86 100644 --- a/lib_dxf/libdxfrw.h +++ b/lib_dxf/libdxfrw.h @@ -27,7 +27,7 @@ class dxfRW public: dxfRW( const char* name ); ~dxfRW(); - // / reads the file specified in constructor + /// reads the file specified in constructor /*! * An interface must be provided. It is used by the class to signal various * components being added. @@ -71,7 +71,7 @@ public: void setEllipseParts( int parts ) { elParts = parts; } /*!< set parts munber when convert ellipse to polyline */ private: - // / used by read() to parse the content of the file + /// used by read() to parse the content of the file bool processDxf(); bool processHeader(); bool processTables(); diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp index de50c23a0c..3ea299c330 100644 --- a/polygon/PolyLine.cpp +++ b/polygon/PolyLine.cpp @@ -88,8 +88,8 @@ CPolyLine::~CPolyLine() #include "clipper.hpp" int CPolyLine::NormalizeAreaOutlines( std::vector* aNewPolygonList ) { - ClipperLib::Polygon raw_polygon; - ClipperLib::Polygons normalized_polygons; + ClipperLib::Path raw_polygon; + ClipperLib::Paths normalized_polygons; unsigned corners_count = m_CornersList.GetCornersCount(); @@ -115,7 +115,7 @@ int CPolyLine::NormalizeAreaOutlines( std::vector* aNewPolygonList ) // enter main outline for( unsigned ii = 0; ii < normalized_polygons.size(); ii++ ) { - ClipperLib::Polygon& polygon = normalized_polygons[ii]; + ClipperLib::Path& polygon = normalized_polygons[ii]; cornerslist.clear(); for( unsigned jj = 0; jj < polygon.size(); jj++ ) cornerslist.push_back( KI_POLY_POINT( KiROUND( polygon[jj].X ), @@ -142,7 +142,7 @@ int CPolyLine::NormalizeAreaOutlines( std::vector* aNewPolygonList ) ClipperLib::SimplifyPolygon( raw_polygon, normalized_polygons ); for( unsigned ii = 0; ii < normalized_polygons.size(); ii++ ) { - ClipperLib::Polygon& polygon = normalized_polygons[ii]; + ClipperLib::Path& polygon = normalized_polygons[ii]; cornerslist.clear(); for( unsigned jj = 0; jj < polygon.size(); jj++ ) cornerslist.push_back( KI_POLY_POINT( KiROUND( polygon[jj].X ), diff --git a/polygon/clipper.cpp b/polygon/clipper.cpp index 46f1760130..162df7d93d 100644 --- a/polygon/clipper.cpp +++ b/polygon/clipper.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 5.1.4 * -* Date : 24 March 2013 * +* Version : 6.1.2 * +* Date : 15 December 2013 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2013 * * * @@ -46,20 +46,102 @@ #include #include #include +#include namespace ClipperLib { -static long64 const loRange = 0x3FFFFFFF; -static long64 const hiRange = 0x3FFFFFFFFFFFFFFFLL; +#ifdef use_int32 + static cInt const loRange = 46340; + static cInt const hiRange = 46340; +#else + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + typedef unsigned long long ulong64; +#endif + static double const pi = 3.141592653589793238; +static double const two_pi = pi *2; +static double const def_arc_tolerance = 0.25; + enum Direction { dRightToLeft, dLeftToRight }; +static int const Unassigned = -1; //edge not currently 'owning' a solution +static int const Skip = -2; //edge that would otherwise close a path + #define HORIZONTAL (-1.0E+40) #define TOLERANCE (1.0e-20) #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) -#define NEAR_EQUAL(a, b) NEAR_ZERO((a) - (b)) -inline long64 Abs(long64 val) +struct TEdge { + IntPoint Bot; + IntPoint Curr; + IntPoint Top; + IntPoint Delta; + double Dx; + PolyType PolyTyp; + EdgeSide Side; + int WindDelta; //1 or -1 depending on winding direction + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + TEdge *Next; + TEdge *Prev; + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; +}; + +struct IntersectNode { + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; +}; + +struct LocalMinima { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; + LocalMinima *Next; +}; + +struct OutPt; + +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + OutRec *FirstLeft; //see comments in clipper.pas + PolyNode *PolyNd; + OutPt *Pts; + OutPt *BottomPt; +}; + +struct OutPt { + int Idx; + IntPoint Pt; + OutPt *Next; + OutPt *Prev; +}; + +struct Join { + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +inline cInt Round(double val) +{ + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +inline cInt Abs(cInt val) { return val < 0 ? -val : val; } @@ -79,7 +161,7 @@ void PolyTree::Clear() PolyNode* PolyTree::GetFirst() const { - if (Childs.size() > 0) + if (!Childs.empty()) return Childs[0]; else return 0; @@ -88,27 +170,27 @@ PolyNode* PolyTree::GetFirst() const int PolyTree::Total() const { - return AllNodes.size(); + return (int)AllNodes.size(); } //------------------------------------------------------------------------------ // PolyNode methods ... //------------------------------------------------------------------------------ -PolyNode::PolyNode(): Childs(), Parent(0), Index(0) +PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) { } //------------------------------------------------------------------------------ int PolyNode::ChildCount() const { - return Childs.size(); + return (int)Childs.size(); } //------------------------------------------------------------------------------ void PolyNode::AddChild(PolyNode& child) { - unsigned cnt = Childs.size(); + unsigned cnt = (unsigned)Childs.size(); Childs.push_back(&child); child.Parent = this; child.Index = cnt; @@ -117,7 +199,7 @@ void PolyNode::AddChild(PolyNode& child) PolyNode* PolyNode::GetNext() const { - if (Childs.size() > 0) + if (!Childs.empty()) return Childs[0]; else return GetNextSiblingUp(); @@ -146,11 +228,20 @@ bool PolyNode::IsHole() const } return result; } +//------------------------------------------------------------------------------ + +bool PolyNode::IsOpen() const +{ + return m_IsOpen; +} +//------------------------------------------------------------------------------ + +#ifndef use_int32 //------------------------------------------------------------------------------ // Int128 class (enables safe math on signed 64bit integers) -// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 -// Int128 val2((long64)9223372036854775807); +// eg Int128 val1((cInt)9223372036854775807); //ie 2^63 -1 +// Int128 val2((cInt)9223372036854775807); // Int128 val3 = val1 * val2; // val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) //------------------------------------------------------------------------------ @@ -159,25 +250,25 @@ class Int128 { public: - ulong64 lo; - long64 hi; + cUInt lo; + cInt hi; - Int128(long64 _lo = 0) + Int128(cInt _lo = 0) { - lo = (ulong64)_lo; + lo = (cUInt)_lo; if (_lo < 0) hi = -1; else hi = 0; } Int128(const Int128 &val): lo(val.lo), hi(val.hi){} - Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} + Int128(const cInt& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} - long64 operator = (const long64 &val) + Int128& operator = (const cInt &val) { lo = (ulong64)val; if (val < 0) hi = -1; else hi = 0; - return val; + return *this; } bool operator == (const Int128 &val) const @@ -262,11 +353,11 @@ class Int128 while (divisor.hi >= 0 && !(divisor > dividend)) { divisor.hi <<= 1; - if ((long64)divisor.lo < 0) divisor.hi++; + if ((cInt)divisor.lo < 0) divisor.hi++; divisor.lo <<= 1; cntr.hi <<= 1; - if ((long64)cntr.lo < 0) cntr.hi++; + if ((cInt)cntr.lo < 0) cntr.hi++; cntr.lo <<= 1; } divisor.lo >>= 1; @@ -301,7 +392,7 @@ class Int128 return result; } else if (rhs.hi == this->hi && rhs.lo == this->lo) - return Int128(1); + return Int128(negate ? -1: 1); else return Int128(0); } @@ -311,15 +402,18 @@ class Int128 const double shift64 = 18446744073709551616.0; //2^64 if (hi < 0) { - if (lo == 0) return (double)hi * shift64; - else return -(double)(~lo + ~hi * shift64); + cUInt lo_ = ~lo + 1; + if (lo_ == 0) return (double)hi * shift64; + else return -(double)(lo_ + ~hi * shift64); } else return (double)(lo + hi * shift64); } -}; -Int128 Int128Mul (long64 lhs, long64 rhs) +}; +//------------------------------------------------------------------------------ + +Int128 Int128Mul (cInt lhs, cInt rhs) { bool negate = (lhs < 0) != (rhs < 0); @@ -337,269 +431,265 @@ Int128 Int128Mul (long64 lhs, long64 rhs) ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; Int128 tmp; - tmp.hi = long64(a + (c >> 32)); - tmp.lo = long64(c << 32); - tmp.lo += long64(b); + tmp.hi = cInt(a + (c >> 32)); + tmp.lo = cInt(c << 32); + tmp.lo += cInt(b); if (tmp.lo < b) tmp.hi++; if (negate) tmp = -tmp; return tmp; -} +}; +#endif //------------------------------------------------------------------------------ +// Miscellaneous global functions //------------------------------------------------------------------------------ -bool FullRangeNeeded(const Polygon &pts) -{ - bool result = false; - for (Polygon::size_type i = 0; i < pts.size(); ++i) - { - if (Abs(pts[i].X) > hiRange || Abs(pts[i].Y) > hiRange) - throw "Coordinate exceeds range bounds."; - else if (Abs(pts[i].X) > loRange || Abs(pts[i].Y) > loRange) - result = true; - } - return result; -} -//------------------------------------------------------------------------------ - -bool Orientation(const Polygon &poly) +bool Orientation(const Path &poly) { return Area(poly) >= 0; } //------------------------------------------------------------------------------ -inline bool PointsEqual( const IntPoint &pt1, const IntPoint &pt2) +double Area(const Path &poly) { - return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); -} -//------------------------------------------------------------------------------ + int size = (int)poly.size(); + if (size < 3) return 0; -double Area(const Polygon &poly) -{ - int highI = (int)poly.size() -1; - if (highI < 2) return 0; - - if (FullRangeNeeded(poly)) { - Int128 a; - a = Int128Mul(poly[highI].X + poly[0].X, poly[0].Y - poly[highI].Y); - for (int i = 1; i <= highI; ++i) - a += Int128Mul(poly[i - 1].X + poly[i].X, poly[i].Y - poly[i -1].Y); - return a.AsDouble() / 2; - } - else + double a = 0; + for (int i = 0, j = size -1; i < size; ++i) { - double a; - a = ((double)poly[highI].X + poly[0].X) * ((double)poly[0].Y - poly[highI].Y); - for (int i = 1; i <= highI; ++i) - a += ((double)poly[i - 1].X + poly[i].X) * ((double)poly[i].Y - poly[i - 1].Y); - return a / 2; + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; } + return -a * 0.5; } //------------------------------------------------------------------------------ -double Area(const OutRec &outRec, bool UseFullInt64Range) +double Area(const OutRec &outRec) { - OutPt *op = outRec.pts; + OutPt *op = outRec.Pts; if (!op) return 0; - if (UseFullInt64Range) { - Int128 a(0); - do { - a += Int128Mul(op->pt.X + op->prev->pt.X, op->prev->pt.Y - op->pt.Y); - op = op->next; - } while (op != outRec.pts); - return a.AsDouble() / 2; - } - else - { - double a = 0; - do { - a = a + (op->pt.X + op->prev->pt.X) * (op->prev->pt.Y - op->pt.Y); - op = op->next; - } while (op != outRec.pts); - return a / 2; - } + double a = 0; + do { + a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); + op = op->Next; + } while (op != outRec.Pts); + return a * 0.5; } //------------------------------------------------------------------------------ -bool PointIsVertex(const IntPoint &pt, OutPt *pp) +bool PointIsVertex(const IntPoint &Pt, OutPt *pp) { OutPt *pp2 = pp; do { - if (PointsEqual(pp2->pt, pt)) return true; - pp2 = pp2->next; + if (pp2->Pt == Pt) return true; + pp2 = pp2->Next; } while (pp2 != pp); return false; } //------------------------------------------------------------------------------ -bool PointInPolygon(const IntPoint &pt, OutPt *pp, bool UseFullInt64Range) +int PointInPolygon (const IntPoint& pt, OutPt* op) { - OutPt *pp2 = pp; - bool result = false; - if (UseFullInt64Range) { - do - { - if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || - ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && - Int128(pt.X - pp2->pt.X) < - Int128Mul(pp2->prev->pt.X - pp2->pt.X, pt.Y - pp2->pt.Y) / - Int128(pp2->prev->pt.Y - pp2->pt.Y)) - result = !result; - pp2 = pp2->next; - } - while (pp2 != pp); - } - else + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf + int result = 0; + OutPt* startOp = op; + for(;;) { - do + if (op->Next->Pt.Y == pt.Y) { - if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || - ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && - (pt.X < (pp2->prev->pt.X - pp2->pt.X) * (pt.Y - pp2->pt.Y) / - (pp2->prev->pt.Y - pp2->pt.Y) + pp2->pt.X )) result = !result; - pp2 = pp2->next; + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; } - while (pp2 != pp); - } + if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) + { + if (op->Pt.X >= pt.X) + { + if (op->Next->Pt.X > pt.X) result = 1 - result; + else + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } else + { + if (op->Next->Pt.X > pt.X) + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } + } + op = op->Next; + if (startOp == op) break; + } return result; } //------------------------------------------------------------------------------ -bool SlopesEqual(TEdge &e1, TEdge &e2, bool UseFullInt64Range) +bool Poly2ContainsPoly1(OutPt* OutPt1, OutPt* OutPt2) { + OutPt* op = OutPt1; + do + { + int res = PointInPolygon(op->Pt, OutPt2); + if (res >= 0) return res != 0; + op = op->Next; + } + while (op != OutPt1); + return true; +} +//---------------------------------------------------------------------- + +bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) +{ +#ifndef use_int32 if (UseFullInt64Range) - return Int128Mul(e1.deltaY, e2.deltaX) == Int128Mul(e1.deltaX, e2.deltaY); - else return e1.deltaY * e2.deltaX == e1.deltaX * e2.deltaY; + return Int128Mul(e1.Delta.Y, e2.Delta.X) == Int128Mul(e1.Delta.X, e2.Delta.Y); + else +#endif + return e1.Delta.Y * e2.Delta.X == e1.Delta.X * e2.Delta.Y; } //------------------------------------------------------------------------------ bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, const IntPoint pt3, bool UseFullInt64Range) { +#ifndef use_int32 if (UseFullInt64Range) return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); - else return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); } //------------------------------------------------------------------------------ bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) { +#ifndef use_int32 if (UseFullInt64Range) return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); - else return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); } //------------------------------------------------------------------------------ -double GetDx(const IntPoint pt1, const IntPoint pt2) +inline bool IsHorizontal(TEdge &e) +{ + return e.Delta.Y == 0; +} +//------------------------------------------------------------------------------ + +inline double GetDx(const IntPoint pt1, const IntPoint pt2) { return (pt1.Y == pt2.Y) ? HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); } //--------------------------------------------------------------------------- -void SetDx(TEdge &e) +inline void SetDx(TEdge &e) { - e.deltaX = (e.xtop - e.xbot); - e.deltaY = (e.ytop - e.ybot); + e.Delta.X = (e.Top.X - e.Bot.X); + e.Delta.Y = (e.Top.Y - e.Bot.Y); - if (e.deltaY == 0) e.dx = HORIZONTAL; - else e.dx = (double)(e.deltaX) / e.deltaY; + if (e.Delta.Y == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Delta.X) / e.Delta.Y; } //--------------------------------------------------------------------------- -void SwapSides(TEdge &edge1, TEdge &edge2) +inline void SwapSides(TEdge &Edge1, TEdge &Edge2) { - EdgeSide side = edge1.side; - edge1.side = edge2.side; - edge2.side = side; + EdgeSide Side = Edge1.Side; + Edge1.Side = Edge2.Side; + Edge2.Side = Side; } //------------------------------------------------------------------------------ -void SwapPolyIndexes(TEdge &edge1, TEdge &edge2) +inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) { - int outIdx = edge1.outIdx; - edge1.outIdx = edge2.outIdx; - edge2.outIdx = outIdx; + int OutIdx = Edge1.OutIdx; + Edge1.OutIdx = Edge2.OutIdx; + Edge2.OutIdx = OutIdx; } //------------------------------------------------------------------------------ -inline long64 Round(double val) +inline cInt TopX(TEdge &edge, const cInt currentY) { - return (val < 0) ? static_cast(val - 0.5) : static_cast(val + 0.5); + return ( currentY == edge.Top.Y ) ? + edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); } //------------------------------------------------------------------------------ -long64 TopX(TEdge &edge, const long64 currentY) -{ - return ( currentY == edge.ytop ) ? - edge.xtop : edge.xbot + Round(edge.dx *(currentY - edge.ybot)); -} -//------------------------------------------------------------------------------ - -bool IntersectPoint(TEdge &edge1, TEdge &edge2, +bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip, bool UseFullInt64Range) { +#ifdef use_xyz + ip.Z = 0; +#endif double b1, b2; - if (SlopesEqual(edge1, edge2, UseFullInt64Range)) + //nb: with very large coordinate values, it's possible for SlopesEqual() to + //return false but for the edge.Dx value be equal due to double precision rounding. + if (SlopesEqual(Edge1, Edge2, UseFullInt64Range) || Edge1.Dx == Edge2.Dx) { - if (edge2.ybot > edge1.ybot) ip.Y = edge2.ybot; - else ip.Y = edge1.ybot; + if (Edge2.Bot.Y > Edge1.Bot.Y) ip = Edge2.Bot; + else ip = Edge1.Bot; return false; } - else if (NEAR_ZERO(edge1.dx)) + else if (Edge1.Delta.X == 0) { - ip.X = edge1.xbot; - if (NEAR_EQUAL(edge2.dx, HORIZONTAL)) - ip.Y = edge2.ybot; + ip.X = Edge1.Bot.X; + if (IsHorizontal(Edge2)) + ip.Y = Edge2.Bot.Y; else { - b2 = edge2.ybot - (edge2.xbot / edge2.dx); - ip.Y = Round(ip.X / edge2.dx + b2); + b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); + ip.Y = Round(ip.X / Edge2.Dx + b2); } } - else if (NEAR_ZERO(edge2.dx)) + else if (Edge2.Delta.X == 0) { - ip.X = edge2.xbot; - if (NEAR_EQUAL(edge1.dx, HORIZONTAL)) - ip.Y = edge1.ybot; + ip.X = Edge2.Bot.X; + if (IsHorizontal(Edge1)) + ip.Y = Edge1.Bot.Y; else { - b1 = edge1.ybot - (edge1.xbot / edge1.dx); - ip.Y = Round(ip.X / edge1.dx + b1); + b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); + ip.Y = Round(ip.X / Edge1.Dx + b1); } } else { - b1 = edge1.xbot - edge1.ybot * edge1.dx; - b2 = edge2.xbot - edge2.ybot * edge2.dx; - double q = (b2-b1) / (edge1.dx - edge2.dx); + b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; + b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; + double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); ip.Y = Round(q); - if (std::fabs(edge1.dx) < std::fabs(edge2.dx)) - ip.X = Round(edge1.dx * q + b1); + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = Round(Edge1.Dx * q + b1); else - ip.X = Round(edge2.dx * q + b2); + ip.X = Round(Edge2.Dx * q + b2); } - if (ip.Y < edge1.ytop || ip.Y < edge2.ytop) + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) { - if (edge1.ytop > edge2.ytop) - { - ip.X = edge1.xtop; - ip.Y = edge1.ytop; - return TopX(edge2, edge1.ytop) < edge1.xtop; - } + if (Edge1.Top.Y > Edge2.Top.Y) + ip.Y = Edge1.Top.Y; else - { - ip.X = edge2.xtop; - ip.Y = edge2.ytop; - return TopX(edge1, edge2.ytop) > edge2.xtop; - } + ip.Y = Edge2.Top.Y; + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = TopX(Edge1, ip.Y); + else + ip.X = TopX(Edge2, ip.Y); } - else - return true; + return true; } //------------------------------------------------------------------------------ @@ -609,9 +699,9 @@ void ReversePolyPtLinks(OutPt *pp) OutPt *pp1, *pp2; pp1 = pp; do { - pp2 = pp1->next; - pp1->next = pp1->prev; - pp1->prev = pp2; + pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; pp1 = pp2; } while( pp1 != pp ); } @@ -620,54 +710,66 @@ void ReversePolyPtLinks(OutPt *pp) void DisposeOutPts(OutPt*& pp) { if (pp == 0) return; - pp->prev->next = 0; + pp->Prev->Next = 0; while( pp ) { OutPt *tmpPp = pp; - pp = pp->next; - delete tmpPp ; + pp = pp->Next; + delete tmpPp; } } //------------------------------------------------------------------------------ -void InitEdge(TEdge *e, TEdge *eNext, - TEdge *ePrev, const IntPoint &pt, PolyType polyType) +inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) { - std::memset( e, 0, sizeof( TEdge )); + std::memset(e, 0, sizeof(TEdge)); + e->Next = eNext; + e->Prev = ePrev; + e->Curr = Pt; + e->OutIdx = Unassigned; +} +//------------------------------------------------------------------------------ - e->next = eNext; - e->prev = ePrev; - e->xcurr = pt.X; - e->ycurr = pt.Y; - if (e->ycurr >= e->next->ycurr) +void InitEdge2(TEdge& e, PolyType Pt) +{ + if (e.Curr.Y >= e.Next->Curr.Y) { - e->xbot = e->xcurr; - e->ybot = e->ycurr; - e->xtop = e->next->xcurr; - e->ytop = e->next->ycurr; - e->windDelta = 1; + e.Bot = e.Curr; + e.Top = e.Next->Curr; } else { - e->xtop = e->xcurr; - e->ytop = e->ycurr; - e->xbot = e->next->xcurr; - e->ybot = e->next->ycurr; - e->windDelta = -1; + e.Top = e.Curr; + e.Bot = e.Next->Curr; } - SetDx(*e); - e->polyType = polyType; - e->outIdx = -1; + SetDx(e); + e.PolyTyp = Pt; } //------------------------------------------------------------------------------ -inline void SwapX(TEdge &e) +TEdge* RemoveEdge(TEdge* e) { - //swap horizontal edges' top and bottom x's so they follow the natural + //removes e from double_linked_list (but without removing from memory) + e->Prev->Next = e->Next; + e->Next->Prev = e->Prev; + TEdge* result = e->Next; + e->Prev = 0; //flag as removed (see ClipperBase.Clear) + return result; +} +//------------------------------------------------------------------------------ + +inline void ReverseHorizontal(TEdge &e) +{ + //swap horizontal edges' Top and Bottom x's so they follow the natural //progression of the bounds - ie so their xbots will align with the //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - e.xcurr = e.xtop; - e.xtop = e.xbot; - e.xbot = e.xcurr; + cInt tmp = e.Top.X; + e.Top.X = e.Bot.X; + e.Bot.X = tmp; +#ifdef use_xyz + tmp = e.Top.Z; + e.Top.Z = e.Bot.Z; + e.Bot.Z = tmp; +#endif } //------------------------------------------------------------------------------ @@ -682,7 +784,7 @@ void SwapPoints(IntPoint &pt1, IntPoint &pt2) bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) { - //precondition: segments are colinear. + //precondition: segments are Collinear. if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) { if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); @@ -703,19 +805,19 @@ bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) { - OutPt *p = btmPt1->prev; - while (PointsEqual(p->pt, btmPt1->pt) && (p != btmPt1)) p = p->prev; - double dx1p = std::fabs(GetDx(btmPt1->pt, p->pt)); - p = btmPt1->next; - while (PointsEqual(p->pt, btmPt1->pt) && (p != btmPt1)) p = p->next; - double dx1n = std::fabs(GetDx(btmPt1->pt, p->pt)); + OutPt *p = btmPt1->Prev; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; + double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + p = btmPt1->Next; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; + double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); - p = btmPt2->prev; - while (PointsEqual(p->pt, btmPt2->pt) && (p != btmPt2)) p = p->prev; - double dx2p = std::fabs(GetDx(btmPt2->pt, p->pt)); - p = btmPt2->next; - while (PointsEqual(p->pt, btmPt2->pt) && (p != btmPt2)) p = p->next; - double dx2n = std::fabs(GetDx(btmPt2->pt, p->pt)); + p = btmPt2->Prev; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; + double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + p = btmPt2->Next; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; + double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); } //------------------------------------------------------------------------------ @@ -723,35 +825,35 @@ bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) OutPt* GetBottomPt(OutPt *pp) { OutPt* dups = 0; - OutPt* p = pp->next; + OutPt* p = pp->Next; while (p != pp) { - if (p->pt.Y > pp->pt.Y) + if (p->Pt.Y > pp->Pt.Y) { pp = p; dups = 0; } - else if (p->pt.Y == pp->pt.Y && p->pt.X <= pp->pt.X) + else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) { - if (p->pt.X < pp->pt.X) + if (p->Pt.X < pp->Pt.X) { dups = 0; pp = p; } else { - if (p->next != pp && p->prev != pp) dups = p; + if (p->Next != pp && p->Prev != pp) dups = p; } } - p = p->next; + p = p->Next; } if (dups) { - //there appears to be at least 2 vertices at bottomPt so ... + //there appears to be at least 2 vertices at BottomPt so ... while (dups != p) { if (!FirstIsBottomPt(p, dups)) pp = dups; - dups = dups->next; - while (!PointsEqual(dups->pt, pp->pt)) dups = dups->next; + dups = dups->Next; + while (dups->Pt != pp->Pt) dups = dups->Next; } } return pp; @@ -761,52 +863,70 @@ OutPt* GetBottomPt(OutPt *pp) bool FindSegment(OutPt* &pp, bool UseFullInt64Range, IntPoint &pt1, IntPoint &pt2) { - //outPt1 & outPt2 => the overlap segment (if the function returns true) + //OutPt1 & OutPt2 => the overlap segment (if the function returns true) if (!pp) return false; OutPt* pp2 = pp; IntPoint pt1a = pt1, pt2a = pt2; do { - if (SlopesEqual(pt1a, pt2a, pp->pt, pp->prev->pt, UseFullInt64Range) && - SlopesEqual(pt1a, pt2a, pp->pt, UseFullInt64Range) && - GetOverlapSegment(pt1a, pt2a, pp->pt, pp->prev->pt, pt1, pt2)) + if (SlopesEqual(pt1a, pt2a, pp->Pt, pp->Prev->Pt, UseFullInt64Range) && + SlopesEqual(pt1a, pt2a, pp->Pt, UseFullInt64Range) && + GetOverlapSegment(pt1a, pt2a, pp->Pt, pp->Prev->Pt, pt1, pt2)) return true; - pp = pp->next; + pp = pp->Next; } while (pp != pp2); return false; } //------------------------------------------------------------------------------ -bool Pt3IsBetweenPt1AndPt2(const IntPoint pt1, +bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, const IntPoint pt2, const IntPoint pt3) { - if (PointsEqual(pt1, pt3) || PointsEqual(pt2, pt3)) return true; - else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X); - else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y); + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); } //------------------------------------------------------------------------------ -OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint pt) +OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint Pt) { if (p1 == p2) throw "JoinError"; OutPt* result = new OutPt; - result->pt = pt; - if (p2 == p1->next) + result->Pt = Pt; + if (p2 == p1->Next) { - p1->next = result; - p2->prev = result; - result->next = p2; - result->prev = p1; + p1->Next = result; + p2->Prev = result; + result->Next = p2; + result->Prev = p1; } else { - p2->next = result; - p1->prev = result; - result->next = p1; - result->prev = p2; + p2->Next = result; + p1->Prev = result; + result->Next = p1; + result->Prev = p2; } return result; } +//------------------------------------------------------------------------------ + +bool HorzSegmentsOverlap(const IntPoint& pt1a, const IntPoint& pt1b, + const IntPoint& pt2a, const IntPoint& pt2b) +{ + //precondition: both segments are horizontal + if ((pt1a.X > pt2a.X) == (pt1a.X < pt2b.X)) return true; + else if ((pt1b.X > pt2a.X) == (pt1b.X < pt2b.X)) return true; + else if ((pt2a.X > pt1a.X) == (pt2a.X < pt1b.X)) return true; + else if ((pt2b.X > pt1a.X) == (pt2b.X < pt1b.X)) return true; + else if ((pt1a.X == pt2a.X) && (pt1b.X == pt2b.X)) return true; + else if ((pt1a.X == pt2b.X) && (pt1b.X == pt2a.X)) return true; + else return false; +} + //------------------------------------------------------------------------------ // ClipperBase class methods ... @@ -816,7 +936,7 @@ ClipperBase::ClipperBase() //constructor { m_MinimaList = 0; m_CurrentLM = 0; - m_UseFullRange = true; + m_UseFullRange = false; } //------------------------------------------------------------------------------ @@ -826,95 +946,331 @@ ClipperBase::~ClipperBase() //destructor } //------------------------------------------------------------------------------ -bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) +void RangeTest(const IntPoint& Pt, bool& useFullRange) { - int len = (int)pg.size(); - if (len < 3) return false; - - Polygon p(len); - p[0] = pg[0]; - int j = 0; - - long64 maxVal; - if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; - - for (int i = 0; i < len; ++i) + if (useFullRange) { - if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) - { - if (Abs(pg[i].X) > hiRange || Abs(pg[i].Y) > hiRange) - throw "Coordinate exceeds range bounds"; - maxVal = hiRange; - m_UseFullRange = true; - } - - if (i == 0 || PointsEqual(p[j], pg[i])) continue; - else if (j > 0 && SlopesEqual(p[j-1], p[j], pg[i], m_UseFullRange)) - { - if (PointsEqual(p[j-1], pg[i])) j--; - } else j++; - p[j] = pg[i]; + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw "Coordinate outside allowed range"; } - if (j < 2) return false; - - len = j+1; - while (len > 2) + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) { - //nb: test for point equality before testing slopes ... - if (PointsEqual(p[j], p[0])) j--; - else if (PointsEqual(p[0], p[1]) || - SlopesEqual(p[j], p[0], p[1], m_UseFullRange)) - p[0] = p[j--]; - else if (SlopesEqual(p[j-1], p[j], p[0], m_UseFullRange)) j--; - else if (SlopesEqual(p[0], p[1], p[2], m_UseFullRange)) - { - for (int i = 2; i <= j; ++i) p[i-1] = p[i]; - j--; - } - else break; - len--; + useFullRange = true; + RangeTest(Pt, useFullRange); } - if (len < 3) return false; +} +//------------------------------------------------------------------------------ + +TEdge* FindNextLocMin(TEdge* E) +{ + for (;;) + { + while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; + if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; + while (IsHorizontal(*E->Prev)) E = E->Prev; + TEdge* E2 = E; + while (IsHorizontal(*E)) E = E->Next; + if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. + if (E2->Prev->Bot.X < E->Bot.X) E = E2; + break; + } + return E; +} +//------------------------------------------------------------------------------ + +TEdge* ClipperBase::ProcessBound(TEdge* E, bool IsClockwise) +{ + TEdge *EStart = E, *Result = E; + TEdge *Horz = 0; + cInt StartX; + if (IsHorizontal(*E)) + { + //it's possible for adjacent overlapping horz edges to start heading left + //before finishing right, so ... + if (IsClockwise) StartX = E->Prev->Bot.X; + else StartX = E->Next->Bot.X; + if (E->Bot.X != StartX) ReverseHorizontal(*E); + } + + if (Result->OutIdx != Skip) + { + if (IsClockwise) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X == Result->Next->Top.X) + { + if (!IsClockwise) Result = Horz->Prev; + } + else if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X) + { + if (!IsClockwise) Result = Horz->Next; + } + else if (Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + } + + if (Result->OutIdx == Skip) + { + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + E = Result; + if (IsClockwise) + { + while (E->Top.Y == E->Next->Bot.Y) E = E->Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && IsHorizontal(*E)) E = E->Prev; + } else + { + while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; + while (E != Result && IsHorizontal(*E)) E = E->Next; + } + if (E == Result) + { + if (IsClockwise) Result = E->Next; + else Result = E->Prev; + } else + { + //there are more edges in the bound beyond result starting with E + if (IsClockwise) + E = Result->Next; + else + E = Result->Prev; + LocalMinima* locMin = new LocalMinima; + locMin->Next = 0; + locMin->Y = E->Bot.Y; + locMin->LeftBound = 0; + locMin->RightBound = E; + locMin->RightBound->WindDelta = 0; + Result = ProcessBound(locMin->RightBound, IsClockwise); + InsertLocalMinima(locMin); + } + } + return Result; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) +{ +#ifdef use_lines + if (!Closed && PolyTyp == ptClip) + throw clipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw clipperException("AddPath: Open paths have been disabled."); +#endif + + int highI = (int)pg.size() -1; + if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; //create a new edge array ... - TEdge *edges = new TEdge [len]; - m_edges.push_back(edges); + TEdge *edges = new TEdge [highI +1]; - //convert vertices to a double-linked-list of edges and initialize ... - edges[0].xcurr = p[0].X; - edges[0].ycurr = p[0].Y; - InitEdge(&edges[len-1], &edges[0], &edges[len-2], p[len-1], polyType); - for (int i = len-2; i > 0; --i) - InitEdge(&edges[i], &edges[i+1], &edges[i-1], p[i], polyType); - InitEdge(&edges[0], &edges[1], &edges[len-1], p[0], polyType); + bool IsFlat = true; + //1. Basic (first) edge initialization ... + try + { + edges[1].Curr = pg[1]; + RangeTest(pg[0], m_UseFullRange); + RangeTest(pg[highI], m_UseFullRange); + InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); + InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], m_UseFullRange); + InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); + } + } + catch(...) + { + delete [] edges; + return false; //almost certainly a vertex has exceeded range + } - //reset xcurr & ycurr and find 'eHighest' (given the Y axis coordinates - //increase downward so the 'highest' edge will have the smallest ytop) ... - TEdge *e = &edges[0]; - TEdge *eHighest = e; + TEdge *eStart = &edges[0]; + if (!Closed) eStart->Prev->OutIdx = Skip; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge *E = eStart, *eLoopStop = eStart; + for (;;) + { + if ((E->Curr == E->Next->Curr)) + { + if (E == E->Next) break; + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E->Prev == E->Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + E = E->Prev; + eLoopStop = E; + continue; + } + E = E->Next; + if (E == eLoopStop) break; + } + + if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) + { + delete [] edges; + return false; + } + + if (!Closed) m_HasOpenPaths = true; + + //3. Do second stage of edge initialization ... + E = eStart; do { - e->xcurr = e->xbot; - e->ycurr = e->ybot; - if (e->ytop < eHighest->ytop) eHighest = e; - e = e->next; + InitEdge2(*E, PolyTyp); + E = E->Next; + if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; } - while ( e != &edges[0]); + while (E != eStart); - //make sure eHighest is positioned so the following loop works safely ... - if (eHighest->windDelta > 0) eHighest = eHighest->next; - if (NEAR_EQUAL(eHighest->dx, HORIZONTAL)) eHighest = eHighest->next; + //4. Finally, add edge bounds to LocalMinima list ... - //finally insert each local minima ... - e = eHighest; - do { - e = AddBoundsToLML(e); + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + { + delete [] edges; + return false; + } + E->Prev->OutIdx = Skip; + if (E->Prev->Bot.X < E->Prev->Top.X) ReverseHorizontal(*E->Prev); + LocalMinima* locMin = new LocalMinima(); + locMin->Next = 0; + locMin->Y = E->Bot.Y; + locMin->LeftBound = 0; + locMin->RightBound = E; + locMin->RightBound->Side = esRight; + locMin->RightBound->WindDelta = 0; + while (E->Next->OutIdx != Skip) + { + E->NextInLML = E->Next; + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + InsertLocalMinima(locMin); + m_edges.push_back(edges); + return true; + } + + m_edges.push_back(edges); + bool clockwise; + TEdge* EMin = 0; + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (!EMin) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + LocalMinima* locMin = new LocalMinima; + locMin->Next = 0; + locMin->Y = E->Bot.Y; + if (E->Dx < E->Prev->Dx) + { + locMin->LeftBound = E->Prev; + locMin->RightBound = E; + clockwise = false; //Q.nextInLML = Q.prev + } else + { + locMin->LeftBound = E; + locMin->RightBound = E->Prev; + clockwise = true; //Q.nextInLML = Q.next + } + locMin->LeftBound->Side = esLeft; + locMin->RightBound->Side = esRight; + + if (!Closed) locMin->LeftBound->WindDelta = 0; + else if (locMin->LeftBound->Next == locMin->RightBound) + locMin->LeftBound->WindDelta = -1; + else locMin->LeftBound->WindDelta = 1; + locMin->RightBound->WindDelta = -locMin->LeftBound->WindDelta; + + E = ProcessBound(locMin->LeftBound, clockwise); + TEdge* E2 = ProcessBound(locMin->RightBound, !clockwise); + + if (locMin->LeftBound->OutIdx == Skip) + locMin->LeftBound = 0; + else if (locMin->RightBound->OutIdx == Skip) + locMin->RightBound = 0; + InsertLocalMinima(locMin); + if (!clockwise) E = E2; } - while( e != eHighest ); return true; } //------------------------------------------------------------------------------ +bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) +{ + bool result = false; + for (Paths::size_type i = 0; i < ppg.size(); ++i) + if (AddPath(ppg[i], PolyTyp, Closed)) result = true; + return result; +} +//------------------------------------------------------------------------------ + void ClipperBase::InsertLocalMinima(LocalMinima *newLm) { if( ! m_MinimaList ) @@ -923,91 +1279,32 @@ void ClipperBase::InsertLocalMinima(LocalMinima *newLm) } else if( newLm->Y >= m_MinimaList->Y ) { - newLm->next = m_MinimaList; + newLm->Next = m_MinimaList; m_MinimaList = newLm; } else { LocalMinima* tmpLm = m_MinimaList; - while( tmpLm->next && ( newLm->Y < tmpLm->next->Y ) ) - tmpLm = tmpLm->next; - newLm->next = tmpLm->next; - tmpLm->next = newLm; + while( tmpLm->Next && ( newLm->Y < tmpLm->Next->Y ) ) + tmpLm = tmpLm->Next; + newLm->Next = tmpLm->Next; + tmpLm->Next = newLm; } } //------------------------------------------------------------------------------ -TEdge* ClipperBase::AddBoundsToLML(TEdge *e) -{ - //Starting at the top of one bound we progress to the bottom where there's - //a local minima. We then go to the top of the next bound. These two bounds - //form the left and right (or right and left) bounds of the local minima. - e->nextInLML = 0; - e = e->next; - for (;;) - { - if (NEAR_EQUAL(e->dx, HORIZONTAL)) - { - //nb: proceed through horizontals when approaching from their right, - // but break on horizontal minima if approaching from their left. - // This ensures 'local minima' are always on the left of horizontals. - if (e->next->ytop < e->ytop && e->next->xbot > e->prev->xbot) break; - if (e->xtop != e->prev->xbot) SwapX(*e); - e->nextInLML = e->prev; - } - else if (e->ycurr == e->prev->ycurr) break; - else e->nextInLML = e->prev; - e = e->next; - } - - //e and e.prev are now at a local minima ... - LocalMinima* newLm = new LocalMinima; - newLm->next = 0; - newLm->Y = e->prev->ybot; - - if ( NEAR_EQUAL(e->dx, HORIZONTAL) ) //horizontal edges never start a left bound - { - if (e->xbot != e->prev->xbot) SwapX(*e); - newLm->leftBound = e->prev; - newLm->rightBound = e; - } else if (e->dx < e->prev->dx) - { - newLm->leftBound = e->prev; - newLm->rightBound = e; - } else - { - newLm->leftBound = e; - newLm->rightBound = e->prev; - } - newLm->leftBound->side = esLeft; - newLm->rightBound->side = esRight; - InsertLocalMinima( newLm ); - - for (;;) - { - if ( e->next->ytop == e->ytop && !NEAR_EQUAL(e->next->dx, HORIZONTAL) ) break; - e->nextInLML = e->next; - e = e->next; - if ( NEAR_EQUAL(e->dx, HORIZONTAL) && e->xbot != e->prev->xtop) SwapX(*e); - } - return e->next; -} -//------------------------------------------------------------------------------ - -bool ClipperBase::AddPolygons(const Polygons &ppg, PolyType polyType) -{ - bool result = false; - for (Polygons::size_type i = 0; i < ppg.size(); ++i) - if (AddPolygon(ppg[i], polyType)) result = true; - return result; -} -//------------------------------------------------------------------------------ - void ClipperBase::Clear() { DisposeLocalMinimaList(); - for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) delete [] m_edges[i]; + for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) + { + //for each edge array in turn, find the first used edge and + //check for and remove any hiddenPts in each edge in the array. + TEdge* edges = m_edges[i]; + delete [] edges; + } m_edges.clear(); m_UseFullRange = false; + m_HasOpenPaths = false; } //------------------------------------------------------------------------------ @@ -1020,25 +1317,22 @@ void ClipperBase::Reset() LocalMinima* lm = m_MinimaList; while( lm ) { - TEdge* e = lm->leftBound; - while( e ) + TEdge* e = lm->LeftBound; + if (e) { - e->xcurr = e->xbot; - e->ycurr = e->ybot; - e->side = esLeft; - e->outIdx = -1; - e = e->nextInLML; + e->Curr = e->Bot; + e->Side = esLeft; + e->OutIdx = Unassigned; } - e = lm->rightBound; - while( e ) + + e = lm->RightBound; + if (e) { - e->xcurr = e->xbot; - e->ycurr = e->ybot; - e->side = esRight; - e->outIdx = -1; - e = e->nextInLML; + e->Curr = e->Bot; + e->Side = esRight; + e->OutIdx = Unassigned; } - lm = lm->next; + lm = lm->Next; } } //------------------------------------------------------------------------------ @@ -1047,7 +1341,7 @@ void ClipperBase::DisposeLocalMinimaList() { while( m_MinimaList ) { - LocalMinima* tmpLm = m_MinimaList->next; + LocalMinima* tmpLm = m_MinimaList->Next; delete m_MinimaList; m_MinimaList = tmpLm; } @@ -1058,7 +1352,7 @@ void ClipperBase::DisposeLocalMinimaList() void ClipperBase::PopLocalMinima() { if( ! m_CurrentLM ) return; - m_CurrentLM = m_CurrentLM->next; + m_CurrentLM = m_CurrentLM->Next; } //------------------------------------------------------------------------------ @@ -1071,100 +1365,102 @@ IntRect ClipperBase::GetBounds() result.left = result.top = result.right = result.bottom = 0; return result; } - result.left = lm->leftBound->xbot; - result.top = lm->leftBound->ybot; - result.right = lm->leftBound->xbot; - result.bottom = lm->leftBound->ybot; + result.left = lm->LeftBound->Bot.X; + result.top = lm->LeftBound->Bot.Y; + result.right = lm->LeftBound->Bot.X; + result.bottom = lm->LeftBound->Bot.Y; while (lm) { - if (lm->leftBound->ybot > result.bottom) - result.bottom = lm->leftBound->ybot; - TEdge* e = lm->leftBound; + if (lm->LeftBound->Bot.Y > result.bottom) + result.bottom = lm->LeftBound->Bot.Y; + TEdge* e = lm->LeftBound; for (;;) { TEdge* bottomE = e; - while (e->nextInLML) + while (e->NextInLML) { - if (e->xbot < result.left) result.left = e->xbot; - if (e->xbot > result.right) result.right = e->xbot; - e = e->nextInLML; + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + e = e->NextInLML; } - if (e->xbot < result.left) result.left = e->xbot; - if (e->xbot > result.right) result.right = e->xbot; - if (e->xtop < result.left) result.left = e->xtop; - if (e->xtop > result.right) result.right = e->xtop; - if (e->ytop < result.top) result.top = e->ytop; + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + if (e->Top.X < result.left) result.left = e->Top.X; + if (e->Top.X > result.right) result.right = e->Top.X; + if (e->Top.Y < result.top) result.top = e->Top.Y; - if (bottomE == lm->leftBound) e = lm->rightBound; + if (bottomE == lm->LeftBound) e = lm->RightBound; else break; } - lm = lm->next; + lm = lm->Next; } return result; } - //------------------------------------------------------------------------------ // TClipper methods ... //------------------------------------------------------------------------------ -Clipper::Clipper() : ClipperBase() //constructor +Clipper::Clipper(int initOptions) : ClipperBase() //constructor { - m_Scanbeam = 0; m_ActiveEdges = 0; m_SortedEdges = 0; - m_IntersectNodes = 0; m_ExecuteLocked = false; m_UseFullRange = false; - m_ReverseOutput = false; + m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); + m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); + m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); + m_HasOpenPaths = false; +#ifdef use_xyz + m_ZFill = 0; +#endif } //------------------------------------------------------------------------------ Clipper::~Clipper() //destructor { Clear(); - DisposeScanbeamList(); + m_Scanbeam.clear(); } //------------------------------------------------------------------------------ +#ifdef use_xyz +void Clipper::ZFillFunction(TZFillCallback zFillFunc) +{ + m_ZFill = zFillFunc; +} +//------------------------------------------------------------------------------ +#endif + void Clipper::Clear() { - if (m_edges.size() == 0) return; //avoids problems with ClipperBase destructor - DisposeAllPolyPts(); + if (m_edges.empty()) return; //avoids problems with ClipperBase destructor + DisposeAllOutRecs(); ClipperBase::Clear(); } //------------------------------------------------------------------------------ -void Clipper::DisposeScanbeamList() -{ - while ( m_Scanbeam ) { - Scanbeam* sb2 = m_Scanbeam->next; - delete m_Scanbeam; - m_Scanbeam = sb2; - } -} -//------------------------------------------------------------------------------ - void Clipper::Reset() { ClipperBase::Reset(); - m_Scanbeam = 0; + m_Scanbeam.clear(); m_ActiveEdges = 0; m_SortedEdges = 0; - DisposeAllPolyPts(); + DisposeAllOutRecs(); LocalMinima* lm = m_MinimaList; while (lm) { InsertScanbeam(lm->Y); - InsertScanbeam(lm->leftBound->ytop); - lm = lm->next; + lm = lm->Next; } } //------------------------------------------------------------------------------ -bool Clipper::Execute(ClipType clipType, Polygons &solution, +bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, PolyFillType clipFillType) { if( m_ExecuteLocked ) return false; + if (m_HasOpenPaths) + throw clipperException("Error: PolyTree struct is need for open path clipping."); m_ExecuteLocked = true; solution.resize(0); m_SubjFillType = subjFillType; @@ -1194,104 +1490,90 @@ bool Clipper::Execute(ClipType clipType, PolyTree& polytree, } //------------------------------------------------------------------------------ -void Clipper::FixHoleLinkage(OutRec &outRec) +void Clipper::FixHoleLinkage(OutRec &outrec) { //skip OutRecs that (a) contain outermost polygons or //(b) already have the correct owner/child linkage ... - if (!outRec.FirstLeft || - (outRec.isHole != outRec.FirstLeft->isHole && - outRec.FirstLeft->pts)) return; + if (!outrec.FirstLeft || + (outrec.IsHole != outrec.FirstLeft->IsHole && + outrec.FirstLeft->Pts)) return; - OutRec* orfl = outRec.FirstLeft; - while (orfl && ((orfl->isHole == outRec.isHole) || !orfl->pts)) + OutRec* orfl = outrec.FirstLeft; + while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) orfl = orfl->FirstLeft; - outRec.FirstLeft = orfl; + outrec.FirstLeft = orfl; } //------------------------------------------------------------------------------ bool Clipper::ExecuteInternal() { - bool succeeded; + bool succeeded = true; try { Reset(); - if (!m_CurrentLM ) return true; - long64 botY = PopScanbeam(); + if (!m_CurrentLM) return false; + cInt botY = PopScanbeam(); do { InsertLocalMinimaIntoAEL(botY); - ClearHorzJoins(); - ProcessHorizontals(); - long64 topY = PopScanbeam(); + ClearGhostJoins(); + ProcessHorizontals(false); + if (m_Scanbeam.empty()) break; + cInt topY = PopScanbeam(); succeeded = ProcessIntersections(botY, topY); if (!succeeded) break; ProcessEdgesAtTopOfScanbeam(topY); botY = topY; - } while( m_Scanbeam ); + } while (!m_Scanbeam.empty() || m_CurrentLM); } - catch(...) { + catch(...) + { succeeded = false; } if (succeeded) { - //tidy up output polygons and fix orientations where necessary ... + //fix orientations ... for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec *outRec = m_PolyOuts[i]; - if (!outRec->pts) continue; - FixupOutPolygon(*outRec); - if (!outRec->pts) continue; - - if ((outRec->isHole ^ m_ReverseOutput) == (Area(*outRec, m_UseFullRange) > 0)) - ReversePolyPtLinks(outRec->pts); + if (!outRec->Pts || outRec->IsOpen) continue; + if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); } - if (m_Joins.size() > 0) JoinCommonEdges(); + if (!m_Joins.empty()) JoinCommonEdges(); + + //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (outRec->Pts && !outRec->IsOpen) + FixupOutPolygon(*outRec); + } + + if (m_StrictSimple) DoSimplePolygons(); } ClearJoins(); - ClearHorzJoins(); + ClearGhostJoins(); return succeeded; } //------------------------------------------------------------------------------ -void Clipper::InsertScanbeam(const long64 Y) +void Clipper::InsertScanbeam(const cInt Y) { - if( !m_Scanbeam ) - { - m_Scanbeam = new Scanbeam; - m_Scanbeam->next = 0; - m_Scanbeam->Y = Y; - } - else if( Y > m_Scanbeam->Y ) - { - Scanbeam* newSb = new Scanbeam; - newSb->Y = Y; - newSb->next = m_Scanbeam; - m_Scanbeam = newSb; - } else - { - Scanbeam* sb2 = m_Scanbeam; - while( sb2->next && ( Y <= sb2->next->Y ) ) sb2 = sb2->next; - if( Y == sb2->Y ) return; //ie ignores duplicates - Scanbeam* newSb = new Scanbeam; - newSb->Y = Y; - newSb->next = sb2->next; - sb2->next = newSb; - } + m_Scanbeam.insert(Y); } //------------------------------------------------------------------------------ -long64 Clipper::PopScanbeam() +cInt Clipper::PopScanbeam() { - long64 Y = m_Scanbeam->Y; - Scanbeam* sb2 = m_Scanbeam; - m_Scanbeam = m_Scanbeam->next; - delete sb2; + cInt Y = *m_Scanbeam.begin(); + m_Scanbeam.erase(m_Scanbeam.begin()); return Y; } //------------------------------------------------------------------------------ -void Clipper::DisposeAllPolyPts(){ +void Clipper::DisposeAllOutRecs(){ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) DisposeOutRec(i); m_PolyOuts.clear(); @@ -1301,7 +1583,7 @@ void Clipper::DisposeAllPolyPts(){ void Clipper::DisposeOutRec(PolyOutList::size_type index) { OutRec *outRec = m_PolyOuts[index]; - if (outRec->pts) DisposeOutPts(outRec->pts); + if (outRec->Pts) DisposeOutPts(outRec->Pts); delete outRec; m_PolyOuts[index] = 0; } @@ -1309,59 +1591,94 @@ void Clipper::DisposeOutRec(PolyOutList::size_type index) void Clipper::SetWindingCount(TEdge &edge) { - TEdge *e = edge.prevInAEL; + TEdge *e = edge.PrevInAEL; //find the edge of the same polytype that immediately preceeds 'edge' in AEL - while ( e && e->polyType != edge.polyType ) e = e->prevInAEL; - if ( !e ) + while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; + if (!e) { - edge.windCnt = edge.windDelta; - edge.windCnt2 = 0; - e = m_ActiveEdges; //ie get ready to calc windCnt2 - } else if ( IsEvenOddFillType(edge) ) + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) { //EvenOdd filling ... - edge.windCnt = 1; - edge.windCnt2 = e->windCnt2; - e = e->nextInAEL; //ie get ready to calc windCnt2 - } else + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + Inside = !Inside; + e2 = e2->PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else { //nonZero, Positive or Negative filling ... - if ( e->windCnt * e->windDelta < 0 ) + if (e->WindCnt * e->WindDelta < 0) { - if (Abs(e->windCnt) > 1) + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Abs(e->WindCnt) > 1) { - if (e->windDelta * edge.windDelta < 0) edge.windCnt = e->windCnt; - else edge.windCnt = e->windCnt + edge.windDelta; - } else - edge.windCnt = e->windCnt + e->windDelta + edge.windDelta; + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); } else { - if ( Abs(e->windCnt) > 1 && e->windDelta * edge.windDelta < 0) - edge.windCnt = e->windCnt; - else if ( e->windCnt + edge.windDelta == 0 ) - edge.windCnt = e->windCnt; - else edge.windCnt = e->windCnt + edge.windDelta; + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; } - edge.windCnt2 = e->windCnt2; - e = e->nextInAEL; //ie get ready to calc windCnt2 + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 } - //update windCnt2 ... - if ( IsEvenOddAltFillType(edge) ) + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) { //EvenOdd filling ... - while ( e != &edge ) + while (e != &edge) { - edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0; - e = e->nextInAEL; + if (e->WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e->NextInAEL; } } else { //nonZero, Positive or Negative filling ... while ( e != &edge ) { - edge.windCnt2 += e->windDelta; - e = e->nextInAEL; + edge.WindCnt2 += e->WindDelta; + e = e->NextInAEL; } } } @@ -1369,7 +1686,7 @@ void Clipper::SetWindingCount(TEdge &edge) bool Clipper::IsEvenOddFillType(const TEdge& edge) const { - if (edge.polyType == ptSubject) + if (edge.PolyTyp == ptSubject) return m_SubjFillType == pftEvenOdd; else return m_ClipFillType == pftEvenOdd; } @@ -1377,7 +1694,7 @@ bool Clipper::IsEvenOddFillType(const TEdge& edge) const bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const { - if (edge.polyType == ptSubject) + if (edge.PolyTyp == ptSubject) return m_ClipFillType == pftEvenOdd; else return m_SubjFillType == pftEvenOdd; } @@ -1386,7 +1703,7 @@ bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const bool Clipper::IsContributing(const TEdge& edge) const { PolyFillType pft, pft2; - if (edge.polyType == ptSubject) + if (edge.PolyTyp == ptSubject) { pft = m_SubjFillType; pft2 = m_ClipFillType; @@ -1399,14 +1716,17 @@ bool Clipper::IsContributing(const TEdge& edge) const switch(pft) { case pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; case pftNonZero: - if (Abs(edge.windCnt) != 1) return false; + if (Abs(edge.WindCnt) != 1) return false; break; case pftPositive: - if (edge.windCnt != 1) return false; + if (edge.WindCnt != 1) return false; break; default: //pftNegative - if (edge.windCnt != -1) return false; + if (edge.WindCnt != -1) return false; } switch(m_ClipType) @@ -1416,94 +1736,120 @@ bool Clipper::IsContributing(const TEdge& edge) const { case pftEvenOdd: case pftNonZero: - return (edge.windCnt2 != 0); + return (edge.WindCnt2 != 0); case pftPositive: - return (edge.windCnt2 > 0); + return (edge.WindCnt2 > 0); default: - return (edge.windCnt2 < 0); + return (edge.WindCnt2 < 0); } + break; case ctUnion: switch(pft2) { case pftEvenOdd: case pftNonZero: - return (edge.windCnt2 == 0); + return (edge.WindCnt2 == 0); case pftPositive: - return (edge.windCnt2 <= 0); + return (edge.WindCnt2 <= 0); default: - return (edge.windCnt2 >= 0); + return (edge.WindCnt2 >= 0); } + break; case ctDifference: - if (edge.polyType == ptSubject) + if (edge.PolyTyp == ptSubject) switch(pft2) { case pftEvenOdd: case pftNonZero: - return (edge.windCnt2 == 0); + return (edge.WindCnt2 == 0); case pftPositive: - return (edge.windCnt2 <= 0); + return (edge.WindCnt2 <= 0); default: - return (edge.windCnt2 >= 0); + return (edge.WindCnt2 >= 0); } else switch(pft2) { case pftEvenOdd: case pftNonZero: - return (edge.windCnt2 != 0); + return (edge.WindCnt2 != 0); case pftPositive: - return (edge.windCnt2 > 0); + return (edge.WindCnt2 > 0); default: - return (edge.windCnt2 < 0); + return (edge.WindCnt2 < 0); } + break; + case ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + break; default: return true; } } //------------------------------------------------------------------------------ -void Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) +OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) { + OutPt* result; TEdge *e, *prevE; - if( NEAR_EQUAL(e2->dx, HORIZONTAL) || ( e1->dx > e2->dx ) ) + if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) { - AddOutPt( e1, pt ); - e2->outIdx = e1->outIdx; - e1->side = esLeft; - e2->side = esRight; + result = AddOutPt(e1, Pt); + e2->OutIdx = e1->OutIdx; + e1->Side = esLeft; + e2->Side = esRight; e = e1; - if (e->prevInAEL == e2) - prevE = e2->prevInAEL; + if (e->PrevInAEL == e2) + prevE = e2->PrevInAEL; else - prevE = e->prevInAEL; + prevE = e->PrevInAEL; } else { - AddOutPt( e2, pt ); - e1->outIdx = e2->outIdx; - e1->side = esRight; - e2->side = esLeft; + result = AddOutPt(e2, Pt); + e1->OutIdx = e2->OutIdx; + e1->Side = esRight; + e2->Side = esLeft; e = e2; - if (e->prevInAEL == e1) - prevE = e1->prevInAEL; + if (e->PrevInAEL == e1) + prevE = e1->PrevInAEL; else - prevE = e->prevInAEL; + prevE = e->PrevInAEL; } - if (prevE && prevE->outIdx >= 0 && - (TopX(*prevE, pt.Y) == TopX(*e, pt.Y)) && - SlopesEqual(*e, *prevE, m_UseFullRange)) - AddJoin(e, prevE, -1, -1); + + if (prevE && prevE->OutIdx >= 0 && + (TopX(*prevE, Pt.Y) == TopX(*e, Pt.Y)) && + SlopesEqual(*e, *prevE, m_UseFullRange) && + (e->WindDelta != 0) && (prevE->WindDelta != 0)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } + return result; } //------------------------------------------------------------------------------ -void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) { - AddOutPt( e1, pt ); - if( e1->outIdx == e2->outIdx ) + AddOutPt( e1, Pt ); + if (e2->WindDelta == 0) AddOutPt(e2, Pt); + if( e1->OutIdx == e2->OutIdx ) { - e1->outIdx = -1; - e2->outIdx = -1; + e1->OutIdx = Unassigned; + e2->OutIdx = Unassigned; } - else if (e1->outIdx < e2->outIdx) + else if (e1->OutIdx < e2->OutIdx) AppendPolygon(e1, e2); else AppendPolygon(e2, e1); @@ -1517,14 +1863,14 @@ void Clipper::AddEdgeToSEL(TEdge *edge) if( !m_SortedEdges ) { m_SortedEdges = edge; - edge->prevInSEL = 0; - edge->nextInSEL = 0; + edge->PrevInSEL = 0; + edge->NextInSEL = 0; } else { - edge->nextInSEL = m_SortedEdges; - edge->prevInSEL = 0; - m_SortedEdges->prevInSEL = edge; + edge->NextInSEL = m_SortedEdges; + edge->PrevInSEL = 0; + m_SortedEdges->PrevInSEL = edge; m_SortedEdges = edge; } } @@ -1536,27 +1882,20 @@ void Clipper::CopyAELToSEL() m_SortedEdges = e; while ( e ) { - e->prevInSEL = e->prevInAEL; - e->nextInSEL = e->nextInAEL; - e = e->nextInAEL; + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e = e->NextInAEL; } } //------------------------------------------------------------------------------ -void Clipper::AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx, int e2OutIdx) +void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) { - JoinRec* jr = new JoinRec; - if (e1OutIdx >= 0) - jr->poly1Idx = e1OutIdx; else - jr->poly1Idx = e1->outIdx; - jr->pt1a = IntPoint(e1->xcurr, e1->ycurr); - jr->pt1b = IntPoint(e1->xtop, e1->ytop); - if (e2OutIdx >= 0) - jr->poly2Idx = e2OutIdx; else - jr->poly2Idx = e2->outIdx; - jr->pt2a = IntPoint(e2->xcurr, e2->ycurr); - jr->pt2b = IntPoint(e2->xtop, e2->ytop); - m_Joins.push_back(jr); + Join* j = new Join; + j->OutPt1 = op1; + j->OutPt2 = op2; + j->OffPt = OffPt; + m_Joins.push_back(j); } //------------------------------------------------------------------------------ @@ -1568,161 +1907,261 @@ void Clipper::ClearJoins() } //------------------------------------------------------------------------------ -void Clipper::AddHorzJoin(TEdge *e, int idx) +void Clipper::ClearGhostJoins() { - HorzJoinRec* hj = new HorzJoinRec; - hj->edge = e; - hj->savedIdx = idx; - m_HorizJoins.push_back(hj); + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) + delete m_GhostJoins[i]; + m_GhostJoins.resize(0); } //------------------------------------------------------------------------------ -void Clipper::ClearHorzJoins() +void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) { - for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); i++) - delete m_HorizJoins[i]; - m_HorizJoins.resize(0); + Join* j = new Join; + j->OutPt1 = op; + j->OutPt2 = 0; + j->OffPt = OffPt; + m_GhostJoins.push_back(j); } //------------------------------------------------------------------------------ -void Clipper::InsertLocalMinimaIntoAEL(const long64 botY) +void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { while( m_CurrentLM && ( m_CurrentLM->Y == botY ) ) { - TEdge* lb = m_CurrentLM->leftBound; - TEdge* rb = m_CurrentLM->rightBound; - - InsertEdgeIntoAEL( lb ); - InsertScanbeam( lb->ytop ); - InsertEdgeIntoAEL( rb ); - - if (IsEvenOddFillType(*lb)) + TEdge* lb = m_CurrentLM->LeftBound; + TEdge* rb = m_CurrentLM->RightBound; + PopLocalMinima(); + OutPt *Op1 = 0; + if (!lb) { - lb->windDelta = 1; - rb->windDelta = 1; + //nb: don't insert LB into either AEL or SEL + InsertEdgeIntoAEL(rb, 0); + SetWindingCount(*rb); + if (IsContributing(*rb)) + Op1 = AddOutPt(rb, rb->Bot); + } + else if (!rb) + { + InsertEdgeIntoAEL(lb, 0); + SetWindingCount(*lb); + if (IsContributing(*lb)) + Op1 = AddOutPt(lb, lb->Bot); + InsertScanbeam(lb->Top.Y); } else { - rb->windDelta = -lb->windDelta; + InsertEdgeIntoAEL(lb, 0); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount( *lb ); + rb->WindCnt = lb->WindCnt; + rb->WindCnt2 = lb->WindCnt2; + if (IsContributing(*lb)) + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + InsertScanbeam(lb->Top.Y); } - SetWindingCount( *lb ); - rb->windCnt = lb->windCnt; - rb->windCnt2 = lb->windCnt2; - if( NEAR_EQUAL(rb->dx, HORIZONTAL) ) - { - //nb: only rightbounds can have a horizontal bottom edge - AddEdgeToSEL( rb ); - InsertScanbeam( rb->nextInLML->ytop ); - } - else - InsertScanbeam( rb->ytop ); + if (rb) + { + if(IsHorizontal(*rb)) AddEdgeToSEL(rb); + else InsertScanbeam( rb->Top.Y ); + } - if( IsContributing(*lb) ) - AddLocalMinPoly( lb, rb, IntPoint(lb->xcurr, m_CurrentLM->Y) ); + if (!lb || !rb) continue; //if any output polygons share an edge, they'll need joining later ... - if (rb->outIdx >= 0 && NEAR_EQUAL(rb->dx, HORIZONTAL)) + if (Op1 && IsHorizontal(*rb) && + m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) { - for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) { - IntPoint pt, pt2; //returned by GetOverlapSegment() but unused here. - HorzJoinRec* hj = m_HorizJoins[i]; - //if horizontals rb and hj.edge overlap, flag for joining later ... - if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), - IntPoint(hj->edge->xtop, hj->edge->ytop), - IntPoint(rb->xbot, rb->ybot), - IntPoint(rb->xtop, rb->ytop), pt, pt2)) - AddJoin(hj->edge, rb, hj->savedIdx); + Join* jr = m_GhostJoins[i]; + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + if (HorzSegmentsOverlap(jr->OutPt1->Pt, jr->OffPt, rb->Bot, rb->Top)) + AddJoin(jr->OutPt1, Op1, jr->OffPt); } } - if( lb->nextInAEL != rb ) + if (lb->OutIdx >= 0 && lb->PrevInAEL && + lb->PrevInAEL->Curr.X == lb->Bot.X && + lb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) && + (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) { - if (rb->outIdx >= 0 && rb->prevInAEL->outIdx >= 0 && - SlopesEqual(*rb->prevInAEL, *rb, m_UseFullRange)) - AddJoin(rb, rb->prevInAEL); + OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); + AddJoin(Op1, Op2, lb->Top); + } - TEdge* e = lb->nextInAEL; - IntPoint pt = IntPoint(lb->xcurr, lb->ycurr); - while( e != rb ) + if(lb->NextInAEL != rb) + { + + if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(*rb->PrevInAEL, *rb, m_UseFullRange) && + (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) { - if(!e) throw clipperException("InsertLocalMinimaIntoAEL: missing rightbound!"); - //nb: For calculating winding counts etc, IntersectEdges() assumes - //that param1 will be to the right of param2 ABOVE the intersection ... - IntersectEdges( rb , e , pt , ipNone); //order important here - e = e->nextInAEL; + OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); + AddJoin(Op1, Op2, rb->Top); + } + + TEdge* e = lb->NextInAEL; + if (e) + { + while( e != rb ) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the Right of param2 ABOVE the intersection ... + IntersectEdges(rb , e , lb->Curr); //order important here + e = e->NextInAEL; + } } } - PopLocalMinima(); + } } //------------------------------------------------------------------------------ void Clipper::DeleteFromAEL(TEdge *e) { - TEdge* AelPrev = e->prevInAEL; - TEdge* AelNext = e->nextInAEL; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted - if( AelPrev ) AelPrev->nextInAEL = AelNext; + if( AelPrev ) AelPrev->NextInAEL = AelNext; else m_ActiveEdges = AelNext; - if( AelNext ) AelNext->prevInAEL = AelPrev; - e->nextInAEL = 0; - e->prevInAEL = 0; + if( AelNext ) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; } //------------------------------------------------------------------------------ void Clipper::DeleteFromSEL(TEdge *e) { - TEdge* SelPrev = e->prevInSEL; - TEdge* SelNext = e->nextInSEL; + TEdge* SelPrev = e->PrevInSEL; + TEdge* SelNext = e->NextInSEL; if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted - if( SelPrev ) SelPrev->nextInSEL = SelNext; + if( SelPrev ) SelPrev->NextInSEL = SelNext; else m_SortedEdges = SelNext; - if( SelNext ) SelNext->prevInSEL = SelPrev; - e->nextInSEL = 0; - e->prevInSEL = 0; + if( SelNext ) SelNext->PrevInSEL = SelPrev; + e->NextInSEL = 0; + e->PrevInSEL = 0; } //------------------------------------------------------------------------------ -void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, - const IntPoint &pt, const IntersectProtects protects) +#ifdef use_xyz + +void Clipper::SetZ(IntPoint& pt, TEdge& e) { - //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before + pt.Z = 0; + if (m_ZFill) + { + //put the 'preferred' point as first parameter ... + if (e.OutIdx < 0) + (*m_ZFill)(e.Bot, e.Top, pt); //outside a path so presume entering + else + (*m_ZFill)(e.Top, e.Bot, pt); //inside a path so presume exiting + } +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, + const IntPoint &Pt, bool protect) +{ + //e1 will be to the Left of e2 BELOW the intersection. Therefore e1 is before //e2 in AEL except when e1 is being inserted at the intersection point ... - bool e1stops = !(ipLeft & protects) && !e1->nextInLML && - e1->xtop == pt.X && e1->ytop == pt.Y; - bool e2stops = !(ipRight & protects) && !e2->nextInLML && - e2->xtop == pt.X && e2->ytop == pt.Y; - bool e1Contributing = ( e1->outIdx >= 0 ); - bool e2contributing = ( e2->outIdx >= 0 ); + bool e1stops = !protect && !e1->NextInLML && + e1->Top.X == Pt.X && e1->Top.Y == Pt.Y; + bool e2stops = !protect && !e2->NextInLML && + e2->Top.X == Pt.X && e2->Top.Y == Pt.Y; + bool e1Contributing = ( e1->OutIdx >= 0 ); + bool e2Contributing = ( e2->OutIdx >= 0 ); + +#ifdef use_lines + //if either edge is on an OPEN path ... + if (e1->WindDelta == 0 || e2->WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1->WindDelta == 0 && e2->WindDelta == 0) + { + if ((e1stops || e2stops) && e1Contributing && e2Contributing) + AddLocalMaxPoly(e1, e2, Pt); + } + + //if intersecting a subj line with a subj poly ... + else if (e1->PolyTyp == e2->PolyTyp && + e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) + { + if (e1->WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + } + else if (e1->PolyTyp != e2->PolyTyp) + { + //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && + (m_ClipType != ctUnion || e2->WindCnt2 == 0)) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + (m_ClipType != ctUnion || e1->WindCnt2 == 0)) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + + if (e1stops) + if (e1->OutIdx < 0) DeleteFromAEL(e1); + else throw clipperException("Error intersecting polylines"); + if (e2stops) + if (e2->OutIdx < 0) DeleteFromAEL(e2); + else throw clipperException("Error intersecting polylines"); + return; + } +#endif //update winding counts... - //assumes that e1 will be to the right of e2 ABOVE the intersection - if ( e1->polyType == e2->polyType ) + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if ( e1->PolyTyp == e2->PolyTyp ) { if ( IsEvenOddFillType( *e1) ) { - int oldE1WindCnt = e1->windCnt; - e1->windCnt = e2->windCnt; - e2->windCnt = oldE1WindCnt; + int oldE1WindCnt = e1->WindCnt; + e1->WindCnt = e2->WindCnt; + e2->WindCnt = oldE1WindCnt; } else { - if (e1->windCnt + e2->windDelta == 0 ) e1->windCnt = -e1->windCnt; - else e1->windCnt += e2->windDelta; - if ( e2->windCnt - e1->windDelta == 0 ) e2->windCnt = -e2->windCnt; - else e2->windCnt -= e1->windDelta; + if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; + else e1->WindCnt += e2->WindDelta; + if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; + else e2->WindCnt -= e1->WindDelta; } } else { - if (!IsEvenOddFillType(*e2)) e1->windCnt2 += e2->windDelta; - else e1->windCnt2 = ( e1->windCnt2 == 0 ) ? 1 : 0; - if (!IsEvenOddFillType(*e1)) e2->windCnt2 -= e1->windDelta; - else e2->windCnt2 = ( e2->windCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; + else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; + else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; } PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; - if (e1->polyType == ptSubject) + if (e1->PolyTyp == ptSubject) { e1FillType = m_SubjFillType; e1FillType2 = m_ClipFillType; @@ -1731,7 +2170,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, e1FillType = m_ClipFillType; e1FillType2 = m_SubjFillType; } - if (e2->polyType == ptSubject) + if (e2->PolyTyp == ptSubject) { e2FillType = m_SubjFillType; e2FillType2 = m_ClipFillType; @@ -1741,82 +2180,97 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, e2FillType2 = m_SubjFillType; } - long64 e1Wc, e2Wc; + cInt e1Wc, e2Wc; switch (e1FillType) { - case pftPositive: e1Wc = e1->windCnt; break; - case pftNegative: e1Wc = -e1->windCnt; break; - default: e1Wc = Abs(e1->windCnt); + case pftPositive: e1Wc = e1->WindCnt; break; + case pftNegative: e1Wc = -e1->WindCnt; break; + default: e1Wc = Abs(e1->WindCnt); } switch(e2FillType) { - case pftPositive: e2Wc = e2->windCnt; break; - case pftNegative: e2Wc = -e2->windCnt; break; - default: e2Wc = Abs(e2->windCnt); + case pftPositive: e2Wc = e2->WindCnt; break; + case pftNegative: e2Wc = -e2->WindCnt; break; + default: e2Wc = Abs(e2->WindCnt); } - if ( e1Contributing && e2contributing ) + if ( e1Contributing && e2Contributing ) { if ( e1stops || e2stops || (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || - (e1->polyType != e2->polyType && m_ClipType != ctXor) ) - AddLocalMaxPoly(e1, e2, pt); + (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) + AddLocalMaxPoly(e1, e2, Pt); else - DoBothEdges( e1, e2, pt ); + { + AddOutPt(e1, Pt); + AddOutPt(e2, Pt); + SwapSides( *e1 , *e2 ); + SwapPolyIndexes( *e1 , *e2 ); + } } else if ( e1Contributing ) { - if (e2Wc == 0 || e2Wc == 1) DoEdge1(e1, e2, pt); + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } } - else if ( e2contributing ) + else if ( e2Contributing ) { - if (e1Wc == 0 || e1Wc == 1) DoEdge2(e1, e2, pt); + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } } else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) { //neither edge is currently contributing ... - long64 e1Wc2, e2Wc2; + cInt e1Wc2, e2Wc2; switch (e1FillType2) { - case pftPositive: e1Wc2 = e1->windCnt2; break; - case pftNegative : e1Wc2 = -e1->windCnt2; break; - default: e1Wc2 = Abs(e1->windCnt2); + case pftPositive: e1Wc2 = e1->WindCnt2; break; + case pftNegative : e1Wc2 = -e1->WindCnt2; break; + default: e1Wc2 = Abs(e1->WindCnt2); } switch (e2FillType2) { - case pftPositive: e2Wc2 = e2->windCnt2; break; - case pftNegative: e2Wc2 = -e2->windCnt2; break; - default: e2Wc2 = Abs(e2->windCnt2); + case pftPositive: e2Wc2 = e2->WindCnt2; break; + case pftNegative: e2Wc2 = -e2->WindCnt2; break; + default: e2Wc2 = Abs(e2->WindCnt2); } - if (e1->polyType != e2->polyType) - AddLocalMinPoly(e1, e2, pt); + if (e1->PolyTyp != e2->PolyTyp) + AddLocalMinPoly(e1, e2, Pt); else if (e1Wc == 1 && e2Wc == 1) switch( m_ClipType ) { case ctIntersection: if (e1Wc2 > 0 && e2Wc2 > 0) - AddLocalMinPoly(e1, e2, pt); + AddLocalMinPoly(e1, e2, Pt); break; case ctUnion: if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) - AddLocalMinPoly(e1, e2, pt); + AddLocalMinPoly(e1, e2, Pt); break; case ctDifference: - if (((e1->polyType == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || - ((e1->polyType == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) - AddLocalMinPoly(e1, e2, pt); + if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, Pt); break; case ctXor: - AddLocalMinPoly(e1, e2, pt); + AddLocalMinPoly(e1, e2, Pt); } else SwapSides( *e1, *e2 ); } if( (e1stops != e2stops) && - ( (e1stops && (e1->outIdx >= 0)) || (e2stops && (e2->outIdx >= 0)) ) ) + ( (e1stops && (e1->OutIdx >= 0)) || (e2stops && (e2->OutIdx >= 0)) ) ) { SwapSides( *e1, *e2 ); SwapPolyIndexes( *e1, *e2 ); @@ -1828,36 +2282,40 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, } //------------------------------------------------------------------------------ -void Clipper::SetHoleState(TEdge *e, OutRec *outRec) +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) { - bool isHole = false; - TEdge *e2 = e->prevInAEL; + bool IsHole = false; + TEdge *e2 = e->PrevInAEL; while (e2) { - if (e2->outIdx >= 0) + if (e2->OutIdx >= 0 && e2->WindDelta != 0) { - isHole = !isHole; - if (! outRec->FirstLeft) - outRec->FirstLeft = m_PolyOuts[e2->outIdx]; + IsHole = !IsHole; + if (! outrec->FirstLeft) + outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; } - e2 = e2->prevInAEL; + e2 = e2->PrevInAEL; } - if (isHole) outRec->isHole = true; + if (IsHole) outrec->IsHole = true; } //------------------------------------------------------------------------------ OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) { //work out which polygon fragment has the correct hole state ... - OutPt *outPt1 = outRec1->bottomPt; - OutPt *outPt2 = outRec2->bottomPt; - if (outPt1->pt.Y > outPt2->pt.Y) return outRec1; - else if (outPt1->pt.Y < outPt2->pt.Y) return outRec2; - else if (outPt1->pt.X < outPt2->pt.X) return outRec1; - else if (outPt1->pt.X > outPt2->pt.X) return outRec2; - else if (outPt1->next == outPt1) return outRec2; - else if (outPt2->next == outPt2) return outRec1; - else if (FirstIsBottomPt(outPt1, outPt2)) return outRec1; + if (!outRec1->BottomPt) + outRec1->BottomPt = GetBottomPt(outRec1->Pts); + if (!outRec2->BottomPt) + outRec2->BottomPt = GetBottomPt(outRec2->Pts); + OutPt *OutPt1 = outRec1->BottomPt; + OutPt *OutPt2 = outRec2->BottomPt; + if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; + else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; + else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; + else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; + else if (OutPt1->Next == OutPt1) return outRec2; + else if (OutPt2->Next == OutPt2) return outRec1; + else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; else return outRec2; } //------------------------------------------------------------------------------ @@ -1873,11 +2331,20 @@ bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) } //------------------------------------------------------------------------------ +OutRec* Clipper::GetOutRec(int Idx) +{ + OutRec* outrec = m_PolyOuts[Idx]; + while (outrec != m_PolyOuts[outrec->Idx]) + outrec = m_PolyOuts[outrec->Idx]; + return outrec; +} +//------------------------------------------------------------------------------ + void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) { //get the start and ends of both output polygons ... - OutRec *outRec1 = m_PolyOuts[e1->outIdx]; - OutRec *outRec2 = m_PolyOuts[e2->outIdx]; + OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; OutRec *holeStateRec; if (Param1RightOfParam2(outRec1, outRec2)) @@ -1887,397 +2354,475 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) else holeStateRec = GetLowermostRec(outRec1, outRec2); - OutPt* p1_lft = outRec1->pts; - OutPt* p1_rt = p1_lft->prev; - OutPt* p2_lft = outRec2->pts; - OutPt* p2_rt = p2_lft->prev; - - EdgeSide side; + //get the start and ends of both output polygons and //join e2 poly onto e1 poly and delete pointers to e2 ... - if( e1->side == esLeft ) + + OutPt* p1_lft = outRec1->Pts; + OutPt* p1_rt = p1_lft->Prev; + OutPt* p2_lft = outRec2->Pts; + OutPt* p2_rt = p2_lft->Prev; + + EdgeSide Side; + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->Side == esLeft ) { - if( e2->side == esLeft ) + if( e2->Side == esLeft ) { //z y x a b c ReversePolyPtLinks(p2_lft); - p2_lft->next = p1_lft; - p1_lft->prev = p2_lft; - p1_rt->next = p2_rt; - p2_rt->prev = p1_rt; - outRec1->pts = p2_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + outRec1->Pts = p2_rt; } else { //x y z a b c - p2_rt->next = p1_lft; - p1_lft->prev = p2_rt; - p2_lft->prev = p1_rt; - p1_rt->next = p2_lft; - outRec1->pts = p2_lft; + p2_rt->Next = p1_lft; + p1_lft->Prev = p2_rt; + p2_lft->Prev = p1_rt; + p1_rt->Next = p2_lft; + outRec1->Pts = p2_lft; } - side = esLeft; + Side = esLeft; } else { - if( e2->side == esRight ) + if( e2->Side == esRight ) { //a b c z y x ReversePolyPtLinks(p2_lft); - p1_rt->next = p2_rt; - p2_rt->prev = p1_rt; - p2_lft->next = p1_lft; - p1_lft->prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; } else { //a b c x y z - p1_rt->next = p2_lft; - p2_lft->prev = p1_rt; - p1_lft->prev = p2_rt; - p2_rt->next = p1_lft; + p1_rt->Next = p2_lft; + p2_lft->Prev = p1_rt; + p1_lft->Prev = p2_rt; + p2_rt->Next = p1_lft; } - side = esRight; + Side = esRight; } + outRec1->BottomPt = 0; if (holeStateRec == outRec2) { - outRec1->bottomPt = outRec2->bottomPt; - outRec1->bottomPt->idx = outRec1->idx; if (outRec2->FirstLeft != outRec1) outRec1->FirstLeft = outRec2->FirstLeft; - outRec1->isHole = outRec2->isHole; + outRec1->IsHole = outRec2->IsHole; } - outRec2->pts = 0; - outRec2->bottomPt = 0; - + outRec2->Pts = 0; + outRec2->BottomPt = 0; outRec2->FirstLeft = outRec1; - int OKIdx = e1->outIdx; - int ObsoleteIdx = e2->outIdx; + int OKIdx = e1->OutIdx; + int ObsoleteIdx = e2->OutIdx; - e1->outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly - e2->outIdx = -1; + e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2->OutIdx = Unassigned; TEdge* e = m_ActiveEdges; while( e ) { - if( e->outIdx == ObsoleteIdx ) + if( e->OutIdx == ObsoleteIdx ) { - e->outIdx = OKIdx; - e->side = side; + e->OutIdx = OKIdx; + e->Side = Side; break; } - e = e->nextInAEL; - } - - for (JoinList::size_type i = 0; i < m_Joins.size(); ++i) - { - if (m_Joins[i]->poly1Idx == ObsoleteIdx) m_Joins[i]->poly1Idx = OKIdx; - if (m_Joins[i]->poly2Idx == ObsoleteIdx) m_Joins[i]->poly2Idx = OKIdx; - } - - for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) - { - if (m_HorizJoins[i]->savedIdx == ObsoleteIdx) - m_HorizJoins[i]->savedIdx = OKIdx; + e = e->NextInAEL; } + outRec2->Idx = outRec1->Idx; } //------------------------------------------------------------------------------ OutRec* Clipper::CreateOutRec() { OutRec* result = new OutRec; - result->isHole = false; + result->IsHole = false; + result->IsOpen = false; result->FirstLeft = 0; - result->pts = 0; - result->bottomPt = 0; - result->polyNode = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size()-1; return result; } //------------------------------------------------------------------------------ -void Clipper::AddOutPt(TEdge *e, const IntPoint &pt) +OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) { - bool ToFront = (e->side == esLeft); - if( e->outIdx < 0 ) + bool ToFront = (e->Side == esLeft); + if( e->OutIdx < 0 ) { OutRec *outRec = CreateOutRec(); - m_PolyOuts.push_back(outRec); - outRec->idx = (int)m_PolyOuts.size()-1; - e->outIdx = outRec->idx; - OutPt* op = new OutPt; - outRec->pts = op; - outRec->bottomPt = op; - op->pt = pt; - op->idx = outRec->idx; - op->next = op; - op->prev = op; - SetHoleState(e, outRec); + outRec->IsOpen = (e->WindDelta == 0); + OutPt* newOp = new OutPt; + outRec->Pts = newOp; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = newOp; + newOp->Prev = newOp; + if (!outRec->IsOpen) + SetHoleState(e, outRec); +#ifdef use_xyz + if (pt == e->Bot) newOp->Pt = e->Bot; + else if (pt == e->Top) newOp->Pt = e->Top; + else SetZ(newOp->Pt, *e); +#endif + e->OutIdx = outRec->Idx; //nb: do this after SetZ ! + return newOp; } else { - OutRec *outRec = m_PolyOuts[e->outIdx]; - OutPt* op = outRec->pts; - if ((ToFront && PointsEqual(pt, op->pt)) || - (!ToFront && PointsEqual(pt, op->prev->pt))) return; + OutRec *outRec = m_PolyOuts[e->OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt* op = outRec->Pts; - OutPt* op2 = new OutPt; - op2->pt = pt; - op2->idx = outRec->idx; - if (op2->pt.Y == outRec->bottomPt->pt.Y && - op2->pt.X < outRec->bottomPt->pt.X) - outRec->bottomPt = op2; - op2->next = op; - op2->prev = op->prev; - op2->prev->next = op2; - op->prev = op2; - if (ToFront) outRec->pts = op2; + if (ToFront && (pt == op->Pt)) return op; + else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; + + OutPt* newOp = new OutPt; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = op; + newOp->Prev = op->Prev; + newOp->Prev->Next = newOp; + op->Prev = newOp; + if (ToFront) outRec->Pts = newOp; +#ifdef use_xyz + if (pt == e->Bot) newOp->Pt = e->Bot; + else if (pt == e->Top) newOp->Pt = e->Top; + else SetZ(newOp->Pt, *e); +#endif + return newOp; } } //------------------------------------------------------------------------------ -void Clipper::ProcessHorizontals() +void Clipper::ProcessHorizontals(bool IsTopOfScanbeam) { TEdge* horzEdge = m_SortedEdges; - while( horzEdge ) + while(horzEdge) { - DeleteFromSEL( horzEdge ); - ProcessHorizontal( horzEdge ); + DeleteFromSEL(horzEdge); + ProcessHorizontal(horzEdge, IsTopOfScanbeam); horzEdge = m_SortedEdges; } } //------------------------------------------------------------------------------ -bool Clipper::IsTopHorz(const long64 XPos) +inline bool IsMinima(TEdge *e) { - TEdge* e = m_SortedEdges; - while( e ) - { - if( ( XPos >= std::min(e->xcurr, e->xtop) ) && - ( XPos <= std::max(e->xcurr, e->xtop) ) ) return false; - e = e->nextInSEL; - } - return true; + return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); } //------------------------------------------------------------------------------ -bool IsMinima(TEdge *e) +inline bool IsMaxima(TEdge *e, const cInt Y) { - return e && (e->prev->nextInLML != e) && (e->next->nextInLML != e); + return e && e->Top.Y == Y && !e->NextInLML; } //------------------------------------------------------------------------------ -bool IsMaxima(TEdge *e, const long64 Y) +inline bool IsIntermediate(TEdge *e, const cInt Y) { - return e && e->ytop == Y && !e->nextInLML; -} -//------------------------------------------------------------------------------ - -bool IsIntermediate(TEdge *e, const long64 Y) -{ - return e->ytop == Y && e->nextInLML; + return e->Top.Y == Y && e->NextInLML; } //------------------------------------------------------------------------------ TEdge *GetMaximaPair(TEdge *e) { - if( !IsMaxima(e->next, e->ytop) || e->next->xtop != e->xtop ) - return e->prev; else - return e->next; + TEdge* result = 0; + if ((e->Next->Top == e->Top) && !e->Next->NextInLML) + result = e->Next; + else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) + result = e->Prev; + + if (result && (result->OutIdx == Skip || + //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ... + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) + return 0; + return result; } //------------------------------------------------------------------------------ -void Clipper::SwapPositionsInAEL(TEdge *edge1, TEdge *edge2) +void Clipper::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) { - if( edge1->nextInAEL == edge2 ) + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if( Edge1->NextInAEL == Edge2 ) { - TEdge* next = edge2->nextInAEL; - if( next ) next->prevInAEL = edge1; - TEdge* prev = edge1->prevInAEL; - if( prev ) prev->nextInAEL = edge2; - edge2->prevInAEL = prev; - edge2->nextInAEL = edge1; - edge1->prevInAEL = edge2; - edge1->nextInAEL = next; + TEdge* Next = Edge2->NextInAEL; + if( Next ) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if( Prev ) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; } - else if( edge2->nextInAEL == edge1 ) + else if( Edge2->NextInAEL == Edge1 ) { - TEdge* next = edge1->nextInAEL; - if( next ) next->prevInAEL = edge2; - TEdge* prev = edge2->prevInAEL; - if( prev ) prev->nextInAEL = edge1; - edge1->prevInAEL = prev; - edge1->nextInAEL = edge2; - edge2->prevInAEL = edge1; - edge2->nextInAEL = next; + TEdge* Next = Edge1->NextInAEL; + if( Next ) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if( Prev ) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; } else { - TEdge* next = edge1->nextInAEL; - TEdge* prev = edge1->prevInAEL; - edge1->nextInAEL = edge2->nextInAEL; - if( edge1->nextInAEL ) edge1->nextInAEL->prevInAEL = edge1; - edge1->prevInAEL = edge2->prevInAEL; - if( edge1->prevInAEL ) edge1->prevInAEL->nextInAEL = edge1; - edge2->nextInAEL = next; - if( edge2->nextInAEL ) edge2->nextInAEL->prevInAEL = edge2; - edge2->prevInAEL = prev; - if( edge2->prevInAEL ) edge2->prevInAEL->nextInAEL = edge2; + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if( Edge1->NextInAEL ) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if( Edge1->PrevInAEL ) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if( Edge2->NextInAEL ) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if( Edge2->PrevInAEL ) Edge2->PrevInAEL->NextInAEL = Edge2; } - if( !edge1->prevInAEL ) m_ActiveEdges = edge1; - else if( !edge2->prevInAEL ) m_ActiveEdges = edge2; + if( !Edge1->PrevInAEL ) m_ActiveEdges = Edge1; + else if( !Edge2->PrevInAEL ) m_ActiveEdges = Edge2; } //------------------------------------------------------------------------------ -void Clipper::SwapPositionsInSEL(TEdge *edge1, TEdge *edge2) +void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) { - if( !( edge1->nextInSEL ) && !( edge1->prevInSEL ) ) return; - if( !( edge2->nextInSEL ) && !( edge2->prevInSEL ) ) return; + if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; + if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; - if( edge1->nextInSEL == edge2 ) + if( Edge1->NextInSEL == Edge2 ) { - TEdge* next = edge2->nextInSEL; - if( next ) next->prevInSEL = edge1; - TEdge* prev = edge1->prevInSEL; - if( prev ) prev->nextInSEL = edge2; - edge2->prevInSEL = prev; - edge2->nextInSEL = edge1; - edge1->prevInSEL = edge2; - edge1->nextInSEL = next; + TEdge* Next = Edge2->NextInSEL; + if( Next ) Next->PrevInSEL = Edge1; + TEdge* Prev = Edge1->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge2; + Edge2->PrevInSEL = Prev; + Edge2->NextInSEL = Edge1; + Edge1->PrevInSEL = Edge2; + Edge1->NextInSEL = Next; } - else if( edge2->nextInSEL == edge1 ) + else if( Edge2->NextInSEL == Edge1 ) { - TEdge* next = edge1->nextInSEL; - if( next ) next->prevInSEL = edge2; - TEdge* prev = edge2->prevInSEL; - if( prev ) prev->nextInSEL = edge1; - edge1->prevInSEL = prev; - edge1->nextInSEL = edge2; - edge2->prevInSEL = edge1; - edge2->nextInSEL = next; + TEdge* Next = Edge1->NextInSEL; + if( Next ) Next->PrevInSEL = Edge2; + TEdge* Prev = Edge2->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge1; + Edge1->PrevInSEL = Prev; + Edge1->NextInSEL = Edge2; + Edge2->PrevInSEL = Edge1; + Edge2->NextInSEL = Next; } else { - TEdge* next = edge1->nextInSEL; - TEdge* prev = edge1->prevInSEL; - edge1->nextInSEL = edge2->nextInSEL; - if( edge1->nextInSEL ) edge1->nextInSEL->prevInSEL = edge1; - edge1->prevInSEL = edge2->prevInSEL; - if( edge1->prevInSEL ) edge1->prevInSEL->nextInSEL = edge1; - edge2->nextInSEL = next; - if( edge2->nextInSEL ) edge2->nextInSEL->prevInSEL = edge2; - edge2->prevInSEL = prev; - if( edge2->prevInSEL ) edge2->prevInSEL->nextInSEL = edge2; + TEdge* Next = Edge1->NextInSEL; + TEdge* Prev = Edge1->PrevInSEL; + Edge1->NextInSEL = Edge2->NextInSEL; + if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; + Edge1->PrevInSEL = Edge2->PrevInSEL; + if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; + Edge2->NextInSEL = Next; + if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; + Edge2->PrevInSEL = Prev; + if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; } - if( !edge1->prevInSEL ) m_SortedEdges = edge1; - else if( !edge2->prevInSEL ) m_SortedEdges = edge2; + if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; + else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; } //------------------------------------------------------------------------------ TEdge* GetNextInAEL(TEdge *e, Direction dir) { - return dir == dLeftToRight ? e->nextInAEL : e->prevInAEL; + return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; } //------------------------------------------------------------------------------ -void Clipper::ProcessHorizontal(TEdge *horzEdge) +void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) { - Direction dir; - long64 horzLeft, horzRight; - - if( horzEdge->xcurr < horzEdge->xtop ) + if (HorzEdge.Bot.X < HorzEdge.Top.X) { - horzLeft = horzEdge->xcurr; - horzRight = horzEdge->xtop; - dir = dLeftToRight; + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = dLeftToRight; } else { - horzLeft = horzEdge->xtop; - horzRight = horzEdge->xcurr; - dir = dRightToLeft; + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = dRightToLeft; } +} +//------------------------------------------------------------------------ - TEdge* eMaxPair; - if( horzEdge->nextInLML ) eMaxPair = 0; - else eMaxPair = GetMaximaPair(horzEdge); +void Clipper::PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam) +{ + //get the last Op for this horizontal edge + //the point may be anywhere along the horizontal ... + OutPt* outPt = m_PolyOuts[horzEdge->OutIdx]->Pts; + if (horzEdge->Side != esLeft) outPt = outPt->Prev; - TEdge* e = GetNextInAEL( horzEdge , dir ); - while( e ) + //First, match up overlapping horizontal edges (eg when one polygon's + //intermediate horz edge overlaps an intermediate horz edge of another, or + //when one polygon sits on top of another) ... + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) { - if ( e->xcurr == horzEdge->xtop && !eMaxPair ) + Join* j = m_GhostJoins[i]; + if (HorzSegmentsOverlap(j->OutPt1->Pt, j->OffPt, horzEdge->Bot, horzEdge->Top)) + AddJoin(j->OutPt1, outPt, j->OffPt); + } + //Also, since horizontal edges at the top of one SB are often removed from + //the AEL before we process the horizontal edges at the bottom of the next, + //we need to create 'ghost' Join records of 'contrubuting' horizontals that + //we can compare with horizontals at the bottom of the next SB. + if (isTopOfScanbeam) + { + if (outPt->Pt == horzEdge->Top) + AddGhostJoin(outPt, horzEdge->Bot); + else + AddGhostJoin(outPt, horzEdge->Top); + } +} +//------------------------------------------------------------------------------ + +/******************************************************************************* +* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * +* Bottom of a scanbeam) are processed as if layered. The order in which HEs * +* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * +* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * +* and with other non-horizontal edges [*]. Once these intersections are * +* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * +* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * +*******************************************************************************/ + +void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) +{ + Direction dir; + cInt horzLeft, horzRight; + + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + TEdge* eLastHorz = horzEdge, *eMaxPair = 0; + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + eLastHorz = eLastHorz->NextInLML; + if (!eLastHorz->NextInLML) + eMaxPair = GetMaximaPair(eLastHorz); + + for (;;) + { + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge* e = GetNextInAEL(horzEdge, dir); + while(e) { - if (SlopesEqual(*e, *horzEdge->nextInLML, m_UseFullRange)) + //Break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + TEdge* eNext = GetNextInAEL(e, dir); //saves eNext for later + + if ((dir == dLeftToRight && e->Curr.X <= horzRight) || + (dir == dRightToLeft && e->Curr.X >= horzLeft)) { - //if output polygons share an edge, they'll need joining later ... - if (horzEdge->outIdx >= 0 && e->outIdx >= 0) - AddJoin(horzEdge->nextInLML, e, horzEdge->outIdx); - break; //we've reached the end of the horizontal line + if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) + PrepareHorzJoins(horzEdge, isTopOfScanbeam); + //so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + if (dir == dLeftToRight) + IntersectEdges(horzEdge, e, e->Top); + else + IntersectEdges(e, horzEdge, e->Top); + if (eMaxPair->OutIdx >= 0) throw clipperException("ProcessHorizontal error"); + return; + } + else if(dir == dLeftToRight) + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges(horzEdge, e, Pt, true); + } + else + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges( e, horzEdge, Pt, true); + } + SwapPositionsInAEL( horzEdge, e ); + } + else if( (dir == dLeftToRight && e->Curr.X >= horzRight) || + (dir == dRightToLeft && e->Curr.X <= horzLeft) ) break; + e = eNext; + } //end while + + if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) + PrepareHorzJoins(horzEdge, isTopOfScanbeam); + + if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML)) + { + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + } else + break; + } //end for (;;) + + if(horzEdge->NextInLML) + { + if(horzEdge->OutIdx >= 0) + { + OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top); + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge* ePrev = horzEdge->PrevInAEL; + TEdge* eNext = horzEdge->NextInAEL; + if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && + ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && + (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) + { + OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + else if (eNext && eNext->Curr.X == horzEdge->Bot.X && + eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) + { + OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); } - else if (e->dx < horzEdge->nextInLML->dx) - //we really have got to the end of the intermediate horz edge so quit. - //nb: More -ve slopes follow more +ve slopes ABOVE the horizontal. - break; } - - TEdge* eNext = GetNextInAEL( e, dir ); - - if (eMaxPair || - ((dir == dLeftToRight) && (e->xcurr < horzRight)) || - ((dir == dRightToLeft) && (e->xcurr > horzLeft))) + else + UpdateEdgeIntoAEL(horzEdge); + } + else if (eMaxPair) + { + if (eMaxPair->OutIdx >= 0) { - //so far we're still in range of the horizontal edge - if( e == eMaxPair ) - { - //horzEdge is evidently a maxima horizontal and we've arrived at its end. - if (dir == dLeftToRight) - IntersectEdges(horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); - else - IntersectEdges(e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); - if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); - return; - } - else if( NEAR_EQUAL(e->dx, HORIZONTAL) && !IsMinima(e) && !(e->xcurr > e->xtop) ) - { - //An overlapping horizontal edge. Overlapping horizontal edges are - //processed as if layered with the current horizontal edge (horizEdge) - //being infinitesimally lower that the next (e). Therfore, we - //intersect with e only if e.xcurr is within the bounds of horzEdge ... - if( dir == dLeftToRight ) - IntersectEdges( horzEdge , e, IntPoint(e->xcurr, horzEdge->ycurr), - (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); - else - IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), - (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); - } - else if( dir == dLeftToRight ) - { - IntersectEdges( horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), - (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); - } + if (dir == dLeftToRight) + IntersectEdges(horzEdge, eMaxPair, horzEdge->Top); else - { - IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), - (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); - } - SwapPositionsInAEL( horzEdge, e ); + IntersectEdges(eMaxPair, horzEdge, horzEdge->Top); + if (eMaxPair->OutIdx >= 0) + throw clipperException("ProcessHorizontal error"); + } else + { + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); } - else if( (dir == dLeftToRight && e->xcurr >= horzRight) || - (dir == dRightToLeft && e->xcurr <= horzLeft) ) break; - e = eNext; - } //end while - - if( horzEdge->nextInLML ) + } else { - if( horzEdge->outIdx >= 0 ) - AddOutPt( horzEdge, IntPoint(horzEdge->xtop, horzEdge->ytop)); - UpdateEdgeIntoAEL( horzEdge ); - } - else - { - if ( horzEdge->outIdx >= 0 ) - IntersectEdges( horzEdge, eMaxPair, - IntPoint(horzEdge->xtop, horzEdge->ycurr), ipBoth); - if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); - DeleteFromAEL(eMaxPair); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); DeleteFromAEL(horzEdge); } } @@ -2285,55 +2830,57 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) void Clipper::UpdateEdgeIntoAEL(TEdge *&e) { - if( !e->nextInLML ) throw + if( !e->NextInLML ) throw clipperException("UpdateEdgeIntoAEL: invalid call"); - TEdge* AelPrev = e->prevInAEL; - TEdge* AelNext = e->nextInAEL; - e->nextInLML->outIdx = e->outIdx; - if( AelPrev ) AelPrev->nextInAEL = e->nextInLML; - else m_ActiveEdges = e->nextInLML; - if( AelNext ) AelNext->prevInAEL = e->nextInLML; - e->nextInLML->side = e->side; - e->nextInLML->windDelta = e->windDelta; - e->nextInLML->windCnt = e->windCnt; - e->nextInLML->windCnt2 = e->windCnt2; - e = e->nextInLML; - e->prevInAEL = AelPrev; - e->nextInAEL = AelNext; - if( !NEAR_EQUAL(e->dx, HORIZONTAL) ) InsertScanbeam( e->ytop ); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); } //------------------------------------------------------------------------------ -bool Clipper::ProcessIntersections(const long64 botY, const long64 topY) +bool Clipper::ProcessIntersections(const cInt botY, const cInt topY) { if( !m_ActiveEdges ) return true; try { BuildIntersectList(botY, topY); - if ( !m_IntersectNodes) return true; - if ( FixupIntersectionOrder() ) ProcessIntersectList(); + size_t IlSize = m_IntersectList.size(); + if (IlSize == 0) return true; + if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); else return false; } - catch(...) { + catch(...) + { m_SortedEdges = 0; DisposeIntersectNodes(); throw clipperException("ProcessIntersections error"); } + m_SortedEdges = 0; return true; } //------------------------------------------------------------------------------ void Clipper::DisposeIntersectNodes() { - while ( m_IntersectNodes ) - { - IntersectNode* iNode = m_IntersectNodes->next; - delete m_IntersectNodes; - m_IntersectNodes = iNode; - } + for (size_t i = 0; i < m_IntersectList.size(); ++i ) + delete m_IntersectList[i]; + m_IntersectList.clear(); } //------------------------------------------------------------------------------ -void Clipper::BuildIntersectList(const long64 botY, const long64 topY) +void Clipper::BuildIntersectList(const cInt botY, const cInt topY) { if ( !m_ActiveEdges ) return; @@ -2342,298 +2889,328 @@ void Clipper::BuildIntersectList(const long64 botY, const long64 topY) m_SortedEdges = e; while( e ) { - e->prevInSEL = e->prevInAEL; - e->nextInSEL = e->nextInAEL; - e->xcurr = TopX( *e, topY ); - e = e->nextInAEL; + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e->Curr.X = TopX( *e, topY ); + e = e->NextInAEL; } //bubblesort ... - bool isModified = true; - while( isModified && m_SortedEdges ) + bool isModified; + do { isModified = false; e = m_SortedEdges; - while( e->nextInSEL ) + while( e->NextInSEL ) { - TEdge *eNext = e->nextInSEL; - IntPoint pt; - if(e->xcurr > eNext->xcurr) + TEdge *eNext = e->NextInSEL; + IntPoint Pt; + if(e->Curr.X > eNext->Curr.X) { - if (!IntersectPoint(*e, *eNext, pt, m_UseFullRange) && e->xcurr > eNext->xcurr +1) + if (!IntersectPoint(*e, *eNext, Pt, m_UseFullRange) && e->Curr.X > eNext->Curr.X +1) throw clipperException("Intersection error"); - if (pt.Y > botY) + if (Pt.Y > botY) { - pt.Y = botY; - pt.X = TopX(*e, pt.Y); + Pt.Y = botY; + if (std::fabs(e->Dx) > std::fabs(eNext->Dx)) + Pt.X = TopX(*eNext, botY); else + Pt.X = TopX(*e, botY); } - AddIntersectNode( e, eNext, pt ); + + IntersectNode * newNode = new IntersectNode; + newNode->Edge1 = e; + newNode->Edge2 = eNext; + newNode->Pt = Pt; + m_IntersectList.push_back(newNode); + SwapPositionsInSEL(e, eNext); isModified = true; } else e = eNext; } - if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0; + if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; else break; } - m_SortedEdges = 0; + while ( isModified ); + m_SortedEdges = 0; //important } //------------------------------------------------------------------------------ -bool ProcessParam1BeforeParam2(const IntersectNode &node1, const IntersectNode &node2) -{ - bool result; - if (node1.pt.Y == node2.pt.Y) - { - if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1) - { - result = node2.pt.X > node1.pt.X; - return node2.edge1->dx > 0 ? !result : result; - } - else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2) - { - result = node2.pt.X > node1.pt.X; - return node2.edge2->dx > 0 ? !result : result; - } - else return node2.pt.X > node1.pt.X; - } - else return node1.pt.Y > node2.pt.Y; -} -//------------------------------------------------------------------------------ - -void Clipper::AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt) -{ - IntersectNode* newNode = new IntersectNode; - newNode->edge1 = e1; - newNode->edge2 = e2; - newNode->pt = pt; - newNode->next = 0; - if( !m_IntersectNodes ) m_IntersectNodes = newNode; - else if( ProcessParam1BeforeParam2(*newNode, *m_IntersectNodes) ) - { - newNode->next = m_IntersectNodes; - m_IntersectNodes = newNode; - } - else - { - IntersectNode* iNode = m_IntersectNodes; - while( iNode->next && ProcessParam1BeforeParam2(*iNode->next, *newNode) ) - iNode = iNode->next; - newNode->next = iNode->next; - iNode->next = newNode; - } -} -//------------------------------------------------------------------------------ void Clipper::ProcessIntersectList() { - while( m_IntersectNodes ) + for (size_t i = 0; i < m_IntersectList.size(); ++i) { - IntersectNode* iNode = m_IntersectNodes->next; + IntersectNode* iNode = m_IntersectList[i]; { - IntersectEdges( m_IntersectNodes->edge1 , - m_IntersectNodes->edge2 , m_IntersectNodes->pt, ipBoth ); - SwapPositionsInAEL( m_IntersectNodes->edge1 , m_IntersectNodes->edge2 ); + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt, true); + SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); } - delete m_IntersectNodes; - m_IntersectNodes = iNode; + delete iNode; } + m_IntersectList.clear(); } //------------------------------------------------------------------------------ -void Clipper::DoMaxima(TEdge *e, long64 topY) +bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) +{ + return node2->Pt.Y < node1->Pt.Y; +} +//------------------------------------------------------------------------------ + +inline bool EdgesAdjacent(const IntersectNode &inode) +{ + return (inode.Edge1->NextInSEL == inode.Edge2) || + (inode.Edge1->PrevInSEL == inode.Edge2); +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersectionOrder() +{ + //pre-condition: intersections are sorted Bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + CopyAELToSEL(); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); + size_t cnt = m_IntersectList.size(); + for (size_t i = 0; i < cnt; ++i) + { + if (!EdgesAdjacent(*m_IntersectList[i])) + { + size_t j = i + 1; + while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; + if (j == cnt) return false; + std::swap(m_IntersectList[i], m_IntersectList[j]); + } + SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e) { TEdge* eMaxPair = GetMaximaPair(e); - long64 X = e->xtop; - TEdge* eNext = e->nextInAEL; - while( eNext != eMaxPair ) + if (!eMaxPair) { - if (!eNext) throw clipperException("DoMaxima error"); - IntersectEdges( e, eNext, IntPoint(X, topY), ipBoth ); + if (e->OutIdx >= 0) + AddOutPt(e, e->Top); + DeleteFromAEL(e); + return; + } + + TEdge* eNext = e->NextInAEL; + while(eNext && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e->Top, true); SwapPositionsInAEL(e, eNext); - eNext = e->nextInAEL; + eNext = e->NextInAEL; } - if( e->outIdx < 0 && eMaxPair->outIdx < 0 ) + + if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) { - DeleteFromAEL( e ); - DeleteFromAEL( eMaxPair ); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); } - else if( e->outIdx >= 0 && eMaxPair->outIdx >= 0 ) + else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) { - IntersectEdges( e, eMaxPair, IntPoint(X, topY), ipNone ); + IntersectEdges( e, eMaxPair, e->Top); } +#ifdef use_lines + else if (e->WindDelta == 0) + { + if (e->OutIdx >= 0) + { + AddOutPt(e, e->Top); + e->OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair->OutIdx >= 0) + { + AddOutPt(eMaxPair, e->Top); + eMaxPair->OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif else throw clipperException("DoMaxima error"); } //------------------------------------------------------------------------------ -void Clipper::ProcessEdgesAtTopOfScanbeam(const long64 topY) +void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) { TEdge* e = m_ActiveEdges; while( e ) { //1. process maxima, treating them as if they're 'bent' horizontal edges, // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - if( IsMaxima(e, topY) && !NEAR_EQUAL(GetMaximaPair(e)->dx, HORIZONTAL) ) + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) { - //'e' might be removed from AEL, as may any following edges so ... - TEdge* ePrev = e->prevInAEL; - DoMaxima(e, topY); + TEdge* eMaxPair = GetMaximaPair(e); + IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); + } + + if(IsMaximaEdge) + { + TEdge* ePrev = e->PrevInAEL; + DoMaxima(e); if( !ePrev ) e = m_ActiveEdges; - else e = ePrev->nextInAEL; + else e = ePrev->NextInAEL; } else { - //2. promote horizontal edges, otherwise update xcurr and ycurr ... - if( IsIntermediate(e, topY) && NEAR_EQUAL(e->nextInLML->dx, HORIZONTAL) ) + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) { - if (e->outIdx >= 0) - { - AddOutPt(e, IntPoint(e->xtop, e->ytop)); - - for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) - { - IntPoint pt, pt2; - HorzJoinRec* hj = m_HorizJoins[i]; - if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), - IntPoint(hj->edge->xtop, hj->edge->ytop), - IntPoint(e->nextInLML->xbot, e->nextInLML->ybot), - IntPoint(e->nextInLML->xtop, e->nextInLML->ytop), pt, pt2)) - AddJoin(hj->edge, e->nextInLML, hj->savedIdx, e->outIdx); - } - - AddHorzJoin(e->nextInLML, e->outIdx); - } UpdateEdgeIntoAEL(e); + if (e->OutIdx >= 0) + AddOutPt(e, e->Bot); AddEdgeToSEL(e); - } else + } + else { - //this just simplifies horizontal processing ... - e->xcurr = TopX( *e, topY ); - e->ycurr = topY; + e->Curr.X = TopX( *e, topY ); + e->Curr.Y = topY; } - e = e->nextInAEL; + + if (m_StrictSimple) + { + TEdge* ePrev = e->PrevInAEL; + if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && + (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) + { + OutPt* op = AddOutPt(ePrev, e->Curr); + OutPt* op2 = AddOutPt(e, e->Curr); + AddJoin(op, op2, e->Curr); //StrictlySimple (type-3) join + } + } + + e = e->NextInAEL; } } - //3. Process horizontals at the top of the scanbeam ... - ProcessHorizontals(); + //3. Process horizontals at the Top of the scanbeam ... + ProcessHorizontals(true); //4. Promote intermediate vertices ... e = m_ActiveEdges; - while( e ) + while(e) { - if( IsIntermediate( e, topY ) ) + if(IsIntermediate(e, topY)) { - if( e->outIdx >= 0 ) AddOutPt(e, IntPoint(e->xtop,e->ytop)); + OutPt* op = 0; + if( e->OutIdx >= 0 ) + op = AddOutPt(e, e->Top); UpdateEdgeIntoAEL(e); //if output polygons share an edge, they'll need joining later ... - TEdge* ePrev = e->prevInAEL; - TEdge* eNext = e->nextInAEL; - if (ePrev && ePrev->xcurr == e->xbot && - ePrev->ycurr == e->ybot && e->outIdx >= 0 && - ePrev->outIdx >= 0 && ePrev->ycurr > ePrev->ytop && - SlopesEqual(*e, *ePrev, m_UseFullRange)) + TEdge* ePrev = e->PrevInAEL; + TEdge* eNext = e->NextInAEL; + if (ePrev && ePrev->Curr.X == e->Bot.X && + ePrev->Curr.Y == e->Bot.Y && op && + ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*e, *ePrev, m_UseFullRange) && + (e->WindDelta != 0) && (ePrev->WindDelta != 0)) { - AddOutPt(ePrev, IntPoint(e->xbot, e->ybot)); - AddJoin(e, ePrev); + OutPt* op2 = AddOutPt(ePrev, e->Bot); + AddJoin(op, op2, e->Top); } - else if (eNext && eNext->xcurr == e->xbot && - eNext->ycurr == e->ybot && e->outIdx >= 0 && - eNext->outIdx >= 0 && eNext->ycurr > eNext->ytop && - SlopesEqual(*e, *eNext, m_UseFullRange)) + else if (eNext && eNext->Curr.X == e->Bot.X && + eNext->Curr.Y == e->Bot.Y && op && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*e, *eNext, m_UseFullRange) && + (e->WindDelta != 0) && (eNext->WindDelta != 0)) { - AddOutPt(eNext, IntPoint(e->xbot, e->ybot)); - AddJoin(e, eNext); + OutPt* op2 = AddOutPt(eNext, e->Bot); + AddJoin(op, op2, e->Top); } } - e = e->nextInAEL; + e = e->NextInAEL; } } //------------------------------------------------------------------------------ -void Clipper::FixupOutPolygon(OutRec &outRec) +void Clipper::FixupOutPolygon(OutRec &outrec) { //FixupOutPolygon() - removes duplicate points and simplifies consecutive //parallel edges by removing the middle vertex. OutPt *lastOK = 0; - outRec.pts = outRec.bottomPt; - OutPt *pp = outRec.bottomPt; + outrec.BottomPt = 0; + OutPt *pp = outrec.Pts; for (;;) { - if (pp->prev == pp || pp->prev == pp->next ) + if (pp->Prev == pp || pp->Prev == pp->Next ) { DisposeOutPts(pp); - outRec.pts = 0; - outRec.bottomPt = 0; + outrec.Pts = 0; return; } - //test for duplicate points and for same slope (cross-product) ... - if ( PointsEqual(pp->pt, pp->next->pt) || - SlopesEqual(pp->prev->pt, pp->pt, pp->next->pt, m_UseFullRange) ) + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) { lastOK = 0; OutPt *tmp = pp; - if (pp == outRec.bottomPt) - outRec.bottomPt = 0; //flags need for updating - pp->prev->next = pp->next; - pp->next->prev = pp->prev; - pp = pp->prev; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; delete tmp; } else if (pp == lastOK) break; else { if (!lastOK) lastOK = pp; - pp = pp->next; + pp = pp->Next; } } - if (!outRec.bottomPt) { - outRec.bottomPt = GetBottomPt(pp); - outRec.bottomPt->idx = outRec.idx; - outRec.pts = outRec.bottomPt; - } + outrec.Pts = pp; } //------------------------------------------------------------------------------ -void Clipper::BuildResult(Polygons &polys) +int PointCount(OutPt *Pts) +{ + if (!Pts) return 0; + int result = 0; + OutPt* p = Pts; + do + { + result++; + p = p->Next; + } + while (p != Pts); + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Paths &polys) { polys.reserve(m_PolyOuts.size()); for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { - if (m_PolyOuts[i]->pts) + if (!m_PolyOuts[i]->Pts) continue; + Path pg; + OutPt* p = m_PolyOuts[i]->Pts->Prev; + int cnt = PointCount(p); + if (cnt < 2) continue; + pg.reserve(cnt); + for (int i = 0; i < cnt; ++i) { - Polygon pg; - OutPt* p = m_PolyOuts[i]->pts; - do - { - pg.push_back(p->pt); - p = p->prev; - } while (p != m_PolyOuts[i]->pts); - if (pg.size() > 2) - polys.push_back(pg); + pg.push_back(p->Pt); + p = p->Prev; } + polys.push_back(pg); } } //------------------------------------------------------------------------------ -int PointCount(OutPt *pts) -{ - if (!pts) return 0; - int result = 0; - OutPt* p = pts; - do - { - result++; - p = p->next; - } - while (p != pts); - return result; -} -//------------------------------------------------------------------------------ - void Clipper::BuildResult2(PolyTree& polytree) { polytree.Clear(); @@ -2642,21 +3219,21 @@ void Clipper::BuildResult2(PolyTree& polytree) for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) { OutRec* outRec = m_PolyOuts[i]; - int cnt = PointCount(outRec->pts); - if (cnt < 3) continue; + int cnt = PointCount(outRec->Pts); + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; FixHoleLinkage(*outRec); PolyNode* pn = new PolyNode(); //nb: polytree takes ownership of all the PolyNodes polytree.AllNodes.push_back(pn); - outRec->polyNode = pn; + outRec->PolyNd = pn; pn->Parent = 0; pn->Index = 0; pn->Contour.reserve(cnt); - OutPt *op = outRec->pts; + OutPt *op = outRec->Pts->Prev; for (int j = 0; j < cnt; j++) { - pn->Contour.push_back(op->pt); - op = op->prev; + pn->Contour.push_back(op->Pt); + op = op->Prev; } } @@ -2665,237 +3242,368 @@ void Clipper::BuildResult2(PolyTree& polytree) for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) { OutRec* outRec = m_PolyOuts[i]; - if (!outRec->polyNode) continue; - if (outRec->FirstLeft) - outRec->FirstLeft->polyNode->AddChild(*outRec->polyNode); + if (!outRec->PolyNd) continue; + if (outRec->IsOpen) + { + outRec->PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec->PolyNd); + } + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); else - polytree.AddChild(*outRec->polyNode); + polytree.AddChild(*outRec->PolyNd); } } //------------------------------------------------------------------------------ void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) { - TEdge *e1 = int1.edge1; - TEdge *e2 = int1.edge2; - IntPoint p = int1.pt; - - int1.edge1 = int2.edge1; - int1.edge2 = int2.edge2; - int1.pt = int2.pt; - - int2.edge1 = e1; - int2.edge2 = e2; - int2.pt = p; + //just swap the contents (because fIntersectNodes is a single-linked-list) + IntersectNode inode = int1; //gets a copy of Int1 + int1.Edge1 = int2.Edge1; + int1.Edge2 = int2.Edge2; + int1.Pt = int2.Pt; + int2.Edge1 = inode.Edge1; + int2.Edge2 = inode.Edge2; + int2.Pt = inode.Pt; } //------------------------------------------------------------------------------ -bool Clipper::FixupIntersectionOrder() +inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) { - if ( !m_IntersectNodes->next ) return true; - - CopyAELToSEL(); - IntersectNode *int1 = m_IntersectNodes; - IntersectNode *int2 = m_IntersectNodes->next; - while (int2) + if (e2.Curr.X == e1.Curr.X) { - TEdge *e1 = int1->edge1; - TEdge *e2; - if (e1->prevInSEL == int1->edge2) e2 = e1->prevInSEL; - else if (e1->nextInSEL == int1->edge2) e2 = e1->nextInSEL; - else + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; +} +//------------------------------------------------------------------------------ + +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, + cInt& Left, cInt& Right) +{ + if (a1 < a2) + { + if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} + else {Left = std::max(a1,b2); Right = std::min(a2,b1);} + } + else + { + if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} + else {Left = std::max(a2,b2); Right = std::min(a1,b1);} + } + return Left < Right; +} +//------------------------------------------------------------------------------ + +inline void UpdateOutPtIdxs(OutRec& outrec) +{ + OutPt* op = outrec.Pts; + do + { + op->Idx = outrec.Idx; + op = op->Prev; + } + while(op != outrec.Pts); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) +{ + if(!m_ActiveEdges) + { + edge->PrevInAEL = 0; + edge->NextInAEL = 0; + m_ActiveEdges = edge; + } + else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) + { + edge->PrevInAEL = 0; + edge->NextInAEL = m_ActiveEdges; + m_ActiveEdges->PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if(!startEdge) startEdge = m_ActiveEdges; + while(startEdge->NextInAEL && + !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) + startEdge = startEdge->NextInAEL; + edge->NextInAEL = startEdge->NextInAEL; + if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; + edge->PrevInAEL = startEdge; + startEdge->NextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) +{ + OutPt* result = new OutPt; + result->Pt = outPt->Pt; + result->Idx = outPt->Idx; + if (InsertAfter) + { + result->Next = outPt->Next; + result->Prev = outPt; + outPt->Next->Prev = result; + outPt->Next = result; + } + else + { + result->Prev = outPt->Prev; + result->Next = outPt; + outPt->Prev->Next = result; + outPt->Prev = result; + } + return result; +} +//------------------------------------------------------------------------------ + +bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, + const IntPoint Pt, bool DiscardLeft) +{ + Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); + Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == dLeftToRight) + { + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, !DiscardLeft); + if (op1b->Pt != Pt) { - //The current intersection (Int1) is out of order (since it doesn't - //contain adjacent edges), so swap it with a subsequent intersection ... - while (int2) - { - if (int2->edge1->nextInSEL == int2->edge2 || - int2->edge1->prevInSEL == int2->edge2) break; - else int2 = int2->next; - } - if ( !int2 ) return false; //oops!!! - - //found an intersect node (Int2) that does contain adjacent edges, - //so prepare to process it before Int1 ... - SwapIntersectNodes(*int1, *int2); - e1 = int1->edge1; - e2 = int1->edge2; + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, DiscardLeft); } - SwapPositionsInSEL(e1, e2); - int1 = int1->next; - int2 = int1->next; } - m_SortedEdges = 0; - - //finally, check the last intersection too ... - return (int1->edge1->prevInSEL == int1->edge2 || - int1->edge1->nextInSEL == int1->edge2); -} -//------------------------------------------------------------------------------ - -bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) -{ - return e2.xcurr == e1.xcurr ? e2.dx > e1.dx : e2.xcurr < e1.xcurr; -} -//------------------------------------------------------------------------------ - -void Clipper::InsertEdgeIntoAEL(TEdge *edge) -{ - edge->prevInAEL = 0; - edge->nextInAEL = 0; - if( !m_ActiveEdges ) + if (Dir2 == dLeftToRight) { - m_ActiveEdges = edge; - } - else if( E2InsertsBeforeE1(*m_ActiveEdges, *edge) ) - { - edge->nextInAEL = m_ActiveEdges; - m_ActiveEdges->prevInAEL = edge; - m_ActiveEdges = edge; + while (op2->Next->Pt.X <= Pt.X && + op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, !DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, !DiscardLeft); + }; } else { - TEdge* e = m_ActiveEdges; - while( e->nextInAEL && !E2InsertsBeforeE1(*e->nextInAEL , *edge) ) - e = e->nextInAEL; - edge->nextInAEL = e->nextInAEL; - if( e->nextInAEL ) e->nextInAEL->prevInAEL = edge; - edge->prevInAEL = e; - e->nextInAEL = edge; - } -} -//---------------------------------------------------------------------- + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, DiscardLeft); + }; + }; -void Clipper::DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt) -{ - AddOutPt(edge1, pt); - SwapSides(*edge1, *edge2); - SwapPolyIndexes(*edge1, *edge2); -} -//---------------------------------------------------------------------- - -void Clipper::DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt) -{ - AddOutPt(edge2, pt); - SwapSides(*edge1, *edge2); - SwapPolyIndexes(*edge1, *edge2); -} -//---------------------------------------------------------------------- - -void Clipper::DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt) -{ - AddOutPt(edge1, pt); - AddOutPt(edge2, pt); - SwapSides( *edge1 , *edge2 ); - SwapPolyIndexes( *edge1 , *edge2 ); -} -//---------------------------------------------------------------------- - -bool Clipper::JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2) -{ - OutRec *outRec1 = m_PolyOuts[j->poly1Idx]; - OutRec *outRec2 = m_PolyOuts[j->poly2Idx]; - if (!outRec1 || !outRec2) return false; - OutPt *pp1a = outRec1->pts; - OutPt *pp2a = outRec2->pts; - IntPoint pt1 = j->pt2a, pt2 = j->pt2b; - IntPoint pt3 = j->pt1a, pt4 = j->pt1b; - if (!FindSegment(pp1a, m_UseFullRange, pt1, pt2)) return false; - if (outRec1 == outRec2) + if ((Dir1 == dLeftToRight) == DiscardLeft) { - //we're searching the same polygon for overlapping segments so - //segment 2 mustn't be the same as segment 1 ... - pp2a = pp1a->next; - if (!FindSegment(pp2a, m_UseFullRange, pt3, pt4) || (pp2a == pp1a)) - return false; - } - else if (!FindSegment(pp2a, m_UseFullRange, pt3, pt4)) return false; - - if (!GetOverlapSegment(pt1, pt2, pt3, pt4, pt1, pt2)) return false; - - OutPt *p3, *p4, *prev = pp1a->prev; - //get p1 & p2 polypts - the overlap start & endpoints on poly1 - if (PointsEqual(pp1a->pt, pt1)) p1 = pp1a; - else if (PointsEqual(prev->pt, pt1)) p1 = prev; - else p1 = InsertPolyPtBetween(pp1a, prev, pt1); - - if (PointsEqual(pp1a->pt, pt2)) p2 = pp1a; - else if (PointsEqual(prev->pt, pt2)) p2 = prev; - else if ((p1 == pp1a) || (p1 == prev)) - p2 = InsertPolyPtBetween(pp1a, prev, pt2); - else if (Pt3IsBetweenPt1AndPt2(pp1a->pt, p1->pt, pt2)) - p2 = InsertPolyPtBetween(pp1a, p1, pt2); else - p2 = InsertPolyPtBetween(p1, prev, pt2); - - //get p3 & p4 polypts - the overlap start & endpoints on poly2 - prev = pp2a->prev; - if (PointsEqual(pp2a->pt, pt1)) p3 = pp2a; - else if (PointsEqual(prev->pt, pt1)) p3 = prev; - else p3 = InsertPolyPtBetween(pp2a, prev, pt1); - - if (PointsEqual(pp2a->pt, pt2)) p4 = pp2a; - else if (PointsEqual(prev->pt, pt2)) p4 = prev; - else if ((p3 == pp2a) || (p3 == prev)) - p4 = InsertPolyPtBetween(pp2a, prev, pt2); - else if (Pt3IsBetweenPt1AndPt2(pp2a->pt, p3->pt, pt2)) - p4 = InsertPolyPtBetween(pp2a, p3, pt2); else - p4 = InsertPolyPtBetween(p3, prev, pt2); - - //p1.pt == p3.pt and p2.pt == p4.pt so join p1 to p3 and p2 to p4 ... - if (p1->next == p2 && p3->prev == p4) - { - p1->next = p3; - p3->prev = p1; - p2->prev = p4; - p4->next = p2; - return true; - } - else if (p1->prev == p2 && p3->next == p4) - { - p1->prev = p3; - p3->next = p1; - p2->next = p4; - p4->prev = p2; - return true; + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; } else - return false; //an orientation is probably wrong + { + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + } + return true; } -//---------------------------------------------------------------------- +//------------------------------------------------------------------------------ -void Clipper::FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx) +bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) { - for (JoinList::size_type k = startIdx; k < m_Joins.size(); k++) + OutPt *op1 = j->OutPt1, *op1b; + OutPt *op2 = j->OutPt2, *op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictSimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); + + if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && + (j->OffPt == j->OutPt2->Pt)) + { + //Strictly Simple join ... + op1b = j->OutPt1->Next; + while (op1b != op1 && (op1b->Pt == j->OffPt)) + op1b = op1b->Next; + bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); + op2b = j->OutPt2->Next; + while (op2b != op2 && (op2b->Pt == j->OffPt)) + op2b = op2b->Next; + bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) { - JoinRec* j2 = m_Joins[k]; - if (j2->poly1Idx == j->poly1Idx && PointIsVertex(j2->pt1a, pt)) - j2->poly1Idx = j->poly2Idx; - if (j2->poly2Idx == j->poly1Idx && PointIsVertex(j2->pt2a, pt)) - j2->poly2Idx = j->poly2Idx; + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; } -} -//---------------------------------------------------------------------- + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) + op1 = op1->Prev; + while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) + op1b = op1b->Next; + if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' -bool Poly2ContainsPoly1(OutPt* outPt1, OutPt* outPt2, bool UseFullInt64Range) -{ - //find the first pt in outPt1 that isn't also a vertex of outPt2 ... - OutPt* outPt = outPt1; - do + op2b = op2; + while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) + op2 = op2->Prev; + while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) + op2b = op2b->Next; + if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1->Pt.X >= Left && op1->Pt.X <= Right) + { + Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + { + Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); + } + else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) + { + Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; + } + else + { + Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); + } + j->OutPt1 = op1; j->OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else { - if (!PointIsVertex(outPt->pt, outPt2)) break; - outPt = outPt->next; + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1->Next; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; + bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1->Prev; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; + if ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; + }; + op2b = op2->Next; + while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; + bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2->Prev; + while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; + if ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } } - while (outPt != outPt1); - bool result; - //sometimes a point on one polygon can be touching the other polygon - //so to be totally confident outPt1 is inside outPt2 repeat ... - do - { - result = PointInPolygon(outPt->pt, outPt2, UseFullInt64Range); - outPt = outPt->next; - } - while (result && outPt != outPt1); - return result; } //---------------------------------------------------------------------- @@ -2905,9 +3613,9 @@ void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec* outRec = m_PolyOuts[i]; - if (outRec->pts && outRec->FirstLeft == OldOutRec) + if (outRec->Pts && outRec->FirstLeft == OldOutRec) { - if (Poly2ContainsPoly1(outRec->pts, NewOutRec->pts, m_UseFullRange)) + if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) outRec->FirstLeft = NewOutRec; } } @@ -2924,16 +3632,24 @@ void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) } //---------------------------------------------------------------------- +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + void Clipper::JoinCommonEdges() { for (JoinList::size_type i = 0; i < m_Joins.size(); i++) { - JoinRec* j = m_Joins[i]; + Join* join = m_Joins[i]; - OutRec *outRec1 = m_PolyOuts[j->poly1Idx]; - OutRec *outRec2 = m_PolyOuts[j->poly2Idx]; + OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); + OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); - if (!outRec1->pts || !outRec2->pts) continue; + if (!outRec1->Pts || !outRec2->Pts) continue; //get the polygon fragment with the correct hole state (FirstLeft) //before calling JoinPoints() ... @@ -2943,157 +3659,89 @@ void Clipper::JoinCommonEdges() else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; else holeStateRec = GetLowermostRec(outRec1, outRec2); - OutPt *p1, *p2; - if (!JoinPoints(j, p1, p2)) continue; + if (!JoinPoints(join, outRec1, outRec2)) continue; if (outRec1 == outRec2) { //instead of joining two polygons, we've just created a new one by //splitting one polygon into two. - outRec1->pts = GetBottomPt(p1); - outRec1->bottomPt = outRec1->pts; - outRec1->bottomPt->idx = outRec1->idx; + outRec1->Pts = join->OutPt1; + outRec1->BottomPt = 0; outRec2 = CreateOutRec(); - m_PolyOuts.push_back(outRec2); - outRec2->idx = (int)m_PolyOuts.size()-1; - j->poly2Idx = outRec2->idx; - outRec2->pts = GetBottomPt(p2); - outRec2->bottomPt = outRec2->pts; - outRec2->bottomPt->idx = outRec2->idx; + outRec2->Pts = join->OutPt2; - if (Poly2ContainsPoly1(outRec2->pts, outRec1->pts, m_UseFullRange)) + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(*outRec2); + + //We now need to check every OutRec.FirstLeft pointer. If it points + //to OutRec1 it may need to point to OutRec2 instead ... + if (m_UsingPolyTree) + for (PolyOutList::size_type j = 0; j < m_PolyOuts.size() - 1; j++) + { + OutRec* oRec = m_PolyOuts[j]; + if (!oRec->Pts || ParseFirstLeft(oRec->FirstLeft) != outRec1 || + oRec->IsHole == outRec1->IsHole) continue; + if (Poly2ContainsPoly1(oRec->Pts, join->OutPt2)) + oRec->FirstLeft = outRec2; + } + + if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) { //outRec2 is contained by outRec1 ... - outRec2->isHole = !outRec1->isHole; + outRec2->IsHole = !outRec1->IsHole; outRec2->FirstLeft = outRec1; - FixupJoinRecs(j, p2, i+1); - //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); - FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation - FixupOutPolygon(*outRec2); // but AFTER calling FixupJoinRecs() - - - if ((outRec2->isHole ^ m_ReverseOutput) == (Area(*outRec2, m_UseFullRange) > 0)) - ReversePolyPtLinks(outRec2->pts); + if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) + ReversePolyPtLinks(outRec2->Pts); - } else if (Poly2ContainsPoly1(outRec1->pts, outRec2->pts, m_UseFullRange)) + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) { //outRec1 is contained by outRec2 ... - outRec2->isHole = outRec1->isHole; - outRec1->isHole = !outRec2->isHole; + outRec2->IsHole = outRec1->IsHole; + outRec1->IsHole = !outRec2->IsHole; outRec2->FirstLeft = outRec1->FirstLeft; outRec1->FirstLeft = outRec2; - FixupJoinRecs(j, p2, i+1); - //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); - FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation - FixupOutPolygon(*outRec2); // but AFTER calling FixupJoinRecs() - - if ((outRec1->isHole ^ m_ReverseOutput) == (Area(*outRec1, m_UseFullRange) > 0)) - ReversePolyPtLinks(outRec1->pts); + if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) + ReversePolyPtLinks(outRec1->Pts); } else { //the 2 polygons are completely separate ... - outRec2->isHole = outRec1->isHole; + outRec2->IsHole = outRec1->IsHole; outRec2->FirstLeft = outRec1->FirstLeft; - FixupJoinRecs(j, p2, i+1); - //fixup FirstLeft pointers that may need reassigning to OutRec2 if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); - - FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation - FixupOutPolygon(*outRec2); // but AFTER calling FixupJoinRecs() } } else { //joined 2 polygons together ... - //cleanup redundant edges ... - FixupOutPolygon(*outRec1); + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->Idx = outRec1->Idx; - //delete the obsolete pointer ... - int OKIdx = outRec1->idx; - int ObsoleteIdx = outRec2->idx; - outRec2->pts = 0; - outRec2->bottomPt = 0; - - outRec1->isHole = holeStateRec->isHole; + outRec1->IsHole = holeStateRec->IsHole; if (holeStateRec == outRec2) outRec1->FirstLeft = outRec2->FirstLeft; outRec2->FirstLeft = outRec1; - //now fixup any subsequent Joins that match this polygon - for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) - { - JoinRec* j2 = m_Joins[k]; - if (j2->poly1Idx == ObsoleteIdx) j2->poly1Idx = OKIdx; - if (j2->poly2Idx == ObsoleteIdx) j2->poly2Idx = OKIdx; - } - //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); } } } -//------------------------------------------------------------------------------ - -void ReversePolygon(Polygon& p) -{ - std::reverse(p.begin(), p.end()); -} -//------------------------------------------------------------------------------ - -void ReversePolygons(Polygons& p) -{ - for (Polygons::size_type i = 0; i < p.size(); ++i) - ReversePolygon(p[i]); -} //------------------------------------------------------------------------------ -// OffsetPolygon functions ... -//------------------------------------------------------------------------------ - -struct DoublePoint -{ - double X; - double Y; - DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} -}; -//------------------------------------------------------------------------------ - -Polygon BuildArc(const IntPoint &pt, - const double a1, const double a2, const double r, double limit) -{ - //see notes in clipper.pas regarding steps - double arcFrac = std::fabs(a2 - a1) / (2 * pi); - int steps = (int)(arcFrac * pi / std::acos(1 - limit / std::fabs(r))); - if (steps < 2) steps = 2; - else if (steps > (int)(222.0 * arcFrac)) steps = (int)(222.0 * arcFrac); - - double x = std::cos(a1); - double y = std::sin(a1); - double c = std::cos((a2 - a1) / steps); - double s = std::sin((a2 - a1) / steps); - Polygon result(steps +1); - for (int i = 0; i <= steps; ++i) - { - result[i].X = pt.X + Round(x * r); - result[i].Y = pt.Y + Round(y * r); - double x2 = x; - x = x * c - s * y; //cross product - y = x2 * s + y * c; //dot product - } - return result; -} +// ClipperOffset support functions ... //------------------------------------------------------------------------------ DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) @@ -3101,414 +3749,797 @@ DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) if(pt2.X == pt1.X && pt2.Y == pt1.Y) return DoublePoint(0, 0); - double dx = (double)(pt2.X - pt1.X); + double Dx = (double)(pt2.X - pt1.X); double dy = (double)(pt2.Y - pt1.Y); - double f = 1 *1.0/ std::sqrt( dx*dx + dy*dy ); - dx *= f; + double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); + Dx *= f; dy *= f; - return DoublePoint(dy, -dx); + return DoublePoint(dy, -Dx); } //------------------------------------------------------------------------------ +// ClipperOffset class //------------------------------------------------------------------------------ -class PolyOffsetBuilder +ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) { -private: - Polygons m_p; - Polygon* m_curr_poly; - std::vector normals; - double m_delta, m_RMin, m_R; - size_t m_i, m_j, m_k; - static const int buffLength = 128; - JoinType m_jointype; - -public: + this->MiterLimit = miterLimit; + this->ArcTolerance = arcTolerance; + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ -PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, - double delta, JoinType jointype, double limit, bool autoFix) +ClipperOffset::~ClipperOffset() { - //nb precondition - out_polys != ptsin_polys - if (NEAR_ZERO(delta)) + Clear(); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Clear() +{ + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + delete m_polyNodes.Childs[i]; + m_polyNodes.Childs.clear(); + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) +{ + int highI = (int)path.size() - 1; + if (highI < 0) return; + PolyNode* newNode = new PolyNode(); + newNode->m_jointype = joinType; + newNode->m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + if (endType == etClosedLine || endType == etClosedPolygon) + while (highI > 0 && path[0] == path[highI]) highI--; + newNode->Contour.reserve(highI + 1); + newNode->Contour.push_back(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) + if (newNode->Contour[j] != path[i]) { - out_polys = in_polys; - return; + j++; + newNode->Contour.push_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; } + if ((endType == etClosedPolygon && j < 2) || + (endType != etClosedPolygon && j < 0)) + { + delete newNode; + return; + } + m_polyNodes.AddChild(*newNode); - this->m_p = in_polys; - this->m_delta = delta; - this->m_jointype = jointype; + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = IntPoint(0, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; + if (newNode->Contour[k].Y > ip.Y || + (newNode->Contour[k].Y == ip.Y && + newNode->Contour[k].X < ip.X)) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + } +} +//------------------------------------------------------------------------------ - //ChecksInput - fixes polygon orientation if necessary and removes - //duplicate vertices. Can be set false when you're sure that polygon - //orientation is correct and that there are no duplicate vertices. - if (autoFix) +void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) +{ + for (Paths::size_type i = 0; i < paths.size(); ++i) + AddPath(paths[i], joinType, endType); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::FixOrientations() +{ + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) { - size_t Len = m_p.size(), botI = 0; - while (botI < Len && m_p[botI].size() == 0) botI++; - if (botI == Len) return; - - //botPt: used to find the lowermost (in inverted Y-axis) & leftmost point - //This point (on m_p[botI]) must be on an outer polygon ring and if - //its orientation is false (counterclockwise) then assume all polygons - //need reversing ... - IntPoint botPt = m_p[botI][0]; - for (size_t i = botI; i < Len; ++i) + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon || + (node.m_endtype == etClosedLine && Orientation(node.Contour))) + ReversePath(node.Contour); + } + } else + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) + ReversePath(node.Contour); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(Paths& solution, double delta) +{ + solution.clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + if (solution.size() > 0) solution.erase(solution.begin()); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(PolyTree& solution, double delta) +{ + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) + { + PolyNode* outerNode = solution.Childs[0]; + solution.Childs.reserve(outerNode->ChildCount()); + solution.Childs[0] = outerNode->Childs[0]; + for (int i = 1; i < outerNode->ChildCount(); ++i) + solution.AddChild(*outerNode->Childs[i]); + } + else + solution.Clear(); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoOffset(double delta) +{ + m_destPolys.clear(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (NEAR_ZERO(delta)) + { + m_destPolys.reserve(m_polyNodes.ChildCount()); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon) + m_destPolys.push_back(node.Contour); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) y = def_arc_tolerance; + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + y = std::fabs(delta) * def_arc_tolerance; + else y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = pi / std::acos(1 - y / std::fabs(delta)); + if (steps > std::fabs(delta) * pi) + steps = std::fabs(delta) * pi; //ie excessive precision check + m_sin = std::sin(two_pi / steps); + m_cos = std::cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.reserve(m_polyNodes.ChildCount() * 2); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + m_srcPoly = node.Contour; + + int len = (int)m_srcPoly.size(); + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) + continue; + + m_destPoly.clear(); + if (len == 1) + { + if (node.m_jointype == jtRound) { - if (m_p[i].size() < 3) continue; - if (UpdateBotPt(m_p[i][0], botPt)) botI = i; - Polygon::iterator it = m_p[i].begin() +1; - while (it != m_p[i].end()) + double X = 1.0, Y = 0.0; + for (cInt j = 1; j <= steps; j++) { - if (PointsEqual(*it, *(it -1))) - it = m_p[i].erase(it); - else - { - if (UpdateBotPt(*it, botPt)) botI = i; - ++it; - } + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; } } - if (!Orientation(m_p[botI])) - ReversePolygons(m_p); + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.push_back(m_destPoly); + continue; } + //build m_normals ... + m_normals.clear(); + m_normals.reserve(len); + for (int j = 0; j < len - 1; ++j) + m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) + m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.push_back(DoublePoint(m_normals[len - 2])); + if (node.m_endtype == etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else if (node.m_endtype == etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + m_destPoly.clear(); + //re-build m_normals ... + DoublePoint n = m_normals[len -1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == etOpenButt) + { + int j = len - 1; + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); + + k = len - 1; + for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); + + if (node.m_endtype == etOpenButt) + { + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + k = 1; + m_sinA = 0; + if (node.m_endtype == etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.push_back(m_destPoly); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) +{ + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + if (m_sinA < 0.00005 && m_sinA > -0.00005) return; + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.push_back(m_srcPoly[j]); + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + else switch (jointype) { - case jtRound: - if (limit <= 0) limit = 0.25; - else if (limit > std::fabs(delta)) limit = std::fabs(delta); - break; - case jtMiter: - if (limit < 2) limit = 2; - break; - default: //unused - limit = 1; - } - m_RMin = 2.0/(limit*limit); - - double deltaSq = delta*delta; - out_polys.clear(); - out_polys.resize(m_p.size()); - for (m_i = 0; m_i < m_p.size(); m_i++) - { - m_curr_poly = &out_polys[m_i]; - size_t len = m_p[m_i].size(); - if (len > 1 && m_p[m_i][0].X == m_p[m_i][len - 1].X && - m_p[m_i][0].Y == m_p[m_i][len-1].Y) len--; - - //when 'shrinking' polygons - to minimize artefacts - //strip those polygons that have an area < pi * delta^2 ... - double a1 = Area(m_p[m_i]); - if (delta < 0) { if (a1 > 0 && a1 < deltaSq *pi) len = 0; } - else if (a1 < 0 && -a1 < deltaSq *pi) len = 0; //holes have neg. area - - if (len == 0 || (len < 3 && delta <= 0)) - continue; - else if (len == 1) + case jtMiter: { - Polygon arc; - arc = BuildArc(m_p[m_i][len-1], 0, 2 * pi, delta, limit); - out_polys[m_i] = arc; - continue; - } - - //build normals ... - normals.clear(); - normals.resize(len); - normals[len-1] = GetUnitNormal(m_p[m_i][len-1], m_p[m_i][0]); - for (m_j = 0; m_j < len -1; ++m_j) - normals[m_j] = GetUnitNormal(m_p[m_i][m_j], m_p[m_i][m_j+1]); - - m_k = len -1; - for (m_j = 0; m_j < len; ++m_j) - { - switch (jointype) - { - case jtMiter: - { - m_R = 1 + (normals[m_j].X*normals[m_k].X + - normals[m_j].Y*normals[m_k].Y); - if (m_R >= m_RMin) DoMiter(); else DoSquare(limit); - break; - } - case jtSquare: DoSquare(1.0); break; - case jtRound: DoRound(limit); break; - } - m_k = m_j; + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; } + case jtSquare: DoSquare(j, k); break; + case jtRound: DoRound(j, k); break; } - - //finally, clean up untidy corners using Clipper ... - Clipper clpr; - clpr.AddPolygons(out_polys, ptSubject); - if (delta > 0) - { - if (!clpr.Execute(ctUnion, out_polys, pftPositive, pftPositive)) - out_polys.clear(); - } - else - { - IntRect r = clpr.GetBounds(); - Polygon outer(4); - outer[0] = IntPoint(r.left - 10, r.bottom + 10); - outer[1] = IntPoint(r.right + 10, r.bottom + 10); - outer[2] = IntPoint(r.right + 10, r.top - 10); - outer[3] = IntPoint(r.left - 10, r.top - 10); - - clpr.AddPolygon(outer, ptSubject); - if (clpr.Execute(ctUnion, out_polys, pftNegative, pftNegative)) - { - out_polys.erase(out_polys.begin()); - ReversePolygons(out_polys); - - } else - out_polys.clear(); - } + k = j; } //------------------------------------------------------------------------------ -private: - -void AddPoint(const IntPoint& pt) +void ClipperOffset::DoSquare(int j, int k) { - if (m_curr_poly->size() == m_curr_poly->capacity()) - m_curr_poly->reserve(m_curr_poly->capacity() + buffLength); - m_curr_poly->push_back(pt); + double dx = std::tan(std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); } //------------------------------------------------------------------------------ -void DoSquare(double mul) +void ClipperOffset::DoMiter(int j, int k, double r) { - IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), - (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); - IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), - (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); - if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) - { - double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); - double a2 = std::atan2(-normals[m_j].Y, -normals[m_j].X); - a1 = std::fabs(a2 - a1); - if (a1 > pi) a1 = pi * 2 - a1; - double dx = std::tan((pi - a1) / 4) * std::fabs(m_delta * mul); - pt1 = IntPoint((long64)(pt1.X -normals[m_k].Y * dx), - (long64)(pt1.Y + normals[m_k].X * dx)); - AddPoint(pt1); - pt2 = IntPoint((long64)(pt2.X + normals[m_j].Y * dx), - (long64)(pt2.Y -normals[m_j].X * dx)); - AddPoint(pt2); - } - else - { - AddPoint(pt1); - AddPoint(m_p[m_i][m_j]); - AddPoint(pt2); - } + double q = m_delta / r; + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); } //------------------------------------------------------------------------------ -void DoMiter() +void ClipperOffset::DoRound(int j, int k) { - if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) - { - double q = m_delta / m_R; - AddPoint(IntPoint((long64)Round(m_p[m_i][m_j].X + - (normals[m_k].X + normals[m_j].X) * q), - (long64)Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); - } - else - { - IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * - m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); - IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * - m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); - AddPoint(pt1); - AddPoint(m_p[m_i][m_j]); - AddPoint(pt2); - } -} -//------------------------------------------------------------------------------ + double a = std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = (int)Round(m_StepsPerRad * std::fabs(a)); -void DoRound(double limit) -{ - IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), - (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); - IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), - (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); - AddPoint(pt1); - //round off reflex angles (ie > 180 deg) unless almost flat (ie < ~10deg). - if ((normals[m_k].X*normals[m_j].Y - normals[m_j].X*normals[m_k].Y) * m_delta >= 0) - { - if (normals[m_j].X * normals[m_k].X + normals[m_j].Y * normals[m_k].Y < 0.985) - { - double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); - double a2 = std::atan2(normals[m_j].Y, normals[m_j].X); - if (m_delta > 0 && a2 < a1) a2 += pi *2; - else if (m_delta < 0 && a2 > a1) a2 -= pi *2; - Polygon arc = BuildArc(m_p[m_i][m_j], a1, a2, m_delta, limit); - for (Polygon::size_type m = 0; m < arc.size(); m++) - AddPoint(arc[m]); - } - } - else - AddPoint(m_p[m_i][m_j]); - AddPoint(pt2); -} -//-------------------------------------------------------------------------- - -bool UpdateBotPt(const IntPoint &pt, IntPoint &botPt) -{ - if (pt.Y > botPt.Y || (pt.Y == botPt.Y && pt.X < botPt.X)) - { - botPt = pt; - return true; - } - else return false; -} -//-------------------------------------------------------------------------- - -}; //end PolyOffsetBuilder - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, - double delta, JoinType jointype, double limit, bool autoFix) -{ - if (&out_polys == &in_polys) + double X = m_normals[k].X, Y = m_normals[k].Y, X2; + for (int i = 0; i < steps; ++i) { - Polygons poly2(in_polys); - PolyOffsetBuilder(poly2, out_polys, delta, jointype, limit, autoFix); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); +} + +//------------------------------------------------------------------------------ +// Miscellaneous public functions +//------------------------------------------------------------------------------ + +void Clipper::DoSimplePolygons() +{ + PolyOutList::size_type i = 0; + while (i < m_PolyOuts.size()) + { + OutRec* outrec = m_PolyOuts[i++]; + OutPt* op = outrec->Pts; + if (!op) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt* op2 = op->Next; + while (op2 != outrec->Pts) + { + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + { + //split the polygon into two ... + OutPt* op3 = op->Prev; + OutPt* op4 = op2->Prev; + op->Prev = op4; + op4->Next = op; + op2->Prev = op3; + op3->Next = op2; + + outrec->Pts = op; + OutRec* outrec2 = CreateOutRec(); + outrec2->Pts = op2; + UpdateOutPtIdxs(*outrec2); + if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2->IsHole = !outrec->IsHole; + outrec2->FirstLeft = outrec; + } + else + if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2->IsHole = outrec->IsHole; + outrec->IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + outrec->FirstLeft = outrec2; + } else + { + //the 2 polygons are separate ... + outrec2->IsHole = outrec->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + } + op2 = op; //ie get ready for the Next iteration + } + op2 = op2->Next; + } + op = op->Next; + } + while (op != outrec->Pts); } - else PolyOffsetBuilder(in_polys, out_polys, delta, jointype, limit, autoFix); } //------------------------------------------------------------------------------ -void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType) +void ReversePath(Path& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePaths(Paths& p) +{ + for (Paths::size_type i = 0; i < p.size(); ++i) + ReversePath(p[i]); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) { Clipper c; - c.AddPolygon(in_poly, ptSubject); + c.StrictlySimple(true); + c.AddPaths(in_polys, ptSubject, true); c.Execute(ctUnion, out_polys, fillType, fillType); } //------------------------------------------------------------------------------ -void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType) -{ - Clipper c; - c.AddPolygons(in_polys, ptSubject); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(Polygons &polys, PolyFillType fillType) +void SimplifyPolygons(Paths &polys, PolyFillType fillType) { SimplifyPolygons(polys, polys, fillType); } //------------------------------------------------------------------------------ -bool PointsAreClose(IntPoint pt1, IntPoint pt2, long64 distSqrd) +inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) { - long64 dx = pt1.X - pt2.X; - long64 dy = pt1.Y - pt2.Y; - return ((dx * dx) + (dy * dy) <= distSqrd); + double Dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (Dx*Dx + dy*dy); } //------------------------------------------------------------------------------ -void CleanPolygon(Polygon& in_poly, Polygon& out_poly, double distance) +double DistanceFromLineSqrd( + const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) +{ + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x¹,y¹) & (x²,y²) is ... + //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 + //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = double(ln1.Y - ln2.Y); + double B = double(ln2.X - ln1.X); + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); +} +//--------------------------------------------------------------------------- + +bool SlopesNearCollinear(const IntPoint& pt1, + const IntPoint& pt2, const IntPoint& pt3, double distSqrd) +{ + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; +} +//------------------------------------------------------------------------------ + +bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) +{ + double Dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((Dx * Dx) + (dy * dy) <= distSqrd); +} +//------------------------------------------------------------------------------ + +OutPt* ExcludeOp(OutPt* op) +{ + OutPt* result = op->Prev; + result->Next = op->Next; + op->Next->Prev = result; + result->Idx = 0; + return result; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) { //distance = proximity in units/pixels below which vertices //will be stripped. Default ~= sqrt(2). - int highI = in_poly.size() -1; - long64 d = (int)(distance * distance); - while (highI > 0 && PointsAreClose(in_poly[highI], in_poly[0], d)) highI--; - if (highI < 2) + + size_t size = in_poly.size(); + + if (size == 0) { out_poly.clear(); return; } - out_poly.resize(highI + 1); - bool UseFullRange = FullRangeNeeded(in_poly); - IntPoint pt = in_poly[highI]; - int i = 0; - int k = 0; - for (;;) + + OutPt* outPts = new OutPt[size]; + for (size_t i = 0; i < size; ++i) { - if (i >= highI) break; - int j = i + 1; - - if (PointsAreClose(pt, in_poly[j], d)) - { - i = j + 1; - while (i <= highI && PointsAreClose(pt, in_poly[i], d)) i++; - continue; - } - - if (PointsAreClose(in_poly[i], in_poly[j], d) || - SlopesEqual(pt, in_poly[i], in_poly[j], UseFullRange)) - { - i = j; - continue; - } - - pt = in_poly[i++]; - out_poly[k++] = pt; + outPts[i].Pt = in_poly[i]; + outPts[i].Next = &outPts[(i + 1) % size]; + outPts[i].Next->Prev = &outPts[i]; + outPts[i].Idx = 0; } - if (i <= highI) out_poly[k++] = in_poly[i]; - if (k > 2 && SlopesEqual(out_poly[k -2], out_poly[k -1], out_poly[0], UseFullRange)) - k--; - if (k < 3) out_poly.clear(); - else if (k <= highI) out_poly.resize(k); + double distSqrd = distance * distance; + OutPt* op = &outPts[0]; + while (op->Idx == 0 && op->Next != op->Prev) + { + if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) + { + ExcludeOp(op->Next); + op = ExcludeOp(op); + size -= 2; + } + else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else + { + op->Idx = 1; + op = op->Next; + } + } + + if (size < 3) size = 0; + out_poly.resize(size); + for (size_t i = 0; i < size; ++i) + { + out_poly[i] = op->Pt; + op = op->Next; + } + delete [] outPts; } //------------------------------------------------------------------------------ -void CleanPolygons(Polygons& in_polys, Polygons& out_polys, double distance) +void CleanPolygon(Path& poly, double distance) { - for (Polygons::size_type i = 0; i < in_polys.size(); ++i) + CleanPolygon(poly, poly, distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) +{ + for (Paths::size_type i = 0; i < in_polys.size(); ++i) CleanPolygon(in_polys[i], out_polys[i], distance); } //------------------------------------------------------------------------------ -void AddPolyNodeToPolygons(PolyNode& polynode, Polygons& polygons) +void CleanPolygons(Paths& polys, double distance) { - if (polynode.Contour.size() > 0) - polygons.push_back(polynode.Contour); + CleanPolygons(polys, polys, distance); +} +//------------------------------------------------------------------------------ + +void Minkowski(const Path& poly, const Path& path, + Paths& solution, bool isSum, bool isClosed) +{ + int delta = (isClosed ? 1 : 0); + size_t polyCnt = poly.size(); + size_t pathCnt = path.size(); + Paths pp; + pp.reserve(pathCnt); + if (isSum) + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); + pp.push_back(p); + } + else + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); + pp.push_back(p); + } + + Paths quads; + quads.reserve((pathCnt + delta) * (polyCnt + 1)); + for (size_t i = 0; i <= pathCnt - 2 + delta; ++i) + for (size_t j = 0; j <= polyCnt - 1; ++j) + { + Path quad; + quad.reserve(4); + quad.push_back(pp[i % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) ReversePath(quad); + quads.push_back(quad); + } + + Clipper c; + c.AddPaths(quads, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& poly, const Path& path, Paths& solution, bool isClosed) +{ + Minkowski(poly, path, solution, true, isClosed); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly, const Path& path, Paths& solution, bool isClosed) +{ + Minkowski(poly, path, solution, false, isClosed); +} +//------------------------------------------------------------------------------ + +enum NodeType {ntAny, ntOpen, ntClosed}; + +void AddPolyNodeToPolygons(const PolyNode& polynode, NodeType nodetype, Paths& paths) +{ + bool match = true; + if (nodetype == ntClosed) match = !polynode.IsOpen(); + else if (nodetype == ntOpen) return; + + if (!polynode.Contour.empty() && match) + paths.push_back(polynode.Contour); for (int i = 0; i < polynode.ChildCount(); ++i) - AddPolyNodeToPolygons(*polynode.Childs[i], polygons); + AddPolyNodeToPolygons(*polynode.Childs[i], nodetype, paths); } //------------------------------------------------------------------------------ -void PolyTreeToPolygons(PolyTree& polytree, Polygons& polygons) +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) { - polygons.resize(0); - polygons.reserve(polytree.Total()); - AddPolyNodeToPolygons(polytree, polygons); + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPolygons(polytree, ntAny, paths); } //------------------------------------------------------------------------------ -std::ostream& operator <<(std::ostream &s, IntPoint& p) +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) { - s << p.X << ' ' << p.Y << "\n"; + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPolygons(polytree, ntClosed, paths); +} +//------------------------------------------------------------------------------ + +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + //Open paths are top level only, so ... + for (int i = 0; i < polytree.ChildCount(); ++i) + if (polytree.Childs[i]->IsOpen()) + paths.push_back(polytree.Childs[i]->Contour); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const IntPoint &p) +{ + s << "(" << p.X << "," << p.Y << ")"; return s; } //------------------------------------------------------------------------------ -std::ostream& operator <<(std::ostream &s, Polygon &p) +std::ostream& operator <<(std::ostream &s, const Path &p) { - for (Polygon::size_type i = 0; i < p.size(); i++) + if (p.empty()) return s; + Path::size_type last = p.size() -1; + for (Path::size_type i = 0; i < last; i++) + s << "(" << p[i].X << "," << p[i].Y << "), "; + s << "(" << p[last].X << "," << p[last].Y << ")\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Paths &p) +{ + for (Paths::size_type i = 0; i < p.size(); i++) s << p[i]; s << "\n"; return s; } //------------------------------------------------------------------------------ -std::ostream& operator <<(std::ostream &s, Polygons &p) +#ifdef use_deprecated + +void OffsetPaths(const Paths &in_polys, Paths &out_polys, + double delta, JoinType jointype, EndType_ endtype, double limit) { - for (Polygons::size_type i = 0; i < p.size(); i++) - s << p[i]; - s << "\n"; - return s; + ClipperOffset co(limit, limit); + co.AddPaths(in_polys, jointype, (EndType)endtype); + co.Execute(out_polys, delta); } //------------------------------------------------------------------------------ +#endif + + } //ClipperLib namespace diff --git a/polygon/clipper.hpp b/polygon/clipper.hpp index f558fe662b..cacdb8b8f1 100644 --- a/polygon/clipper.hpp +++ b/polygon/clipper.hpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 5.1.4 * -* Date : 24 March 2013 * +* Version : 6.1.2 * +* Date : 15 December 2013 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2013 * * * @@ -34,11 +34,29 @@ #ifndef clipper_hpp #define clipper_hpp +#define CLIPPER_VERSION "6.1.2" + +//use_int32: When enabled 32bit ints are used instead of 64bit ints. This +//improve performance but coordinate values are limited to the range +/- 46340 +//#define use_int32 + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. +//#define use_xyz + +//use_lines: Enables line clipping. Adds a very minor cost to performance. +//#define use_lines + +//use_deprecated: Enables support for the obsolete OffsetPaths() function +//which has been replace with the ClipperOffset class. +#define use_deprecated + #include +#include #include #include #include #include +#include namespace ClipperLib { @@ -50,23 +68,64 @@ enum PolyType { ptSubject, ptClip }; //see http://glprogramming.com/red/chapter11.html enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; -typedef signed long long long64; -typedef unsigned long long ulong64; +#ifdef use_int32 +typedef int cInt; +typedef unsigned int cUInt; +#else +typedef signed long long cInt; +typedef unsigned long long cUInt; +#endif struct IntPoint { -public: - long64 X; - long64 Y; - IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {}; - friend std::ostream& operator <<(std::ostream &s, IntPoint &p); + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; +#else + IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; +#endif + + friend inline bool operator== (const IntPoint& a, const IntPoint& b) + { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!= (const IntPoint& a, const IntPoint& b) + { + return a.X != b.X || a.Y != b.Y; + } }; +//------------------------------------------------------------------------------ -typedef std::vector< IntPoint > Polygon; -typedef std::vector< Polygon > Polygons; +typedef std::vector< IntPoint > Path; +typedef std::vector< Path > Paths; +inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} +inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} -std::ostream& operator <<(std::ostream &s, Polygon &p); -std::ostream& operator <<(std::ostream &s, Polygons &p); +std::ostream& operator <<(std::ostream &s, const IntPoint &p); +std::ostream& operator <<(std::ostream &s, const Path &p); +std::ostream& operator <<(std::ostream &s, const Paths &p); + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt); +#endif + +enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; +enum JoinType {jtSquare, jtRound, jtMiter}; +enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; +#ifdef use_deprecated + enum EndType_ {etClosed, etButt = 2, etSquare, etRound}; +#endif class PolyNode; typedef std::vector< PolyNode* > PolyNodes; @@ -75,17 +134,22 @@ class PolyNode { public: PolyNode(); - Polygon Contour; + Path Contour; PolyNodes Childs; PolyNode* Parent; PolyNode* GetNext() const; bool IsHole() const; + bool IsOpen() const; int ChildCount() const; private: - PolyNode* GetNextSiblingUp() const; unsigned Index; //node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode* GetNextSiblingUp() const; void AddChild(PolyNode& child); friend class Clipper; //to access Index + friend class ClipperOffset; }; class PolyTree: public PolyNode @@ -99,113 +163,55 @@ private: PolyNodes AllNodes; friend class Clipper; //to access AllNodes }; - -enum JoinType { jtSquare, jtRound, jtMiter }; -bool Orientation(const Polygon &poly); -double Area(const Polygon &poly); +bool Orientation(const Path &poly); +double Area(const Path &poly); -void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, - double delta, JoinType jointype = jtSquare, double limit = 0, bool autoFix = true); +#ifdef use_deprecated + void OffsetPaths(const Paths &in_polys, Paths &out_polys, + double delta, JoinType jointype, EndType_ endtype, double limit = 0); +#endif -void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(Polygons &polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); -void CleanPolygon(Polygon& in_poly, Polygon& out_poly, double distance = 1.415); -void CleanPolygons(Polygons& in_polys, Polygons& out_polys, double distance = 1.415); +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); +void CleanPolygon(Path& poly, double distance = 1.415); +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); +void CleanPolygons(Paths& polys, double distance = 1.415); -void PolyTreeToPolygons(PolyTree& polytree, Polygons& polygons); +void MinkowskiSum(const Path& poly, const Path& path, Paths& solution, bool isClosed); +void MinkowskiDiff(const Path& poly, const Path& path, Paths& solution, bool isClosed); -void ReversePolygon(Polygon& p); -void ReversePolygons(Polygons& p); +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); -//used internally ... +void ReversePath(Path& p); +void ReversePaths(Paths& p); + +struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; + +//enums that are used internally ... enum EdgeSide { esLeft = 1, esRight = 2}; -enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 }; -struct TEdge { - long64 xbot; - long64 ybot; - long64 xcurr; - long64 ycurr; - long64 xtop; - long64 ytop; - double dx; - long64 deltaX; - long64 deltaY; - PolyType polyType; - EdgeSide side; - int windDelta; //1 or -1 depending on winding direction - int windCnt; - int windCnt2; //winding count of the opposite polytype - int outIdx; - TEdge *next; - TEdge *prev; - TEdge *nextInLML; - TEdge *nextInAEL; - TEdge *prevInAEL; - TEdge *nextInSEL; - TEdge *prevInSEL; -}; - -struct IntersectNode { - TEdge *edge1; - TEdge *edge2; - IntPoint pt; - IntersectNode *next; -}; - -struct LocalMinima { - long64 Y; - TEdge *leftBound; - TEdge *rightBound; - LocalMinima *next; -}; - -struct Scanbeam { - long64 Y; - Scanbeam *next; -}; - -struct OutPt; //forward declaration - -struct OutRec { - int idx; - bool isHole; - OutRec *FirstLeft; //see comments in clipper.pas - PolyNode *polyNode; - OutPt *pts; - OutPt *bottomPt; -}; - -struct OutPt { - int idx; - IntPoint pt; - OutPt *next; - OutPt *prev; -}; - -struct JoinRec { - IntPoint pt1a; - IntPoint pt1b; - int poly1Idx; - IntPoint pt2a; - IntPoint pt2b; - int poly2Idx; -}; - -struct HorzJoinRec { - TEdge *edge; - int savedIdx; -}; - -struct IntRect { long64 left; long64 top; long64 right; long64 bottom; }; +//forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinima; +struct Scanbeam; +struct OutPt; +struct OutRec; +struct Join; typedef std::vector < OutRec* > PolyOutList; typedef std::vector < TEdge* > EdgeList; -typedef std::vector < JoinRec* > JoinList; -typedef std::vector < HorzJoinRec* > HorzJoinList; +typedef std::vector < Join* > JoinList; +typedef std::vector < IntersectNode* > IntersectList; + + +//------------------------------------------------------------------------------ //ClipperBase is the ancestor to the Clipper class. It should not be //instantiated directly. This class simply abstracts the conversion of sets of @@ -215,29 +221,38 @@ class ClipperBase public: ClipperBase(); virtual ~ClipperBase(); - bool AddPolygon(const Polygon &pg, PolyType polyType); - bool AddPolygons( const Polygons &ppg, PolyType polyType); + bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); virtual void Clear(); IntRect GetBounds(); + bool PreserveCollinear() {return m_PreserveCollinear;}; + void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; protected: void DisposeLocalMinimaList(); - TEdge* AddBoundsToLML(TEdge *e); + TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); void PopLocalMinima(); virtual void Reset(); + TEdge* ProcessBound(TEdge* E, bool IsClockwise); void InsertLocalMinima(LocalMinima *newLm); + void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed); + TEdge* DescendToMin(TEdge *&E); + void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); LocalMinima *m_CurrentLM; LocalMinima *m_MinimaList; bool m_UseFullRange; EdgeList m_edges; + bool m_PreserveCollinear; + bool m_HasOpenPaths; }; +//------------------------------------------------------------------------------ class Clipper : public virtual ClipperBase { public: - Clipper(); + Clipper(int initOptions = 0); ~Clipper(); bool Execute(ClipType clipType, - Polygons &solution, + Paths &solution, PolyFillType subjFillType = pftEvenOdd, PolyFillType clipFillType = pftEvenOdd); bool Execute(ClipType clipType, @@ -247,31 +262,40 @@ public: void Clear(); bool ReverseSolution() {return m_ReverseOutput;}; void ReverseSolution(bool value) {m_ReverseOutput = value;}; + bool StrictlySimple() {return m_StrictSimple;}; + void StrictlySimple(bool value) {m_StrictSimple = value;}; + //set the callback function for z value filling on intersections (otherwise Z is 0) +#ifdef use_xyz + void ZFillFunction(TZFillCallback zFillFunc); +#endif protected: void Reset(); virtual bool ExecuteInternal(); private: PolyOutList m_PolyOuts; JoinList m_Joins; - HorzJoinList m_HorizJoins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; ClipType m_ClipType; - Scanbeam *m_Scanbeam; + std::set< cInt, std::greater > m_Scanbeam; TEdge *m_ActiveEdges; TEdge *m_SortedEdges; - IntersectNode *m_IntersectNodes; bool m_ExecuteLocked; PolyFillType m_ClipFillType; PolyFillType m_SubjFillType; bool m_ReverseOutput; bool m_UsingPolyTree; - void DisposeScanbeamList(); + bool m_StrictSimple; +#ifdef use_xyz + TZFillCallback m_ZFill; //custom callback +#endif void SetWindingCount(TEdge& edge); bool IsEvenOddFillType(const TEdge& edge) const; bool IsEvenOddAltFillType(const TEdge& edge) const; - void InsertScanbeam(const long64 Y); - long64 PopScanbeam(); - void InsertLocalMinimaIntoAEL(const long64 botY); - void InsertEdgeIntoAEL(TEdge *edge); + void InsertScanbeam(const cInt Y); + cInt PopScanbeam(); + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); void AddEdgeToSEL(TEdge *edge); void CopyAELToSEL(); void DeleteFromSEL(TEdge *e); @@ -279,48 +303,79 @@ private: void UpdateEdgeIntoAEL(TEdge *&e); void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); bool IsContributing(const TEdge& edge) const; - bool IsTopHorz(const long64 XPos); + bool IsTopHorz(const cInt XPos); void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); - void DoMaxima(TEdge *e, long64 topY); - void ProcessHorizontals(); - void ProcessHorizontal(TEdge *horzEdge); + void DoMaxima(TEdge *e); + void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam); + void ProcessHorizontals(bool IsTopOfScanbeam); + void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); - void AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* GetOutRec(int idx); void AppendPolygon(TEdge *e1, TEdge *e2); - void DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt); - void DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt); - void DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt); void IntersectEdges(TEdge *e1, TEdge *e2, - const IntPoint &pt, const IntersectProtects protects); + const IntPoint &pt, bool protect = false); OutRec* CreateOutRec(); - void AddOutPt(TEdge *e, const IntPoint &pt); - void DisposeAllPolyPts(); + OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + void DisposeAllOutRecs(); void DisposeOutRec(PolyOutList::size_type index); - bool ProcessIntersections(const long64 botY, const long64 topY); - void AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt); - void BuildIntersectList(const long64 botY, const long64 topY); + bool ProcessIntersections(const cInt botY, const cInt topY); + void BuildIntersectList(const cInt botY, const cInt topY); void ProcessIntersectList(); - void ProcessEdgesAtTopOfScanbeam(const long64 topY); - void BuildResult(Polygons& polys); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths& polys); void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *OutRec); + void SetHoleState(TEdge *e, OutRec *outrec); void DisposeIntersectNodes(); bool FixupIntersectionOrder(); - void FixupOutPolygon(OutRec &outRec); + void FixupOutPolygon(OutRec &outrec); bool IsHole(TEdge *e); - void FixHoleLinkage(OutRec &outRec); - void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + void FixHoleLinkage(OutRec &outrec); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); void ClearJoins(); - void AddHorzJoin(TEdge *e, int idx); - void ClearHorzJoins(); - bool JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2); - void FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx); + void ClearGhostJoins(); + void AddGhostJoin(OutPt *op, const IntPoint offPt); + bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); void JoinCommonEdges(); + void DoSimplePolygons(); void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec); +#ifdef use_xyz + void SetZ(IntPoint& pt, TEdge& e); +#endif }; - //------------------------------------------------------------------------------ + +class ClipperOffset +{ +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ~ClipperOffset(); + void AddPath(const Path& path, JoinType joinType, EndType endType); + void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + void Execute(Paths& solution, double delta); + void Execute(PolyTree& solution, double delta); + void Clear(); + double MiterLimit; + double ArcTolerance; +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int& k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; //------------------------------------------------------------------------------ class clipperException : public std::exception diff --git a/polygon/poly2tri/common/shapes.cc b/polygon/poly2tri/common/shapes.cc index b8c4c2f9b0..b7a087f96e 100644 --- a/polygon/poly2tri/common/shapes.cc +++ b/polygon/poly2tri/common/shapes.cc @@ -32,63 +32,74 @@ #include namespace p2t { - -Triangle::Triangle(Point& a, Point& b, Point& c) +Triangle::Triangle( Point& a, Point& b, Point& c ) { - points_[0] = &a; points_[1] = &b; points_[2] = &c; - neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL; - constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false; - delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; - interior_ = false; + points_[0] = &a; points_[1] = &b; points_[2] = &c; + neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL; + constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false; + delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; + interior_ = false; } + // Update neighbor pointers -void Triangle::MarkNeighbor(Point* p1, Point* p2, Triangle* t) +void Triangle::MarkNeighbor( Point* p1, Point* p2, Triangle* t ) { - if ((p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2])) - neighbors_[0] = t; - else if ((p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0])) - neighbors_[1] = t; - else if ((p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0])) - neighbors_[2] = t; - else - assert(0); + if( (p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2]) ) + neighbors_[0] = t; + else if( (p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0]) ) + neighbors_[1] = t; + else if( (p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0]) ) + neighbors_[2] = t; + else + assert( 0 ); } + // Exhaustive search to update neighbor pointers -void Triangle::MarkNeighbor(Triangle& t) +void Triangle::MarkNeighbor( Triangle& t ) { - if (t.Contains(points_[1], points_[2])) { - neighbors_[0] = &t; - t.MarkNeighbor(points_[1], points_[2], this); - } else if (t.Contains(points_[0], points_[2])) { - neighbors_[1] = &t; - t.MarkNeighbor(points_[0], points_[2], this); - } else if (t.Contains(points_[0], points_[1])) { - neighbors_[2] = &t; - t.MarkNeighbor(points_[0], points_[1], this); - } + if( t.Contains( points_[1], points_[2] ) ) + { + neighbors_[0] = &t; + t.MarkNeighbor( points_[1], points_[2], this ); + } + else if( t.Contains( points_[0], points_[2] ) ) + { + neighbors_[1] = &t; + t.MarkNeighbor( points_[0], points_[2], this ); + } + else if( t.Contains( points_[0], points_[1] ) ) + { + neighbors_[2] = &t; + t.MarkNeighbor( points_[0], points_[1], this ); + } } + /** * Clears all references to all other triangles and points */ void Triangle::Clear() { - Triangle *t; - for( int i=0; i<3; i++ ) + Triangle* t; + + for( int i = 0; i<3; i++ ) { t = neighbors_[i]; + if( t != NULL ) { t->ClearNeighbor( this ); } } + ClearNeighbors(); - points_[0]=points_[1]=points_[2] = NULL; + points_[0] = points_[1] = points_[2] = NULL; } -void Triangle::ClearNeighbor(Triangle *triangle ) + +void Triangle::ClearNeighbor( Triangle* triangle ) { if( neighbors_[0] == triangle ) { @@ -104,263 +115,379 @@ void Triangle::ClearNeighbor(Triangle *triangle ) } } + void Triangle::ClearNeighbors() { - neighbors_[0] = NULL; - neighbors_[1] = NULL; - neighbors_[2] = NULL; + neighbors_[0] = NULL; + neighbors_[1] = NULL; + neighbors_[2] = NULL; } + void Triangle::ClearDelunayEdges() { - delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; + delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; } -Point* Triangle::OppositePoint(Triangle& t, Point& p) + +Point* Triangle::OppositePoint( Triangle& t, Point& p ) { - Point *cw = t.PointCW(p); - double x = cw->x; - double y = cw->y; - x = p.x; - y = p.y; - return PointCW(*cw); + Point* cw = t.PointCW( p ); + double x = cw->x; + double y = cw->y; + + x = p.x; + y = p.y; + return PointCW( *cw ); } + // Legalized triangle by rotating clockwise around point(0) -void Triangle::Legalize(Point& point) +void Triangle::Legalize( Point& point ) { - points_[1] = points_[0]; - points_[0] = points_[2]; - points_[2] = &point; + points_[1] = points_[0]; + points_[0] = points_[2]; + points_[2] = &point; } + // Legalize triagnle by rotating clockwise around oPoint -void Triangle::Legalize(Point& opoint, Point& npoint) +void Triangle::Legalize( Point& opoint, Point& npoint ) { - if (&opoint == points_[0]) { - points_[1] = points_[0]; - points_[0] = points_[2]; - points_[2] = &npoint; - } else if (&opoint == points_[1]) { - points_[2] = points_[1]; - points_[1] = points_[0]; - points_[0] = &npoint; - } else if (&opoint == points_[2]) { - points_[0] = points_[2]; - points_[2] = points_[1]; - points_[1] = &npoint; - } else { - assert(0); - } -} - -int Triangle::Index(const Point* p) -{ - if (p == points_[0]) { - return 0; - } else if (p == points_[1]) { - return 1; - } else if (p == points_[2]) { - return 2; - } - assert(0); -} - -int Triangle::EdgeIndex(const Point* p1, const Point* p2) -{ - if (points_[0] == p1) { - if (points_[1] == p2) { - return 2; - } else if (points_[2] == p2) { - return 1; + if( &opoint == points_[0] ) + { + points_[1] = points_[0]; + points_[0] = points_[2]; + points_[2] = &npoint; } - } else if (points_[1] == p1) { - if (points_[2] == p2) { - return 0; - } else if (points_[0] == p2) { - return 2; + else if( &opoint == points_[1] ) + { + points_[2] = points_[1]; + points_[1] = points_[0]; + points_[0] = &npoint; } - } else if (points_[2] == p1) { - if (points_[0] == p2) { - return 1; - } else if (points_[1] == p2) { - return 0; + else if( &opoint == points_[2] ) + { + points_[0] = points_[2]; + points_[2] = points_[1]; + points_[1] = &npoint; + } + else + { + assert( 0 ); } - } - return -1; } -void Triangle::MarkConstrainedEdge(const int index) + +int Triangle::Index( const Point* p ) { - constrained_edge[index] = true; + if( p == points_[0] ) + { + return 0; + } + else if( p == points_[1] ) + { + return 1; + } + else if( p == points_[2] ) + { + return 2; + } + + assert( 0 ); } -void Triangle::MarkConstrainedEdge(Edge& edge) + +int Triangle::EdgeIndex( const Point* p1, const Point* p2 ) { - MarkConstrainedEdge(edge.p, edge.q); + if( points_[0] == p1 ) + { + if( points_[1] == p2 ) + { + return 2; + } + else if( points_[2] == p2 ) + { + return 1; + } + } + else if( points_[1] == p1 ) + { + if( points_[2] == p2 ) + { + return 0; + } + else if( points_[0] == p2 ) + { + return 2; + } + } + else if( points_[2] == p1 ) + { + if( points_[0] == p2 ) + { + return 1; + } + else if( points_[1] == p2 ) + { + return 0; + } + } + + return -1; } + +void Triangle::MarkConstrainedEdge( const int index ) +{ + constrained_edge[index] = true; +} + + +void Triangle::MarkConstrainedEdge( Edge& edge ) +{ + MarkConstrainedEdge( edge.p, edge.q ); +} + + // Mark edge as constrained -void Triangle::MarkConstrainedEdge(Point* p, Point* q) +void Triangle::MarkConstrainedEdge( Point* p, Point* q ) { - if ((q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0])) { - constrained_edge[2] = true; - } else if ((q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0])) { - constrained_edge[1] = true; - } else if ((q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1])) { - constrained_edge[0] = true; - } + if( (q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0]) ) + { + constrained_edge[2] = true; + } + else if( (q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0]) ) + { + constrained_edge[1] = true; + } + else if( (q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1]) ) + { + constrained_edge[0] = true; + } } -// The point counter-clockwise to given point -Point* Triangle::PointCW(Point& point) -{ - if (&point == points_[0]) { - return points_[2]; - } else if (&point == points_[1]) { - return points_[0]; - } else if (&point == points_[2]) { - return points_[1]; - } - assert(0); -} // The point counter-clockwise to given point -Point* Triangle::PointCCW(Point& point) +Point* Triangle::PointCW( Point& point ) { - if (&point == points_[0]) { - return points_[1]; - } else if (&point == points_[1]) { - return points_[2]; - } else if (&point == points_[2]) { - return points_[0]; - } - assert(0); + if( &point == points_[0] ) + { + return points_[2]; + } + else if( &point == points_[1] ) + { + return points_[0]; + } + else if( &point == points_[2] ) + { + return points_[1]; + } + + assert( 0 ); } + +// The point counter-clockwise to given point +Point* Triangle::PointCCW( Point& point ) +{ + if( &point == points_[0] ) + { + return points_[1]; + } + else if( &point == points_[1] ) + { + return points_[2]; + } + else if( &point == points_[2] ) + { + return points_[0]; + } + + assert( 0 ); +} + + // The neighbor clockwise to given point -Triangle* Triangle::NeighborCW(Point& point) +Triangle* Triangle::NeighborCW( Point& point ) { - if (&point == points_[0]) { - return neighbors_[1]; - } else if (&point == points_[1]) { - return neighbors_[2]; - } - return neighbors_[0]; + if( &point == points_[0] ) + { + return neighbors_[1]; + } + else if( &point == points_[1] ) + { + return neighbors_[2]; + } + + return neighbors_[0]; } + // The neighbor counter-clockwise to given point -Triangle* Triangle::NeighborCCW(Point& point) +Triangle* Triangle::NeighborCCW( Point& point ) { - if (&point == points_[0]) { - return neighbors_[2]; - } else if (&point == points_[1]) { - return neighbors_[0]; - } - return neighbors_[1]; + if( &point == points_[0] ) + { + return neighbors_[2]; + } + else if( &point == points_[1] ) + { + return neighbors_[0]; + } + + return neighbors_[1]; } -bool Triangle::GetConstrainedEdgeCCW(Point& p) -{ - if (&p == points_[0]) { - return constrained_edge[2]; - } else if (&p == points_[1]) { - return constrained_edge[0]; - } - return constrained_edge[1]; -} -bool Triangle::GetConstrainedEdgeCW(Point& p) +bool Triangle::GetConstrainedEdgeCCW( Point& p ) { - if (&p == points_[0]) { + if( &p == points_[0] ) + { + return constrained_edge[2]; + } + else if( &p == points_[1] ) + { + return constrained_edge[0]; + } + return constrained_edge[1]; - } else if (&p == points_[1]) { - return constrained_edge[2]; - } - return constrained_edge[0]; } -void Triangle::SetConstrainedEdgeCCW(Point& p, bool ce) + +bool Triangle::GetConstrainedEdgeCW( Point& p ) { - if (&p == points_[0]) { - constrained_edge[2] = ce; - } else if (&p == points_[1]) { - constrained_edge[0] = ce; - } else { - constrained_edge[1] = ce; - } + if( &p == points_[0] ) + { + return constrained_edge[1]; + } + else if( &p == points_[1] ) + { + return constrained_edge[2]; + } + + return constrained_edge[0]; } -void Triangle::SetConstrainedEdgeCW(Point& p, bool ce) + +void Triangle::SetConstrainedEdgeCCW( Point& p, bool ce ) { - if (&p == points_[0]) { - constrained_edge[1] = ce; - } else if (&p == points_[1]) { - constrained_edge[2] = ce; - } else { - constrained_edge[0] = ce; - } + if( &p == points_[0] ) + { + constrained_edge[2] = ce; + } + else if( &p == points_[1] ) + { + constrained_edge[0] = ce; + } + else + { + constrained_edge[1] = ce; + } } -bool Triangle::GetDelunayEdgeCCW(Point& p) + +void Triangle::SetConstrainedEdgeCW( Point& p, bool ce ) { - if (&p == points_[0]) { - return delaunay_edge[2]; - } else if (&p == points_[1]) { - return delaunay_edge[0]; - } - return delaunay_edge[1]; + if( &p == points_[0] ) + { + constrained_edge[1] = ce; + } + else if( &p == points_[1] ) + { + constrained_edge[2] = ce; + } + else + { + constrained_edge[0] = ce; + } } -bool Triangle::GetDelunayEdgeCW(Point& p) + +bool Triangle::GetDelunayEdgeCCW( Point& p ) { - if (&p == points_[0]) { + if( &p == points_[0] ) + { + return delaunay_edge[2]; + } + else if( &p == points_[1] ) + { + return delaunay_edge[0]; + } + return delaunay_edge[1]; - } else if (&p == points_[1]) { - return delaunay_edge[2]; - } - return delaunay_edge[0]; } -void Triangle::SetDelunayEdgeCCW(Point& p, bool e) + +bool Triangle::GetDelunayEdgeCW( Point& p ) { - if (&p == points_[0]) { - delaunay_edge[2] = e; - } else if (&p == points_[1]) { - delaunay_edge[0] = e; - } else { - delaunay_edge[1] = e; - } + if( &p == points_[0] ) + { + return delaunay_edge[1]; + } + else if( &p == points_[1] ) + { + return delaunay_edge[2]; + } + + return delaunay_edge[0]; } -void Triangle::SetDelunayEdgeCW(Point& p, bool e) + +void Triangle::SetDelunayEdgeCCW( Point& p, bool e ) { - if (&p == points_[0]) { - delaunay_edge[1] = e; - } else if (&p == points_[1]) { - delaunay_edge[2] = e; - } else { - delaunay_edge[0] = e; - } + if( &p == points_[0] ) + { + delaunay_edge[2] = e; + } + else if( &p == points_[1] ) + { + delaunay_edge[0] = e; + } + else + { + delaunay_edge[1] = e; + } } + +void Triangle::SetDelunayEdgeCW( Point& p, bool e ) +{ + if( &p == points_[0] ) + { + delaunay_edge[1] = e; + } + else if( &p == points_[1] ) + { + delaunay_edge[2] = e; + } + else + { + delaunay_edge[0] = e; + } +} + + // The neighbor across to given point -Triangle& Triangle::NeighborAcross(Point& opoint) +Triangle& Triangle::NeighborAcross( Point& opoint ) { - if (&opoint == points_[0]) { - return *neighbors_[0]; - } else if (&opoint == points_[1]) { - return *neighbors_[1]; - } - return *neighbors_[2]; + if( &opoint == points_[0] ) + { + return *neighbors_[0]; + } + else if( &opoint == points_[1] ) + { + return *neighbors_[1]; + } + + return *neighbors_[2]; } + void Triangle::DebugPrint() { - std::cout << points_[0]->x << "," << points_[0]->y << " "; - std::cout << points_[1]->x << "," << points_[1]->y << " "; - std::cout << points_[2]->x << "," << points_[2]->y << "\n"; + std::cout << points_[0]->x << "," << points_[0]->y << " "; + std::cout << points_[1]->x << "," << points_[1]->y << " "; + std::cout << points_[2]->x << "," << points_[2]->y << "\n"; } - } - diff --git a/polygon/poly2tri/common/shapes.h b/polygon/poly2tri/common/shapes.h index 8c19d91ef8..c65f485eaf 100644 --- a/polygon/poly2tri/common/shapes.h +++ b/polygon/poly2tri/common/shapes.h @@ -39,287 +39,313 @@ #include namespace p2t { - struct Edge; -struct Point { +struct Point +{ + double x, y; - double x, y; + /// Default constructor does nothing (for performance). + Point() + { + x = 0.0; + y = 0.0; + } - /// Default constructor does nothing (for performance). - Point() - { - x = 0.0; - y = 0.0; - } + /// The edges this point constitutes an upper ending point + std::vector edge_list; - /// The edges this point constitutes an upper ending point - std::vector edge_list; + /// Construct using coordinates. + Point( double x, double y ) : x( x ), y( y ) {} - /// Construct using coordinates. - Point(double x, double y) : x(x), y(y) {} + /// Set this point to all zeros. + void set_zero() + { + x = 0.0; + y = 0.0; + } - /// Set this point to all zeros. - void set_zero() - { - x = 0.0; - y = 0.0; - } + /// Set this point to some specified coordinates. + void set( double x_, double y_ ) + { + x = x_; + y = y_; + } - /// Set this point to some specified coordinates. - void set(double x_, double y_) - { - x = x_; - y = y_; - } + /// Negate this point. + Point operator -() const + { + Point v; - /// Negate this point. - Point operator -() const - { - Point v; - v.set(-x, -y); - return v; - } + v.set( -x, -y ); + return v; + } - /// Add a point to this point. - void operator +=(const Point& v) - { - x += v.x; - y += v.y; - } + /// Add a point to this point. + void operator +=( const Point& v ) + { + x += v.x; + y += v.y; + } - /// Subtract a point from this point. - void operator -=(const Point& v) - { - x -= v.x; - y -= v.y; - } + /// Subtract a point from this point. + void operator -=( const Point& v ) + { + x -= v.x; + y -= v.y; + } - /// Multiply this point by a scalar. - void operator *=(double a) - { - x *= a; - y *= a; - } + /// Multiply this point by a scalar. + void operator *=( double a ) + { + x *= a; + y *= a; + } - /// Get the length of this point (the norm). - double Length() const - { - return sqrt(x * x + y * y); - } + /// Get the length of this point (the norm). + double Length() const + { + return sqrt( x * x + y * y ); + } - /// Convert this point into a unit point. Returns the Length. - double Normalize() - { - double len = Length(); - x /= len; - y /= len; - return len; - } + /// Convert this point into a unit point. Returns the Length. + double Normalize() + { + double len = Length(); + x /= len; + y /= len; + return len; + } }; // Represents a simple polygon's edge -struct Edge { +struct Edge +{ + Point* p, * q; - Point* p, *q; + /// Constructor + Edge( Point& p1, Point& p2 ) : p( &p1 ), q( &p2 ) + { + if( p1.y > p2.y ) + { + q = &p1; + p = &p2; + } + else if( p1.y == p2.y ) + { + if( p1.x > p2.x ) + { + q = &p1; + p = &p2; + } + else if( p1.x == p2.x ) + { + // Repeat points + assert( false ); + } + } - /// Constructor - Edge(Point& p1, Point& p2) : p(&p1), q(&p2) - { - if (p1.y > p2.y) { - q = &p1; - p = &p2; - } else if (p1.y == p2.y) { - if (p1.x > p2.x) { - q = &p1; - p = &p2; - } else if (p1.x == p2.x) { - // Repeat points - assert(false); - } + q->edge_list.push_back( this ); } - - q->edge_list.push_back(this); - } }; // Triangle-based data structures are know to have better performance than quad-edge structures // See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator" -// "Triangulations in CGAL" -class Triangle { +// "Triangulations in CGAL" +class Triangle +{ public: /// Constructor -Triangle(Point& a, Point& b, Point& c); + Triangle( Point& a, Point& b, Point& c ); /// Flags to determine if an edge is a Constrained edge -bool constrained_edge[3]; + bool constrained_edge[3]; /// Flags to determine if an edge is a Delauney edge -bool delaunay_edge[3]; + bool delaunay_edge[3]; -Point* GetPoint(const int& index); -Point* PointCW(Point& point); -Point* PointCCW(Point& point); -Point* OppositePoint(Triangle& t, Point& p); + Point* GetPoint( const int& index ); + Point* PointCW( Point& point ); + Point* PointCCW( Point& point ); + Point* OppositePoint( Triangle& t, Point& p ); -Triangle* GetNeighbor(const int& index); -void MarkNeighbor(Point* p1, Point* p2, Triangle* t); -void MarkNeighbor(Triangle& t); + Triangle* GetNeighbor( const int& index ); + void MarkNeighbor( Point* p1, Point* p2, Triangle* t ); + void MarkNeighbor( Triangle& t ); -void MarkConstrainedEdge(const int index); -void MarkConstrainedEdge(Edge& edge); -void MarkConstrainedEdge(Point* p, Point* q); + void MarkConstrainedEdge( const int index ); + void MarkConstrainedEdge( Edge& edge ); + void MarkConstrainedEdge( Point* p, Point* q ); -int Index(const Point* p); -int EdgeIndex(const Point* p1, const Point* p2); + int Index( const Point* p ); + int EdgeIndex( const Point* p1, const Point* p2 ); -Triangle* NeighborCW(Point& point); -Triangle* NeighborCCW(Point& point); -bool GetConstrainedEdgeCCW(Point& p); -bool GetConstrainedEdgeCW(Point& p); -void SetConstrainedEdgeCCW(Point& p, bool ce); -void SetConstrainedEdgeCW(Point& p, bool ce); -bool GetDelunayEdgeCCW(Point& p); -bool GetDelunayEdgeCW(Point& p); -void SetDelunayEdgeCCW(Point& p, bool e); -void SetDelunayEdgeCW(Point& p, bool e); + Triangle* NeighborCW( Point& point ); + Triangle* NeighborCCW( Point& point ); + bool GetConstrainedEdgeCCW( Point& p ); + bool GetConstrainedEdgeCW( Point& p ); + void SetConstrainedEdgeCCW( Point& p, bool ce ); + void SetConstrainedEdgeCW( Point& p, bool ce ); + bool GetDelunayEdgeCCW( Point& p ); + bool GetDelunayEdgeCW( Point& p ); + void SetDelunayEdgeCCW( Point& p, bool e ); + void SetDelunayEdgeCW( Point& p, bool e ); + + bool Contains( Point* p ); + bool Contains( const Edge& e ); + bool Contains( Point* p, Point* q ); + void Legalize( Point& point ); + void Legalize( Point& opoint, Point& npoint ); -bool Contains(Point* p); -bool Contains(const Edge& e); -bool Contains(Point* p, Point* q); -void Legalize(Point& point); -void Legalize(Point& opoint, Point& npoint); /** * Clears all references to all other triangles and points */ -void Clear(); -void ClearNeighbor(Triangle *triangle ); -void ClearNeighbors(); -void ClearDelunayEdges(); + void Clear(); + void ClearNeighbor( Triangle* triangle ); + void ClearNeighbors(); + void ClearDelunayEdges(); -inline bool IsInterior(); -inline void IsInterior(bool b); + inline bool IsInterior(); + inline void IsInterior( bool b ); -Triangle& NeighborAcross(Point& opoint); + Triangle& NeighborAcross( Point& opoint ); -void DebugPrint(); + void DebugPrint(); private: /// Triangle points -Point* points_[3]; + Point* points_[3]; /// Neighbor list -Triangle* neighbors_[3]; + Triangle* neighbors_[3]; /// Has this triangle been marked as an interior triangle? -bool interior_; + bool interior_; }; -inline bool cmp(const Point* a, const Point* b) +inline bool cmp( const Point* a, const Point* b ) { - if (a->y < b->y) { - return true; - } else if (a->y == b->y) { - // Make sure q is point with greater x value - if (a->x < b->x) { - return true; + if( a->y < b->y ) + { + return true; } - } - return false; + else if( a->y == b->y ) + { + // Make sure q is point with greater x value + if( a->x < b->x ) + { + return true; + } + } + + return false; } + /// Add two points_ component-wise. -inline Point operator +(const Point& a, const Point& b) +inline Point operator +( const Point& a, const Point& b ) { - return Point(a.x + b.x, a.y + b.y); + return Point( a.x + b.x, a.y + b.y ); } + /// Subtract two points_ component-wise. -inline Point operator -(const Point& a, const Point& b) +inline Point operator -( const Point& a, const Point& b ) { - return Point(a.x - b.x, a.y - b.y); + return Point( a.x - b.x, a.y - b.y ); } + /// Multiply point by scalar -inline Point operator *(double s, const Point& a) +inline Point operator *( double s, const Point& a ) { - return Point(s * a.x, s * a.y); + return Point( s * a.x, s * a.y ); } -inline bool operator ==(const Point& a, const Point& b) + +inline bool operator ==( const Point& a, const Point& b ) { - return a.x == b.x && a.y == b.y; + return a.x == b.x && a.y == b.y; } -inline bool operator !=(const Point& a, const Point& b) + +inline bool operator !=( const Point& a, const Point& b ) { - return !(a.x == b.x) && !(a.y == b.y); + return !(a.x == b.x) && !(a.y == b.y); } + /// Peform the dot product on two vectors. -inline double Dot(const Point& a, const Point& b) +inline double Dot( const Point& a, const Point& b ) { - return a.x * b.x + a.y * b.y; + return a.x * b.x + a.y * b.y; } + /// Perform the cross product on two vectors. In 2D this produces a scalar. -inline double Cross(const Point& a, const Point& b) +inline double Cross( const Point& a, const Point& b ) { - return a.x * b.y - a.y * b.x; + return a.x * b.y - a.y * b.x; } + /// Perform the cross product on a point and a scalar. In 2D this produces /// a point. -inline Point Cross(const Point& a, double s) +inline Point Cross( const Point& a, double s ) { - return Point(s * a.y, -s * a.x); + return Point( s * a.y, -s * a.x ); } + /// Perform the cross product on a scalar and a point. In 2D this produces /// a point. -inline Point Cross(const double s, const Point& a) +inline Point Cross( const double s, const Point& a ) { - return Point(-s * a.y, s * a.x); + return Point( -s * a.y, s * a.x ); } -inline Point* Triangle::GetPoint(const int& index) + +inline Point* Triangle::GetPoint( const int& index ) { - return points_[index]; + return points_[index]; } -inline Triangle* Triangle::GetNeighbor(const int& index) + +inline Triangle* Triangle::GetNeighbor( const int& index ) { - return neighbors_[index]; + return neighbors_[index]; } -inline bool Triangle::Contains(Point* p) + +inline bool Triangle::Contains( Point* p ) { - return p == points_[0] || p == points_[1] || p == points_[2]; + return p == points_[0] || p == points_[1] || p == points_[2]; } -inline bool Triangle::Contains(const Edge& e) + +inline bool Triangle::Contains( const Edge& e ) { - return Contains(e.p) && Contains(e.q); + return Contains( e.p ) && Contains( e.q ); } -inline bool Triangle::Contains(Point* p, Point* q) + +inline bool Triangle::Contains( Point* p, Point* q ) { - return Contains(p) && Contains(q); + return Contains( p ) && Contains( q ); } + inline bool Triangle::IsInterior() { - return interior_; + return interior_; } -inline void Triangle::IsInterior(bool b) + +inline void Triangle::IsInterior( bool b ) { - interior_ = b; + interior_ = b; } - } #endif - - diff --git a/polygon/poly2tri/common/utils.h b/polygon/poly2tri/common/utils.h index 78416f2f31..3de9fb11bf 100644 --- a/polygon/poly2tri/common/utils.h +++ b/polygon/poly2tri/common/utils.h @@ -1,4 +1,4 @@ -/* +/* * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors * http://code.google.com/p/poly2tri/ * @@ -28,7 +28,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + #ifndef UTILS_H #define UTILS_H @@ -39,12 +39,13 @@ #include namespace p2t { +const double PI_3div4 = 3 * M_PI / 4; +const double PI_div2 = 1.57079632679489661923; +const double EPSILON = 1e-12; -const double PI_3div4 = 3 * M_PI / 4; -const double PI_div2 = 1.57079632679489661923; -const double EPSILON = 1e-12; - -enum Orientation { CW, CCW, COLLINEAR }; +enum Orientation { + CW, CCW, COLLINEAR +}; /** * Forumla to calculate signed area
@@ -56,68 +57,77 @@ enum Orientation { CW, CCW, COLLINEAR }; * = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3) * */ -Orientation Orient2d(Point& pa, Point& pb, Point& pc) +Orientation Orient2d( Point& pa, Point& pb, Point& pc ) { - double detleft = (pa.x - pc.x) * (pb.y - pc.y); - double detright = (pa.y - pc.y) * (pb.x - pc.x); - double val = detleft - detright; - if (val > -EPSILON && val < EPSILON) { - return COLLINEAR; - } else if (val > 0) { - return CCW; - } - return CW; + double detleft = (pa.x - pc.x) * (pb.y - pc.y); + double detright = (pa.y - pc.y) * (pb.x - pc.x); + double val = detleft - detright; + + if( val > -EPSILON && val < EPSILON ) + { + return COLLINEAR; + } + else if( val > 0 ) + { + return CCW; + } + + return CW; } + /* -bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) + * bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) + * { + * double pdx = pd.x; + * double pdy = pd.y; + * double adx = pa.x - pdx; + * double ady = pa.y - pdy; + * double bdx = pb.x - pdx; + * double bdy = pb.y - pdy; + * + * double adxbdy = adx * bdy; + * double bdxady = bdx * ady; + * double oabd = adxbdy - bdxady; + * + * if (oabd <= EPSILON) { + * return false; + * } + * + * double cdx = pc.x - pdx; + * double cdy = pc.y - pdy; + * + * double cdxady = cdx * ady; + * double adxcdy = adx * cdy; + * double ocad = cdxady - adxcdy; + * + * if (ocad <= EPSILON) { + * return false; + * } + * + * return true; + * } + * + */ + +bool InScanArea( Point& pa, Point& pb, Point& pc, Point& pd ) { - double pdx = pd.x; - double pdy = pd.y; - double adx = pa.x - pdx; - double ady = pa.y - pdy; - double bdx = pb.x - pdx; - double bdy = pb.y - pdy; + double oadb = (pa.x - pb.x) * (pd.y - pb.y) - (pd.x - pb.x) * (pa.y - pb.y); - double adxbdy = adx * bdy; - double bdxady = bdx * ady; - double oabd = adxbdy - bdxady; + if( oadb >= -EPSILON ) + { + return false; + } - if (oabd <= EPSILON) { - return false; - } + double oadc = (pa.x - pc.x) * (pd.y - pc.y) - (pd.x - pc.x) * (pa.y - pc.y); - double cdx = pc.x - pdx; - double cdy = pc.y - pdy; + if( oadc <= EPSILON ) + { + return false; + } - double cdxady = cdx * ady; - double adxcdy = adx * cdy; - double ocad = cdxady - adxcdy; - - if (ocad <= EPSILON) { - return false; - } - - return true; + return true; } - -*/ - -bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) -{ - double oadb = (pa.x - pb.x)*(pd.y - pb.y) - (pd.x - pb.x)*(pa.y - pb.y); - if (oadb >= -EPSILON) { - return false; - } - - double oadc = (pa.x - pc.x)*(pd.y - pc.y) - (pd.x - pc.x)*(pa.y - pc.y); - if (oadc <= EPSILON) { - return false; - } - return true; -} - } #endif - From 2cead53fc901b954f8e33bd883d3c1913083f511 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Fri, 3 Jan 2014 17:16:40 -0600 Subject: [PATCH 51/57] fix compiler warnings --- polygon/poly2tri/sweep/sweep.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/polygon/poly2tri/sweep/sweep.cc b/polygon/poly2tri/sweep/sweep.cc index 258df5db08..83d54f2666 100644 --- a/polygon/poly2tri/sweep/sweep.cc +++ b/polygon/poly2tri/sweep/sweep.cc @@ -115,7 +115,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl if (o1 == COLLINEAR) { if( triangle->Contains(&eq, p1)) { triangle->MarkConstrainedEdge(&eq, p1 ); - // We are modifying the constraint maybe it would be better to + // We are modifying the constraint maybe it would be better to // not change the given constraint and just keep a variable for the new constraint tcx.edge_event.constrained_edge->q = p1; triangle = &triangle->NeighborAcross(point); @@ -132,7 +132,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl if (o2 == COLLINEAR) { if( triangle->Contains(&eq, p2)) { triangle->MarkConstrainedEdge(&eq, p2 ); - // We are modifying the constraint maybe it would be better to + // We are modifying the constraint maybe it would be better to // not change the given constraint and just keep a variable for the new constraint tcx.edge_event.constrained_edge->q = p2; triangle = &triangle->NeighborAcross(point); @@ -252,7 +252,7 @@ void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) // True if HoleAngle exceeds 90 degrees. bool Sweep::LargeHole_DontFill(Node* node) { - + Node* nextNode = node->next; Node* prevNode = node->prev; if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) @@ -803,7 +803,8 @@ void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& Sweep::~Sweep() { // Clean up memory - for(int i = 0; i < nodes_.size(); i++) { + for( unsigned i = 0; i < nodes_.size(); i++ ) + { delete nodes_[i]; } From a53caab12b6e58da3bbb5c3436db473733bcad5f Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Fri, 3 Jan 2014 17:17:50 -0600 Subject: [PATCH 52/57] force switch to boost::context, add --force option to bzr clean-tree --- CMakeModules/download_boost.cmake | 2 +- common/CMakeLists.txt | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 74395f11cb..9d87d5cb1b 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -181,7 +181,7 @@ ExternalProject_Add( boost # to ignore previously applied patches PATCH_COMMAND bzr revert # bzr revert is insufficient to remove "added" files: - COMMAND bzr clean-tree -q + COMMAND bzr clean-tree -q --force COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_minkowski.patch" COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/boost_cstdint.patch" diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 20101747ad..44569f29e2 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -176,10 +176,11 @@ set( COMMON_SRCS geometry/shape_index.cpp ) -# TODO: remove this whole if test and remove the sytem/*.s sources once every platform is tested with -# boost::context library -if( UNIX AND NOT APPLE ) -else() + +# TODO: remove this whole "if test" on or after 14-Jan-2014 and remove the system/*.s +# sources if no one complains by then. +# boost::context library replaces this functionality: +if( false ) enable_language( C CXX ASM ) set_source_files_properties( system/fcontext.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp" ) list( APPEND COMMON_SRCS system/fcontext.s ) From 16a70dd2677fa8a3ac8d403518e5985e4f7854df Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Fri, 3 Jan 2014 17:19:54 -0600 Subject: [PATCH 53/57] comment out broken tool_modview.cpp's useless footprint select button. somebody needs to fix this. --- pcbnew/modview_frame.cpp | 7 +++---- pcbnew/tool_modview.cpp | 13 +++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp index 08b93c170f..83428fdaf2 100644 --- a/pcbnew/modview_frame.cpp +++ b/pcbnew/modview_frame.cpp @@ -112,11 +112,10 @@ static wxAcceleratorEntry accels[] = wxAcceleratorEntry( wxACCEL_NORMAL, WXK_SPACE, ID_SET_RELATIVE_OFFSET ) }; -#define ACCEL_TABLE_CNT ( sizeof( accels ) / sizeof( wxAcceleratorEntry ) ) -#define EXTRA_BORDER_SIZE 2 +#define EXTRA_BORDER_SIZE 2 -#define FOOTPRINT_VIEWER_FRAME_NAME wxT( "ModViewFrame" ) +#define FOOTPRINT_VIEWER_FRAME_NAME wxT( "ModViewFrame" ) FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( PCB_BASE_FRAME* aParent, FP_LIB_TABLE* aTable, @@ -125,7 +124,7 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( PCB_BASE_FRAME* aParent, PCB_BASE_FRAME( aParent, MODULE_VIEWER_FRAME_TYPE, _( "Footprint Library Browser" ), wxDefaultPosition, wxDefaultSize, aStyle, GetFootprintViewerFrameName() ) { - wxAcceleratorTable table( ACCEL_TABLE_CNT, accels ); + wxAcceleratorTable table( DIM( accels ), accels ); m_footprintLibTable = aTable; m_FrameName = GetFootprintViewerFrameName(); diff --git a/pcbnew/tool_modview.cpp b/pcbnew/tool_modview.cpp index 8d40fa2601..5376497e21 100644 --- a/pcbnew/tool_modview.cpp +++ b/pcbnew/tool_modview.cpp @@ -97,9 +97,18 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateHToolbar() { // The library browser is called from a "load component" command m_mainToolBar->AddSeparator(); - m_mainToolBar->AddTool( ID_MODVIEW_FOOTPRINT_EXPORT_TO_BOARD, - wxEmptyString, KiBitmap( export_footprint_names_xpm ), + + /* + this ID_MODVIEW_FOOTPRINT_EXPORT_TO_BOARD control + is broken it does not lead to a fetched footprint on linux, either 3.0 nor 2.8 wx: + and I really don't like the drop down menu here: + + whoever broke it, please fix it: + + m_mainToolBar->AddTool( ID_MODVIEW_FOOTPRINT_EXPORT_TO_BOARD, wxEmptyString, + KiBitmap( export_footprint_names_xpm ), _( "Insert footprint in board" ) ); + */ } // after adding the buttons to the toolbar, must call Realize() to From f27e85eeb0f33b35329eba4546d25fa54db1d96b Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Fri, 3 Jan 2014 18:07:20 -0600 Subject: [PATCH 54/57] fix some compiler warnings --- common/geometry/shape_collisions.cpp | 108 +++++++++++++-------------- polygon/poly2tri/common/shapes.cc | 7 ++ 2 files changed, 60 insertions(+), 55 deletions(-) diff --git a/common/geometry/shape_collisions.cpp b/common/geometry/shape_collisions.cpp index abd13caccf..604335b43e 100644 --- a/common/geometry/shape_collisions.cpp +++ b/common/geometry/shape_collisions.cpp @@ -160,69 +160,67 @@ bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, { switch( aA->Type() ) { - case SH_RECT: - switch( aB->Type() ) - { - case SH_CIRCLE: - return Collide( *static_cast( aA ), - *static_cast( aB ), aClearance, aNeedMTV, aMTV ); - - case SH_LINE_CHAIN: - return Collide( *static_cast( aA ), - *static_cast( aB ), aClearance, aNeedMTV, aMTV ); - - default: - break; - } - break; - + case SH_RECT: + switch( aB->Type() ) + { case SH_CIRCLE: - switch( aB->Type() ) - { - case SH_RECT: - return Collide( *static_cast( aB ), - *static_cast( aA ), aClearance, aNeedMTV, aMTV ); - - case SH_CIRCLE: - return Collide( *static_cast( aA ), - *static_cast( aB ), aClearance, aNeedMTV, aMTV ); - - case SH_LINE_CHAIN: - return Collide( *static_cast( aA ), - *static_cast( aB ), aClearance, aNeedMTV, aMTV ); - - default: - break; - } - break; + return Collide( *static_cast( aA ), + *static_cast( aB ), aClearance, aNeedMTV, aMTV ); case SH_LINE_CHAIN: - switch( aB->Type() ) - { - case SH_RECT: - return Collide( *static_cast( aB ), - *static_cast( aA ), aClearance, aNeedMTV, aMTV ); - - case SH_CIRCLE: - return Collide( *static_cast( aB ), - *static_cast( aA ), aClearance, aNeedMTV, aMTV ); - - case SH_LINE_CHAIN: - return Collide( *static_cast( aA ), - *static_cast( aB ), aClearance, aNeedMTV, aMTV ); - - default: - break; - } - break; + return Collide( *static_cast( aA ), + *static_cast( aB ), aClearance, aNeedMTV, aMTV ); default: break; + } + break; + + case SH_CIRCLE: + switch( aB->Type() ) + { + case SH_RECT: + return Collide( *static_cast( aB ), + *static_cast( aA ), aClearance, aNeedMTV, aMTV ); + + case SH_CIRCLE: + return Collide( *static_cast( aA ), + *static_cast( aB ), aClearance, aNeedMTV, aMTV ); + + case SH_LINE_CHAIN: + return Collide( *static_cast( aA ), + *static_cast( aB ), aClearance, aNeedMTV, aMTV ); + + default: + break; + } + break; + + case SH_LINE_CHAIN: + switch( aB->Type() ) + { + case SH_RECT: + return Collide( *static_cast( aB ), + *static_cast( aA ), aClearance, aNeedMTV, aMTV ); + + case SH_CIRCLE: + return Collide( *static_cast( aB ), + *static_cast( aA ), aClearance, aNeedMTV, aMTV ); + + case SH_LINE_CHAIN: + return Collide( *static_cast( aA ), + *static_cast( aB ), aClearance, aNeedMTV, aMTV ); + + default: + break; + } + break; + + default: + break; } - bool unsupported_collision = true; - - assert( unsupported_collision == false ); + assert( 0 ); // unsupported_collision return false; } diff --git a/polygon/poly2tri/common/shapes.cc b/polygon/poly2tri/common/shapes.cc index b7a087f96e..06eb1f80c5 100644 --- a/polygon/poly2tri/common/shapes.cc +++ b/polygon/poly2tri/common/shapes.cc @@ -133,11 +133,15 @@ void Triangle::ClearDelunayEdges() Point* Triangle::OppositePoint( Triangle& t, Point& p ) { Point* cw = t.PointCW( p ); + + /* double x = cw->x; double y = cw->y; x = p.x; y = p.y; + */ + return PointCW( *cw ); } @@ -195,6 +199,7 @@ int Triangle::Index( const Point* p ) } assert( 0 ); + return 0; // you better hope its a Debug build. } @@ -285,6 +290,7 @@ Point* Triangle::PointCW( Point& point ) } assert( 0 ); + return NULL; // you better hope its a Debug build. } @@ -305,6 +311,7 @@ Point* Triangle::PointCCW( Point& point ) } assert( 0 ); + return NULL; // you better hope its a Debug build. } From 4bd4c43d1ab30e8d3d92b53c492708a67ecccdf5 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Fri, 3 Jan 2014 18:46:06 -0600 Subject: [PATCH 55/57] This is why we cannot have nice things. This is why we test our changes before committing. Pray for angus johnson who is support too many languages, and needs to find a text editor that removes trailing whitespace. --- polygon/clipper.cpp | 401 ++++++++++++++++++++++---------------------- 1 file changed, 205 insertions(+), 196 deletions(-) diff --git a/polygon/clipper.cpp b/polygon/clipper.cpp index 162df7d93d..b463c2176b 100644 --- a/polygon/clipper.cpp +++ b/polygon/clipper.cpp @@ -136,7 +136,7 @@ struct Join { inline cInt Round(double val) { - if ((val < 0)) return static_cast(val - 0.5); + if ((val < 0)) return static_cast(val - 0.5); else return static_cast(val + 0.5); } //------------------------------------------------------------------------------ @@ -154,7 +154,7 @@ void PolyTree::Clear() { for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) delete AllNodes[i]; - AllNodes.resize(0); + AllNodes.resize(0); Childs.resize(0); } //------------------------------------------------------------------------------ @@ -198,27 +198,27 @@ void PolyNode::AddChild(PolyNode& child) //------------------------------------------------------------------------------ PolyNode* PolyNode::GetNext() const -{ - if (!Childs.empty()) - return Childs[0]; +{ + if (!Childs.empty()) + return Childs[0]; else - return GetNextSiblingUp(); -} + return GetNextSiblingUp(); +} //------------------------------------------------------------------------------ PolyNode* PolyNode::GetNextSiblingUp() const -{ +{ if (!Parent) //protects against PolyTree.GetNextSiblingUp() return 0; else if (Index == Parent->Childs.size() - 1) return Parent->GetNextSiblingUp(); else return Parent->Childs[Index + 1]; -} +} //------------------------------------------------------------------------------ bool PolyNode::IsHole() const -{ +{ bool result = true; PolyNode* node = Parent; while (node) @@ -227,13 +227,13 @@ bool PolyNode::IsHole() const node = node->Parent; } return result; -} +} //------------------------------------------------------------------------------ bool PolyNode::IsOpen() const -{ +{ return m_IsOpen; -} +} //------------------------------------------------------------------------------ #ifndef use_int32 @@ -255,15 +255,15 @@ class Int128 Int128(cInt _lo = 0) { - lo = (cUInt)_lo; - if (_lo < 0) hi = -1; else hi = 0; + lo = (cUInt)_lo; + if (_lo < 0) hi = -1; else hi = 0; } Int128(const Int128 &val): lo(val.lo), hi(val.hi){} Int128(const cInt& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} - + Int128& operator = (const cInt &val) { lo = (ulong64)val; @@ -331,7 +331,7 @@ class Int128 { if (lo == 0) return Int128(-hi,0); - else + else return Int128(~hi,~lo +1); } @@ -362,12 +362,12 @@ class Int128 } divisor.lo >>= 1; if ((divisor.hi & 1) == 1) - divisor.lo |= 0x8000000000000000LL; + divisor.lo |= 0x8000000000000000LL; divisor.hi = (ulong64)divisor.hi >> 1; cntr.lo >>= 1; if ((cntr.hi & 1) == 1) - cntr.lo |= 0x8000000000000000LL; + cntr.lo |= 0x8000000000000000LL; cntr.hi >>= 1; while (cntr.hi != 0 || cntr.lo != 0) @@ -380,12 +380,12 @@ class Int128 } divisor.lo >>= 1; if ((divisor.hi & 1) == 1) - divisor.lo |= 0x8000000000000000LL; + divisor.lo |= 0x8000000000000000LL; divisor.hi >>= 1; cntr.lo >>= 1; if ((cntr.hi & 1) == 1) - cntr.lo |= 0x8000000000000000LL; + cntr.lo |= 0x8000000000000000LL; cntr.hi >>= 1; } if (negate) result = -result; @@ -501,7 +501,7 @@ int PointInPolygon (const IntPoint& pt, OutPt* op) { if (op->Next->Pt.Y == pt.Y) { - if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; } if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) @@ -511,7 +511,7 @@ int PointInPolygon (const IntPoint& pt, OutPt* op) if (op->Next->Pt.X > pt.X) result = 1 - result; else { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); if (!d) return -1; if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; @@ -520,16 +520,16 @@ int PointInPolygon (const IntPoint& pt, OutPt* op) { if (op->Next->Pt.X > pt.X) { - double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); if (!d) return -1; if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; } } - } + } op = op->Next; if (startOp == op) break; - } + } return result; } //------------------------------------------------------------------------------ @@ -541,10 +541,10 @@ bool Poly2ContainsPoly1(OutPt* OutPt1, OutPt* OutPt2) { int res = PointInPolygon(op->Pt, OutPt2); if (res >= 0) return res != 0; - op = op->Next; + op = op->Next; } while (op != OutPt1); - return true; + return true; } //---------------------------------------------------------------------- @@ -553,7 +553,7 @@ bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) #ifndef use_int32 if (UseFullInt64Range) return Int128Mul(e1.Delta.Y, e2.Delta.X) == Int128Mul(e1.Delta.X, e2.Delta.Y); - else + else #endif return e1.Delta.Y * e2.Delta.X == e1.Delta.X * e2.Delta.Y; } @@ -565,7 +565,7 @@ bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, #ifndef use_int32 if (UseFullInt64Range) return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); - else + else #endif return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); } @@ -577,7 +577,7 @@ bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, #ifndef use_int32 if (UseFullInt64Range) return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); - else + else #endif return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); } @@ -632,11 +632,11 @@ inline cInt TopX(TEdge &edge, const cInt currentY) bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip, bool UseFullInt64Range) { -#ifdef use_xyz +#ifdef use_xyz ip.Z = 0; #endif double b1, b2; - //nb: with very large coordinate values, it's possible for SlopesEqual() to + //nb: with very large coordinate values, it's possible for SlopesEqual() to //return false but for the edge.Dx value be equal due to double precision rounding. if (SlopesEqual(Edge1, Edge2, UseFullInt64Range) || Edge1.Dx == Edge2.Dx) { @@ -665,8 +665,8 @@ bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); ip.Y = Round(ip.X / Edge1.Dx + b1); } - } - else + } + else { b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; @@ -674,11 +674,11 @@ bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, ip.Y = Round(q); if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ip.X = Round(Edge1.Dx * q + b1); - else + else ip.X = Round(Edge2.Dx * q + b2); } - if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) { if (Edge1.Top.Y > Edge2.Top.Y) ip.Y = Edge1.Top.Y; @@ -688,7 +688,7 @@ bool IntersectPoint(TEdge &Edge1, TEdge &Edge2, ip.X = TopX(Edge1, ip.Y); else ip.X = TopX(Edge2, ip.Y); - } + } return true; } //------------------------------------------------------------------------------ @@ -765,7 +765,7 @@ inline void ReverseHorizontal(TEdge &e) cInt tmp = e.Top.X; e.Top.X = e.Bot.X; e.Bot.X = tmp; -#ifdef use_xyz +#ifdef use_xyz tmp = e.Top.Z; e.Top.Z = e.Bot.Z; e.Bot.Z = tmp; @@ -860,7 +860,7 @@ OutPt* GetBottomPt(OutPt *pp) } //------------------------------------------------------------------------------ -bool FindSegment(OutPt* &pp, bool UseFullInt64Range, +bool FindSegment(OutPt* &pp, bool UseFullInt64Range, IntPoint &pt1, IntPoint &pt2) { //OutPt1 & OutPt2 => the overlap segment (if the function returns true) @@ -914,7 +914,7 @@ OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint Pt) } //------------------------------------------------------------------------------ -bool HorzSegmentsOverlap(const IntPoint& pt1a, const IntPoint& pt1b, +bool HorzSegmentsOverlap(const IntPoint& pt1a, const IntPoint& pt1b, const IntPoint& pt2a, const IntPoint& pt2b) { //precondition: both segments are horizontal @@ -950,10 +950,10 @@ void RangeTest(const IntPoint& Pt, bool& useFullRange) { if (useFullRange) { - if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) throw "Coordinate outside allowed range"; } - else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) { useFullRange = true; RangeTest(Pt, useFullRange); @@ -991,7 +991,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool IsClockwise) else StartX = E->Next->Bot.X; if (E->Bot.X != StartX) ReverseHorizontal(*E); } - + if (Result->OutIdx != Skip) { if (IsClockwise) @@ -1005,31 +1005,31 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool IsClockwise) //unless a Skip edge is encountered when that becomes the top divide Horz = Result; while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; - if (Horz->Prev->Top.X == Result->Next->Top.X) + if (Horz->Prev->Top.X == Result->Next->Top.X) { if (!IsClockwise) Result = Horz->Prev; } else if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; } - while (E != Result) + while (E != Result) { E->NextInLML = E->Next; if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); E = E->Next; } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); Result = Result->Next; //move to the edge just beyond current bound } else { - while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) Result = Result->Prev; if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) { Horz = Result; while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; - if (Horz->Next->Top.X == Result->Prev->Top.X) + if (Horz->Next->Top.X == Result->Prev->Top.X) { if (!IsClockwise) Result = Horz->Next; } @@ -1039,17 +1039,17 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool IsClockwise) while (E != Result) { E->NextInLML = E->Prev; - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) ReverseHorizontal(*E); E = E->Prev; } - if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) ReverseHorizontal(*E); Result = Result->Prev; //move to the edge just beyond current bound } } - if (Result->OutIdx == Skip) + if (Result->OutIdx == Skip) { //if edges still remain in the current bound beyond the skip edge then //create another LocMin and call ProcessBound once more @@ -1073,7 +1073,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool IsClockwise) { //there are more edges in the bound beyond result starting with E if (IsClockwise) - E = Result->Next; + E = Result->Next; else E = Result->Prev; LocalMinima* locMin = new LocalMinima; @@ -1144,10 +1144,10 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) eLoopStop = E; continue; } - if (E->Prev == E->Next) + if (E->Prev == E->Next) break; //only two vertices else if (Closed && - SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && (!m_PreserveCollinear || !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) { @@ -1187,9 +1187,9 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) //Totally flat paths must be handled differently when adding them //to LocalMinima list to avoid endless loops etc ... - if (IsFlat) + if (IsFlat) { - if (Closed) + if (Closed) { delete [] edges; return false; @@ -1211,7 +1211,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) } InsertLocalMinima(locMin); m_edges.push_back(edges); - return true; + return true; } m_edges.push_back(edges); @@ -1228,7 +1228,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) LocalMinima* locMin = new LocalMinima; locMin->Next = 0; locMin->Y = E->Bot.Y; - if (E->Dx < E->Prev->Dx) + if (E->Dx < E->Prev->Dx) { locMin->LeftBound = E->Prev; locMin->RightBound = E; @@ -1297,7 +1297,7 @@ void ClipperBase::Clear() DisposeLocalMinimaList(); for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) { - //for each edge array in turn, find the first used edge and + //for each edge array in turn, find the first used edge and //check for and remove any hiddenPts in each edge in the array. TEdge* edges = m_edges[i]; delete [] edges; @@ -1410,7 +1410,7 @@ Clipper::Clipper(int initOptions) : ClipperBase() //constructor m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); m_HasOpenPaths = false; -#ifdef use_xyz +#ifdef use_xyz m_ZFill = 0; #endif } @@ -1423,9 +1423,9 @@ Clipper::~Clipper() //destructor } //------------------------------------------------------------------------------ -#ifdef use_xyz +#ifdef use_xyz void Clipper::ZFillFunction(TZFillCallback zFillFunc) -{ +{ m_ZFill = zFillFunc; } //------------------------------------------------------------------------------ @@ -1494,7 +1494,7 @@ void Clipper::FixHoleLinkage(OutRec &outrec) { //skip OutRecs that (a) contain outermost polygons or //(b) already have the correct owner/child linkage ... - if (!outrec.FirstLeft || + if (!outrec.FirstLeft || (outrec.IsHole != outrec.FirstLeft->IsHole && outrec.FirstLeft->Pts)) return; @@ -1524,7 +1524,7 @@ bool Clipper::ExecuteInternal() botY = topY; } while (!m_Scanbeam.empty() || m_CurrentLM); } - catch(...) + catch(...) { succeeded = false; } @@ -1599,7 +1599,7 @@ void Clipper::SetWindingCount(TEdge &edge) edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); edge.WindCnt2 = 0; e = m_ActiveEdges; //ie get ready to calc WindCnt2 - } + } else if (edge.WindDelta == 0 && m_ClipType != ctUnion) { edge.WindCnt = 1; @@ -1616,7 +1616,7 @@ void Clipper::SetWindingCount(TEdge &edge) TEdge *e2 = e->PrevInAEL; while (e2) { - if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) Inside = !Inside; e2 = e2->PrevInAEL; } @@ -1628,7 +1628,7 @@ void Clipper::SetWindingCount(TEdge &edge) } edge.WindCnt2 = e->WindCnt2; e = e->NextInAEL; //ie get ready to calc WindCnt2 - } + } else { //nonZero, Positive or Negative filling ... @@ -1639,11 +1639,11 @@ void Clipper::SetWindingCount(TEdge &edge) if (Abs(e->WindCnt) > 1) { //outside prev poly but still inside another. - //when reversing direction of prev poly use the same WC + //when reversing direction of prev poly use the same WC if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; //otherwise continue to 'decrease' WC ... else edge.WindCnt = e->WindCnt + edge.WindDelta; - } + } else //now outside all polys of same polytype so set own WC ... edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); @@ -1651,7 +1651,7 @@ void Clipper::SetWindingCount(TEdge &edge) { //prev edge is 'increasing' WindCount (WC) away from zero //so we're inside the previous polygon ... - if (edge.WindDelta == 0) + if (edge.WindDelta == 0) edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); //if wind direction is reversing prev then use same WC else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; @@ -1715,14 +1715,14 @@ bool Clipper::IsContributing(const TEdge& edge) const switch(pft) { - case pftEvenOdd: + case pftEvenOdd: //return false if a subj line has been flagged as inside a subj polygon if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; break; case pftNonZero: if (Abs(edge.WindCnt) != 1) return false; break; - case pftPositive: + case pftPositive: if (edge.WindCnt != 1) return false; break; default: //pftNegative @@ -1734,24 +1734,24 @@ bool Clipper::IsContributing(const TEdge& edge) const case ctIntersection: switch(pft2) { - case pftEvenOdd: - case pftNonZero: + case pftEvenOdd: + case pftNonZero: return (edge.WindCnt2 != 0); - case pftPositive: + case pftPositive: return (edge.WindCnt2 > 0); - default: + default: return (edge.WindCnt2 < 0); } break; case ctUnion: switch(pft2) { - case pftEvenOdd: - case pftNonZero: + case pftEvenOdd: + case pftNonZero: return (edge.WindCnt2 == 0); - case pftPositive: + case pftPositive: return (edge.WindCnt2 <= 0); - default: + default: return (edge.WindCnt2 >= 0); } break; @@ -1759,23 +1759,23 @@ bool Clipper::IsContributing(const TEdge& edge) const if (edge.PolyTyp == ptSubject) switch(pft2) { - case pftEvenOdd: - case pftNonZero: + case pftEvenOdd: + case pftNonZero: return (edge.WindCnt2 == 0); - case pftPositive: + case pftPositive: return (edge.WindCnt2 <= 0); - default: + default: return (edge.WindCnt2 >= 0); } else switch(pft2) { - case pftEvenOdd: - case pftNonZero: + case pftEvenOdd: + case pftNonZero: return (edge.WindCnt2 != 0); - case pftPositive: + case pftPositive: return (edge.WindCnt2 > 0); - default: + default: return (edge.WindCnt2 < 0); } break; @@ -1783,15 +1783,15 @@ bool Clipper::IsContributing(const TEdge& edge) const if (edge.WindDelta == 0) //XOr always contributing unless open switch(pft2) { - case pftEvenOdd: - case pftNonZero: + case pftEvenOdd: + case pftNonZero: return (edge.WindCnt2 == 0); - case pftPositive: + case pftPositive: return (edge.WindCnt2 <= 0); - default: + default: return (edge.WindCnt2 >= 0); } - else + else return true; break; default: @@ -1812,7 +1812,7 @@ OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) e2->Side = esRight; e = e1; if (e->PrevInAEL == e2) - prevE = e2->PrevInAEL; + prevE = e2->PrevInAEL; else prevE = e->PrevInAEL; } else @@ -1849,9 +1849,9 @@ void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) e1->OutIdx = Unassigned; e2->OutIdx = Unassigned; } - else if (e1->OutIdx < e2->OutIdx) - AppendPolygon(e1, e2); - else + else if (e1->OutIdx < e2->OutIdx) + AppendPolygon(e1, e2); + else AppendPolygon(e2, e1); } //------------------------------------------------------------------------------ @@ -1939,8 +1939,8 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) InsertEdgeIntoAEL(rb, 0); SetWindingCount(*rb); if (IsContributing(*rb)) - Op1 = AddOutPt(rb, rb->Bot); - } + Op1 = AddOutPt(rb, rb->Bot); + } else if (!rb) { InsertEdgeIntoAEL(lb, 0); @@ -1957,7 +1957,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) rb->WindCnt = lb->WindCnt; rb->WindCnt2 = lb->WindCnt2; if (IsContributing(*lb)) - Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); InsertScanbeam(lb->Top.Y); } @@ -1970,7 +1970,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) if (!lb || !rb) continue; //if any output polygons share an edge, they'll need joining later ... - if (Op1 && IsHorizontal(*rb) && + if (Op1 && IsHorizontal(*rb) && m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) { for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) @@ -1983,7 +1983,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) } } - if (lb->OutIdx >= 0 && lb->PrevInAEL && + if (lb->OutIdx >= 0 && lb->PrevInAEL && lb->PrevInAEL->Curr.X == lb->Bot.X && lb->PrevInAEL->OutIdx >= 0 && SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) && @@ -2016,7 +2016,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) } } } - + } } //------------------------------------------------------------------------------ @@ -2089,7 +2089,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, } //if intersecting a subj line with a subj poly ... - else if (e1->PolyTyp == e2->PolyTyp && + else if (e1->PolyTyp == e2->PolyTyp && e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) { if (e1->WindDelta == 0) @@ -2112,13 +2112,13 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, else if (e1->PolyTyp != e2->PolyTyp) { //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... - if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && (m_ClipType != ctUnion || e2->WindCnt2 == 0)) { AddOutPt(e1, Pt); if (e1Contributing) e1->OutIdx = Unassigned; } - else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && (m_ClipType != ctUnion || e1->WindCnt2 == 0)) { AddOutPt(e2, Pt); @@ -2129,7 +2129,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, if (e1stops) if (e1->OutIdx < 0) DeleteFromAEL(e1); else throw clipperException("Error intersecting polylines"); - if (e2stops) + if (e2stops) if (e2->OutIdx < 0) DeleteFromAEL(e2); else throw clipperException("Error intersecting polylines"); return; @@ -2196,10 +2196,10 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, if ( e1Contributing && e2Contributing ) { - if ( e1stops || e2stops || + if ( e1stops || e2stops || (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) - AddLocalMaxPoly(e1, e2, Pt); + AddLocalMaxPoly(e1, e2, Pt); else { AddOutPt(e1, Pt); @@ -2210,7 +2210,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, } else if ( e1Contributing ) { - if (e2Wc == 0 || e2Wc == 1) + if (e2Wc == 0 || e2Wc == 1) { AddOutPt(e1, Pt); SwapSides(*e1, *e2); @@ -2219,14 +2219,14 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, } else if ( e2Contributing ) { - if (e1Wc == 0 || e1Wc == 1) + if (e1Wc == 0 || e1Wc == 1) { AddOutPt(e2, Pt); SwapSides(*e1, *e2); SwapPolyIndexes(*e1, *e2); } - } - else if ( (e1Wc == 0 || e1Wc == 1) && + } + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) { //neither edge is currently contributing ... @@ -2303,9 +2303,9 @@ void Clipper::SetHoleState(TEdge *e, OutRec *outrec) OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) { //work out which polygon fragment has the correct hole state ... - if (!outRec1->BottomPt) + if (!outRec1->BottomPt) outRec1->BottomPt = GetBottomPt(outRec1->Pts); - if (!outRec2->BottomPt) + if (!outRec2->BottomPt) outRec2->BottomPt = GetBottomPt(outRec2->Pts); OutPt *OutPt1 = outRec1->BottomPt; OutPt *OutPt2 = outRec2->BottomPt; @@ -2347,11 +2347,11 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; OutRec *holeStateRec; - if (Param1RightOfParam2(outRec1, outRec2)) + if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; - else if (Param1RightOfParam2(outRec2, outRec1)) + else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; - else + else holeStateRec = GetLowermostRec(outRec1, outRec2); //get the start and ends of both output polygons and @@ -2552,7 +2552,7 @@ TEdge *GetMaximaPair(TEdge *e) void Clipper::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) { //check that one or other edge hasn't already been removed from AEL ... - if (Edge1->NextInAEL == Edge1->PrevInAEL || + if (Edge1->NextInAEL == Edge1->PrevInAEL || Edge2->NextInAEL == Edge2->PrevInAEL) return; if( Edge1->NextInAEL == Edge2 ) @@ -2684,10 +2684,10 @@ void Clipper::PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam) //the AEL before we process the horizontal edges at the bottom of the next, //we need to create 'ghost' Join records of 'contrubuting' horizontals that //we can compare with horizontals at the bottom of the next SB. - if (isTopOfScanbeam) + if (isTopOfScanbeam) { if (outPt->Pt == horzEdge->Top) - AddGhostJoin(outPt, horzEdge->Bot); + AddGhostJoin(outPt, horzEdge->Bot); else AddGhostJoin(outPt, horzEdge->Top); } @@ -2712,7 +2712,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); TEdge* eLastHorz = horzEdge, *eMaxPair = 0; - while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) eLastHorz = eLastHorz->NextInLML; if (!eLastHorz->NextInLML) eMaxPair = GetMaximaPair(eLastHorz); @@ -2725,7 +2725,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) { //Break if we've got to the end of an intermediate horizontal edge ... //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && e->Dx < horzEdge->NextInLML->Dx) break; TEdge* eNext = GetNextInAEL(e, dir); //saves eNext for later @@ -2733,7 +2733,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) if ((dir == dLeftToRight && e->Curr.X <= horzRight) || (dir == dRightToLeft && e->Curr.X >= horzLeft)) { - if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) + if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) PrepareHorzJoins(horzEdge, isTopOfScanbeam); //so far we're still in range of the horizontal Edge but make sure //we're at the last of consec. horizontals when matching with eMaxPair @@ -2803,14 +2803,14 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) } } else - UpdateEdgeIntoAEL(horzEdge); + UpdateEdgeIntoAEL(horzEdge); } else if (eMaxPair) { if (eMaxPair->OutIdx >= 0) { if (dir == dLeftToRight) - IntersectEdges(horzEdge, eMaxPair, horzEdge->Top); + IntersectEdges(horzEdge, eMaxPair, horzEdge->Top); else IntersectEdges(eMaxPair, horzEdge, horzEdge->Top); if (eMaxPair->OutIdx >= 0) @@ -2861,7 +2861,7 @@ bool Clipper::ProcessIntersections(const cInt botY, const cInt topY) if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); else return false; } - catch(...) + catch(...) { m_SortedEdges = 0; DisposeIntersectNodes(); @@ -2974,7 +2974,7 @@ bool Clipper::FixupIntersectionOrder() CopyAELToSEL(); std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); size_t cnt = m_IntersectList.size(); - for (size_t i = 0; i < cnt; ++i) + for (size_t i = 0; i < cnt; ++i) { if (!EdgesAdjacent(*m_IntersectList[i])) { @@ -3020,7 +3020,7 @@ void Clipper::DoMaxima(TEdge *e) #ifdef use_lines else if (e->WindDelta == 0) { - if (e->OutIdx >= 0) + if (e->OutIdx >= 0) { AddOutPt(e, e->Top); e->OutIdx = Unassigned; @@ -3033,7 +3033,7 @@ void Clipper::DoMaxima(TEdge *e) eMaxPair->OutIdx = Unassigned; } DeleteFromAEL(eMaxPair); - } + } #endif else throw clipperException("DoMaxima error"); } @@ -3070,7 +3070,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if (e->OutIdx >= 0) AddOutPt(e, e->Bot); AddEdgeToSEL(e); - } + } else { e->Curr.X = TopX( *e, topY ); @@ -3078,7 +3078,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) } if (m_StrictSimple) - { + { TEdge* ePrev = e->PrevInAEL; if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) @@ -3103,7 +3103,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsIntermediate(e, topY)) { OutPt* op = 0; - if( e->OutIdx >= 0 ) + if( e->OutIdx >= 0 ) op = AddOutPt(e, e->Top); UpdateEdgeIntoAEL(e); @@ -3152,9 +3152,9 @@ void Clipper::FixupOutPolygon(OutRec &outrec) } //test for duplicate points and collinear edges ... - if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && - (!m_PreserveCollinear || + (!m_PreserveCollinear || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) { lastOK = 0; @@ -3243,12 +3243,12 @@ void Clipper::BuildResult2(PolyTree& polytree) { OutRec* outRec = m_PolyOuts[i]; if (!outRec->PolyNd) continue; - if (outRec->IsOpen) + if (outRec->IsOpen) { outRec->PolyNd->m_IsOpen = true; polytree.AddChild(*outRec->PolyNd); } - else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); else polytree.AddChild(*outRec->PolyNd); @@ -3271,24 +3271,24 @@ void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) { - if (e2.Curr.X == e1.Curr.X) + if (e2.Curr.X == e1.Curr.X) { if (e2.Top.Y > e1.Top.Y) - return e2.Top.X < TopX(e1, e2.Top.Y); + return e2.Top.X < TopX(e1, e2.Top.Y); else return e1.Top.X > TopX(e2, e1.Top.Y); - } + } else return e2.Curr.X < e1.Curr.X; } //------------------------------------------------------------------------------ -bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, cInt& Left, cInt& Right) { if (a1 < a2) { if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} else {Left = std::max(a1,b2); Right = std::min(a2,b1);} - } + } else { if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} @@ -3299,7 +3299,7 @@ bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, //------------------------------------------------------------------------------ inline void UpdateOutPtIdxs(OutRec& outrec) -{ +{ OutPt* op = outrec.Pts; do { @@ -3324,11 +3324,11 @@ void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) edge->NextInAEL = m_ActiveEdges; m_ActiveEdges->PrevInAEL = edge; m_ActiveEdges = edge; - } + } else { if(!startEdge) startEdge = m_ActiveEdges; - while(startEdge->NextInAEL && + while(startEdge->NextInAEL && !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) startEdge = startEdge->NextInAEL; edge->NextInAEL = startEdge->NextInAEL; @@ -3350,7 +3350,7 @@ OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) result->Prev = outPt; outPt->Next->Prev = result; outPt->Next = result; - } + } else { result->Prev = outPt->Prev; @@ -3374,24 +3374,24 @@ bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, //So, to facilitate this while inserting Op1b and Op2b ... //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) - if (Dir1 == dLeftToRight) + if (Dir1 == dLeftToRight) { - while (op1->Next->Pt.X <= Pt.X && - op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) op1 = op1->Next; if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; op1b = DupOutPt(op1, !DiscardLeft); - if (op1b->Pt != Pt) + if (op1b->Pt != Pt) { op1 = op1b; op1->Pt = Pt; op1b = DupOutPt(op1, !DiscardLeft); } - } + } else { - while (op1->Next->Pt.X >= Pt.X && - op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) op1 = op1->Next; if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; op1b = DupOutPt(op1, DiscardLeft); @@ -3405,7 +3405,7 @@ bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, if (Dir2 == dLeftToRight) { - while (op2->Next->Pt.X <= Pt.X && + while (op2->Next->Pt.X <= Pt.X && op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) op2 = op2->Next; if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; @@ -3418,8 +3418,8 @@ bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, }; } else { - while (op2->Next->Pt.X >= Pt.X && - op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) op2 = op2->Next; if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; op2b = DupOutPt(op2, DiscardLeft); @@ -3468,11 +3468,11 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) { //Strictly Simple join ... op1b = j->OutPt1->Next; - while (op1b != op1 && (op1b->Pt == j->OffPt)) + while (op1b != op1 && (op1b->Pt == j->OffPt)) op1b = op1b->Next; bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); op2b = j->OutPt2->Next; - while (op2b != op2 && (op2b->Pt == j->OffPt)) + while (op2b != op2 && (op2b->Pt == j->OffPt)) op2b = op2b->Next; bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); if (reverse1 == reverse2) return false; @@ -3499,7 +3499,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) j->OutPt2 = op1b; return true; } - } + } else if (isHorizontal) { //treat horizontal joins differently to non-horizontal joins since with @@ -3529,18 +3529,18 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) //on the discard Side as either may still be needed for other joins ... IntPoint Pt; bool DiscardLeftSide; - if (op1->Pt.X >= Left && op1->Pt.X <= Right) + if (op1->Pt.X >= Left && op1->Pt.X <= Right) { Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); - } - else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) { Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); - } + } else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) { Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; - } + } else { Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); @@ -3608,12 +3608,12 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) //---------------------------------------------------------------------- void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) -{ - +{ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec* outRec = m_PolyOuts[i]; - if (outRec->Pts && outRec->FirstLeft == OldOutRec) + if (outRec->Pts && outRec->FirstLeft == OldOutRec) { if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) outRec->FirstLeft = NewOutRec; @@ -3623,7 +3623,7 @@ void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) //---------------------------------------------------------------------- void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) -{ +{ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec* outRec = m_PolyOuts[i]; @@ -3634,7 +3634,7 @@ void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) static OutRec* ParseFirstLeft(OutRec* FirstLeft) { - while (FirstLeft && !FirstLeft->Pts) + while (FirstLeft && !FirstLeft->Pts) FirstLeft = FirstLeft->FirstLeft; return FirstLeft; } @@ -3696,7 +3696,7 @@ void Clipper::JoinCommonEdges() if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) ReversePolyPtLinks(outRec2->Pts); - + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) { //outRec1 is contained by outRec2 ... @@ -3710,7 +3710,7 @@ void Clipper::JoinCommonEdges() if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) ReversePolyPtLinks(outRec1->Pts); - } + } else { //the 2 polygons are completely separate ... @@ -3720,7 +3720,7 @@ void Clipper::JoinCommonEdges() //fixup FirstLeft pointers that may need reassigning to OutRec2 if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); } - + } else { //joined 2 polygons together ... @@ -3730,7 +3730,7 @@ void Clipper::JoinCommonEdges() outRec2->Idx = outRec1->Idx; outRec1->IsHole = holeStateRec->IsHole; - if (holeStateRec == outRec2) + if (holeStateRec == outRec2) outRec1->FirstLeft = outRec2->FirstLeft; outRec2->FirstLeft = outRec1; @@ -3746,7 +3746,7 @@ void Clipper::JoinCommonEdges() DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) { - if(pt2.X == pt1.X && pt2.Y == pt1.Y) + if(pt2.X == pt1.X && pt2.Y == pt1.Y) return DoublePoint(0, 0); double Dx = (double)(pt2.X - pt1.X); @@ -3807,7 +3807,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType (path[i].Y == newNode->Contour[k].Y && path[i].X < newNode->Contour[k].X)) k = j; } - if ((endType == etClosedPolygon && j < 2) || + if ((endType == etClosedPolygon && j < 2) || (endType != etClosedPolygon && j < 0)) { delete newNode; @@ -3841,7 +3841,7 @@ void ClipperOffset::FixOrientations() { //fixup orientations of all closed paths if the orientation of the //closed path with the lowermost vertex is wrong ... - if (m_lowest.X >= 0 && + if (m_lowest.X >= 0 && !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) { for (int i = 0; i < m_polyNodes.ChildCount(); ++i) @@ -3868,7 +3868,7 @@ void ClipperOffset::Execute(Paths& solution, double delta) solution.clear(); FixOrientations(); DoOffset(delta); - + //now clean up 'corners' ... Clipper clpr; clpr.AddPaths(m_destPolys, ptSubject, true); @@ -3939,7 +3939,7 @@ void ClipperOffset::DoOffset(double delta) m_delta = delta; //if Zero offset, just copy any CLOSED polygons to m_p and return ... - if (NEAR_ZERO(delta)) + if (NEAR_ZERO(delta)) { m_destPolys.reserve(m_polyNodes.ChildCount()); for (int i = 0; i < m_polyNodes.ChildCount(); i++) @@ -3957,12 +3957,12 @@ void ClipperOffset::DoOffset(double delta) double y; if (ArcTolerance <= 0.0) y = def_arc_tolerance; - else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) y = std::fabs(delta) * def_arc_tolerance; else y = ArcTolerance; //see offset_triginometry2.svg in the documentation folder ... double steps = pi / std::acos(1 - y / std::fabs(delta)); - if (steps > std::fabs(delta) * pi) + if (steps > std::fabs(delta) * pi) steps = std::fabs(delta) * pi; //ie excessive precision check m_sin = std::sin(two_pi / steps); m_cos = std::cos(two_pi / steps); @@ -4187,7 +4187,7 @@ void ClipperOffset::DoRound(int j, int k) void Clipper::DoSimplePolygons() { PolyOutList::size_type i = 0; - while (i < m_PolyOuts.size()) + while (i < m_PolyOuts.size()) { OutRec* outrec = m_PolyOuts[i++]; OutPt* op = outrec->Pts; @@ -4195,9 +4195,9 @@ void Clipper::DoSimplePolygons() do //for each Pt in Polygon until duplicate found do ... { OutPt* op2 = op->Next; - while (op2 != outrec->Pts) + while (op2 != outrec->Pts) { - if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) { //split the polygon into two ... OutPt* op3 = op->Prev; @@ -4255,6 +4255,15 @@ void ReversePaths(Paths& p) } //------------------------------------------------------------------------------ +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) { Clipper c; @@ -4295,7 +4304,7 @@ double DistanceFromLineSqrd( } //--------------------------------------------------------------------------- -bool SlopesNearCollinear(const IntPoint& pt1, +bool SlopesNearCollinear(const IntPoint& pt1, const IntPoint& pt2, const IntPoint& pt3, double distSqrd) { return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; @@ -4324,10 +4333,10 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) { //distance = proximity in units/pixels below which vertices //will be stripped. Default ~= sqrt(2). - + size_t size = in_poly.size(); - - if (size == 0) + + if (size == 0) { out_poly.clear(); return; @@ -4344,13 +4353,13 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) double distSqrd = distance * distance; OutPt* op = &outPts[0]; - while (op->Idx == 0 && op->Next != op->Prev) + while (op->Idx == 0 && op->Next != op->Prev) { if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) { op = ExcludeOp(op); size--; - } + } else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) { ExcludeOp(op->Next); @@ -4399,7 +4408,7 @@ void CleanPolygons(Paths& polys, double distance) } //------------------------------------------------------------------------------ -void Minkowski(const Path& poly, const Path& path, +void Minkowski(const Path& poly, const Path& path, Paths& solution, bool isSum, bool isClosed) { int delta = (isClosed ? 1 : 0); @@ -4426,7 +4435,7 @@ void Minkowski(const Path& poly, const Path& path, pp.push_back(p); } - Paths quads; + Paths quads; quads.reserve((pathCnt + delta) * (polyCnt + 1)); for (size_t i = 0; i <= pathCnt - 2 + delta; ++i) for (size_t j = 0; j <= polyCnt - 1; ++j) @@ -4476,7 +4485,7 @@ void AddPolyNodeToPolygons(const PolyNode& polynode, NodeType nodetype, Paths& p void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) { - paths.resize(0); + paths.resize(0); paths.reserve(polytree.Total()); AddPolyNodeToPolygons(polytree, ntAny, paths); } @@ -4484,7 +4493,7 @@ void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) { - paths.resize(0); + paths.resize(0); paths.reserve(polytree.Total()); AddPolyNodeToPolygons(polytree, ntClosed, paths); } @@ -4492,7 +4501,7 @@ void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) { - paths.resize(0); + paths.resize(0); paths.reserve(polytree.Total()); //Open paths are top level only, so ... for (int i = 0; i < polytree.ChildCount(); ++i) @@ -4534,7 +4543,7 @@ void OffsetPaths(const Paths &in_polys, Paths &out_polys, double delta, JoinType jointype, EndType_ endtype, double limit) { ClipperOffset co(limit, limit); - co.AddPaths(in_polys, jointype, (EndType)endtype); + co.AddPaths(in_polys, jointype, (EndType)endtype); co.Execute(out_polys, delta); } //------------------------------------------------------------------------------ From fc14556d43a49e9a180cc439ce74615411afa6ff Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Sat, 4 Jan 2014 03:13:18 +0100 Subject: [PATCH 56/57] [MacOSX] Added support in boost context for PPC processors, now is possible again compile kicad testing for PPC ( https://svn.boost.org/trac/boost/ticket/8266 ) --- CMakeModules/download_boost.cmake | 10 + patches/patch_macosx_context_ppc_v2.patch | 782 ++++++++++++++++++++++ 2 files changed, 792 insertions(+) create mode 100644 patches/patch_macosx_context_ppc_v2.patch diff --git a/CMakeModules/download_boost.cmake b/CMakeModules/download_boost.cmake index 9d87d5cb1b..dd72e7c565 100644 --- a/CMakeModules/download_boost.cmake +++ b/CMakeModules/download_boost.cmake @@ -201,6 +201,16 @@ ExternalProject_Add( boost COMMAND bzr add libs/context/src/asm/make_x86_64_ms_pe_gas.S COMMAND bzr add libs/context/src/asm/jump_x86_64_ms_pe_gas.S + COMMAND bzr patch -p0 "${PROJECT_SOURCE_DIR}/patches/patch_macosx_context_ppc_v2.patch" #https://svn.boost.org/trac/boost/ticket/8266 + COMMAND bzr add libs/context/build/Jamfile.v2 + COMMAND bzr add libs/context/build/architecture.jam + COMMAND bzr add libs/context/src/asm/jump_combined_sysv_macho_gas.S + COMMAND bzr add libs/context/src/asm/jump_ppc32_sysv_macho_gas.S + COMMAND bzr add libs/context/src/asm/jump_ppc64_sysv_macho_gas.S + COMMAND bzr add libs/context/src/asm/make_combined_sysv_macho_gas.S + COMMAND bzr add libs/context/src/asm/make_ppc32_sysv_macho_gas.S + COMMAND bzr add libs/context/src/asm/make_ppc64_sysv_macho_gas.S + # [Mis-]use this step to erase all the boost headers and libraries before # replacing them below. UPDATE_COMMAND ${CMAKE_COMMAND} -E remove_directory "${BOOST_ROOT}" diff --git a/patches/patch_macosx_context_ppc_v2.patch b/patches/patch_macosx_context_ppc_v2.patch new file mode 100644 index 0000000000..e4e795608a --- /dev/null +++ b/patches/patch_macosx_context_ppc_v2.patch @@ -0,0 +1,782 @@ +=== modified file 'libs/context/build/Jamfile.v2' +--- libs/context/build/Jamfile.v2 2013-12-30 19:16:18 +0000 ++++ libs/context/build/Jamfile.v2 2014-01-03 18:10:41 +0000 +@@ -188,6 +188,15 @@ + elf + ; + ++alias asm_context_sources ++ : [ make asm/make_ppc32_sysv_macho_gas.o : asm/make_ppc32_sysv_macho_gas.S : @gas ] ++ [ make asm/jump_ppc32_sysv_macho_gas.o : asm/jump_ppc32_sysv_macho_gas.S : @gas ] ++ : 32 ++ power ++ mach-o ++ darwin ++ ; ++ + # POWERPC_64 + alias asm_context_sources + : asm/make_ppc64_sysv_elf_gas.S +@@ -215,6 +224,15 @@ + elf + ; + ++alias asm_context_sources ++ : [ make asm/make_ppc64_sysv_macho_gas.o : asm/make_ppc64_sysv_macho_gas.S : @gas ] ++ [ make asm/jump_ppc64_sysv_macho_gas.o : asm/jump_ppc64_sysv_macho_gas.S : @gas ] ++ : 64 ++ power ++ mach-o ++ darwin ++ ; ++ + # SPARC + alias asm_context_sources + : asm/make_sparc_sysv_elf_gas.S +@@ -414,6 +432,25 @@ + ; + + alias asm_context_sources ++ : asm/make_i386_x86_64_sysv_macho_gas.S ++ asm/jump_i386_x86_64_sysv_macho_gas.S ++ : 32_64 ++ x86 ++ mach-o ++ darwin ++ darwin ++ ; ++ ++alias asm_context_sources ++ : [ make asm/make_i386_x86_64_sysv_macho_gas.o : asm/make_i386_x86_64_sysv_macho_gas.S : @gas ] ++ [ make asm/jump_i386_x86_64_sysv_macho_gas.o : asm/jump_i386_x86_64_sysv_macho_gas.S : @gas ] ++ : 32_64 ++ x86 ++ mach-o ++ darwin ++ ; ++ ++alias asm_context_sources + : asm/make_x86_64_ms_pe_masm.asm + asm/jump_x86_64_ms_pe_masm.asm + dummy.cpp +@@ -424,6 +461,25 @@ + intel + ; + ++#COMBINED ++ ++alias asm_context_sources ++ : asm/make_combined_sysv_macho_gas.S ++ asm/jump_combined_sysv_macho_gas.S ++ : combined ++ mach-o ++ darwin ++ darwin ++ ; ++ ++alias asm_context_sources ++ : [ make asm/make_combined_sysv_macho_gas.o : asm/make_combined_sysv_macho_gas.S : @gas ] ++ [ make asm/jump_combined_sysv_macho_gas.o : asm/jump_combined_sysv_macho_gas.S : @gas ] ++ : combined ++ mach-o ++ darwin ++ ; ++ + alias asm_context_sources + : asm/make_x86_64_ms_pe_masm.asm + asm/jump_x86_64_ms_pe_masm.asm + +=== modified file 'libs/context/build/architecture.jam' +--- libs/context/build/architecture.jam 2013-12-30 19:16:18 +0000 ++++ libs/context/build/architecture.jam 2014-01-02 10:47:09 +0000 +@@ -71,6 +71,14 @@ + { + return x86 ; + } ++ else if [ configure.builds /boost/architecture//ppc : $(properties) : ppc ] ++ { ++ return ppc ; ++ } ++ else if [ configure.builds /boost/architecture//combined : $(properties) : combined ] ++ { ++ return combined ; ++ } + } + } + + +=== added file 'libs/context/src/asm/jump_combined_sysv_macho_gas.S' +--- libs/context/src/asm/jump_combined_sysv_macho_gas.S 1970-01-01 00:00:00 +0000 ++++ libs/context/src/asm/jump_combined_sysv_macho_gas.S 2014-01-03 17:46:02 +0000 +@@ -0,0 +1,20 @@ ++/* ++ Copyright Sergue E. Leontiev 2013. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++// Stub file for universal binary ++ ++#if defined(__i386__) ++ #include "jump_i386_sysv_macho_gas.S" ++#elif defined(__x86_64__) ++ #include "jump_x86_64_sysv_macho_gas.S" ++#elif defined(__ppc__) ++ #include "jump_ppc32_sysv_macho_gas.S" ++#elif defined(__ppc64__) ++ #include "jump_ppc64_sysv_macho_gas.S" ++#else ++ #error "No arch's" ++#endif + +=== added file 'libs/context/src/asm/jump_ppc32_sysv_macho_gas.S' +--- libs/context/src/asm/jump_ppc32_sysv_macho_gas.S 1970-01-01 00:00:00 +0000 ++++ libs/context/src/asm/jump_ppc32_sysv_macho_gas.S 2014-01-03 15:18:19 +0000 +@@ -0,0 +1,180 @@ ++/* ++ Copyright Oliver Kowalke 2009. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++/******************************************************************* ++ * * ++ * ------------------------------------------------------------- * ++ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | * ++ * ------------------------------------------------------------- * ++ * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | * ++ * ------------------------------------------------------------- * ++ * | R13 | R14 | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | * ++ * ------------------------------------------------------------- * ++ * | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | * ++ * ------------------------------------------------------------- * ++ * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | R31 | SP | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 20 | 21 | 22 | | * ++ * ------------------------------------------------------------- * ++ * | 80 | 84 | 88 | | * ++ * ------------------------------------------------------------- * ++ * | CR | LR | PC | | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 23 | 24 | 25 | | * ++ * ------------------------------------------------------------- * ++ * | 92 | 96 | 100 | | * ++ * ------------------------------------------------------------- * ++ * | sp | size|| | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | * ++ * ------------------------------------------------------------- * ++ * | 104 | 108 | 112 | 116 | 120 | 124 | 128 | 132 | 136 | 140 | * ++ * ------------------------------------------------------------- * ++ * | F14 | F15 | F16 | F17 | F18 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | * ++ * ------------------------------------------------------------- * ++ * | 144 | 148 | 152 | 156 | 160 | 164 | 168 | 172 | 176 | 180 | * ++ * ------------------------------------------------------------- * ++ * | F19 | F20 | F21 | F22 | F23 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * ++ * ------------------------------------------------------------- * ++ * | 184 | 188 | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * ++ * ------------------------------------------------------------- * ++ * | F24 | F25 | F26 | F27 | F28 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | | * ++ * ------------------------------------------------------------- * ++ * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | | * ++ * ------------------------------------------------------------- * ++ * | F29 | F30 | F31 | fpscr | | * ++ * ------------------------------------------------------------- * ++ * * ++ * *****************************************************************/ ++ ++.machine ppc ++ ++.text ++.globl _jump_fcontext ++.align 2 ++_jump_fcontext: ++ stw r13, 0(r3) ; save R13 ++ stw r14, 4(r3) ; save R14 ++ stw r15, 8(r3) ; save R15 ++ stw r16, 12(r3) ; save R16 ++ stw r17, 16(r3) ; save R17 ++ stw r18, 20(r3) ; save R18 ++ stw r19, 24(r3) ; save R19 ++ stw r20, 28(r3) ; save R20 ++ stw r21, 32(r3) ; save R21 ++ stw r22, 36(r3) ; save R22 ++ stw r23, 40(r3) ; save R23 ++ stw r24, 44(r3) ; save R24 ++ stw r25, 48(r3) ; save R25 ++ stw r26, 52(r3) ; save R26 ++ stw r27, 56(r3) ; save R27 ++ stw r28, 60(r3) ; save R28 ++ stw r29, 64(r3) ; save R29 ++ stw r30, 68(r3) ; save R30 ++ stw r31, 72(r3) ; save R31 ++ stw r1, 76(r3) ; save SP ++ ++ mfcr r0 ; load CR ++ stw r0, 80(r3) ; save CR ++ mflr r0 ; load LR ++ stw r0, 84(r3) ; save LR ++ stw r0, 88(r3) ; save LR as PC ++ ++ cmpwi cr7, r6, 0 ; test if fpu env should be preserved ++ beq cr7, l1 ++ ++ stfd f14, 104(r3) ; save F14 ++ stfd f15, 112(r3) ; save F15 ++ stfd f16, 120(r3) ; save F16 ++ stfd f17, 128(r3) ; save F17 ++ stfd f18, 136(r3) ; save F18 ++ stfd f19, 144(r3) ; save F19 ++ stfd f20, 152(r3) ; save F20 ++ stfd f21, 160(r3) ; save F21 ++ stfd f22, 168(r3) ; save F22 ++ stfd f23, 176(r3) ; save F23 ++ stfd f24, 184(r3) ; save F24 ++ stfd f25, 192(r3) ; save F25 ++ stfd f26, 200(r3) ; save F26 ++ stfd f27, 208(r3) ; save F27 ++ stfd f28, 216(r3) ; save F28 ++ stfd f29, 224(r3) ; save F29 ++ stfd f30, 232(r3) ; save F30 ++ stfd f31, 240(r3) ; save F31 ++ mffs f0 ; load FPSCR ++ stfd f0, 248(r3) ; save FPSCR ++ ++ lfd f14, 104(r4) ; restore F14 ++ lfd f15, 112(r4) ; restore F15 ++ lfd f16, 120(r4) ; restore F16 ++ lfd f17, 128(r4) ; restore F17 ++ lfd f18, 136(r4) ; restore F18 ++ lfd f19, 144(r4) ; restore F19 ++ lfd f20, 152(r4) ; restore F20 ++ lfd f21, 160(r4) ; restore F21 ++ lfd f22, 168(r4) ; restore F22 ++ lfd f23, 176(r4) ; restore F23 ++ lfd f24, 184(r4) ; restore F24 ++ lfd f25, 192(r4) ; restore F25 ++ lfd f26, 200(r4) ; restore F26 ++ lfd f27, 208(r4) ; restore F27 ++ lfd f28, 216(r4) ; restore F28 ++ lfd f29, 224(r4) ; restore F29 ++ lfd f30, 232(r4) ; restore F30 ++ lfd f31, 240(r4) ; restore F31 ++ lfd f0, 248(r4) ; load FPSCR ++ mtfsf 0xff, f0 ; restore FPSCR ++l1: ++ ++ lwz r13, 0(r4) ; restore R13 ++ lwz r14, 4(r4) ; restore R14 ++ lwz r15, 8(r4) ; restore R15 ++ lwz r16, 12(r4) ; restore R16 ++ lwz r17, 16(r4) ; restore R17 ++ lwz r18, 20(r4) ; restore R18 ++ lwz r19, 24(r4) ; restore R19 ++ lwz r20, 28(r4) ; restore R20 ++ lwz r21, 32(r4) ; restore R21 ++ lwz r22, 36(r4) ; restore R22 ++ lwz r23, 40(r4) ; restore R23 ++ lwz r24, 44(r4) ; restore R24 ++ lwz r25, 48(r4) ; restore R25 ++ lwz r26, 52(r4) ; restore R26 ++ lwz r27, 56(r4) ; restore R27 ++ lwz r28, 60(r4) ; restore R28 ++ lwz r29, 64(r4) ; restore R29 ++ lwz r30, 68(r4) ; restore R30 ++ lwz r31, 72(r4) ; restore R31 ++ lwz r1, 76(r4) ; restore SP ++ ++ lwz r0, 80(r4) ; load CR ++ mtcr r0 ; restore CR ++ lwz r0, 84(r4) ; load LR ++ mtlr r0 ; restore LR ++ ++ mr r3, r5 ; use third arg as return value after jump ++ ; and as first arg in context function ++ ++ lwz r0, 88(r4) ; load PC ++ mtctr r0 ; restore CTR ++ ++ bctr ; jump to context + +=== added file 'libs/context/src/asm/jump_ppc64_sysv_macho_gas.S' +--- libs/context/src/asm/jump_ppc64_sysv_macho_gas.S 1970-01-01 00:00:00 +0000 ++++ libs/context/src/asm/jump_ppc64_sysv_macho_gas.S 2014-01-03 17:54:53 +0000 +@@ -0,0 +1,193 @@ ++/* ++ Copyright Oliver Kowalke 2009. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++/******************************************************************* ++ * * ++ * ------------------------------------------------------------- * ++ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | * ++ * ------------------------------------------------------------- * ++ * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | * ++ * ------------------------------------------------------------- * ++ * | R13 | R14 | R15 | R16 | R17 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | * ++ * ------------------------------------------------------------- * ++ * | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | * ++ * ------------------------------------------------------------- * ++ * | R18 | R19 | R20 | R21 | R22 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | * ++ * ------------------------------------------------------------- * ++ * | 80 | 84 | 88 | 92 | 96 | 100 | 104 | 108 | 112 | 116 | * ++ * ------------------------------------------------------------- * ++ * | R23 | R24 | R25 | R26 | R27 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * ++ * ------------------------------------------------------------- * ++ * | 120 | 124 | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * ++ * ------------------------------------------------------------- * ++ * | R28 | R29 | R30 | R31 | SP | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 40 | 41 | 42 | 43 | 44 | 45 | | * ++ * ------------------------------------------------------------- * ++ * | 160 | 164 | 168 | 172 | 176 | 180 | | * ++ * ------------------------------------------------------------- * ++ * | CR | LR | PC | | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 46 | 47 | 48 | 49 | | * ++ * ------------------------------------------------------------- * ++ * | 184 | 188 | 192 | 196 | | * ++ * ------------------------------------------------------------- * ++ * | sp | size | | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | * ++ * ------------------------------------------------------------- * ++ * | 200 | 204 | 208 | 212 | 216 | 220 | 224 | 228 | 232 | 236 | * ++ * ------------------------------------------------------------- * ++ * | F14 | F15 | F16 | F17 | F18 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | * ++ * ------------------------------------------------------------- * ++ * | 240 | 244 | 248 | 252 | 256 | 260 | 264 | 268 | 272 | 276 | * ++ * ------------------------------------------------------------- * ++ * | F19 | F20 | F21 | F22 | F23 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * ++ * ------------------------------------------------------------- * ++ * | 280 | 284 | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * ++ * ------------------------------------------------------------- * ++ * | F24 | F25 | F26 | F27 | F28 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | | * ++ * ------------------------------------------------------------- * ++ * | 320 | 324 | 328 | 332 | 336 | 340 | 344 | 348 | | * ++ * ------------------------------------------------------------- * ++ * | F29 | F30 | F31 | fpscr | | * ++ * ------------------------------------------------------------- * ++ * * ++ * *****************************************************************/ ++ ++.text ++.align 2 ++.globl jump_fcontext ++ ++_jump_fcontext: ++ std r13, 0(r3) ; save R13 ++ std r14, 8(r3) ; save R14 ++ std r15, 16(r3) ; save R15 ++ std r16, 24(r3) ; save R16 ++ std r17, 32(r3) ; save R17 ++ std r18, 40(r3) ; save R18 ++ std r19, 48(r3) ; save R19 ++ std r20, 56(r3) ; save R20 ++ std r21, 64(r3) ; save R21 ++ std r22, 72(r3) ; save R22 ++ std r23, 80(r3) ; save R23 ++ std r24, 88(r3) ; save R24 ++ std r25, 96(r3) ; save R25 ++ std r26, 104(r3) ; save R26 ++ std r27, 112(r3) ; save R27 ++ std r28, 120(r3) ; save R28 ++ std r29, 128(r3) ; save R29 ++ std r30, 136(r3) ; save R30 ++ std r31, 144(r3) ; save R31 ++ std r1, 152(r3) ; save SP ++ ++ mfcr r0 ; load CR ++ std r0, 160(r3) ; save CR ++ mflr r0 ; load LR ++ std r0, 168(r3) ; save LR ++ std r0, 176(r3) ; save LR as PC ++ ++ cmpwi cr7, r6, 0 ; test if fpu env should be preserved ++ beq cr7, l1 ++ ++ stfd f14, 200(r3) ; save F14 ++ stfd f15, 208(r3) ; save F15 ++ stfd f16, 216(r3) ; save F16 ++ stfd f17, 224(r3) ; save F17 ++ stfd f18, 232(r3) ; save F18 ++ stfd f19, 240(r3) ; save F19 ++ stfd f20, 248(r3) ; save F20 ++ stfd f21, 256(r3) ; save F21 ++ stfd f22, 264(r3) ; save F22 ++ stfd f23, 272(r3) ; save F23 ++ stfd f24, 280(r3) ; save F24 ++ stfd f25, 288(r3) ; save F25 ++ stfd f26, 296(r3) ; save F26 ++ stfd f27, 304(r3) ; save F27 ++ stfd f28, 312(r3) ; save F28 ++ stfd f29, 320(r3) ; save F29 ++ stfd f30, 328(r3) ; save F30 ++ stfd f31, 336(r3) ; save F31 ++ mffs f0 ; load FPSCR ++ stfd f0, 344(r3) ; save FPSCR ++ ++ lfd f14, 200(r4) ; restore F14 ++ lfd f15, 208(r4) ; restore F15 ++ lfd f16, 216(r4) ; restore F16 ++ lfd f17, 224(r4) ; restore F17 ++ lfd f18, 232(r4) ; restore F18 ++ lfd f19, 240(r4) ; restore F19 ++ lfd f20, 248(r4) ; restore F20 ++ lfd f21, 256(r4) ; restore F21 ++ lfd f22, 264(r4) ; restore F22 ++ lfd f23, 272(r4) ; restore F23 ++ lfd f24, 280(r4) ; restore F24 ++ lfd f25, 288(r4) ; restore F25 ++ lfd f26, 296(r4) ; restore F26 ++ lfd f27, 304(r4) ; restore F27 ++ lfd f28, 312(r4) ; restore F28 ++ lfd f29, 320(r4) ; restore F29 ++ lfd f30, 328(r4) ; restore F30 ++ lfd f31, 336(r4) ; restore F31 ++ lfd f0, 344(r4) ; load FPSCR ++ mtfsf 0xff, f0 ; restore FPSCR ++l1: ++ ++ ld r13, 0(r4) ; restore R13 ++ ld r14, 8(r4) ; restore R14 ++ ld r15, 16(r4) ; restore R15 ++ ld r16, 24(r4) ; restore R16 ++ ld r17, 32(r4) ; restore R17 ++ ld r18, 40(r4) ; restore R18 ++ ld r19, 48(r4) ; restore R19 ++ ld r20, 56(r4) ; restore R20 ++ ld r21, 64(r4) ; restore R21 ++ ld r22, 72(r4) ; restore R22 ++ ld r23, 80(r4) ; restore R23 ++ ld r24, 88(r4) ; restore R24 ++ ld r25, 96(r4) ; restore R25 ++ ld r26, 104(r4) ; restore R26 ++ ld r27, 112(r4) ; restore R27 ++ ld r28, 120(r4) ; restore R28 ++ ld r29, 128(r4) ; restore R29 ++ ld r30, 136(r4) ; restore r30 ++ ld r31, 144(r4) ; restore r31 ++ ld r1, 152(r4) ; restore SP ++ ++ ld r0, 160(r4) ; load CR ++ mtcr r0 ; restore CR ++ ld r0, 168(r4) ; load LR ++ mtlr r0 ; restore LR ++ ++ mr r3, r5 ; use third arg as return value after jump ++ ; and as first arg in context function ++ ++ ld r0, 176(r4) ; load PC ++ mtctr r0 ; restore CTR ++ ++ bctr ; jump to context + +=== added file 'libs/context/src/asm/make_combined_sysv_macho_gas.S' +--- libs/context/src/asm/make_combined_sysv_macho_gas.S 1970-01-01 00:00:00 +0000 ++++ libs/context/src/asm/make_combined_sysv_macho_gas.S 2014-01-03 17:50:32 +0000 +@@ -0,0 +1,20 @@ ++/* ++ Copyright Sergue E. Leontiev 2013. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++// Stub file for universal binary ++ ++#if defined(__i386__) ++ #include "make_i386_sysv_macho_gas.S" ++#elif defined(__x86_64__) ++ #include "make_x86_64_sysv_macho_gas.S" ++#elif defined(__ppc__) ++ #include "make_ppc32_sysv_macho_gas.S" ++#elif defined(__ppc64__) ++ #include "make_ppc64_sysv_macho_gas.S" ++#else ++ #error "No arch's" ++#endif + +=== added file 'libs/context/src/asm/make_ppc32_sysv_macho_gas.S' +--- libs/context/src/asm/make_ppc32_sysv_macho_gas.S 1970-01-01 00:00:00 +0000 ++++ libs/context/src/asm/make_ppc32_sysv_macho_gas.S 2014-01-02 21:27:23 +0000 +@@ -0,0 +1,109 @@ ++/* ++ Copyright Oliver Kowalke 2009. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++/******************************************************************* ++ * * ++ * ------------------------------------------------------------- * ++ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | * ++ * ------------------------------------------------------------- * ++ * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | * ++ * ------------------------------------------------------------- * ++ * | R13 | R14 | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | * ++ * ------------------------------------------------------------- * ++ * | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | * ++ * ------------------------------------------------------------- * ++ * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | R31 | SP | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 20 | 21 | 22 | | * ++ * ------------------------------------------------------------- * ++ * | 80 | 84 | 88 | | * ++ * ------------------------------------------------------------- * ++ * | CR | LR | PC | | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 23 | 24 | 25 | | * ++ * ------------------------------------------------------------- * ++ * | 92 | 96 | 100 | | * ++ * ------------------------------------------------------------- * ++ * | sp | size|| | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | * ++ * ------------------------------------------------------------- * ++ * | 104 | 108 | 112 | 116 | 120 | 124 | 128 | 132 | 136 | 140 | * ++ * ------------------------------------------------------------- * ++ * | F14 | F15 | F16 | F17 | F18 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | * ++ * ------------------------------------------------------------- * ++ * | 144 | 148 | 152 | 156 | 160 | 164 | 168 | 172 | 176 | 180 | * ++ * ------------------------------------------------------------- * ++ * | F19 | F20 | F21 | F22 | F23 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * ++ * ------------------------------------------------------------- * ++ * | 184 | 188 | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * ++ * ------------------------------------------------------------- * ++ * | F24 | F25 | F26 | F27 | F28 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | | * ++ * ------------------------------------------------------------- * ++ * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | | * ++ * ------------------------------------------------------------- * ++ * | F29 | F30 | F31 | fpscr | | * ++ * ------------------------------------------------------------- * ++ * * ++ * *****************************************************************/ ++ ++.text ++.globl _make_fcontext ++.align 2 ++_make_fcontext: ++ mflr r6 ; save return address into R6 ++ ++ mr r0, r3 ++ subi r3, r3, 256 ; reserve space for fcontext_t at top of context stack ++ ++ ; call align_stack, R3 contains address at 16 byte boundary after return ++ ; == pointer to fcontext_t and address of context stack ++ clrrwi r3, r3, 4 ++ ++ stw r0, 92(r3) ; save address of context stack (base) in fcontext_t ++ stw r4, 96(r3) ; save context stack size in fcontext_t ++ stw r5, 88(r3) ; save address of context function in fcontext_t ++ ++ subi r0, r3, 64 ; reserve 64 bytes (linkage + parameter area), R4 % 16 == 0 ++ stw r0, 76(r3) ; save address in R3 as stack pointer for context function ++ ++ mflr r0 ; load LR ++ bl l1 ; jump to label 1 ++l1: ++ mflr r4 ; load LR into R4 ++ addi r4, r4, lo16((finish - .)+4) ; compute abs address of label finish ++ mtlr r0 ; restore LR ++ stw r4, 84(r3) ; save address of finish as return address for context function ++ ; entered after context function returns ++ ++ mtlr r6 ; restore return address from R6 ++ ++ blr ++ ++finish: ++ ; SP points to same address as SP on entry of context function ++ mflr r0 ; save return address into R0 ++ stw r0, 4(r1) ; save return address on stack, set up stack frame ++ stwu r1, -16(r1) ; allocate stack space, SP % 16 == 0 ++ ++ li r3, 0 ; exit code is zero ++ bl __exit ; exit application + +=== added file 'libs/context/src/asm/make_ppc64_sysv_macho_gas.S' +--- libs/context/src/asm/make_ppc64_sysv_macho_gas.S 1970-01-01 00:00:00 +0000 ++++ libs/context/src/asm/make_ppc64_sysv_macho_gas.S 2014-01-03 18:04:45 +0000 +@@ -0,0 +1,123 @@ ++/* ++ Copyright Oliver Kowalke 2009. ++ Distributed under the Boost Software License, Version 1.0. ++ (See accompanying file LICENSE_1_0.txt or copy at ++ http://www.boost.org/LICENSE_1_0.txt) ++*/ ++ ++/******************************************************************* ++ * * ++ * ------------------------------------------------------------- * ++ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | * ++ * ------------------------------------------------------------- * ++ * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | * ++ * ------------------------------------------------------------- * ++ * | R13 | R14 | R15 | R16 | R17 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | * ++ * ------------------------------------------------------------- * ++ * | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | * ++ * ------------------------------------------------------------- * ++ * | R18 | R19 | R20 | R21 | R22 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | * ++ * ------------------------------------------------------------- * ++ * | 80 | 84 | 88 | 92 | 96 | 100 | 104 | 108 | 112 | 116 | * ++ * ------------------------------------------------------------- * ++ * | R23 | R24 | R25 | R26 | R27 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * ++ * ------------------------------------------------------------- * ++ * | 120 | 124 | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * ++ * ------------------------------------------------------------- * ++ * | R28 | R29 | R30 | R31 | SP | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 40 | 41 | 42 | 43 | 44 | 45 | | * ++ * ------------------------------------------------------------- * ++ * | 160 | 164 | 168 | 172 | 176 | 180 | | * ++ * ------------------------------------------------------------- * ++ * | CR | LR | PC | | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 46 | 47 | 48 | 49 | | * ++ * ------------------------------------------------------------- * ++ * | 184 | 188 | 192 | 196 | | * ++ * ------------------------------------------------------------- * ++ * | sp | size | | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | * ++ * ------------------------------------------------------------- * ++ * | 200 | 204 | 208 | 212 | 216 | 220 | 224 | 228 | 232 | 236 | * ++ * ------------------------------------------------------------- * ++ * | F14 | F15 | F16 | F17 | F18 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | * ++ * ------------------------------------------------------------- * ++ * | 240 | 244 | 248 | 252 | 256 | 260 | 264 | 268 | 272 | 276 | * ++ * ------------------------------------------------------------- * ++ * | F19 | F20 | F21 | F22 | F23 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * ++ * ------------------------------------------------------------- * ++ * | 280 | 284 | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * ++ * ------------------------------------------------------------- * ++ * | F24 | F25 | F26 | F27 | F28 | * ++ * ------------------------------------------------------------- * ++ * ------------------------------------------------------------- * ++ * | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | | * ++ * ------------------------------------------------------------- * ++ * | 320 | 324 | 328 | 332 | 336 | 340 | 344 | 348 | | * ++ * ------------------------------------------------------------- * ++ * | F29 | F30 | F31 | fpscr | | * ++ * ------------------------------------------------------------- * ++ * * ++ * *****************************************************************/ ++ ++.text ++.globl _make_fcontext ++_make_fcontext: ++ mflr r6 ; save return address into R6 ++ ++ mr r0, r3 ++ subi r3, r3, 352 ; reserve space for fcontext_t at top of context stack ++ ++ ; call align_stack, R3 contains address at 16 byte boundary after return ++ ; == pointer to fcontext_t and address of context stack ++ clrrdi r3, r3, 4 ++ ++ std r0, 184(r3) ; save address of context stack (base) in fcontext_t ++ std r4, 192(r3) ; save context stack size in fcontext_t ++ std r5, 176(r3) ; save address of context function in fcontext_t ++ ++ subi r0, r3, 64 ; 64 bytes on stack for parameter area (== 8 registers) ++ std r0, 152(r3) ; save the stack base ++ ++ mflr r0 ; load LR ++ bl l1 ; jump to label 1 ++l1: ++ mflr r4 ; load LR into R4 ++ addi r4, r4, lo16((finish - .) + 4) ; compute abs address of label finish ++ mtlr r0 ; restore LR ++ std r4, 168(r3) ; save address of finish as return address for context function ++ ; entered after context function returns ++ ++ mtlr r6 ; restore return address from R6 ++ ++ blr ++ ++finish: ++ ; SP points to same address as SP on entry of context function ++ mflr r0 ; save return address into R0 ++ stw r0, 8(r1) ; save return address on stack, set up stack frame ++ stwu r1, -32(r1) ; allocate stack space, SP % 16 == 0 ++ ++ li r3, 0 ; set return value to zero ++ bl __exit ; exit application ++ nop + From 5437f43790a947bef0524f65a71d29396b3a2cde Mon Sep 17 00:00:00 2001 From: Marco Serantoni Date: Sat, 4 Jan 2014 14:40:22 +0100 Subject: [PATCH 57/57] =?UTF-8?q?[MacOSX]=C2=A0fixes=20some=20corner=20cas?= =?UTF-8?q?e=20compiling=20on=20x86=20in=20boost?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- patches/boost_macosx_x86.patch | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/patches/boost_macosx_x86.patch b/patches/boost_macosx_x86.patch index f14c5f49f8..9f8f1f898f 100644 --- a/patches/boost_macosx_x86.patch +++ b/patches/boost_macosx_x86.patch @@ -5,16 +5,16 @@ ; alias asm_context_sources -+ : asm/make_i386_x86_64_sysv_macho_gas.S -+ asm/jump_i386_x86_64_sysv_macho_gas.S -+ : 32_64 -+ x86 -+ mach-o -+ darwin -+ darwin -+ ; -+ -+alias asm_context_sources ++# : asm/make_i386_x86_64_sysv_macho_gas.S ++# asm/jump_i386_x86_64_sysv_macho_gas.S ++# : 32_64 ++# x86 ++# mach-o ++# darwin ++# darwin ++# ; ++# ++#alias asm_context_sources + : [ make asm/make_i386_x86_64_sysv_macho_gas.o : asm/make_i386_x86_64_sysv_macho_gas.S : @gas ] + [ make asm/jump_i386_x86_64_sysv_macho_gas.o : asm/jump_i386_x86_64_sysv_macho_gas.S : @gas ] + : 32_64