Merge pull request #3879 from sliptonic/feature/vcarve

[Path] Feature/vcarve  using new PathVoronoi from mlampert.  Needs testing
This commit is contained in:
sliptonic
2020-09-30 09:47:31 -05:00
committed by GitHub
27 changed files with 3806 additions and 7 deletions

View File

@@ -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);
}

View File

@@ -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}
)

View 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
View 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

View 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()];
}

View 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

View 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>

View 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;
}

View 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;
}

View 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

View 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>

View 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;
}

View 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>

View 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, &degree)) {
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;
}

View 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;
}

View 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

View 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>

View 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;
}

View File

@@ -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

View File

@@ -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>

View 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

View 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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The tool and its settings to be used for this operation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Threshold is used by the medial axis filter to remove unwanted segments. If the resulting path contains unwanted segments, decrease this value. &lt;/p&gt;&lt;p&gt;Valid values are 0.0 - 1.0&lt;/p&gt;&lt;p&gt;Default = 0.8&lt;/p&gt;&lt;p&gt;1.0 will remove nothing.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>

View File

@@ -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",

View File

@@ -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')

View File

@@ -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]

View 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

View 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")