Merge pull request #3879 from sliptonic/feature/vcarve
[Path] Feature/vcarve using new PathVoronoi from mlampert. Needs testing
This commit is contained in:
@@ -46,6 +46,14 @@
|
||||
#include "FeaturePathShape.h"
|
||||
#include "AreaPy.h"
|
||||
#include "FeatureArea.h"
|
||||
#include "Voronoi.h"
|
||||
#include "VoronoiCell.h"
|
||||
#include "VoronoiCellPy.h"
|
||||
#include "VoronoiEdge.h"
|
||||
#include "VoronoiEdgePy.h"
|
||||
#include "VoronoiPy.h"
|
||||
#include "VoronoiVertex.h"
|
||||
#include "VoronoiVertexPy.h"
|
||||
|
||||
namespace Path {
|
||||
extern PyObject* initModule();
|
||||
@@ -67,11 +75,15 @@ PyMOD_INIT_FUNC(Path)
|
||||
Base::Console().Log("Loading Path module... done\n");
|
||||
|
||||
// Add Types to module
|
||||
Base::Interpreter().addType(&Path::CommandPy ::Type, pathModule, "Command");
|
||||
Base::Interpreter().addType(&Path::PathPy ::Type, pathModule, "Path");
|
||||
Base::Interpreter().addType(&Path::ToolPy ::Type, pathModule, "Tool");
|
||||
Base::Interpreter().addType(&Path::TooltablePy ::Type, pathModule, "Tooltable");
|
||||
Base::Interpreter().addType(&Path::AreaPy ::Type, pathModule, "Area");
|
||||
Base::Interpreter().addType(&Path::CommandPy ::Type, pathModule, "Command");
|
||||
Base::Interpreter().addType(&Path::PathPy ::Type, pathModule, "Path");
|
||||
Base::Interpreter().addType(&Path::ToolPy ::Type, pathModule, "Tool");
|
||||
Base::Interpreter().addType(&Path::TooltablePy ::Type, pathModule, "Tooltable");
|
||||
Base::Interpreter().addType(&Path::AreaPy ::Type, pathModule, "Area");
|
||||
Base::Interpreter().addType(&Path::VoronoiPy ::Type, pathModule, "Voronoi");
|
||||
Base::Interpreter().addType(&Path::VoronoiCellPy ::Type, pathModule, "VoronoiCell");
|
||||
Base::Interpreter().addType(&Path::VoronoiEdgePy ::Type, pathModule, "VoronoiEdge");
|
||||
Base::Interpreter().addType(&Path::VoronoiVertexPy ::Type, pathModule, "VoronoiVertex");
|
||||
|
||||
// NOTE: To finish the initialization of our own type objects we must
|
||||
// call PyType_Ready, otherwise we run into a segmentation fault, later on.
|
||||
@@ -94,6 +106,10 @@ PyMOD_INIT_FUNC(Path)
|
||||
Path::FeatureAreaPython ::init();
|
||||
Path::FeatureAreaView ::init();
|
||||
Path::FeatureAreaViewPython ::init();
|
||||
Path::Voronoi ::init();
|
||||
Path::VoronoiCell ::init();
|
||||
Path::VoronoiEdge ::init();
|
||||
Path::VoronoiVertex ::init();
|
||||
|
||||
PyMOD_Return(pathModule);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ generate_from_xml(TooltablePy)
|
||||
generate_from_xml(FeaturePathCompoundPy)
|
||||
generate_from_xml(AreaPy)
|
||||
generate_from_xml(FeatureAreaPy)
|
||||
generate_from_xml(VoronoiPy)
|
||||
generate_from_xml(VoronoiCellPy)
|
||||
generate_from_xml(VoronoiEdgePy)
|
||||
generate_from_xml(VoronoiVertexPy)
|
||||
|
||||
SET(Python_SRCS
|
||||
CommandPy.xml
|
||||
@@ -52,6 +56,14 @@ SET(Python_SRCS
|
||||
AreaPyImp.cpp
|
||||
FeatureAreaPy.xml
|
||||
FeatureAreaPyImp.cpp
|
||||
VoronoiPy.xml
|
||||
VoronoiPyImp.cpp
|
||||
VoronoiCellPy.xml
|
||||
VoronoiCellPyImp.cpp
|
||||
VoronoiEdgePy.xml
|
||||
VoronoiEdgePyImp.cpp
|
||||
VoronoiVertexPy.xml
|
||||
VoronoiVertexPyImp.cpp
|
||||
)
|
||||
|
||||
SET(Mod_SRCS
|
||||
@@ -90,6 +102,14 @@ SET(Path_SRCS
|
||||
FeatureArea.h
|
||||
PathSegmentWalker.h
|
||||
PathSegmentWalker.cpp
|
||||
Voronoi.cpp
|
||||
Voronoi.h
|
||||
VoronoiCell.cpp
|
||||
VoronoiCell.h
|
||||
VoronoiEdge.cpp
|
||||
VoronoiEdge.h
|
||||
VoronoiVertex.cpp
|
||||
VoronoiVertex.h
|
||||
${Mod_SRCS}
|
||||
${Python_SRCS}
|
||||
)
|
||||
|
||||
310
src/Mod/Path/App/Voronoi.cpp
Normal file
310
src/Mod/Path/App/Voronoi.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <cinttypes>
|
||||
# include <iomanip>
|
||||
# include <boost/algorithm/string.hpp>
|
||||
# include <boost/lexical_cast.hpp>
|
||||
#endif
|
||||
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/Writer.h>
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Exception.h>
|
||||
#include "Voronoi.h"
|
||||
|
||||
using namespace Base;
|
||||
using namespace Path;
|
||||
|
||||
TYPESYSTEM_SOURCE(Path::Voronoi , Base::BaseClass);
|
||||
|
||||
// Helpers
|
||||
|
||||
// Voronoi::diagram_type
|
||||
|
||||
Voronoi::diagram_type::diagram_type()
|
||||
:scale(1000)
|
||||
{
|
||||
}
|
||||
|
||||
double Voronoi::diagram_type::getScale() const {
|
||||
return scale;
|
||||
}
|
||||
|
||||
void Voronoi::diagram_type::setScale(double s) {
|
||||
scale = s;
|
||||
}
|
||||
|
||||
Base::Vector3d Voronoi::diagram_type::scaledVector(double x, double y, double z) const {
|
||||
return Base::Vector3d(x / scale, y / scale, z);
|
||||
}
|
||||
|
||||
Base::Vector3d Voronoi::diagram_type::scaledVector(const point_type &p, double z) const {
|
||||
return scaledVector(p.x(), p.y(), z);
|
||||
}
|
||||
|
||||
Base::Vector3d Voronoi::diagram_type::scaledVector(const vertex_type &v, double z) const {
|
||||
return scaledVector(v.x(), v.y(), z);
|
||||
}
|
||||
|
||||
|
||||
int Voronoi::diagram_type::index(const Voronoi::diagram_type::cell_type *cell) const {
|
||||
auto it = cell_index.find(intptr_t(cell));
|
||||
if (it == cell_index.end()) {
|
||||
return Voronoi::InvalidIndex;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
int Voronoi::diagram_type::index(const Voronoi::diagram_type::edge_type *edge) const {
|
||||
auto it = edge_index.find(intptr_t(edge));
|
||||
if (it == edge_index.end()) {
|
||||
return Voronoi::InvalidIndex;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
int Voronoi::diagram_type::index(const Voronoi::diagram_type::vertex_type *vertex) const {
|
||||
auto it = vertex_index.find(intptr_t(vertex));
|
||||
if (it == vertex_index.end()) {
|
||||
return Voronoi::InvalidIndex;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void Voronoi::diagram_type::reIndex() {
|
||||
int idx = 0;
|
||||
cell_index.clear();
|
||||
edge_index.clear();
|
||||
vertex_index.clear();
|
||||
|
||||
idx = 0;
|
||||
for (auto it = cells().begin(); it != cells().end(); ++it, ++idx) {
|
||||
cell_index[intptr_t(&(*it))] = idx;
|
||||
}
|
||||
idx = 0;
|
||||
for (auto it = edges().begin(); it != edges().end(); ++it, ++idx) {
|
||||
edge_index[intptr_t(&(*it))] = idx;
|
||||
}
|
||||
idx = 0;
|
||||
for (auto it = vertices().begin(); it != vertices().end(); ++it, ++idx) {
|
||||
vertex_index[intptr_t(&(*it))] = idx;
|
||||
}
|
||||
}
|
||||
|
||||
Voronoi::point_type Voronoi::diagram_type::retrievePoint(const Voronoi::diagram_type::cell_type *cell) const {
|
||||
Voronoi::diagram_type::cell_type::source_index_type index = cell->source_index();
|
||||
Voronoi::diagram_type::cell_type::source_category_type category = cell->source_category();
|
||||
if (category == boost::polygon::SOURCE_CATEGORY_SINGLE_POINT) {
|
||||
return points[index];
|
||||
}
|
||||
index -= points.size();
|
||||
if (category == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) {
|
||||
return low(segments[index]);
|
||||
} else {
|
||||
return high(segments[index]);
|
||||
}
|
||||
}
|
||||
|
||||
Voronoi::segment_type Voronoi::diagram_type::retrieveSegment(const Voronoi::diagram_type::cell_type *cell) const {
|
||||
Voronoi::diagram_type::cell_type::source_index_type index = cell->source_index() - points.size();
|
||||
return segments[index];
|
||||
}
|
||||
|
||||
|
||||
// Voronoi
|
||||
|
||||
Voronoi::Voronoi()
|
||||
:vd(new diagram_type)
|
||||
{
|
||||
}
|
||||
|
||||
Voronoi::~Voronoi()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Voronoi::addPoint(const Voronoi::point_type &p) {
|
||||
Voronoi::point_type pi;
|
||||
pi.x(p.x() * vd->getScale());
|
||||
pi.y(p.y() * vd->getScale());
|
||||
vd->points.push_back(pi);
|
||||
}
|
||||
|
||||
void Voronoi::addSegment(const Voronoi::segment_type &s) {
|
||||
Voronoi::point_type pil, pih;
|
||||
pil.x(low(s).x() * vd->getScale());
|
||||
pil.y(low(s).y() * vd->getScale());
|
||||
pih.x(high(s).x() * vd->getScale());
|
||||
pih.y(high(s).y() * vd->getScale());
|
||||
vd->segments.push_back(segment_type(pil, pih));
|
||||
}
|
||||
|
||||
long Voronoi::numPoints() const {
|
||||
return vd->points.size();
|
||||
}
|
||||
|
||||
long Voronoi::numSegments() const {
|
||||
return vd->segments.size();
|
||||
}
|
||||
|
||||
|
||||
long Voronoi::numCells() const {
|
||||
return vd->num_cells();
|
||||
}
|
||||
|
||||
long Voronoi::numEdges() const {
|
||||
return vd->num_edges();
|
||||
}
|
||||
|
||||
long Voronoi::numVertices() const {
|
||||
return vd->num_vertices();
|
||||
}
|
||||
|
||||
void Voronoi::construct()
|
||||
{
|
||||
vd->clear();
|
||||
construct_voronoi(vd->points.begin(), vd->points.end(), vd->segments.begin(), vd->segments.end(), (voronoi_diagram_type*)vd);
|
||||
vd->reIndex();
|
||||
}
|
||||
|
||||
void Voronoi::colorExterior(const Voronoi::diagram_type::edge_type *edge, std::size_t colorValue) {
|
||||
if (edge->color()) {
|
||||
// end recursion
|
||||
return;
|
||||
}
|
||||
edge->color(colorValue);
|
||||
edge->twin()->color(colorValue);
|
||||
auto v = edge->vertex1();
|
||||
if (v == NULL || !edge->is_primary()) {
|
||||
return;
|
||||
}
|
||||
v->color(colorValue);
|
||||
auto e = v->incident_edge();
|
||||
do {
|
||||
colorExterior(e, colorValue);
|
||||
e = e->rot_next();
|
||||
} while (e != v->incident_edge());
|
||||
}
|
||||
|
||||
void Voronoi::colorExterior(Voronoi::color_type color) {
|
||||
for (diagram_type::const_edge_iterator it = vd->edges().begin(); it != vd->edges().end(); ++it) {
|
||||
if (it->is_infinite()) {
|
||||
colorExterior(&(*it), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Voronoi::colorTwins(Voronoi::color_type color) {
|
||||
for (diagram_type::const_edge_iterator it = vd->edges().begin(); it != vd->edges().end(); ++it) {
|
||||
if (!it->color()) {
|
||||
auto twin = it->twin();
|
||||
if (!twin->color()) {
|
||||
twin->color(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double Voronoi::diagram_type::angleOfSegment(int i, Voronoi::diagram_type::angle_map_t *angle) const {
|
||||
Voronoi::diagram_type::angle_map_t::const_iterator a = angle ? angle->find(i) : Voronoi::diagram_type::angle_map_t::const_iterator();
|
||||
if (!angle || a == angle->end()) {
|
||||
Voronoi::point_type p0 = low(segments[i]);
|
||||
Voronoi::point_type p1 = high(segments[i]);
|
||||
double ang = 0;
|
||||
if (p0.x() == p1.x()) {
|
||||
if ((p0.y() > 0 && p1.y() > 0) || (p0.y() > 0 && p1.y() > 0)) {
|
||||
ang = M_PI_2;
|
||||
} else {
|
||||
ang = -M_PI_2;
|
||||
}
|
||||
} else {
|
||||
ang = atan((p0.y() - p1.y()) / (p0.x() - p1.x()));
|
||||
}
|
||||
if (angle) {
|
||||
angle->insert(angle_map_t::value_type(i, ang));
|
||||
}
|
||||
return ang;
|
||||
}
|
||||
return a->second;
|
||||
}
|
||||
|
||||
static bool pointsMatch(const Voronoi::point_type &p0, const Voronoi::point_type &p1) {
|
||||
return long(p0.x()) == long(p1.x()) && long(p0.y()) == long(p1.y());
|
||||
}
|
||||
|
||||
bool Voronoi::diagram_type::segmentsAreConnected(int i, int j) const {
|
||||
return
|
||||
pointsMatch(low(segments[i]), low(segments[j]))
|
||||
|| pointsMatch(low(segments[i]), high(segments[j]))
|
||||
|| pointsMatch(high(segments[i]), low(segments[j]))
|
||||
|| pointsMatch(high(segments[i]), high(segments[j]));
|
||||
}
|
||||
|
||||
void Voronoi::colorColinear(Voronoi::color_type color, double degree) {
|
||||
double rad = degree * M_PI / 180;
|
||||
|
||||
Voronoi::diagram_type::angle_map_t angle;
|
||||
int psize = vd->points.size();
|
||||
|
||||
for (diagram_type::const_edge_iterator it = vd->edges().begin(); it != vd->edges().end(); ++it) {
|
||||
int i0 = it->cell()->source_index() - psize;
|
||||
int i1 = it->twin()->cell()->source_index() - psize;
|
||||
if (it->color() == 0
|
||||
&& it->cell()->contains_segment()
|
||||
&& it->twin()->cell()->contains_segment()
|
||||
&& vd->segmentsAreConnected(i0, i1)) {
|
||||
double a0 = vd->angleOfSegment(i0, &angle);
|
||||
double a1 = vd->angleOfSegment(i1, &angle);
|
||||
double a = a0 - a1;
|
||||
if (a > M_PI_2) {
|
||||
a -= M_PI;
|
||||
} else if (a < -M_PI_2) {
|
||||
a += M_PI;
|
||||
}
|
||||
if (fabs(a) < rad) {
|
||||
it->color(color);
|
||||
it->twin()->color(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Voronoi::resetColor(Voronoi::color_type color) {
|
||||
for (auto it = vd->cells().begin(); it != vd->cells().end(); ++it) {
|
||||
if (color == 0 || it->color() == color) {
|
||||
it->color(0);
|
||||
}
|
||||
}
|
||||
for (auto it = vd->edges().begin(); it != vd->edges().end(); ++it) {
|
||||
if (it->color() == color) {
|
||||
it->color(0);
|
||||
}
|
||||
}
|
||||
for (auto it = vd->vertices().begin(); it != vd->vertices().end(); ++it) {
|
||||
if (it->color() == color) {
|
||||
it->color(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
131
src/Mod/Path/App/Voronoi.h
Normal file
131
src/Mod/Path/App/Voronoi.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef PATH_VORONOI_H
|
||||
#define PATH_VORONOI_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <Base/BaseClass.h>
|
||||
#include <Base/Handle.h>
|
||||
#include <Base/Vector3D.h>
|
||||
|
||||
#include <vector>
|
||||
#include <boost/polygon/point_concept.hpp>
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
#include <boost/polygon/segment_concept.hpp>
|
||||
#include <boost/polygon/voronoi.hpp>
|
||||
|
||||
namespace Path
|
||||
{
|
||||
class PathExport Voronoi
|
||||
: public Base::BaseClass
|
||||
{
|
||||
TYPESYSTEM_HEADER();
|
||||
|
||||
public:
|
||||
//constructors
|
||||
Voronoi();
|
||||
~Voronoi();
|
||||
|
||||
typedef std::size_t color_type;
|
||||
static const int InvalidIndex = INT_MAX;
|
||||
static const color_type ColorMask = 0x07FFFFFFFFFFFFFFul; // top 5 bits reserved internally
|
||||
|
||||
// types
|
||||
typedef double coordinate_type;
|
||||
typedef boost::polygon::point_data<coordinate_type> point_type;
|
||||
typedef boost::polygon::segment_data<coordinate_type> segment_type;
|
||||
typedef boost::polygon::voronoi_diagram<double> voronoi_diagram_type;
|
||||
|
||||
class diagram_type
|
||||
: public voronoi_diagram_type
|
||||
, public Base::Handled
|
||||
{
|
||||
public:
|
||||
diagram_type();
|
||||
|
||||
double getScale() const;
|
||||
void setScale(double s);
|
||||
|
||||
Base::Vector3d scaledVector(double x, double y, double z) const;
|
||||
Base::Vector3d scaledVector(const point_type &p, double z) const;
|
||||
Base::Vector3d scaledVector(const vertex_type &v, double z) const;
|
||||
|
||||
typedef std::map<intptr_t, int> cell_map_type;
|
||||
typedef std::map<intptr_t, int> edge_map_type;
|
||||
typedef std::map<intptr_t, int> vertex_map_type;
|
||||
|
||||
int index(const cell_type *cell) const;
|
||||
int index(const edge_type *edge) const;
|
||||
int index(const vertex_type *vertex) const;
|
||||
|
||||
void reIndex();
|
||||
|
||||
std::vector<point_type> points;
|
||||
std::vector<segment_type> segments;
|
||||
|
||||
point_type retrievePoint(const cell_type *cell) const;
|
||||
segment_type retrieveSegment(const cell_type *cell) const;
|
||||
|
||||
typedef std::map<int, double> angle_map_t;
|
||||
double angleOfSegment(int i, angle_map_t *angle = 0) const;
|
||||
bool segmentsAreConnected(int i, int j) const;
|
||||
|
||||
private:
|
||||
double scale;
|
||||
cell_map_type cell_index;
|
||||
edge_map_type edge_index;
|
||||
vertex_map_type vertex_index;
|
||||
};
|
||||
|
||||
void addPoint(const point_type &p);
|
||||
void addSegment(const segment_type &p);
|
||||
long numPoints() const;
|
||||
long numSegments() const;
|
||||
|
||||
void construct();
|
||||
long numCells() const;
|
||||
long numEdges() const;
|
||||
long numVertices() const;
|
||||
|
||||
void resetColor(color_type color);
|
||||
void colorExterior(color_type color);
|
||||
void colorTwins(color_type color);
|
||||
void colorColinear(color_type color, double degree);
|
||||
|
||||
template<typename T>
|
||||
T* create(int index) {
|
||||
return new T(vd, index);
|
||||
}
|
||||
|
||||
double getScale() const { return vd->getScale(); }
|
||||
void setScale(double scale) { vd->setScale(scale); }
|
||||
|
||||
private:
|
||||
Base::Reference<diagram_type> vd;
|
||||
friend class VoronoiPy;
|
||||
void colorExterior(const Voronoi::diagram_type::edge_type *edge, std::size_t colorValue);
|
||||
};
|
||||
|
||||
} //namespace Path
|
||||
|
||||
#endif // PATH_VORONOI_H
|
||||
94
src/Mod/Path/App/VoronoiCell.cpp
Normal file
94
src/Mod/Path/App/VoronoiCell.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <cinttypes>
|
||||
# include <iomanip>
|
||||
# include <boost/algorithm/string.hpp>
|
||||
# include <boost/lexical_cast.hpp>
|
||||
#endif
|
||||
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/Writer.h>
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Exception.h>
|
||||
#include "Voronoi.h"
|
||||
#include "VoronoiCell.h"
|
||||
|
||||
using namespace Base;
|
||||
using namespace Path;
|
||||
|
||||
TYPESYSTEM_SOURCE(Path::VoronoiCell , Base::Persistence)
|
||||
|
||||
VoronoiCell::VoronoiCell(Voronoi::diagram_type *d, long index)
|
||||
: dia(d)
|
||||
, index(index)
|
||||
, ptr(0)
|
||||
{
|
||||
if (dia && long(dia->num_cells()) > index) {
|
||||
ptr = &(dia->cells()[index]);
|
||||
}
|
||||
}
|
||||
|
||||
VoronoiCell::VoronoiCell(Voronoi::diagram_type *d, const Voronoi::diagram_type::cell_type *e)
|
||||
: dia(d)
|
||||
, index(Voronoi::InvalidIndex)
|
||||
, ptr(e)
|
||||
{
|
||||
if (d && e) {
|
||||
index = dia->index(e);
|
||||
}
|
||||
}
|
||||
|
||||
VoronoiCell::~VoronoiCell() {
|
||||
}
|
||||
|
||||
bool VoronoiCell::isBound(void) const {
|
||||
if (ptr != 0 && dia.isValid() && index != Voronoi::InvalidIndex) {
|
||||
if (&(dia->cells()[index]) == ptr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ptr = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
Voronoi::point_type VoronoiCell::sourcePoint() const {
|
||||
int index = ptr->source_index();
|
||||
int category = ptr->source_category();
|
||||
if (category == boost::polygon::SOURCE_CATEGORY_SINGLE_POINT) {
|
||||
return dia->points[index];
|
||||
}
|
||||
if (category == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) {
|
||||
return low(dia->segments[index - dia->points.size()]);
|
||||
} else {
|
||||
return high(dia->segments[index - dia->points.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
Voronoi::segment_type VoronoiCell::sourceSegment() const {
|
||||
return dia->segments[ptr->source_index() - dia->points.size()];
|
||||
}
|
||||
|
||||
57
src/Mod/Path/App/VoronoiCell.h
Normal file
57
src/Mod/Path/App/VoronoiCell.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef PATH_VORONOICELL_H
|
||||
#define PATH_VORONOICELL_H
|
||||
|
||||
#include <Base/Handle.h>
|
||||
#include <Base/BaseClass.h>
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/VectorPy.h>
|
||||
#include "Voronoi.h"
|
||||
|
||||
namespace Path
|
||||
{
|
||||
|
||||
class Voronoi;
|
||||
|
||||
class PathExport VoronoiCell
|
||||
: public Base::BaseClass
|
||||
{
|
||||
TYPESYSTEM_HEADER();
|
||||
public:
|
||||
|
||||
VoronoiCell(Voronoi::diagram_type *dia = 0, long index = Voronoi::InvalidIndex);
|
||||
VoronoiCell(Voronoi::diagram_type *dia, const Voronoi::diagram_type::cell_type *cell);
|
||||
~VoronoiCell();
|
||||
|
||||
bool isBound(void) const;
|
||||
|
||||
Voronoi::point_type sourcePoint() const;
|
||||
Voronoi::segment_type sourceSegment() const;
|
||||
|
||||
Base::Reference<Voronoi::diagram_type> dia;
|
||||
long index;
|
||||
mutable const Voronoi::diagram_type::cell_type *ptr;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
70
src/Mod/Path/App/VoronoiCellPy.xml
Normal file
70
src/Mod/Path/App/VoronoiCellPy.xml
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||
<PythonExport
|
||||
Father="BaseClassPy"
|
||||
Name="VoronoiCellPy"
|
||||
Twin="VoronoiCell"
|
||||
TwinPointer="VoronoiCell"
|
||||
Include="Mod/Path/App/Voronoi.h"
|
||||
FatherInclude="Base/BaseClassPy.h"
|
||||
Namespace="Path"
|
||||
FatherNamespace="Base"
|
||||
Constructor="true"
|
||||
RichCompare="true"
|
||||
Delete="true">
|
||||
<Documentation>
|
||||
<Author Licence="LGPL" Name="sliptonic" EMail="shopinthewoods@gmail.com" />
|
||||
<UserDocu>Cell of a Voronoi diagram</UserDocu>
|
||||
</Documentation>
|
||||
<Attribute Name="Index" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Internal id of the element.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Index" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Color" ReadOnly="false">
|
||||
<Documentation>
|
||||
<UserDocu>Assigned color of the receiver.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Color" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="SourceIndex" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns the index of the cell's source</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="SourceIndex" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="SourceCategory" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns the index of the cell's source</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="SourceCategory" Type="Int"/>
|
||||
</Attribute>
|
||||
<Attribute Name="IncidentEdge" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Incident edge of the cell - if exists</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="IncidentEdge" Type="Object"/>
|
||||
</Attribute>
|
||||
<Methode Name="containsPoint" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns true if the cell contains a point site</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="containsSegment" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns true if the cell contains a segment site</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="isDegenerate" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns true if the cell doesn't have an incident edge</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getSource" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns the Source for the cell</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
209
src/Mod/Path/App/VoronoiCellPyImp.cpp
Normal file
209
src/Mod/Path/App/VoronoiCellPyImp.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <boost/algorithm/string.hpp>
|
||||
#endif
|
||||
|
||||
#include "Mod/Path/App/Voronoi.h"
|
||||
#include "Mod/Path/App/VoronoiCell.h"
|
||||
#include "Mod/Path/App/VoronoiCellPy.h"
|
||||
#include "Mod/Path/App/VoronoiEdge.h"
|
||||
#include "Mod/Path/App/VoronoiEdgePy.h"
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/GeometryPyCXX.h>
|
||||
#include <Base/PlacementPy.h>
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/VectorPy.h>
|
||||
|
||||
// files generated out of VoronoiCellPy.xml
|
||||
#include "VoronoiCellPy.cpp"
|
||||
|
||||
using namespace Path;
|
||||
|
||||
// returns a string which represents the object e.g. when printed in python
|
||||
std::string VoronoiCellPy::representation(void) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.precision(5);
|
||||
ss << "VoronoiCell(";
|
||||
VoronoiCell *c = getVoronoiCellPtr();
|
||||
if (c->isBound()) {
|
||||
ss << c->ptr->source_category() << ":" << c->ptr->source_index();
|
||||
}
|
||||
ss << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
PyObject *VoronoiCellPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
|
||||
{
|
||||
// create a new instance of VoronoiCellPy and the Twin object
|
||||
return new VoronoiCellPy(new VoronoiCell);
|
||||
}
|
||||
|
||||
// constructor method
|
||||
int VoronoiCellPy::PyInit(PyObject* args, PyObject* /*kwd*/)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "no arguments accepted");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PyObject* VoronoiCellPy::richCompare(PyObject *lhs, PyObject *rhs, int op) {
|
||||
PyObject *cmp = Py_False;
|
||||
if ( PyObject_TypeCheck(lhs, &VoronoiCellPy::Type)
|
||||
&& PyObject_TypeCheck(rhs, &VoronoiCellPy::Type)
|
||||
&& op == Py_EQ) {
|
||||
const VoronoiCell *vl = static_cast<VoronoiCellPy*>(lhs)->getVoronoiCellPtr();
|
||||
const VoronoiCell *vr = static_cast<VoronoiCellPy*>(rhs)->getVoronoiCellPtr();
|
||||
if (vl->index == vr->index && vl->dia == vr->dia) {
|
||||
cmp = Py_True;
|
||||
}
|
||||
}
|
||||
Py_INCREF(cmp);
|
||||
return cmp;
|
||||
}
|
||||
|
||||
const Voronoi::voronoi_diagram_type::cell_type* getCellFromPy(VoronoiCellPy *c, bool throwIfNotBound = true) {
|
||||
auto self = c->getVoronoiCellPtr();
|
||||
if (self->isBound()) {
|
||||
return self->ptr;
|
||||
}
|
||||
if (throwIfNotBound) {
|
||||
throw Py::TypeError("Cell not bound to voronoi diagram");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VoronoiCell* getVoronoiCellFromPy(const VoronoiCellPy *c, PyObject *args = 0) {
|
||||
VoronoiCell *self = c->getVoronoiCellPtr();
|
||||
if (!self->isBound()) {
|
||||
throw Py::TypeError("Cell not bound to voronoi diagram");
|
||||
}
|
||||
if (args && !PyArg_ParseTuple(args, "")) {
|
||||
throw Py::RuntimeError("No arguments accepted");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
Py::Long VoronoiCellPy::getIndex(void) const {
|
||||
VoronoiCell *c = getVoronoiCellPtr();
|
||||
if (c->isBound()) {
|
||||
return Py::Long(c->dia->index(c->ptr));
|
||||
}
|
||||
return Py::Long(-1);
|
||||
}
|
||||
|
||||
Py::Long VoronoiCellPy::getColor(void) const {
|
||||
VoronoiCell *c = getVoronoiCellPtr();
|
||||
if (c->isBound()) {
|
||||
return Py::Long(c->ptr->color() & Voronoi::ColorMask);
|
||||
}
|
||||
return Py::Long(0);
|
||||
}
|
||||
|
||||
void VoronoiCellPy::setColor(Py::Long color) {
|
||||
getCellFromPy(this)->color(long(color) & Voronoi::ColorMask);
|
||||
}
|
||||
|
||||
Py::Long VoronoiCellPy::getSourceIndex(void) const
|
||||
{
|
||||
VoronoiCell *c = getVoronoiCellFromPy(this);
|
||||
return Py::Long(c->ptr->source_index());
|
||||
}
|
||||
|
||||
Py::Int VoronoiCellPy::getSourceCategory(void) const
|
||||
{
|
||||
VoronoiCell *c = getVoronoiCellFromPy(this);
|
||||
return Py::Int(c->ptr->source_category());
|
||||
}
|
||||
|
||||
Py::Object VoronoiCellPy::getIncidentEdge(void) const
|
||||
{
|
||||
VoronoiCell *c = getVoronoiCellFromPy(this);
|
||||
return Py::asObject(new VoronoiEdgePy(new VoronoiEdge(c->dia, c->ptr->incident_edge())));
|
||||
}
|
||||
|
||||
PyObject* VoronoiCellPy::containsPoint(PyObject *args)
|
||||
{
|
||||
VoronoiCell *c = getVoronoiCellFromPy(this, args);
|
||||
PyObject *chk = c->ptr->contains_point() ? Py_True : Py_False;
|
||||
Py_INCREF(chk);
|
||||
return chk;
|
||||
}
|
||||
|
||||
PyObject* VoronoiCellPy::containsSegment(PyObject *args)
|
||||
{
|
||||
VoronoiCell *c = getVoronoiCellFromPy(this, args);
|
||||
PyObject *chk = c->ptr->contains_segment() ? Py_True : Py_False;
|
||||
Py_INCREF(chk);
|
||||
return chk;
|
||||
}
|
||||
|
||||
PyObject* VoronoiCellPy::isDegenerate(PyObject *args)
|
||||
{
|
||||
VoronoiCell *c = getVoronoiCellFromPy(this, args);
|
||||
PyObject *chk = c->ptr->is_degenerate() ? Py_True : Py_False;
|
||||
Py_INCREF(chk);
|
||||
return chk;
|
||||
}
|
||||
|
||||
PyObject* VoronoiCellPy::getSource(PyObject *args)
|
||||
{
|
||||
double z = 0;
|
||||
if (!PyArg_ParseTuple(args, "|d", &z)) {
|
||||
throw Py::TypeError("Optional z argument (double) accepted");
|
||||
}
|
||||
|
||||
VoronoiCell *c = getVoronoiCellFromPy(this);
|
||||
if (c->ptr->contains_point()) {
|
||||
Base::Vector3d v = c->dia->scaledVector(c->dia->retrievePoint(c->ptr), z);
|
||||
return new Base::VectorPy(new Base::Vector3d(v));
|
||||
}
|
||||
Voronoi::segment_type s = c->dia->retrieveSegment(c->ptr);
|
||||
Base::Vector3d v0 = c->dia->scaledVector(low(s), z);
|
||||
Base::Vector3d v1 = c->dia->scaledVector(high(s), z);
|
||||
Py::List list;
|
||||
list.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(v0))));
|
||||
list.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(v1))));
|
||||
return Py::new_reference_to(list);
|
||||
}
|
||||
|
||||
|
||||
// custom attributes get/set
|
||||
|
||||
PyObject* VoronoiCellPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VoronoiCellPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
76
src/Mod/Path/App/VoronoiEdge.cpp
Normal file
76
src/Mod/Path/App/VoronoiEdge.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <cinttypes>
|
||||
# include <iomanip>
|
||||
# include <boost/algorithm/string.hpp>
|
||||
# include <boost/lexical_cast.hpp>
|
||||
#endif
|
||||
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/Writer.h>
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Exception.h>
|
||||
#include "Voronoi.h"
|
||||
#include "VoronoiEdge.h"
|
||||
|
||||
using namespace Base;
|
||||
using namespace Path;
|
||||
|
||||
TYPESYSTEM_SOURCE(Path::VoronoiEdge , Base::Persistence)
|
||||
|
||||
VoronoiEdge::VoronoiEdge(Voronoi::diagram_type *d, long index)
|
||||
: dia(d)
|
||||
, index(index)
|
||||
, ptr(0)
|
||||
{
|
||||
if (dia && long(dia->num_edges()) > index) {
|
||||
ptr = &(dia->edges()[index]);
|
||||
}
|
||||
}
|
||||
|
||||
VoronoiEdge::VoronoiEdge(Voronoi::diagram_type *d, const Voronoi::diagram_type::edge_type *e)
|
||||
: dia(d)
|
||||
, index(Voronoi::InvalidIndex)
|
||||
, ptr(e)
|
||||
{
|
||||
if (d && e) {
|
||||
index = dia->index(e);
|
||||
}
|
||||
}
|
||||
|
||||
VoronoiEdge::~VoronoiEdge() {
|
||||
}
|
||||
|
||||
bool VoronoiEdge::isBound(void) const {
|
||||
if (ptr != 0 && dia.isValid() && index != Voronoi::InvalidIndex) {
|
||||
if (&(dia->edges()[index]) == ptr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ptr = 0;
|
||||
return false;
|
||||
}
|
||||
54
src/Mod/Path/App/VoronoiEdge.h
Normal file
54
src/Mod/Path/App/VoronoiEdge.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef PATH_VORONOIEDGE_H
|
||||
#define PATH_VORONOIEDGE_H
|
||||
|
||||
#include <Base/Handle.h>
|
||||
#include <Base/BaseClass.h>
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/VectorPy.h>
|
||||
#include "Voronoi.h"
|
||||
|
||||
namespace Path
|
||||
{
|
||||
|
||||
class Voronoi;
|
||||
|
||||
class PathExport VoronoiEdge
|
||||
: public Base::BaseClass
|
||||
{
|
||||
TYPESYSTEM_HEADER();
|
||||
public:
|
||||
|
||||
VoronoiEdge(Voronoi::diagram_type *dia = 0, long index = Voronoi::InvalidIndex);
|
||||
VoronoiEdge(Voronoi::diagram_type *dia, const Voronoi::diagram_type::edge_type *edge);
|
||||
~VoronoiEdge();
|
||||
|
||||
bool isBound(void) const;
|
||||
|
||||
Base::Reference<Voronoi::diagram_type> dia;
|
||||
long index;
|
||||
mutable const Voronoi::diagram_type::edge_type *ptr;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
119
src/Mod/Path/App/VoronoiEdgePy.xml
Normal file
119
src/Mod/Path/App/VoronoiEdgePy.xml
Normal file
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||
<PythonExport
|
||||
Father="BaseClassPy"
|
||||
Name="VoronoiEdgePy"
|
||||
Twin="VoronoiEdge"
|
||||
TwinPointer="VoronoiEdge"
|
||||
Include="Mod/Path/App/Voronoi.h"
|
||||
FatherInclude="Base/BaseClassPy.h"
|
||||
Namespace="Path"
|
||||
FatherNamespace="Base"
|
||||
Constructor="true"
|
||||
RichCompare="true"
|
||||
Delete="true">
|
||||
<Documentation>
|
||||
<Author Licence="LGPL" Name="sliptonic" EMail="shopinthewoods@gmail.com" />
|
||||
<UserDocu>Edge of a Voronoi diagram</UserDocu>
|
||||
</Documentation>
|
||||
<Attribute Name="Index" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Internal id of the element.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Index" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Color" ReadOnly="false">
|
||||
<Documentation>
|
||||
<UserDocu>Assigned color of the receiver.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Color" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Cell" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>cell the edge belongs to</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Cell" Type="Object"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Vertices" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Begin and End voronoi vertex</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Vertices" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Next" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>CCW next edge whithin voronoi cell</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Next" Type="Object"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Prev" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>CCW previous edge whithin voronoi cell</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Prev" Type="Object"/>
|
||||
</Attribute>
|
||||
<Attribute Name="RotNext" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Rotated CCW next edge whithin voronoi cell</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="RotNext" Type="Object"/>
|
||||
</Attribute>
|
||||
<Attribute Name="RotPrev" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Rotated CCW previous edge whithin voronoi cell</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="RotPrev" Type="Object"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Twin" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Twin edge</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Twin" Type="Object"/>
|
||||
</Attribute>
|
||||
<Methode Name="isFinite" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns true if both vertices are finite</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="isInfinite" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns true if the end vertex is infinite</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="isLinear" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns true if edge is straight</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="isCurved" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns true if edge is curved</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="isPrimary" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns false if edge goes through endpoint of the segment site</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="isSecondary" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns true if edge goes through endpoint of the segment site</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="toGeom" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns a geom representation of the edge (line segment or arc of parabola)</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getDistances" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns the distance of the vertices to the input source</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getSegmentAngle" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns the angle (in degree) of the segments if the edge was formed by two segments</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
531
src/Mod/Path/App/VoronoiEdgePyImp.cpp
Normal file
531
src/Mod/Path/App/VoronoiEdgePyImp.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <boost/algorithm/string.hpp>
|
||||
#endif
|
||||
|
||||
#include "Mod/Path/App/Voronoi.h"
|
||||
#include "Mod/Path/App/Voronoi.h"
|
||||
#include "Mod/Path/App/VoronoiCell.h"
|
||||
#include "Mod/Path/App/VoronoiCellPy.h"
|
||||
#include "Mod/Path/App/VoronoiEdge.h"
|
||||
#include "Mod/Path/App/VoronoiEdgePy.h"
|
||||
#include "Mod/Path/App/VoronoiVertex.h"
|
||||
#include "Mod/Path/App/VoronoiVertexPy.h"
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/GeometryPyCXX.h>
|
||||
#include <Base/PlacementPy.h>
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/VectorPy.h>
|
||||
#include <Mod/Part/App/LineSegmentPy.h>
|
||||
#include <Mod/Part/App/ArcOfParabolaPy.h>
|
||||
|
||||
// files generated out of VoronoiEdgePy.xml
|
||||
#include "VoronoiEdgePy.cpp"
|
||||
|
||||
using namespace Path;
|
||||
|
||||
// returns a string which represents the object e.g. when printed in python
|
||||
std::string VoronoiEdgePy::representation(void) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.precision(5);
|
||||
ss << "VoronoiEdge(";
|
||||
VoronoiEdge *e = getVoronoiEdgePtr();
|
||||
if (e->isBound()) {
|
||||
const Voronoi::diagram_type::vertex_type *v0 = e->ptr->vertex0();
|
||||
const Voronoi::diagram_type::vertex_type *v1 = e->ptr->vertex1();
|
||||
if (v0) {
|
||||
ss << "[" << (v0->x() / e->dia->getScale()) << ", " << (v0->y() / e->dia->getScale()) << "]";
|
||||
} else {
|
||||
ss << "[~]";
|
||||
}
|
||||
ss << ", ";
|
||||
if (v1) {
|
||||
ss << "[" << (v1->x() / e->dia->getScale()) << ", " << (v1->y() / e->dia->getScale()) << "]";
|
||||
} else {
|
||||
ss << "[~]";
|
||||
}
|
||||
}
|
||||
ss << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
PyObject *VoronoiEdgePy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
|
||||
{
|
||||
// create a new instance of VoronoiEdgePy and the Twin object
|
||||
return new VoronoiEdgePy(new VoronoiEdge);
|
||||
}
|
||||
|
||||
// constructor method
|
||||
int VoronoiEdgePy::PyInit(PyObject* args, PyObject* /*kwd*/)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "no arguments accepted");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PyObject* VoronoiEdgePy::richCompare(PyObject *lhs, PyObject *rhs, int op) {
|
||||
PyObject *cmp = Py_False;
|
||||
if ( PyObject_TypeCheck(lhs, &VoronoiEdgePy::Type)
|
||||
&& PyObject_TypeCheck(rhs, &VoronoiEdgePy::Type)
|
||||
&& op == Py_EQ) {
|
||||
const VoronoiEdge *vl = static_cast<VoronoiEdgePy*>(lhs)->getVoronoiEdgePtr();
|
||||
const VoronoiEdge *vr = static_cast<VoronoiEdgePy*>(rhs)->getVoronoiEdgePtr();
|
||||
if (vl->index == vr->index && vl->dia == vr->dia) {
|
||||
cmp = Py_True;
|
||||
} else {
|
||||
std::cerr << "VoronoiEdge==(" << vl->index << " != " << vr->index << " || " << (vl->dia == vr->dia) << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
Py_INCREF(cmp);
|
||||
return cmp;
|
||||
}
|
||||
|
||||
const Voronoi::voronoi_diagram_type::edge_type* getEdgeFromPy(VoronoiEdgePy *e, bool throwIfNotBound = true) {
|
||||
auto self = e->getVoronoiEdgePtr();
|
||||
if (self->isBound()) {
|
||||
return self->ptr;
|
||||
}
|
||||
if (throwIfNotBound) {
|
||||
throw Py::TypeError("Edge not bound to voronoi diagram");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VoronoiEdge* getVoronoiEdgeFromPy(const VoronoiEdgePy *e, PyObject *args = 0) {
|
||||
VoronoiEdge *self = e->getVoronoiEdgePtr();
|
||||
if (!self->isBound()) {
|
||||
throw Py::TypeError("Edge not bound to voronoi diagram");
|
||||
}
|
||||
if (args && !PyArg_ParseTuple(args, "")) {
|
||||
throw Py::RuntimeError("No arguments accepted");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
Py::Long VoronoiEdgePy::getIndex(void) const {
|
||||
VoronoiEdge *e = getVoronoiEdgePtr();
|
||||
if (e->isBound()) {
|
||||
return Py::Long(e->dia->index(e->ptr));
|
||||
}
|
||||
return Py::Long(-1);
|
||||
}
|
||||
|
||||
Py::Long VoronoiEdgePy::getColor(void) const {
|
||||
VoronoiEdge *e = getVoronoiEdgePtr();
|
||||
if (e->isBound()) {
|
||||
return Py::Long(e->ptr->color() & Voronoi::ColorMask);
|
||||
}
|
||||
return Py::Long(0);
|
||||
}
|
||||
|
||||
void VoronoiEdgePy::setColor(Py::Long color) {
|
||||
getEdgeFromPy(this)->color(long(color) & Voronoi::ColorMask);
|
||||
}
|
||||
|
||||
Py::List VoronoiEdgePy::getVertices(void) const
|
||||
{
|
||||
Py::List list;
|
||||
VoronoiEdge *e = getVoronoiEdgePtr();
|
||||
if (e->isBound()) {
|
||||
auto v0 = e->ptr->vertex0();
|
||||
auto v1 = e->ptr->vertex1();
|
||||
if (v0) {
|
||||
list.append(Py::asObject(new VoronoiVertexPy(new VoronoiVertex(e->dia, v0))));
|
||||
} else {
|
||||
Py_INCREF(Py_None);
|
||||
list.append(Py::asObject(Py_None));
|
||||
}
|
||||
if (v1) {
|
||||
list.append(Py::asObject(new VoronoiVertexPy(new VoronoiVertex(e->dia, v1))));
|
||||
} else {
|
||||
Py_INCREF(Py_None);
|
||||
list.append(Py::asObject(Py_None));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Py::Object VoronoiEdgePy::getTwin(void) const
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this);
|
||||
return Py::asObject(new VoronoiEdgePy(new VoronoiEdge(e->dia, e->ptr->twin())));
|
||||
}
|
||||
|
||||
Py::Object VoronoiEdgePy::getNext(void) const
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this);
|
||||
return Py::asObject(new VoronoiEdgePy(new VoronoiEdge(e->dia, e->ptr->next())));
|
||||
}
|
||||
|
||||
Py::Object VoronoiEdgePy::getPrev(void) const
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this);
|
||||
return Py::asObject(new VoronoiEdgePy(new VoronoiEdge(e->dia, e->ptr->prev())));
|
||||
}
|
||||
|
||||
Py::Object VoronoiEdgePy::getRotNext(void) const
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this);
|
||||
return Py::asObject(new VoronoiEdgePy(new VoronoiEdge(e->dia, e->ptr->rot_next())));
|
||||
}
|
||||
|
||||
Py::Object VoronoiEdgePy::getRotPrev(void) const
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this);
|
||||
return Py::asObject(new VoronoiEdgePy(new VoronoiEdge(e->dia, e->ptr->rot_prev())));
|
||||
}
|
||||
|
||||
Py::Object VoronoiEdgePy::getCell(void) const
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this);
|
||||
return Py::asObject(new VoronoiCellPy(new VoronoiCell(e->dia, e->ptr->cell())));
|
||||
}
|
||||
|
||||
|
||||
PyObject* VoronoiEdgePy::isFinite(PyObject *args)
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this, args);
|
||||
PyObject *chk = e->ptr->is_finite() ? Py_True : Py_False;
|
||||
Py_INCREF(chk);
|
||||
return chk;
|
||||
}
|
||||
|
||||
PyObject* VoronoiEdgePy::isInfinite(PyObject *args)
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this, args);
|
||||
PyObject *chk = e->ptr->is_infinite() ? Py_True : Py_False;
|
||||
Py_INCREF(chk);
|
||||
return chk;
|
||||
}
|
||||
|
||||
PyObject* VoronoiEdgePy::isLinear(PyObject *args)
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this, args);
|
||||
PyObject *chk = e->ptr->is_linear() ? Py_True : Py_False;
|
||||
Py_INCREF(chk);
|
||||
return chk;
|
||||
}
|
||||
|
||||
PyObject* VoronoiEdgePy::isCurved(PyObject *args)
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this, args);
|
||||
PyObject *chk = e->ptr->is_curved() ? Py_True : Py_False;
|
||||
Py_INCREF(chk);
|
||||
return chk;
|
||||
}
|
||||
|
||||
PyObject* VoronoiEdgePy::isPrimary(PyObject *args)
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this, args);
|
||||
PyObject *chk = e->ptr->is_primary() ? Py_True : Py_False;
|
||||
Py_INCREF(chk);
|
||||
return chk;
|
||||
}
|
||||
|
||||
PyObject* VoronoiEdgePy::isSecondary(PyObject *args)
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this, args);
|
||||
PyObject *chk = e->ptr->is_secondary() ? Py_True : Py_False;
|
||||
Py_INCREF(chk);
|
||||
return chk;
|
||||
}
|
||||
|
||||
namespace {
|
||||
Voronoi::point_type orthognalProjection(const Voronoi::point_type &point, const Voronoi::segment_type &segment) {
|
||||
// move segment so it goes through the origin (s)
|
||||
Voronoi::point_type offset;
|
||||
{
|
||||
offset.x(low(segment).x());
|
||||
offset.y(low(segment).y());
|
||||
}
|
||||
Voronoi::point_type s;
|
||||
{
|
||||
s.x(high(segment).x() - offset.x());
|
||||
s.y(high(segment).y() - offset.y());
|
||||
}
|
||||
// move point accordingly so it maintains it's relation to s (p)
|
||||
Voronoi::point_type p;
|
||||
{
|
||||
p.x(point.x() - offset.x());
|
||||
p.y(point.y() - offset.y());
|
||||
}
|
||||
// calculate the orthogonal projection of p onto s
|
||||
// ((p dot s) / (s dot s)) * s (https://en.wikibooks.org/wiki/Linear_Algebra/Orthogonal_Projection_Onto_a_Line)
|
||||
// and it back by original offset to get the projected point
|
||||
double proj = (p.x() * s.x() + p.y() * s.y()) / (s.x() * s.x() + s.y() * s.y());
|
||||
Voronoi::point_type pt;
|
||||
{
|
||||
pt.x(offset.x() + proj * s.x());
|
||||
pt.y(offset.y() + proj * s.y());
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* VoronoiEdgePy::toGeom(PyObject *args)
|
||||
{
|
||||
double z = 0.0;
|
||||
if (!PyArg_ParseTuple(args, "|d", &z)) {
|
||||
throw Py::RuntimeError("single argument of type double accepted");
|
||||
}
|
||||
VoronoiEdge *e = getVoronoiEdgePtr();
|
||||
if (e->isBound()) {
|
||||
if (e->ptr->is_linear()) {
|
||||
if (e->ptr->is_finite()) {
|
||||
auto v0 = e->ptr->vertex0();
|
||||
auto v1 = e->ptr->vertex1();
|
||||
if (v0 && v1) {
|
||||
auto p = new Part::GeomLineSegment;
|
||||
p->setPoints(e->dia->scaledVector(*v0, z), e->dia->scaledVector(*v1, z));
|
||||
return new Part::LineSegmentPy(p);
|
||||
}
|
||||
} else {
|
||||
// infinite linear, need to clip somehow
|
||||
const Voronoi::diagram_type::cell_type *c0 = e->ptr->cell();
|
||||
const Voronoi::diagram_type::cell_type *c1 = e->ptr->twin()->cell();
|
||||
Voronoi::point_type origin;
|
||||
Voronoi::point_type direction;
|
||||
if (c0->contains_point() && c1->contains_point()) {
|
||||
Voronoi::point_type p0 = e->dia->retrievePoint(c0);
|
||||
Voronoi::point_type p1 = e->dia->retrievePoint(c1);
|
||||
origin.x((p0.x() + p1.x()) / 2.);
|
||||
origin.y((p0.y() + p1.y()) / 2.);
|
||||
direction.x(p0.y() - p1.y());
|
||||
direction.y(p1.x() - p0.x());
|
||||
} else {
|
||||
origin = c0->contains_segment() ? e->dia->retrievePoint(c1) : e->dia->retrievePoint(c0);
|
||||
Voronoi::segment_type segment = c0->contains_segment() ? e->dia->retrieveSegment(c0) : e->dia->retrieveSegment(c1);
|
||||
Voronoi::coordinate_type dx = high(segment).x() - low(segment).x();
|
||||
Voronoi::coordinate_type dy = high(segment).y() - low(segment).y();
|
||||
if ((low(segment) == origin) ^ c0->contains_point()) {
|
||||
direction.x(dy);
|
||||
direction.y(-dx);
|
||||
} else {
|
||||
direction.x(-dy);
|
||||
direction.y(dx);
|
||||
}
|
||||
}
|
||||
double k = 10.0; // <-- need something smarter here
|
||||
Voronoi::point_type begin;
|
||||
Voronoi::point_type end;
|
||||
if (e->ptr->vertex0()) {
|
||||
begin.x(e->ptr->vertex0()->x());
|
||||
begin.y(e->ptr->vertex0()->y());
|
||||
} else {
|
||||
begin.x(origin.x() - direction.x() * k);
|
||||
begin.y(origin.y() - direction.y() * k);
|
||||
}
|
||||
if (e->ptr->vertex1()) {
|
||||
end.x(e->ptr->vertex1()->x());
|
||||
end.y(e->ptr->vertex1()->y());
|
||||
} else {
|
||||
end.x(origin.x() + direction.x() * k);
|
||||
end.y(origin.y() + direction.y() * k);
|
||||
}
|
||||
auto p = new Part::GeomLineSegment;
|
||||
p->setPoints(e->dia->scaledVector(begin, z), e->dia->scaledVector(end, z));
|
||||
return new Part::LineSegmentPy(p);
|
||||
}
|
||||
} else {
|
||||
// parabolic curve, which is always formed by a point and an edge
|
||||
Voronoi::point_type point = e->ptr->cell()->contains_point() ? e->dia->retrievePoint(e->ptr->cell()) : e->dia->retrievePoint(e->ptr->twin()->cell());
|
||||
Voronoi::segment_type segment = e->ptr->cell()->contains_point() ? e->dia->retrieveSegment(e->ptr->twin()->cell()) : e->dia->retrieveSegment(e->ptr->cell());
|
||||
// the location is the mid point betwenn the normal on the segment through point
|
||||
// this is only the mid point of the segment if the parabola is symmetric
|
||||
Voronoi::point_type loc;
|
||||
{
|
||||
Voronoi::point_type proj = orthognalProjection(point, segment);
|
||||
// the location is the mid point between the projection on the segment and the point
|
||||
loc.x((proj.x() + point.x()) / 2);
|
||||
loc.y((proj.y() + point.y()) / 2);
|
||||
}
|
||||
Voronoi::point_type axis;
|
||||
{
|
||||
axis.x(point.x() - loc.x());
|
||||
axis.y(point.y() - loc.y());
|
||||
}
|
||||
auto p = new Part::GeomParabola;
|
||||
{
|
||||
p->setCenter(e->dia->scaledVector(point, z));
|
||||
p->setLocation(e->dia->scaledVector(loc, z));
|
||||
p->setAngleXU(atan2(axis.y(), axis.x()));
|
||||
p->setFocal(sqrt(axis.x() * axis.x() + axis.y() * axis.y()) / e->dia->getScale());
|
||||
}
|
||||
auto a = new Part::GeomArcOfParabola;
|
||||
{
|
||||
a->setHandle(Handle(Geom_Parabola)::DownCast(p->handle()));
|
||||
|
||||
// figure out the arc parameters
|
||||
auto v0 = e->ptr->vertex0();
|
||||
auto v1 = e->ptr->vertex1();
|
||||
double param0 = 0;
|
||||
double param1 = 0;
|
||||
if (!p->closestParameter(e->dia->scaledVector(*v0, z), param0)) {
|
||||
std::cerr << "closestParameter(v0) failed" << std::endl;
|
||||
}
|
||||
if (!p->closestParameter(e->dia->scaledVector(*v1, z), param1)) {
|
||||
std::cerr << "closestParameter(v0) failed" << std::endl;
|
||||
}
|
||||
a->setRange(param0, param1, false);
|
||||
}
|
||||
return new Part::ArcOfParabolaPy(a);
|
||||
}
|
||||
}
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
double distanceBetween(const Voronoi::diagram_type::vertex_type &v0, const Voronoi::point_type &p1, double scale) {
|
||||
double x = v0.x() - p1.x();
|
||||
double y = v0.y() - p1.y();
|
||||
return sqrt(x * x + y * y) / scale;
|
||||
}
|
||||
|
||||
void addDistanceBetween(const Voronoi::diagram_type::vertex_type *v0, const Voronoi::point_type &p1, Py::List *list, double scale) {
|
||||
if (v0) {
|
||||
list->append(Py::Float(distanceBetween(*v0, p1, scale)));
|
||||
} else {
|
||||
Py_INCREF(Py_None);
|
||||
list->append(Py::asObject(Py_None));
|
||||
}
|
||||
}
|
||||
|
||||
void addProjectedDistanceBetween(const Voronoi::diagram_type::vertex_type *v0, const Voronoi::segment_type &segment, Py::List *list, double scale) {
|
||||
if (v0) {
|
||||
Voronoi::point_type p0;
|
||||
{
|
||||
p0.x(v0->x());
|
||||
p0.y(v0->y());
|
||||
}
|
||||
Voronoi::point_type p1 = orthognalProjection(p0, segment);
|
||||
list->append(Py::Float(distanceBetween(*v0, p1, scale)));
|
||||
} else {
|
||||
Py_INCREF(Py_None);
|
||||
list->append(Py::asObject(Py_None));
|
||||
}
|
||||
}
|
||||
|
||||
bool addDistancesToPoint(const VoronoiEdge *edge, Voronoi::point_type p, Py::List *list, double scale) {
|
||||
addDistanceBetween(edge->ptr->vertex0(), p, list, scale);
|
||||
addDistanceBetween(edge->ptr->vertex1(), p, list, scale);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool retrieveDistances(const VoronoiEdge *edge, Py::List *list) {
|
||||
const Voronoi::diagram_type::cell_type *c0 = edge->ptr->cell();
|
||||
if (c0->contains_point()) {
|
||||
return addDistancesToPoint(edge, edge->dia->retrievePoint(c0), list, edge->dia->getScale());
|
||||
}
|
||||
const Voronoi::diagram_type::cell_type *c1 = edge->ptr->twin()->cell();
|
||||
if (c1->contains_point()) {
|
||||
return addDistancesToPoint(edge, edge->dia->retrievePoint(c1), list, edge->dia->getScale());
|
||||
}
|
||||
// at this point both cells are sourced from segments and it does not matter which one we use
|
||||
Voronoi::segment_type segment = edge->dia->retrieveSegment(c0);
|
||||
addProjectedDistanceBetween(edge->ptr->vertex0(), segment, list, edge->dia->getScale());
|
||||
addProjectedDistanceBetween(edge->ptr->vertex1(), segment, list, edge->dia->getScale());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* VoronoiEdgePy::getDistances(PyObject *args)
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this, args);
|
||||
Py::List list;
|
||||
retrieveDistances(e, &list);
|
||||
return Py::new_reference_to(list);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream &str, const Voronoi::point_type &p) {
|
||||
return str << "[" << int(p.x()) << ", " << int(p.y()) << "]";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream &str, const Voronoi::segment_type &s) {
|
||||
return str << '<' << low(s) << '-' << high(s) << '>';
|
||||
}
|
||||
|
||||
static bool pointsMatch(const Voronoi::point_type &p0, const Voronoi::point_type &p1) {
|
||||
return long(p0.x()) == long(p1.x()) && long(p0.y()) == long(p1.y());
|
||||
}
|
||||
|
||||
static void printCompare(const char *label, const Voronoi::point_type &p0, const Voronoi::point_type &p1) {
|
||||
std::cerr << " " << label <<": " << pointsMatch(p1, p0) << pointsMatch(p0, p1) << " " << p0 << ' ' << p1 << std::endl;
|
||||
}
|
||||
|
||||
PyObject* VoronoiEdgePy::getSegmentAngle(PyObject *args)
|
||||
{
|
||||
VoronoiEdge *e = getVoronoiEdgeFromPy(this, args);
|
||||
|
||||
if (e->ptr->cell()->contains_segment() && e->ptr->twin()->cell()->contains_segment()) {
|
||||
int i0 = e->ptr->cell()->source_index() - e->dia->points.size();
|
||||
int i1 = e->ptr->twin()->cell()->source_index() - e->dia->points.size();
|
||||
if (e->dia->segmentsAreConnected(i0, i1)) {
|
||||
double a0 = e->dia->angleOfSegment(i0);
|
||||
double a1 = e->dia->angleOfSegment(i1);
|
||||
double a = a0 - a1;
|
||||
if (a > M_PI_2) {
|
||||
a -= M_PI;
|
||||
} else if (a < -M_PI_2) {
|
||||
a += M_PI;
|
||||
}
|
||||
return Py::new_reference_to(Py::Float(a));
|
||||
} else {
|
||||
std::cerr << "indices: " << std::endl;
|
||||
std::cerr << " " << e->dia->segments[i0] << std::endl;
|
||||
std::cerr << " " << e->dia->segments[i1] << std::endl;
|
||||
std::cerr << " connected: " << e->dia->segmentsAreConnected(i0, i1) << std::endl;
|
||||
printCompare("l/l", low(e->dia->segments[i0]), low(e->dia->segments[i1]));
|
||||
printCompare("l/h", low(e->dia->segments[i0]), high(e->dia->segments[i1]));
|
||||
printCompare("h/l", high(e->dia->segments[i0]), low(e->dia->segments[i1]));
|
||||
printCompare("h/h", high(e->dia->segments[i0]), high(e->dia->segments[i1]));
|
||||
}
|
||||
} else {
|
||||
std::cerr << "constains_segment(" << e->ptr->cell()->contains_segment() << ", " << e->ptr->twin()->cell()->contains_segment() << ")" << std::endl;
|
||||
}
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
// custom attributes get/set
|
||||
|
||||
PyObject* VoronoiEdgePy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VoronoiEdgePy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
107
src/Mod/Path/App/VoronoiPy.xml
Normal file
107
src/Mod/Path/App/VoronoiPy.xml
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||
<PythonExport
|
||||
Father="BaseClassPy"
|
||||
Name="VoronoiPy"
|
||||
Twin="Voronoi"
|
||||
TwinPointer="Voronoi"
|
||||
Include="Mod/Path/App/Voronoi.h"
|
||||
Namespace="Path"
|
||||
FatherInclude="Base/BaseClassPy.h"
|
||||
FatherNamespace="Base"
|
||||
Constructor="true"
|
||||
Delete="true">
|
||||
<Documentation>
|
||||
<Author Licence="LGPL" Name="sliptonic" EMail="shopinthewoods@gmail.com" />
|
||||
<UserDocu>Voronoi([segments]): Create voronoi for given collection of line segments</UserDocu>
|
||||
</Documentation>
|
||||
<Attribute Name="Cells" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>List of all cells of the voronoi diagram</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Cells" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Edges" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>List of all edges of the voronoi diagram</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Edges" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Vertices" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>List of all vertices of the voronoi diagram</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Vertices" Type="List"/>
|
||||
</Attribute>
|
||||
<Methode Name="numCells" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return number of cells</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="numEdges" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return number of edges</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="numVertices" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return number of vertices</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addPoint">
|
||||
<Documentation>
|
||||
<UserDocu>addPoint(vector|vector2d) add given point to input collection</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addSegment">
|
||||
<Documentation>
|
||||
<UserDocu>addSegment(vector|vector2d, vector|vector2d) add given segment to input collection</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="construct">
|
||||
<Documentation>
|
||||
<UserDocu>constructs the voronoi diagram from the input collections</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="colorExterior">
|
||||
<Documentation>
|
||||
<UserDocu>assign given color to all exterior edges and vertices</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="colorTwins">
|
||||
<Documentation>
|
||||
<UserDocu>assign given color to all twins of edges (which one is considered a twin is arbitrary)</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="colorColinear">
|
||||
<Documentation>
|
||||
<UserDocu>assign given color to all edges sourced by two segments almost in line with each other (optional angle in degrees)</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="resetColor">
|
||||
<Documentation>
|
||||
<UserDocu>assign color 0 to all elements with the given color</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getPoints" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Get list of all input points.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="numPoints" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return number of input points</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getSegments" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Get list of all input segments.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="numSegments" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Return number of input segments</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
346
src/Mod/Path/App/VoronoiPyImp.cpp
Normal file
346
src/Mod/Path/App/VoronoiPyImp.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <boost/algorithm/string.hpp>
|
||||
#endif
|
||||
|
||||
#include "Mod/Path/App/Voronoi.h"
|
||||
#include "Mod/Path/App/VoronoiCell.h"
|
||||
#include "Mod/Path/App/VoronoiEdge.h"
|
||||
#include "Mod/Path/App/VoronoiVertex.h"
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/GeometryPyCXX.h>
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/VectorPy.h>
|
||||
|
||||
// files generated out of VoronoiPy.xml
|
||||
#include "VoronoiPy.h"
|
||||
#include "VoronoiPy.cpp"
|
||||
#include "VoronoiCellPy.h"
|
||||
#include "VoronoiEdgePy.h"
|
||||
#include "VoronoiVertexPy.h"
|
||||
|
||||
using namespace Path;
|
||||
|
||||
// returns a string which represents the object e.g. when printed in python
|
||||
std::string VoronoiPy::representation(void) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.precision(5);
|
||||
ss << "Voronoi("
|
||||
<< "{" << getVoronoiPtr()->numSegments() << ", " << getVoronoiPtr()->numPoints() << "}"
|
||||
<< " -> "
|
||||
<< "{" << getVoronoiPtr()->numCells() << ", " << getVoronoiPtr()->numEdges() << ", " << getVoronoiPtr()->numVertices() << "}"
|
||||
<< ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
PyObject *VoronoiPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
|
||||
{
|
||||
// create a new instance of VoronoiPy and its twin object
|
||||
return new VoronoiPy(new Voronoi);
|
||||
}
|
||||
|
||||
// constructor
|
||||
int VoronoiPy::PyInit(PyObject* args, PyObject* /*kwds*/)
|
||||
{
|
||||
Voronoi *vo = getVoronoiPtr();
|
||||
double scale = vo->getScale();
|
||||
if (!PyArg_ParseTuple(args, "|d", &scale)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "scale argument (double) accepted, default = 1000");
|
||||
return -1;
|
||||
}
|
||||
vo->setScale(scale);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Voronoi::point_type getPointFromPy(PyObject *obj) {
|
||||
if (obj) {
|
||||
if (PyObject_TypeCheck(obj, &Base::VectorPy::Type)) {
|
||||
Base::Vector3d *vect = (static_cast<Base::VectorPy*>(obj))->getVectorPtr();
|
||||
return Voronoi::point_type(vect->x, vect->y);
|
||||
} else if (PyObject_TypeCheck(obj, Base::Vector2dPy::type_object())) {
|
||||
Base::Vector2d vect = Py::toVector2d(obj);
|
||||
return Voronoi::point_type(vect.x, vect.y);
|
||||
}
|
||||
}
|
||||
throw Py::TypeError("Points must be Base::Vector or Base::Vector2d");
|
||||
return Voronoi::point_type();
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::addPoint(PyObject *args) {
|
||||
PyObject *obj = 0;
|
||||
if (PyArg_ParseTuple(args, "O", &obj)) {
|
||||
getVoronoiPtr()->addPoint(getPointFromPy(obj));
|
||||
}
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::addSegment(PyObject *args) {
|
||||
PyObject *objBegin = 0;
|
||||
PyObject *objEnd = 0;
|
||||
|
||||
if (PyArg_ParseTuple(args, "OO", &objBegin, &objEnd)) {
|
||||
auto p0 = getPointFromPy(objBegin);
|
||||
auto p1 = getPointFromPy(objEnd);
|
||||
getVoronoiPtr()->addSegment(Voronoi::segment_type(p0, p1));
|
||||
}
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::construct(PyObject *args) {
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
throw Py::RuntimeError("no arguments accepted");
|
||||
}
|
||||
getVoronoiPtr()->construct();
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::numCells(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
throw Py::RuntimeError("no arguments accepted");
|
||||
}
|
||||
return PyLong_FromLong(getVoronoiPtr()->numCells());
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::numEdges(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
throw Py::RuntimeError("no arguments accepted");
|
||||
}
|
||||
return PyLong_FromLong(getVoronoiPtr()->numEdges());
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::numVertices(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
throw Py::RuntimeError("no arguments accepted");
|
||||
}
|
||||
return PyLong_FromLong(getVoronoiPtr()->numVertices());
|
||||
}
|
||||
|
||||
Py::List VoronoiPy::getVertices(void) const {
|
||||
Py::List list;
|
||||
for (int i=0; i<getVoronoiPtr()->numVertices(); ++i) {
|
||||
list.append(Py::asObject(new VoronoiVertexPy(getVoronoiPtr()->create<VoronoiVertex>(i))));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Py::List VoronoiPy::getEdges(void) const {
|
||||
Py::List list;
|
||||
for (int i=0; i<getVoronoiPtr()->numEdges(); ++i) {
|
||||
list.append(Py::asObject(new VoronoiEdgePy(getVoronoiPtr()->create<VoronoiEdge>(i))));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Py::List VoronoiPy::getCells(void) const {
|
||||
Py::List list;
|
||||
for (int i=0; i<getVoronoiPtr()->numCells(); ++i) {
|
||||
list.append(Py::asObject(new VoronoiCellPy(getVoronoiPtr()->create<VoronoiCell>(i))));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
typedef std::map<uintptr_t,bool> exterior_map_t;
|
||||
typedef std::map<int32_t, std::set<int32_t> > coordinate_map_t;
|
||||
|
||||
#define VORONOI_USE_EXTERIOR_CACHE 1
|
||||
|
||||
static bool callbackWithVertex(Voronoi::diagram_type *dia, PyObject *callback, const Voronoi::diagram_type::vertex_type *v, bool &bail, exterior_map_t &cache) {
|
||||
bool rc = false;
|
||||
if (!bail && v->color() == 0) {
|
||||
#if VORONOI_USE_EXTERIOR_CACHE
|
||||
auto it = cache.find(uintptr_t(v));
|
||||
if (it == cache.end()) {
|
||||
#endif
|
||||
PyObject *vx = new VoronoiVertexPy(new VoronoiVertex(dia, v));
|
||||
PyObject *arglist = Py_BuildValue("(O)", vx);
|
||||
PyObject *result = PyEval_CallObject(callback, arglist);
|
||||
Py_DECREF(arglist);
|
||||
Py_DECREF(vx);
|
||||
if (result == NULL) {
|
||||
bail = true;
|
||||
} else {
|
||||
rc = result == Py_True;
|
||||
Py_DECREF(result);
|
||||
cache.insert(exterior_map_t::value_type(uintptr_t(v), rc));
|
||||
}
|
||||
#if VORONOI_USE_EXTERIOR_CACHE
|
||||
} else {
|
||||
rc = it->second;
|
||||
}
|
||||
#else
|
||||
(void)cache;
|
||||
#endif
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::colorExterior(PyObject *args) {
|
||||
Voronoi::color_type color = 0;
|
||||
PyObject *callback = 0;
|
||||
if (!PyArg_ParseTuple(args, "k|O", &color, &callback)) {
|
||||
throw Py::RuntimeError("colorExterior requires an integer (color) argument");
|
||||
}
|
||||
Voronoi *vo = getVoronoiPtr();
|
||||
vo->colorExterior(color);
|
||||
if (callback) {
|
||||
exterior_map_t cache;
|
||||
coordinate_map_t pts;
|
||||
for (auto e = vo->vd->edges().begin(); e != vo->vd->edges().end(); ++e) {
|
||||
if (e->is_finite() && e->color() == 0) {
|
||||
const Voronoi::diagram_type::vertex_type *v0 = e->vertex0();
|
||||
const Voronoi::diagram_type::vertex_type *v1 = e->vertex1();
|
||||
bool bail = false;
|
||||
if (callbackWithVertex(vo->vd, callback, v0, bail, cache) && callbackWithVertex(vo->vd, callback, v1, bail, cache)) {
|
||||
vo->colorExterior(&(*e), color);
|
||||
} else if (!bail && callbackWithVertex(vo->vd, callback, v1, bail, cache)) {
|
||||
if (pts.empty()) {
|
||||
for (auto s = vo->vd->segments.begin(); s != vo->vd->segments.end(); ++s) {
|
||||
pts[low(*s).x()].insert(low(*s).y());
|
||||
pts[high(*s).x()].insert(high(*s).y());
|
||||
}
|
||||
}
|
||||
auto ys = pts.find(int32_t(v0->x()));
|
||||
if (ys != pts.end() && ys->second.find(v0->y()) != ys->second.end()) {
|
||||
vo->colorExterior(&(*e), color);
|
||||
}
|
||||
}
|
||||
if (bail) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::colorTwins(PyObject *args) {
|
||||
Voronoi::color_type color = 0;
|
||||
if (!PyArg_ParseTuple(args, "k", &color)) {
|
||||
throw Py::RuntimeError("colorTwins requires an integer (color) argument");
|
||||
}
|
||||
getVoronoiPtr()->colorTwins(color);
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::colorColinear(PyObject *args) {
|
||||
Voronoi::color_type color = 0;
|
||||
double degree = 10.;
|
||||
if (!PyArg_ParseTuple(args, "k|d", &color, °ree)) {
|
||||
throw Py::RuntimeError("colorColinear requires an integer (color) and optionally a derivation in degrees argument (default 10)");
|
||||
}
|
||||
getVoronoiPtr()->colorColinear(color, degree);
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::resetColor(PyObject *args) {
|
||||
Voronoi::color_type color = 0;
|
||||
if (!PyArg_ParseTuple(args, "k", &color)) {
|
||||
throw Py::RuntimeError("clearColor requires an integer (color) argument");
|
||||
}
|
||||
|
||||
getVoronoiPtr()->resetColor(color);
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::getPoints(PyObject *args) {
|
||||
double z = 0;
|
||||
if (!PyArg_ParseTuple(args, "|d", &z)) {
|
||||
throw Py::RuntimeError("Optional z argument (double) accepted");
|
||||
}
|
||||
Voronoi *vo = getVoronoiPtr();
|
||||
Py::List list;
|
||||
for (auto it = vo->vd->points.begin(); it != vo->vd->points.end(); ++it) {
|
||||
list.append(Py::asObject(new Base::VectorPy(new Base::Vector3d(vo->vd->scaledVector(*it, z)))));
|
||||
}
|
||||
return Py::new_reference_to(list);
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::getSegments(PyObject *args) {
|
||||
double z = 0;
|
||||
if (!PyArg_ParseTuple(args, "|d", &z)) {
|
||||
throw Py::RuntimeError("Optional z argument (double) accepted");
|
||||
}
|
||||
Voronoi *vo = getVoronoiPtr();
|
||||
Py::List list;
|
||||
for (auto it = vo->vd->segments.begin(); it != vo->vd->segments.end(); ++it) {
|
||||
PyObject *p0 = new Base::VectorPy(new Base::Vector3d(vo->vd->scaledVector(low(*it), z)));
|
||||
PyObject *p1 = new Base::VectorPy(new Base::Vector3d(vo->vd->scaledVector(high(*it), z)));
|
||||
PyObject *tp = PyTuple_New(2);
|
||||
PyTuple_SetItem(tp, 0, p0);
|
||||
PyTuple_SetItem(tp, 1, p1);
|
||||
list.append(Py::asObject(tp));
|
||||
}
|
||||
return Py::new_reference_to(list);
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::numPoints(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
throw Py::RuntimeError("no arguments accepted");
|
||||
}
|
||||
return PyLong_FromLong(getVoronoiPtr()->vd->points.size());
|
||||
}
|
||||
|
||||
PyObject* VoronoiPy::numSegments(PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
throw Py::RuntimeError("no arguments accepted");
|
||||
}
|
||||
return PyLong_FromLong(getVoronoiPtr()->vd->segments.size());
|
||||
}
|
||||
|
||||
|
||||
// custom attributes get/set
|
||||
|
||||
PyObject *VoronoiPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VoronoiPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
76
src/Mod/Path/App/VoronoiVertex.cpp
Normal file
76
src/Mod/Path/App/VoronoiVertex.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <cinttypes>
|
||||
# include <iomanip>
|
||||
# include <boost/algorithm/string.hpp>
|
||||
# include <boost/lexical_cast.hpp>
|
||||
#endif
|
||||
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/Writer.h>
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/Exception.h>
|
||||
#include "Voronoi.h"
|
||||
#include "VoronoiVertex.h"
|
||||
|
||||
using namespace Base;
|
||||
using namespace Path;
|
||||
|
||||
TYPESYSTEM_SOURCE(Path::VoronoiVertex , Base::Persistence)
|
||||
|
||||
VoronoiVertex::VoronoiVertex(Voronoi::diagram_type *d, long index)
|
||||
: dia(d)
|
||||
, index(index)
|
||||
, ptr(0)
|
||||
{
|
||||
if (dia && long(dia->num_vertices()) > index) {
|
||||
ptr = &(dia->vertices()[index]);
|
||||
}
|
||||
}
|
||||
|
||||
VoronoiVertex::VoronoiVertex(Voronoi::diagram_type *d, const Voronoi::diagram_type::vertex_type *v)
|
||||
: dia(d)
|
||||
, index(Voronoi::InvalidIndex)
|
||||
, ptr(v)
|
||||
{
|
||||
if (dia && v) {
|
||||
index = dia->index(v);
|
||||
}
|
||||
}
|
||||
|
||||
VoronoiVertex::~VoronoiVertex() {
|
||||
}
|
||||
|
||||
bool VoronoiVertex::isBound(void) const {
|
||||
if (ptr != 0 && dia.isValid() && index != Voronoi::InvalidIndex) {
|
||||
if (&(dia->vertices()[index]) == ptr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ptr = 0;
|
||||
return false;
|
||||
}
|
||||
54
src/Mod/Path/App/VoronoiVertex.h
Normal file
54
src/Mod/Path/App/VoronoiVertex.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef PATH_VORONOIVERTEX_H
|
||||
#define PATH_VORONOIVERTEX_H
|
||||
|
||||
#include <Base/Handle.h>
|
||||
#include <Base/BaseClass.h>
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/VectorPy.h>
|
||||
#include "Voronoi.h"
|
||||
|
||||
namespace Path
|
||||
{
|
||||
|
||||
class Voronoi;
|
||||
|
||||
class PathExport VoronoiVertex
|
||||
: public Base::BaseClass
|
||||
{
|
||||
TYPESYSTEM_HEADER();
|
||||
public:
|
||||
|
||||
VoronoiVertex(Voronoi::diagram_type *dia = 0, long index = Voronoi::InvalidIndex);
|
||||
VoronoiVertex(Voronoi::diagram_type *dia, const Voronoi::diagram_type::vertex_type *v);
|
||||
~VoronoiVertex();
|
||||
|
||||
bool isBound(void) const;
|
||||
|
||||
Base::Reference<Voronoi::diagram_type> dia;
|
||||
long index;
|
||||
mutable const Voronoi::diagram_type::vertex_type *ptr;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
55
src/Mod/Path/App/VoronoiVertexPy.xml
Normal file
55
src/Mod/Path/App/VoronoiVertexPy.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
|
||||
<PythonExport
|
||||
Father="BaseClassPy"
|
||||
Name="VoronoiVertexPy"
|
||||
Twin="VoronoiVertex"
|
||||
TwinPointer="VoronoiVertex"
|
||||
Include="Mod/Path/App/Voronoi.h"
|
||||
FatherInclude="Base/BaseClassPy.h"
|
||||
Namespace="Path"
|
||||
FatherNamespace="Base"
|
||||
Constructor="true"
|
||||
RichCompare="true"
|
||||
Delete="true">
|
||||
<Documentation>
|
||||
<Author Licence="LGPL" Name="sliptonic" EMail="shopinthewoods@gmail.com" />
|
||||
<UserDocu>Vertex of a Voronoi diagram</UserDocu>
|
||||
</Documentation>
|
||||
<Attribute Name="Index" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Internal id of the element.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Index" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Color" ReadOnly="false">
|
||||
<Documentation>
|
||||
<UserDocu>Assigned color of the receiver.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Color" Type="Long"/>
|
||||
</Attribute>
|
||||
<Attribute Name="X" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>X position</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="X" Type="Float"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Y" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Y position</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Y" Type="Float"/>
|
||||
</Attribute>
|
||||
<Attribute Name="IncidentEdge" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Y position</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="IncidentEdge" Type="Object"/>
|
||||
</Attribute>
|
||||
<Methode Name="toGeom" Const="true">
|
||||
<Documentation>
|
||||
<UserDocu>Returns a Vertex - or None if not possible</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
178
src/Mod/Path/App/VoronoiVertexPyImp.cpp
Normal file
178
src/Mod/Path/App/VoronoiVertexPyImp.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) sliptonic (shopinthewoods@gmail.com) 2020 *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <boost/algorithm/string.hpp>
|
||||
#endif
|
||||
|
||||
#include "Voronoi.h"
|
||||
#include "VoronoiPy.h"
|
||||
#include "VoronoiEdge.h"
|
||||
#include "VoronoiEdgePy.h"
|
||||
#include "VoronoiVertex.h"
|
||||
#include "VoronoiVertexPy.h"
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/GeometryPyCXX.h>
|
||||
#include <Base/PlacementPy.h>
|
||||
#include <Base/Vector3D.h>
|
||||
#include <Base/VectorPy.h>
|
||||
|
||||
// files generated out of VoronoiVertexPy.xml
|
||||
#include "VoronoiVertexPy.cpp"
|
||||
|
||||
using namespace Path;
|
||||
|
||||
// returns a string which represents the object e.g. when printed in python
|
||||
std::string VoronoiVertexPy::representation(void) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.precision(5);
|
||||
ss << "VoronoiVertex(";
|
||||
VoronoiVertex *v = getVoronoiVertexPtr();
|
||||
if (v->isBound()) {
|
||||
ss << "[" << (v->ptr->x() / v->dia->getScale()) << ", " << (v->ptr->y() / v->dia->getScale()) << "]";
|
||||
}
|
||||
ss << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
PyObject *VoronoiVertexPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
|
||||
{
|
||||
// create a new instance of VoronoiVertexPy and the Twin object
|
||||
return new VoronoiVertexPy(new VoronoiVertex);
|
||||
}
|
||||
|
||||
// constructor method
|
||||
int VoronoiVertexPy::PyInit(PyObject* args, PyObject* /*kwd*/)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "no arguments accepted");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PyObject* VoronoiVertexPy::richCompare(PyObject *lhs, PyObject *rhs, int op) {
|
||||
PyObject *cmp = Py_False;
|
||||
if ( PyObject_TypeCheck(lhs, &VoronoiVertexPy::Type)
|
||||
&& PyObject_TypeCheck(rhs, &VoronoiVertexPy::Type)
|
||||
&& op == Py_EQ) {
|
||||
const VoronoiVertex *vl = static_cast<VoronoiVertexPy*>(lhs)->getVoronoiVertexPtr();
|
||||
const VoronoiVertex *vr = static_cast<VoronoiVertexPy*>(rhs)->getVoronoiVertexPtr();
|
||||
if (vl->index == vr->index && vl->dia == vr->dia) {
|
||||
cmp = Py_True;
|
||||
}
|
||||
}
|
||||
Py_INCREF(cmp);
|
||||
return cmp;
|
||||
}
|
||||
|
||||
const Voronoi::voronoi_diagram_type::vertex_type* getVertexFromPy(VoronoiVertexPy *v, bool throwIfNotBound = true) {
|
||||
auto self = v->getVoronoiVertexPtr();
|
||||
if (self->isBound()) {
|
||||
return self->ptr;
|
||||
}
|
||||
if (throwIfNotBound) {
|
||||
throw Py::TypeError("Vertex not bound to voronoi diagram");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VoronoiVertex* getVoronoiVertexFromPy(const VoronoiVertexPy *v, PyObject *args = 0) {
|
||||
VoronoiVertex *self = v->getVoronoiVertexPtr();
|
||||
if (!self->isBound()) {
|
||||
throw Py::TypeError("Vertex not bound to voronoi diagram");
|
||||
}
|
||||
if (args && !PyArg_ParseTuple(args, "")) {
|
||||
throw Py::RuntimeError("No arguments accepted");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
Py::Long VoronoiVertexPy::getIndex(void) const {
|
||||
VoronoiVertex *v = getVoronoiVertexPtr();
|
||||
if (v->isBound()) {
|
||||
return Py::Long(v->dia->index(v->ptr));
|
||||
}
|
||||
return Py::Long(-1);
|
||||
}
|
||||
|
||||
Py::Long VoronoiVertexPy::getColor(void) const {
|
||||
VoronoiVertex *v = getVoronoiVertexPtr();
|
||||
if (v->isBound()) {
|
||||
return Py::Long(v->ptr->color() & Voronoi::ColorMask);
|
||||
}
|
||||
return Py::Long(0);
|
||||
}
|
||||
|
||||
void VoronoiVertexPy::setColor(Py::Long color) {
|
||||
getVertexFromPy(this)->color(long(color) & Voronoi::ColorMask);
|
||||
}
|
||||
|
||||
Py::Float VoronoiVertexPy::getX(void) const
|
||||
{
|
||||
VoronoiVertex *v = getVoronoiVertexFromPy(this);
|
||||
return Py::Float(v->ptr->x() / v->dia->getScale());
|
||||
}
|
||||
|
||||
Py::Float VoronoiVertexPy::getY(void) const
|
||||
{
|
||||
VoronoiVertex *v = getVoronoiVertexFromPy(this);
|
||||
return Py::Float(v->ptr->y() / v->dia->getScale());
|
||||
}
|
||||
|
||||
Py::Object VoronoiVertexPy::getIncidentEdge() const {
|
||||
VoronoiVertex *v = getVoronoiVertexFromPy(this);
|
||||
return Py::asObject(new VoronoiEdgePy(new VoronoiEdge(v->dia, v->ptr->incident_edge())));
|
||||
}
|
||||
|
||||
PyObject* VoronoiVertexPy::toGeom(PyObject *args)
|
||||
{
|
||||
double z = 0.0;
|
||||
if (!PyArg_ParseTuple(args, "|d", &z)) {
|
||||
throw Py::RuntimeError("single argument of type double accepted");
|
||||
}
|
||||
VoronoiVertex *v = getVoronoiVertexPtr();
|
||||
if (v->isBound()) {
|
||||
return new Base::VectorPy(new Base::Vector3d(v->ptr->x() / v->dia->getScale(), v->ptr->y() / v->dia->getScale(), z));
|
||||
}
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
// custom attributes get/set
|
||||
|
||||
PyObject* VoronoiVertexPy::getCustomAttributes(const char* /*attr*/) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VoronoiVertexPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@ SET(PathScripts_SRCS
|
||||
PathScripts/PathUtil.py
|
||||
PathScripts/PathUtils.py
|
||||
PathScripts/PathUtilsGui.py
|
||||
PathScripts/PathVcarve.py
|
||||
PathScripts/PathVcarveGui.py
|
||||
PathScripts/PathWaterline.py
|
||||
PathScripts/PathWaterlineGui.py
|
||||
PathScripts/PostUtils.py
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<file>icons/Path-ToolController.svg</file>
|
||||
<file>icons/Path-Toolpath.svg</file>
|
||||
<file>icons/Path-ToolTable.svg</file>
|
||||
<file>icons/Path-Vcarve.svg</file>
|
||||
<file>icons/Path-Waterline.svg</file>
|
||||
<file>icons/arrow-ccw.svg</file>
|
||||
<file>icons/arrow-cw.svg</file>
|
||||
@@ -111,6 +112,7 @@
|
||||
<file>panels/PageOpSlotEdit.ui</file>
|
||||
<file>panels/PageOpSurfaceEdit.ui</file>
|
||||
<file>panels/PageOpWaterlineEdit.ui</file>
|
||||
<file>panels/PageOpVcarveEdit.ui</file>
|
||||
<file>panels/PathEdit.ui</file>
|
||||
<file>panels/PointEdit.ui</file>
|
||||
<file>panels/SetupGlobal.ui</file>
|
||||
|
||||
678
src/Mod/Path/Gui/Resources/icons/Path-Vcarve.svg
Normal file
678
src/Mod/Path/Gui/Resources/icons/Path-Vcarve.svg
Normal file
@@ -0,0 +1,678 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg2816"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||
sodipodi:docname="Path-Vcarve.svg">
|
||||
<defs
|
||||
id="defs2818">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3853">
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3855" />
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3857" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4513">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3681">
|
||||
<stop
|
||||
id="stop3697"
|
||||
offset="0"
|
||||
style="stop-color:#fff110;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#cf7008;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3685" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective2824" />
|
||||
<inkscape:perspective
|
||||
id="perspective3622"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3622-9"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3653"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3675"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3697"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3720"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3742"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3764"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3785"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3806"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3806-3"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3835"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3614"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3614-8"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3643"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3643-3"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3672"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3672-5"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3701"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3701-8"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3746"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
|
||||
id="pattern5231"
|
||||
xlink:href="#Strips1_1-4"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective5224"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
inkscape:stockid="Stripes 1:1"
|
||||
id="Strips1_1-4"
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
|
||||
height="1"
|
||||
width="2"
|
||||
patternUnits="userSpaceOnUse"
|
||||
inkscape:collect="always">
|
||||
<rect
|
||||
id="rect4483-4"
|
||||
height="2"
|
||||
width="1"
|
||||
y="-0.5"
|
||||
x="0"
|
||||
style="fill:black;stroke:none" />
|
||||
</pattern>
|
||||
<inkscape:perspective
|
||||
id="perspective5224-9"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
|
||||
id="pattern5231-4"
|
||||
xlink:href="#Strips1_1-6"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective5224-3"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
inkscape:stockid="Stripes 1:1"
|
||||
id="Strips1_1-6"
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
|
||||
height="1"
|
||||
width="2"
|
||||
patternUnits="userSpaceOnUse"
|
||||
inkscape:collect="always">
|
||||
<rect
|
||||
id="rect4483-0"
|
||||
height="2"
|
||||
width="1"
|
||||
y="-0.5"
|
||||
x="0"
|
||||
style="fill:black;stroke:none" />
|
||||
</pattern>
|
||||
<pattern
|
||||
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
|
||||
id="pattern5296"
|
||||
xlink:href="#pattern5231-3"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective5288"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
|
||||
id="pattern5231-3"
|
||||
xlink:href="#Strips1_1-4-3"
|
||||
inkscape:collect="always" />
|
||||
<pattern
|
||||
inkscape:stockid="Stripes 1:1"
|
||||
id="Strips1_1-4-3"
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
|
||||
height="1"
|
||||
width="2"
|
||||
patternUnits="userSpaceOnUse"
|
||||
inkscape:collect="always">
|
||||
<rect
|
||||
id="rect4483-4-6"
|
||||
height="2"
|
||||
width="1"
|
||||
y="-0.5"
|
||||
x="0"
|
||||
style="fill:black;stroke:none" />
|
||||
</pattern>
|
||||
<pattern
|
||||
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
|
||||
id="pattern5330"
|
||||
xlink:href="#Strips1_1-9"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective5323"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
inkscape:stockid="Stripes 1:1"
|
||||
id="Strips1_1-9"
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
|
||||
height="1"
|
||||
width="2"
|
||||
patternUnits="userSpaceOnUse"
|
||||
inkscape:collect="always">
|
||||
<rect
|
||||
id="rect4483-3"
|
||||
height="2"
|
||||
width="1"
|
||||
y="-0.5"
|
||||
x="0"
|
||||
style="fill:black;stroke:none" />
|
||||
</pattern>
|
||||
<inkscape:perspective
|
||||
id="perspective5361"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective5383"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective5411"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3681"
|
||||
id="linearGradient3687"
|
||||
x1="37.89756"
|
||||
y1="41.087898"
|
||||
x2="4.0605712"
|
||||
y2="40.168594"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(127.27273,-51.272729)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3681"
|
||||
id="linearGradient3695"
|
||||
x1="37.894287"
|
||||
y1="40.484772"
|
||||
x2="59.811455"
|
||||
y2="43.558987"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(127.27273,-51.272729)" />
|
||||
<linearGradient
|
||||
id="linearGradient3681-3">
|
||||
<stop
|
||||
id="stop3697-3"
|
||||
offset="0"
|
||||
style="stop-color:#fff110;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#cf7008;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3685-4" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="43.558987"
|
||||
x2="59.811455"
|
||||
y1="40.484772"
|
||||
x1="37.894287"
|
||||
gradientTransform="translate(-37.00068,-20.487365)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3608"
|
||||
xlink:href="#linearGradient3681-3"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-2">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-2" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-4" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="7.9319997"
|
||||
fx="32.151962"
|
||||
cy="7.9319997"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient4538"
|
||||
xlink:href="#linearGradient4513-2"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-1">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-8" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-6" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="7.9319997"
|
||||
fx="32.151962"
|
||||
cy="7.9319997"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient4538-6"
|
||||
xlink:href="#linearGradient4513-1"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-1-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-8-7" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-6-5" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="35.869175"
|
||||
fx="32.151962"
|
||||
cy="35.869175"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3069"
|
||||
xlink:href="#linearGradient4513-1-3"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-1-2">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-8-6" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-6-6" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="35.869175"
|
||||
fx="32.151962"
|
||||
cy="35.869175"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3102"
|
||||
xlink:href="#linearGradient4513-1-2"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4513-1"
|
||||
id="radialGradient3132"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.39492727,-0.00639802,0.01748379,1.079213,34.386898,-11.189684)"
|
||||
cx="32.151962"
|
||||
cy="27.950663"
|
||||
fx="32.151962"
|
||||
fy="27.950663"
|
||||
r="23.634638" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4031"
|
||||
id="linearGradient4055"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(71.494719,-3.1982556)"
|
||||
x1="30.000002"
|
||||
y1="14"
|
||||
x2="36"
|
||||
y2="54.227272" />
|
||||
<linearGradient
|
||||
id="linearGradient4031">
|
||||
<stop
|
||||
id="stop4033"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4035"
|
||||
offset="1"
|
||||
style="stop-color:#888a85;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3853"
|
||||
id="linearGradient3859"
|
||||
x1="36.573002"
|
||||
y1="5.1688194"
|
||||
x2="27.702478"
|
||||
y2="56.270748"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,-1,0,63.650913)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3898-8-9">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3900-2-2" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3902-4-7" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3898-8-9"
|
||||
id="linearGradient3143"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="35.05999"
|
||||
y1="53.008698"
|
||||
x2="27.286415"
|
||||
y2="7.311924"
|
||||
gradientTransform="matrix(0.97351465,0,0,1,14.847529,-9)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7.77"
|
||||
inkscape:cx="62.383362"
|
||||
inkscape:cy="22.520305"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:window-width="1076"
|
||||
inkscape:window-height="605"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1287"
|
||||
inkscape:window-maximized="0"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-global="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3366"
|
||||
empspacing="2"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
<sodipodi:guide
|
||||
position="0,62"
|
||||
orientation="0,1"
|
||||
id="guide128"
|
||||
inkscape:locked="false" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata2821">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title>Path-Engrave</dc:title>
|
||||
<dc:date>2016-02-24</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Engrave.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by/4.0/">https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
style="fill:#73d216;stroke:#73d216;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 5,25 V 38 L 33,53 51,49 V 34 Z"
|
||||
id="path3865"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#8ae234;stroke:none"
|
||||
d="m 3,30 v 4 l 30,16 16,-4 v -4 l -14,4 z"
|
||||
id="path3867"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#172a04;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 3,23 V 37 L 33,53 51,49 V 35 Z"
|
||||
id="path3863"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient3859);fill-opacity:1;fill-rule:nonzero;stroke:#0b1521;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
d="M 61,61 H 3 V 35 L 33,51 49,47 V 37 L 35,41 3,25 V 5 h 58 z"
|
||||
id="rect3105"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccccc" />
|
||||
<path
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:#729fcf;stroke-width:1.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
d="M 59,59 H 5 V 38.311295 L 33,53 51,48.586777 50.982782,34.327135 35,39 5,24 V 7 h 54 z"
|
||||
id="rect3105-3"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccccc" />
|
||||
<g
|
||||
id="g131"
|
||||
transform="matrix(1.0065176,0,0,0.880385,-0.29981087,1.1805708)">
|
||||
<path
|
||||
sodipodi:nodetypes="cccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="rect4417-1-2"
|
||||
d="M 55,2 H 37 V 42 L 45.999998,47 55,41 C 55,28 55,15 55,2 Z"
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient3143);fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 23 KiB |
127
src/Mod/Path/Gui/Resources/panels/PageOpVcarveEdit.ui
Normal file
127
src/Mod/Path/Gui/Resources/panels/PageOpVcarveEdit.ui
Normal file
@@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>140</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_2">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>ToolController</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="toolController">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The tool and its settings to be used for this operation.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Discretization Deflection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="discretize">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>This value is used in discretizing arcs into segments. Smaller values will result in larger gcode. Larger values may cause unwanted segments in the medial line path.</p></body></html></string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.001000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Threshold</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="threshold">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Threshold is used by the medial axis filter to remove unwanted segments. If the resulting path contains unwanted segments, decrease this value. </p><p>Valid values are 0.0 - 1.0</p><p>Default = 0.8</p><p>1.0 will remove nothing.</p></body></html></string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>180</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -97,7 +97,7 @@ class PathWorkbench (Workbench):
|
||||
"Path_MillFace", "Path_Helix", "Path_Adaptive",
|
||||
"Path_Slot"]
|
||||
threedopcmdlist = ["Path_Pocket_3D"]
|
||||
engravecmdlist = ["Path_Engrave", "Path_Deburr"]
|
||||
engravecmdlist = ["Path_Engrave", "Path_Deburr", "Path_Vcarve"]
|
||||
modcmdlist = ["Path_OperationCopy", "Path_Array", "Path_SimpleCopy"]
|
||||
dressupcmdlist = ["Path_DressupAxisMap", "Path_DressupPathBoundary",
|
||||
"Path_DressupDogbone", "Path_DressupDragKnife",
|
||||
|
||||
@@ -35,7 +35,6 @@ else:
|
||||
|
||||
Processed = False
|
||||
|
||||
|
||||
def Startup():
|
||||
global Processed # pylint: disable=global-statement
|
||||
if not Processed:
|
||||
@@ -82,6 +81,7 @@ def Startup():
|
||||
from PathScripts import PathToolLibraryEditor
|
||||
from PathScripts import PathUtilsGui
|
||||
# from PathScripts import PathWaterlineGui # Added in initGui.py due to OCL dependency
|
||||
from PathScripts import PathVcarveGui
|
||||
Processed = True
|
||||
else:
|
||||
PathLog.debug('Skipping PathGui initialisation')
|
||||
|
||||
@@ -47,6 +47,34 @@ class MESHGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
return obj.TypeId[0:4] == 'Mesh'
|
||||
|
||||
class VCARVEGate:
|
||||
def allow(self, doc, obj, sub):
|
||||
try:
|
||||
shape = obj.Shape
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return False
|
||||
|
||||
if math.fabs(shape.Volume) < 1e-9 and len(shape.Wires) > 0:
|
||||
return True
|
||||
|
||||
if shape.ShapeType == 'Face':
|
||||
return True
|
||||
|
||||
elif shape.ShapeType == 'Solid':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
return True
|
||||
|
||||
elif shape.ShapeType == 'Compound':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
return True
|
||||
|
||||
if sub:
|
||||
subShape = shape.getElement(sub)
|
||||
if subShape.ShapeType == 'Edge':
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class ENGRAVEGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
|
||||
@@ -300,6 +328,10 @@ def surfaceselect():
|
||||
FreeCADGui.Selection.addSelectionGate(gate)
|
||||
FreeCAD.Console.PrintWarning("Surfacing Select Mode\n")
|
||||
|
||||
def vcarveselect():
|
||||
FreeCADGui.Selection.addSelectionGate(VCARVEGate())
|
||||
FreeCAD.Console.PrintWarning("Vcarve Select Mode\n")
|
||||
|
||||
|
||||
def probeselect():
|
||||
FreeCADGui.Selection.addSelectionGate(PROBEGate())
|
||||
@@ -328,6 +360,7 @@ def select(op):
|
||||
opsel['Surface'] = surfaceselect
|
||||
opsel['Waterline'] = surfaceselect
|
||||
opsel['Adaptive'] = adaptiveselect
|
||||
opsel['Vcarve'] = vcarveselect
|
||||
opsel['Probe'] = probeselect
|
||||
opsel['Custom'] = customselect
|
||||
return opsel[op]
|
||||
|
||||
302
src/Mod/Path/PathScripts/PathVcarve.py
Normal file
302
src/Mod/Path/PathScripts/PathVcarve.py
Normal file
@@ -0,0 +1,302 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2020 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * 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 Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import Part
|
||||
import Path
|
||||
import PathScripts.PathEngraveBase as PathEngraveBase
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
|
||||
import traceback
|
||||
|
||||
import math
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
__doc__ = "Class and implementation of Path Vcarve operation"
|
||||
|
||||
PRIMARY = 0
|
||||
EXTERIOR1 = 1
|
||||
EXTERIOR2 = 4
|
||||
TWIN = 2
|
||||
COLINEAR = 3
|
||||
SECONDARY = 5
|
||||
|
||||
if True:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt tanslation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
VD = []
|
||||
|
||||
|
||||
class ObjectVcarve(PathEngraveBase.ObjectOp):
|
||||
'''Proxy class for Vcarve operation.'''
|
||||
|
||||
def opFeatures(self, obj):
|
||||
'''opFeatures(obj) ... return all standard features and edges based geomtries'''
|
||||
return PathOp.FeatureTool | PathOp.FeatureHeights | PathOp.FeatureBaseFaces
|
||||
|
||||
def setupAdditionalProperties(self, obj):
|
||||
if not hasattr(obj, 'BaseShapes'):
|
||||
obj.addProperty("App::PropertyLinkList", "BaseShapes", "Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathVcarve",
|
||||
"Additional base objects to be engraved"))
|
||||
obj.setEditorMode('BaseShapes', 2) # hide
|
||||
if not hasattr(obj, 'BaseObject'):
|
||||
obj.addProperty("App::PropertyLink", "BaseObject", "Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathVcarve",
|
||||
"Additional base objects to be engraved"))
|
||||
obj.setEditorMode('BaseObject', 2) # hide
|
||||
|
||||
def initOperation(self, obj):
|
||||
'''initOperation(obj) ... create vcarve specific properties.'''
|
||||
obj.addProperty("App::PropertyFloat", "Discretize", "Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathVcarve",
|
||||
"The deflection value for discretizing arcs"))
|
||||
obj.addProperty("App::PropertyFloat", "Threshold", "Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathVcarve",
|
||||
"cutoff for removing colinear segments (degrees). \
|
||||
default=10.0."))
|
||||
obj.addProperty("App::PropertyFloat", "Tolerance", "Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathVcarve", ""))
|
||||
obj.Threshold = 10.0
|
||||
obj.Discretize = 0.01
|
||||
obj.Tolerance = PathPreferences.defaultGeometryTolerance()
|
||||
self.setupAdditionalProperties(obj)
|
||||
|
||||
def opOnDocumentRestored(self, obj):
|
||||
# upgrade ...
|
||||
self.setupAdditionalProperties(obj)
|
||||
|
||||
def buildPathMedial(self, obj, Faces):
|
||||
'''constructs a medial axis path using openvoronoi'''
|
||||
|
||||
def insert_many_wires(vd, wires):
|
||||
for wire in wires:
|
||||
PathLog.debug('discretize value: {}'.format(obj.Discretize))
|
||||
pts = wire.discretize(QuasiDeflection=obj.Discretize)
|
||||
ptv = [FreeCAD.Vector(p[0], p[1]) for p in pts]
|
||||
ptv.append(ptv[0])
|
||||
|
||||
for i in range(len(pts)):
|
||||
vd.addSegment(ptv[i], ptv[i+1])
|
||||
|
||||
def calculate_depth(MIC):
|
||||
# given a maximum inscribed circle (MIC) and tool angle,
|
||||
# return depth of cut.
|
||||
|
||||
r = obj.ToolController.Tool.Diameter / 2
|
||||
toolangle = obj.ToolController.Tool.CuttingEdgeAngle
|
||||
maxdepth = r / math.tan(math.radians(toolangle/2))
|
||||
|
||||
d = round(MIC / math.tan(math.radians(toolangle / 2)), 4)
|
||||
return d if d <= maxdepth else maxdepth
|
||||
|
||||
def getEdges(vd, color=[PRIMARY]):
|
||||
if type(color) == int:
|
||||
color = [color]
|
||||
geomList = []
|
||||
for e in vd.Edges:
|
||||
if e.Color not in color:
|
||||
continue
|
||||
if e.toGeom() is None:
|
||||
continue
|
||||
p1 = e.Vertices[0].toGeom(calculate_depth(0-e.getDistances()[0]))
|
||||
p2 = e.Vertices[-1].toGeom(calculate_depth(0-e.getDistances()[-1]))
|
||||
newedge = Part.Edge(Part.Vertex(p1), Part.Vertex(p2))
|
||||
|
||||
newedge.fixTolerance(obj.Tolerance, Part.Vertex)
|
||||
geomList.append(newedge)
|
||||
|
||||
return geomList
|
||||
|
||||
def sortEm(mywire, unmatched):
|
||||
remaining = []
|
||||
wireGrowing = False
|
||||
|
||||
# end points of existing wire
|
||||
wireverts = [mywire.Edges[0].valueAt(mywire.Edges[0].FirstParameter),
|
||||
mywire.Edges[-1].valueAt(mywire.Edges[-1].LastParameter)]
|
||||
|
||||
for i, candidate in enumerate(unmatched):
|
||||
|
||||
# end points of candidate edge
|
||||
cverts = [candidate.Edges[0].valueAt(candidate.Edges[0].FirstParameter),
|
||||
candidate.Edges[-1].valueAt(candidate.Edges[-1].LastParameter)]
|
||||
|
||||
# ignore short segments below tolerance level
|
||||
if PathGeom.pointsCoincide(cverts[0], cverts[1], obj.Tolerance):
|
||||
continue
|
||||
|
||||
# iterate the combination of endpoints. If a match is found,
|
||||
# make an edge from the common endpoint to the other end of
|
||||
# the candidate wire. Add the edge to the wire and return it.
|
||||
|
||||
# This generates a new edge rather than using the candidate to
|
||||
# avoid vertexes with close but different vectors
|
||||
for wvert in wireverts:
|
||||
for idx, cvert in enumerate(cverts):
|
||||
if PathGeom.pointsCoincide(wvert, cvert, obj.Tolerance):
|
||||
wireGrowing = True
|
||||
elist = mywire.Edges
|
||||
otherIndex = int(not(idx))
|
||||
|
||||
newedge = Part.Edge(Part.Vertex(wvert),
|
||||
Part.Vertex(cverts[otherIndex]))
|
||||
|
||||
elist.append(newedge)
|
||||
mywire = Part.Wire(Part.__sortEdges__(elist))
|
||||
remaining.extend(unmatched[i+1:])
|
||||
return mywire, remaining, wireGrowing
|
||||
|
||||
# if not matched, add to remaining list to test later
|
||||
remaining.append(candidate)
|
||||
|
||||
return mywire, remaining, wireGrowing
|
||||
|
||||
def getWires(candidateList):
|
||||
|
||||
chains = []
|
||||
while len(candidateList) > 0:
|
||||
cur_wire = Part.Wire(candidateList.pop(0))
|
||||
|
||||
wireGrowing = True
|
||||
while wireGrowing:
|
||||
cur_wire, candidateList, wireGrowing = sortEm(cur_wire,
|
||||
candidateList)
|
||||
|
||||
chains.append(cur_wire)
|
||||
|
||||
return chains
|
||||
|
||||
def cutWire(w):
|
||||
path = []
|
||||
path.append(Path.Command("G0 Z{}".format(obj.SafeHeight.Value)))
|
||||
e = w.Edges[0]
|
||||
p = e.valueAt(e.FirstParameter)
|
||||
path.append(Path.Command("G0 X{} Y{} Z{}".format(p.x, p.y,
|
||||
obj.SafeHeight.Value)))
|
||||
c = Path.Command("G1 X{} Y{} Z{} F{}".format(p.x, p.y, p.z,
|
||||
obj.ToolController.HorizFeed.Value))
|
||||
path.append(c)
|
||||
for e in w.Edges:
|
||||
path.extend(PathGeom.cmdsForEdge(e,
|
||||
hSpeed=obj.ToolController.HorizFeed.Value))
|
||||
|
||||
return path
|
||||
|
||||
VD.clear()
|
||||
pathlist = []
|
||||
pathlist.append(Path.Command("(starting)"))
|
||||
for f in Faces:
|
||||
vd = Path.Voronoi()
|
||||
insert_many_wires(vd, f.Wires)
|
||||
|
||||
vd.construct()
|
||||
|
||||
for e in vd.Edges:
|
||||
e.Color = PRIMARY if e.isPrimary() else SECONDARY
|
||||
vd.colorExterior(EXTERIOR1)
|
||||
vd.colorExterior(EXTERIOR2,
|
||||
lambda v: not f.isInside(v.toGeom(f.BoundBox.ZMin),
|
||||
obj.Tolerance, True))
|
||||
vd.colorColinear(COLINEAR, obj.Threshold)
|
||||
vd.colorTwins(TWIN)
|
||||
|
||||
edgelist = getEdges(vd)
|
||||
|
||||
for wire in getWires(edgelist):
|
||||
pathlist.extend(cutWire(wire))
|
||||
VD.append((f, vd, getWires(edgelist)))
|
||||
|
||||
self.commandlist = pathlist
|
||||
|
||||
def opExecute(self, obj):
|
||||
'''opExecute(obj) ... process engraving operation'''
|
||||
PathLog.track()
|
||||
|
||||
if not hasattr(obj.ToolController.Tool, "CuttingEdgeAngle"):
|
||||
FreeCAD.Console.PrintError(
|
||||
translate("Path_Vcarve", "VCarve requires an engraving \
|
||||
cutter with CuttingEdgeAngle") + "\n")
|
||||
|
||||
if obj.ToolController.Tool.CuttingEdgeAngle >= 180.0:
|
||||
FreeCAD.Console.PrintError(
|
||||
translate("Path_Vcarve",
|
||||
"Engraver Cutting Edge Angle must be < 180 degrees.") + "\n")
|
||||
return
|
||||
try:
|
||||
if obj.Base:
|
||||
PathLog.track()
|
||||
for base in obj.Base:
|
||||
faces = []
|
||||
for sub in base[1]:
|
||||
shape = getattr(base[0].Shape, sub)
|
||||
if isinstance(shape, Part.Face):
|
||||
faces.append(shape)
|
||||
|
||||
modelshape = Part.makeCompound(faces)
|
||||
|
||||
elif len(self.model) == 1 and self.model[0].isDerivedFrom('Sketcher::SketchObject') or \
|
||||
self.model[0].isDerivedFrom('Part::Part2DObject'):
|
||||
PathLog.track()
|
||||
|
||||
modelshape = self.model[0].Shape
|
||||
self.buildPathMedial(obj, modelshape.Faces)
|
||||
|
||||
except Exception as e:
|
||||
PathLog.error(e)
|
||||
traceback.print_exc()
|
||||
PathLog.error(translate('PathVcarve',
|
||||
'The Job Base Object has no engraveable element.\
|
||||
Engraving operation will produce no output.'))
|
||||
|
||||
def opUpdateDepths(self, obj, ignoreErrors=False):
|
||||
'''updateDepths(obj) ... engraving is always done at \
|
||||
the top most z-value'''
|
||||
job = PathUtils.findParentJob(obj)
|
||||
self.opSetDefaultValues(obj, job)
|
||||
|
||||
|
||||
def SetupProperties():
|
||||
return ["Discretize"]
|
||||
|
||||
|
||||
def Create(name, obj=None):
|
||||
'''Create(name) ... Creates and returns a Vcarve operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
ObjectVcarve(obj, name)
|
||||
return obj
|
||||
152
src/Mod/Path/PathScripts/PathVcarveGui.py
Normal file
152
src/Mod/Path/PathScripts/PathVcarveGui.py
Normal file
@@ -0,0 +1,152 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2017 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * 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 Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts.PathVcarve as PathVcarve
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
__title__ = "Path Vcarve Operation UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Vcarve operation page controller and command implementation."
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
|
||||
'''Enhanced base geometry page to also allow special base objects.'''
|
||||
|
||||
def super(self):
|
||||
return super(TaskPanelBaseGeometryPage, self)
|
||||
|
||||
def addBaseGeometry(self, selection):
|
||||
added = False
|
||||
shapes = self.obj.BaseShapes
|
||||
for sel in selection:
|
||||
job = PathUtils.findParentJob(self.obj)
|
||||
base = job.Proxy.resourceClone(job, sel.Object)
|
||||
if not base:
|
||||
PathLog.notice((translate("Path", "%s is not a Base Model object of the job %s") + "\n") % (sel.Object.Label, job.Label))
|
||||
continue
|
||||
if base in shapes:
|
||||
PathLog.notice((translate("Path", "Base shape %s already in the list") + "\n") % (sel.Object.Label))
|
||||
continue
|
||||
if base.isDerivedFrom('Part::Part2DObject'):
|
||||
if sel.HasSubObjects:
|
||||
# selectively add some elements of the drawing to the Base
|
||||
for sub in sel.SubElementNames:
|
||||
if 'Vertex' in sub:
|
||||
PathLog.info(translate("Path", "Ignoring vertex"))
|
||||
else:
|
||||
self.obj.Proxy.addBase(self.obj, base, sub)
|
||||
else:
|
||||
# when adding an entire shape to BaseShapes we can take its sub shapes out of Base
|
||||
self.obj.Base = [(p, el) for p, el in self.obj.Base if p != base]
|
||||
shapes.append(base)
|
||||
self.obj.BaseShapes = shapes
|
||||
added = True
|
||||
else:
|
||||
# user wants us to engrave an edge of face of a base model
|
||||
base = self.super().addBaseGeometry(selection)
|
||||
added = added or base
|
||||
|
||||
return added
|
||||
|
||||
def setFields(self, obj):
|
||||
self.super().setFields(obj)
|
||||
self.form.baseList.blockSignals(True)
|
||||
for shape in self.obj.BaseShapes:
|
||||
item = QtGui.QListWidgetItem(shape.Label)
|
||||
item.setData(self.super().DataObject, shape)
|
||||
item.setData(self.super().DataObjectSub, None)
|
||||
self.form.baseList.addItem(item)
|
||||
self.form.baseList.blockSignals(False)
|
||||
|
||||
def updateBase(self):
|
||||
PathLog.track()
|
||||
shapes = []
|
||||
for i in range(self.form.baseList.count()):
|
||||
item = self.form.baseList.item(i)
|
||||
obj = item.data(self.super().DataObject)
|
||||
sub = item.data(self.super().DataObjectSub)
|
||||
if not sub:
|
||||
shapes.append(obj)
|
||||
PathLog.debug("Setting new base shapes: %s -> %s" % (self.obj.BaseShapes, shapes))
|
||||
self.obj.BaseShapes = shapes
|
||||
return self.super().updateBase()
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
'''Page controller class for the Vcarve operation.'''
|
||||
|
||||
def getForm(self):
|
||||
'''getForm() ... returns UI'''
|
||||
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpVcarveEdit.ui")
|
||||
|
||||
def getFields(self, obj):
|
||||
'''getFields(obj) ... transfers values from UI to obj's proprties'''
|
||||
if obj.Discretize != self.form.discretize.value():
|
||||
obj.Discretize = self.form.discretize.value()
|
||||
if obj.Threshold != self.form.threshold.value():
|
||||
obj.Threshold = self.form.threshold.value()
|
||||
self.updateToolController(obj, self.form.toolController)
|
||||
|
||||
def setFields(self, obj):
|
||||
'''setFields(obj) ... transfers obj's property values to UI'''
|
||||
self.form.discretize.setValue(obj.Discretize)
|
||||
self.form.threshold.setValue(obj.Threshold)
|
||||
self.setupToolController(obj, self.form.toolController)
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
|
||||
signals = []
|
||||
signals.append(self.form.discretize.editingFinished)
|
||||
signals.append(self.form.threshold.editingFinished)
|
||||
signals.append(self.form.toolController.currentIndexChanged)
|
||||
return signals
|
||||
|
||||
def taskPanelBaseGeometryPage(self, obj, features):
|
||||
'''taskPanelBaseGeometryPage(obj, features) ... return page for adding base geometries.'''
|
||||
return TaskPanelBaseGeometryPage(obj, features)
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Vcarve', PathVcarve.Create, TaskPanelOpPage,
|
||||
'Path-Vcarve', QtCore.QT_TRANSLATE_NOOP("PathVcarve", "Vcarve"),
|
||||
QtCore.QT_TRANSLATE_NOOP("PathVcarve", "Creates a medial line engraving path"),
|
||||
PathVcarve.SetupProperties)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathVcarveGui... done\n")
|
||||
Reference in New Issue
Block a user