Path.Area: fixed non-circular curve handling, etc.
* Fixed non-circular curve orientation handling * Section changed to use Part::CrossSection, because it seems BRepAlgoAPI_Section has trouble with non-circular curves (LastParameter becomes huge which causes discretization to produce many many points) * Exposed Area.makeSections() to section with variable heights * Modified Area.setPlane() to accept non-planar shape * Exposed Area.getPlane() to obtain current workplane * Exposed Area.Shapes attribute to return the current holding children shape.
This commit is contained in:
parent
41c7827287
commit
228a0dc905
|
@ -24,6 +24,7 @@
|
|||
#ifndef _PreComp_
|
||||
#endif
|
||||
|
||||
#include "boost/date_time/posix_time/posix_time.hpp"
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
#include <BRepLib.hxx>
|
||||
|
@ -52,22 +53,27 @@
|
|||
#include <gp_Circ.hxx>
|
||||
#include <gp_GTrsf.hxx>
|
||||
#include <Standard_Version.hxx>
|
||||
#include <GCPnts_UniformDeflection.hxx>
|
||||
#include <GCPnts_QuasiUniformDeflection.hxx>
|
||||
#include <BRepBndLib.hxx>
|
||||
#include <BRepLib_MakeFace.hxx>
|
||||
#include <Bnd_Box.hxx>
|
||||
#include <BRepAlgoAPI_Section.hxx>
|
||||
#include <BRepBuilderAPI_Copy.hxx>
|
||||
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Console.h>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
#include <Mod/Part/App/FaceMakerBullseye.h>
|
||||
#include <Mod/Part/App/CrossSection.h>
|
||||
#include "Area.h"
|
||||
#include "../libarea/Area.h"
|
||||
|
||||
using namespace Path;
|
||||
using namespace boost::posix_time;
|
||||
|
||||
CAreaParams::CAreaParams()
|
||||
:PARAM_INIT(PARAM_FNAME,AREA_PARAMS_CAREA)
|
||||
|
@ -78,17 +84,17 @@ AreaParams::AreaParams()
|
|||
{}
|
||||
|
||||
CAreaConfig::CAreaConfig(const CAreaParams &p, bool noFitArcs)
|
||||
:params(p)
|
||||
{
|
||||
// Arc fitting is lossy. we shall reduce the number of unecessary fit
|
||||
if(noFitArcs)
|
||||
params.FitArcs=false;
|
||||
|
||||
#define AREA_CONF_SAVE_AND_APPLY(_param) \
|
||||
PARAM_FNAME(_param) = BOOST_PP_CAT(CArea::get_,PARAM_FARG(_param))();\
|
||||
BOOST_PP_CAT(CArea::set_,PARAM_FARG(_param))(params.PARAM_FNAME(_param));
|
||||
BOOST_PP_CAT(CArea::set_,PARAM_FARG(_param))(p.PARAM_FNAME(_param));
|
||||
|
||||
PARAM_FOREACH(AREA_CONF_SAVE_AND_APPLY,AREA_PARAMS_CAREA)
|
||||
|
||||
// Arc fitting is lossy. we shall reduce the number of unecessary fit
|
||||
if(noFitArcs)
|
||||
CArea::set_fit_arcs(false);
|
||||
|
||||
}
|
||||
|
||||
CAreaConfig::~CAreaConfig() {
|
||||
|
@ -117,15 +123,16 @@ Area::Area(const Area &other, bool deep_copy)
|
|||
,myShapes(other.myShapes)
|
||||
,myTrsf(other.myTrsf)
|
||||
,myParams(other.myParams)
|
||||
,myShapePlane(other.myShapePlane)
|
||||
,myWorkPlane(other.myWorkPlane)
|
||||
,myHaveFace(other.myHaveFace)
|
||||
,myHaveSolid(other.myHaveSolid)
|
||||
,myShapeDone(false)
|
||||
{
|
||||
if(!deep_copy) return;
|
||||
if(!deep_copy || !other.isBuilt())
|
||||
return;
|
||||
if(other.myArea)
|
||||
myArea.reset(new CArea(*other.myArea));
|
||||
myShapePlane = other.myShapePlane;
|
||||
myShape = other.myShape;
|
||||
myShapeDone = other.myShapeDone;
|
||||
mySections.reserve(other.mySections.size());
|
||||
|
@ -142,16 +149,18 @@ void Area::setPlane(const TopoDS_Shape &shape) {
|
|||
myWorkPlane.Nullify();
|
||||
return;
|
||||
}
|
||||
BRepLib_FindSurface planeFinder(shape,-1,Standard_True);
|
||||
if (!planeFinder.Found())
|
||||
TopoDS_Shape plane;
|
||||
gp_Trsf trsf;
|
||||
findPlane(shape,plane,trsf);
|
||||
if (plane.IsNull())
|
||||
throw Base::ValueError("shape is not planar");
|
||||
myWorkPlane = shape;
|
||||
myTrsf.SetTransformation(GeomAdaptor_Surface(
|
||||
planeFinder.Surface()).Plane().Position());
|
||||
myWorkPlane = plane;
|
||||
myTrsf = trsf;
|
||||
clean();
|
||||
}
|
||||
|
||||
bool Area::isCoplanar(const TopoDS_Shape &s1, const TopoDS_Shape &s2) {
|
||||
if(s1.IsNull() || s2.IsNull()) return false;
|
||||
if(s1.IsEqual(s2)) return true;
|
||||
TopoDS_Builder builder;
|
||||
TopoDS_Compound comp;
|
||||
|
@ -234,7 +243,8 @@ void Area::add(CArea &area, const TopoDS_Wire& wire,
|
|||
ccurve.append(CVertex(Point(p.X(),p.Y())));
|
||||
|
||||
for (;xp.More();xp.Next()) {
|
||||
BRepAdaptor_Curve curve(xp.Current());
|
||||
const TopoDS_Edge &edge = TopoDS::Edge(xp.Current());
|
||||
BRepAdaptor_Curve curve(edge);
|
||||
bool reversed = (xp.Current().Orientation()==TopAbs_REVERSED);
|
||||
|
||||
p = curve.Value(reversed?curve.FirstParameter():curve.LastParameter());
|
||||
|
@ -270,18 +280,31 @@ void Area::add(CArea &area, const TopoDS_Wire& wire,
|
|||
//fall through
|
||||
} default: {
|
||||
// Discretize all other type of curves
|
||||
GCPnts_UniformDeflection discretizer(curve, deflection,
|
||||
GCPnts_QuasiUniformDeflection discretizer(curve, deflection,
|
||||
curve.FirstParameter(), curve.LastParameter());
|
||||
if (discretizer.IsDone () && discretizer.NbPoints () > 0) {
|
||||
if (discretizer.IsDone () && discretizer.NbPoints () > 1) {
|
||||
int nbPoints = discretizer.NbPoints ();
|
||||
for (int i=1; i<=nbPoints; i++) {
|
||||
gp_Pnt pt = discretizer.Value (i);
|
||||
ccurve.append(CVertex(Point(pt.X(),pt.Y())));
|
||||
if(to_edges) {
|
||||
area.append(ccurve);
|
||||
ccurve.m_vertices.pop_front();
|
||||
//strangly OCC discretizer points are one-based, not zero-based, why?
|
||||
if(reversed) {
|
||||
for (int i=nbPoints-1; i>=1; --i) {
|
||||
gp_Pnt pt = discretizer.Value (i);
|
||||
ccurve.append(CVertex(Point(pt.X(),pt.Y())));
|
||||
if(to_edges) {
|
||||
area.append(ccurve);
|
||||
ccurve.m_vertices.pop_front();
|
||||
}
|
||||
}
|
||||
}else{
|
||||
for (int i=2; i<=nbPoints; i++) {
|
||||
gp_Pnt pt = discretizer.Value (i);
|
||||
ccurve.append(CVertex(Point(pt.X(),pt.Y())));
|
||||
if(to_edges) {
|
||||
area.append(ccurve);
|
||||
ccurve.m_vertices.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}else
|
||||
Standard_Failure::Raise("Curve discretization failed");
|
||||
}}
|
||||
|
@ -371,13 +394,10 @@ void Area::addToBuild(CArea &area, const TopoDS_Shape &shape) {
|
|||
TopExp_Explorer it(shape, TopAbs_FACE);
|
||||
myHaveFace = it.More();
|
||||
}
|
||||
const TopoDS_Shape *plane;
|
||||
if(myParams.Coplanar == CoplanarNone)
|
||||
plane = NULL;
|
||||
else
|
||||
plane = myWorkPlane.IsNull()?&myShapePlane:&myWorkPlane;
|
||||
TopoDS_Shape plane = getPlane();
|
||||
CArea areaOpen;
|
||||
mySkippedShapes += add(area,shape,&myTrsf,myParams.Deflection,plane,
|
||||
mySkippedShapes += add(area,shape,&myTrsf,myParams.Deflection,
|
||||
myParams.Coplanar==CoplanarNone?NULL:&plane,
|
||||
myHaveSolid||myParams.Coplanar==CoplanarForce,&areaOpen,
|
||||
myParams.OpenMode==OpenModeEdges,myParams.Reorient);
|
||||
if(areaOpen.m_curves.size()) {
|
||||
|
@ -392,8 +412,272 @@ namespace Part {
|
|||
extern PartExport std::list<TopoDS_Edge> sort_Edges(double tol3d, std::list<TopoDS_Edge>& edges);
|
||||
}
|
||||
|
||||
void Area::explode(const TopoDS_Shape &shape) {
|
||||
const TopoDS_Shape &plane = getPlane();
|
||||
bool haveShape = false;
|
||||
for(TopExp_Explorer it(shape, TopAbs_FACE); it.More(); it.Next()) {
|
||||
haveShape = true;
|
||||
if(myParams.Coplanar!=CoplanarNone && !isCoplanar(it.Current(),plane)){
|
||||
++mySkippedShapes;
|
||||
if(myParams.Coplanar == CoplanarForce)
|
||||
continue;
|
||||
}
|
||||
for(TopExp_Explorer itw(it.Current(), TopAbs_WIRE); itw.More(); itw.Next()) {
|
||||
for(BRepTools_WireExplorer xp(TopoDS::Wire(itw.Current()));xp.More();xp.Next())
|
||||
add(*myArea,BRepBuilderAPI_MakeWire(
|
||||
TopoDS::Edge(xp.Current())).Wire(),&myTrsf,myParams.Deflection,true);
|
||||
}
|
||||
}
|
||||
if(haveShape) return;
|
||||
for(TopExp_Explorer it(shape, TopAbs_EDGE); it.More(); it.Next()) {
|
||||
if(myParams.Coplanar!=CoplanarNone && !isCoplanar(it.Current(),plane)){
|
||||
++mySkippedShapes;
|
||||
if(myParams.Coplanar == CoplanarForce)
|
||||
continue;
|
||||
}
|
||||
add(*myArea,BRepBuilderAPI_MakeWire(
|
||||
TopoDS::Edge(it.Current())).Wire(),&myTrsf,myParams.Deflection,true);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void show(const TopoDS_Shape &shape, const char *name) {
|
||||
App::Document *pcDoc = App::GetApplication().getActiveDocument();
|
||||
if (!pcDoc)
|
||||
pcDoc = App::GetApplication().newDocument();
|
||||
Part::Feature *pcFeature = (Part::Feature *)pcDoc->addObject("Part::Feature", name);
|
||||
// copy the data
|
||||
//TopoShape* shape = new MeshObject(*pShape->getTopoShapeObjectPtr());
|
||||
pcFeature->Shape.setValue(shape);
|
||||
//pcDoc->recompute();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Area::findPlane(const TopoDS_Shape &shape,
|
||||
TopoDS_Shape &plane, gp_Trsf &trsf)
|
||||
{
|
||||
return findPlane(shape,TopAbs_FACE,plane,trsf) ||
|
||||
findPlane(shape,TopAbs_WIRE,plane,trsf) ||
|
||||
findPlane(shape,TopAbs_EDGE,plane,trsf);
|
||||
}
|
||||
|
||||
bool Area::findPlane(const TopoDS_Shape &shape, int type,
|
||||
TopoDS_Shape &dst, gp_Trsf &dst_trsf)
|
||||
{
|
||||
bool haveShape = false;
|
||||
double top_z;
|
||||
bool top_found = false;
|
||||
gp_Trsf trsf;
|
||||
for(TopExp_Explorer it(shape,(TopAbs_ShapeEnum)type); it.More(); it.Next()) {
|
||||
haveShape = true;
|
||||
const TopoDS_Shape &plane = it.Current();
|
||||
BRepLib_FindSurface planeFinder(plane,-1,Standard_True);
|
||||
if (!planeFinder.Found())
|
||||
continue;
|
||||
gp_Ax3 pos = GeomAdaptor_Surface(planeFinder.Surface()).Plane().Position();
|
||||
gp_Dir dir(pos.Direction());
|
||||
trsf.SetTransformation(pos);
|
||||
|
||||
//pos.Location().Z() is always 0, why? As a walk around, use the first vertex Z
|
||||
gp_Pnt origin = pos.Location();
|
||||
|
||||
for(TopExp_Explorer it(plane.Moved(trsf),TopAbs_VERTEX);it.More();) {
|
||||
origin.SetZ(BRep_Tool::Pnt(TopoDS::Vertex(it.Current())).Z());
|
||||
break;
|
||||
}
|
||||
|
||||
if(fabs(dir.X())<Precision::Confusion() &&
|
||||
fabs(dir.Y())<Precision::Confusion())
|
||||
{
|
||||
if(top_found && top_z > origin.Z())
|
||||
continue;
|
||||
top_found = true;
|
||||
top_z = origin.Z();
|
||||
}else if(!dst.IsNull())
|
||||
continue;
|
||||
dst = plane;
|
||||
|
||||
//Some how the plane returned by BRepLib_FindSurface has Z always set to 0.
|
||||
//We need to manually translate Z to its actual value
|
||||
gp_Trsf trsf2;
|
||||
trsf2.SetTranslationPart(gp_XYZ(0,0,-origin.Z()));
|
||||
dst_trsf = trsf.Multiplied(trsf2);
|
||||
}
|
||||
return haveShape;
|
||||
}
|
||||
|
||||
std::vector<shared_ptr<Area> > Area::makeSections(
|
||||
PARAM_ARGS(PARAM_FARG,AREA_PARAMS_SECTION_EXTRA),
|
||||
const std::vector<double> &_heights,
|
||||
const TopoDS_Shape &_plane)
|
||||
{
|
||||
TopoDS_Shape plane;
|
||||
gp_Trsf trsf;
|
||||
|
||||
if(!_plane.IsNull())
|
||||
findPlane(_plane,plane,trsf);
|
||||
else
|
||||
plane = getPlane(&trsf);
|
||||
|
||||
if(plane.IsNull())
|
||||
throw Base::ValueError("failed to obtain section plane");
|
||||
|
||||
TopLoc_Location loc(trsf);
|
||||
|
||||
Bnd_Box bounds;
|
||||
for(const Shape &s : myShapes) {
|
||||
const TopoDS_Shape &shape = s.shape.Moved(loc);
|
||||
BRepBndLib::Add(shape, bounds, Standard_False);
|
||||
}
|
||||
bounds.SetGap(0.0);
|
||||
Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
|
||||
bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
|
||||
|
||||
bool hit_bottom = false;
|
||||
std::vector<double> heights;
|
||||
if(_heights.empty()) {
|
||||
if(mode != SectionModeAbsolute && myParams.SectionOffset<0)
|
||||
throw Base::ValueError("only positive section offset is allowed in non-absolute mode");
|
||||
if(myParams.SectionCount>1 && myParams.Stepdown<Precision::Confusion())
|
||||
throw Base::ValueError("invalid stepdown");
|
||||
|
||||
if(mode == SectionModeBoundBox)
|
||||
zMax -= myParams.SectionOffset;
|
||||
else if(mode == SectionModeWorkplane)
|
||||
zMax = -myParams.SectionOffset;
|
||||
else {
|
||||
gp_Pnt pt(0,0,myParams.SectionOffset);
|
||||
double z = pt.Transformed(loc).Z();
|
||||
if(z < zMax)
|
||||
zMax = z;
|
||||
}
|
||||
if(zMax <= zMin)
|
||||
throw Base::ValueError("section offset too big");
|
||||
|
||||
int count = myParams.SectionCount;
|
||||
if(count<0 || count*myParams.Stepdown > zMax-zMin) {
|
||||
count = ceil((zMax-zMin)/myParams.Stepdown);
|
||||
if((count-1)*myParams.Stepdown < zMax-zMin)
|
||||
++count;
|
||||
}
|
||||
heights.reserve(count);
|
||||
for(int i=0;i<count;++i,zMax-=myParams.Stepdown) {
|
||||
if(zMax < zMin) {
|
||||
hit_bottom = true;
|
||||
break;
|
||||
}
|
||||
heights.push_back(zMax);
|
||||
}
|
||||
}else{
|
||||
heights.reserve(_heights.size());
|
||||
for(double z : _heights) {
|
||||
switch(mode) {
|
||||
case SectionModeAbsolute: {
|
||||
gp_Pnt pt(0,0,z);
|
||||
z = pt.Transformed(loc).Z();
|
||||
break;
|
||||
}case SectionModeBoundBox:
|
||||
z = zMax - z;
|
||||
break;
|
||||
case SectionModeWorkplane:
|
||||
z = -z;
|
||||
break;
|
||||
default:
|
||||
throw Base::ValueError("invalid section mode");
|
||||
}
|
||||
if((zMin-z)>Precision::Confusion()) {
|
||||
hit_bottom = true;
|
||||
continue;
|
||||
}else if ((z-zMax)>Precision::Confusion())
|
||||
continue;
|
||||
heights.push_back(z);
|
||||
}
|
||||
}
|
||||
|
||||
if(hit_bottom)
|
||||
heights.push_back(zMin);
|
||||
else if(heights.empty())
|
||||
heights.push_back(zMax);
|
||||
|
||||
std::vector<shared_ptr<Area> > sections;
|
||||
sections.reserve(heights.size());
|
||||
for(double z : heights) {
|
||||
gp_Pln pln(gp_Pnt(0,0,z),gp_Dir(0,0,1));
|
||||
Standard_Real a,b,c,d;
|
||||
pln.Coefficients(a,b,c,d);
|
||||
BRepLib_MakeFace mkFace(pln,xMin,xMax,yMin,yMax);
|
||||
const TopoDS_Shape &face = mkFace.Face();
|
||||
|
||||
shared_ptr<Area> area(new Area(&myParams));
|
||||
area->setPlane(face);
|
||||
for(const Shape &s : myShapes) {
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound comp;
|
||||
builder.MakeCompound(comp);
|
||||
for(TopExp_Explorer it(s.shape.Moved(loc), TopAbs_SOLID); it.More(); it.Next()) {
|
||||
Part::CrossSection section(a,b,c,it.Current());
|
||||
std::list<TopoDS_Wire> wires = section.slice(-d);
|
||||
if(wires.empty()) {
|
||||
Base::Console().Warning("Section return no wires\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
Part::FaceMakerBullseye mkFace;
|
||||
mkFace.setPlane(pln);
|
||||
for(const TopoDS_Wire &wire : wires)
|
||||
mkFace.addWire(wire);
|
||||
try {
|
||||
mkFace.Build();
|
||||
if (mkFace.Shape().IsNull())
|
||||
Base::Console().Warning("FaceMakerBullseye return null shape on section\n");
|
||||
else {
|
||||
builder.Add(comp,mkFace.Shape());
|
||||
continue;
|
||||
}
|
||||
}catch (Base::Exception &e){
|
||||
Base::Console().Warning("FaceMakerBullseye failed on section: %s\n", e.what());
|
||||
}
|
||||
for(const TopoDS_Wire &wire : wires)
|
||||
builder.Add(comp,wire);
|
||||
}
|
||||
|
||||
// Make sure the compound has at least one edge
|
||||
for(TopExp_Explorer it(comp,TopAbs_EDGE);it.More();) {
|
||||
area->add(comp,s.op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(area->myShapes.size())
|
||||
sections.push_back(area);
|
||||
else
|
||||
Base::Console().Warning("Discard empty section\n");
|
||||
}
|
||||
return std::move(sections);
|
||||
}
|
||||
|
||||
TopoDS_Shape Area::getPlane(gp_Trsf *trsf) {
|
||||
if(!myWorkPlane.IsNull()) {
|
||||
if(trsf) *trsf = myTrsf;
|
||||
return myWorkPlane;
|
||||
}
|
||||
if(!isBuilt()) {
|
||||
myShapePlane.Nullify();
|
||||
for(const Shape &s : myShapes)
|
||||
findPlane(s.shape,myShapePlane,myTrsf);
|
||||
if(myShapePlane.IsNull())
|
||||
throw Base::ValueError("shapes are not planar");
|
||||
}
|
||||
if(trsf) *trsf = myTrsf;
|
||||
return myShapePlane;
|
||||
}
|
||||
|
||||
bool Area::isBuilt() const {
|
||||
return (myArea || mySections.size());
|
||||
}
|
||||
|
||||
|
||||
void Area::build() {
|
||||
if(myArea || mySections.size()) return;
|
||||
if(isBuilt()) return;
|
||||
|
||||
if(myShapes.empty())
|
||||
throw Base::ValueError("no shape added");
|
||||
|
@ -401,144 +685,13 @@ void Area::build() {
|
|||
#define AREA_SRC(_param) myParams.PARAM_FNAME(_param)
|
||||
PARAM_ENUM_CONVERT(AREA_SRC,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL);
|
||||
|
||||
if(myWorkPlane.IsNull()) {
|
||||
myShapePlane.Nullify();
|
||||
for(const Shape &s : myShapes) {
|
||||
bool haveShape = false;
|
||||
bool done = false;
|
||||
TopoDS_Shape shapePlane;
|
||||
gp_Trsf trsf;
|
||||
gp_Ax3 pos;
|
||||
#define AREA_CHECK_PLANE(_type) \
|
||||
shapePlane.Nullify();\
|
||||
for(TopExp_Explorer it(s.shape, TopAbs_##_type); it.More(); it.Next()) {\
|
||||
haveShape = true;\
|
||||
BRepLib_FindSurface planeFinder(it.Current(),-1,Standard_True);\
|
||||
if (!planeFinder.Found())\
|
||||
continue;\
|
||||
shapePlane = it.Current();\
|
||||
pos = GeomAdaptor_Surface(planeFinder.Surface()).Plane().Position();\
|
||||
trsf.SetTransformation(pos);\
|
||||
gp_Dir dir(pos.Direction());\
|
||||
if(fabs(dir.X())<Precision::Confusion() &&\
|
||||
fabs(dir.Y())<Precision::Confusion()) {\
|
||||
myShapePlane = shapePlane;\
|
||||
myTrsf = trsf;\
|
||||
done = true;\
|
||||
break;\
|
||||
}\
|
||||
if(myShapePlane.IsNull()) {\
|
||||
myShapePlane = shapePlane;\
|
||||
myTrsf = trsf;\
|
||||
}\
|
||||
}\
|
||||
if(done) break;\
|
||||
if(haveShape) continue;
|
||||
|
||||
//Try to find a plane by iterating through shapes, prefer plane paralell with XY0
|
||||
AREA_CHECK_PLANE(FACE)
|
||||
AREA_CHECK_PLANE(WIRE)
|
||||
AREA_CHECK_PLANE(EDGE)
|
||||
}
|
||||
if(myShapePlane.IsNull())
|
||||
throw Base::ValueError("shapes are not planar");
|
||||
}
|
||||
|
||||
if(myHaveSolid && myParams.SectionCount) {
|
||||
|
||||
if(myParams.SectionOffset < 0)
|
||||
throw Base::ValueError("invalid section offset");
|
||||
if(myParams.SectionCount>1 && myParams.Stepdown<Precision::Confusion())
|
||||
throw Base::ValueError("invalid stepdown");
|
||||
|
||||
TopLoc_Location loc(myTrsf);
|
||||
Bnd_Box bounds;
|
||||
for(const Shape &s : myShapes)
|
||||
BRepBndLib::Add(s.shape.Moved(loc), bounds);
|
||||
|
||||
bounds.SetGap(0.0);
|
||||
Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
|
||||
bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
|
||||
|
||||
zMax -= myParams.SectionOffset;
|
||||
if(zMax <= zMin)
|
||||
throw Base::ValueError("section offset too big");
|
||||
|
||||
int error = 0;
|
||||
int count = myParams.SectionCount;
|
||||
if(count<0 || count*myParams.Stepdown > zMax-zMin) {
|
||||
count = ceil((zMax-zMin)/myParams.Stepdown);
|
||||
if((count-1)*myParams.Stepdown < zMax-zMin)
|
||||
++count;
|
||||
}
|
||||
for(int i=0;i<count;++i,zMax-=myParams.Stepdown) {
|
||||
if(zMax < zMin) zMax = zMin;
|
||||
gp_Pln pln(gp_Pnt(0,0,zMax),gp_Dir(0,0,1));
|
||||
BRepLib_MakeFace mkFace(pln,xMin,xMax,yMin,yMax);
|
||||
const TopoDS_Shape &face = mkFace.Face();
|
||||
|
||||
shared_ptr<Area> area(new Area(&myParams));
|
||||
area->setPlane(face);
|
||||
for(const Shape &s : myShapes) {
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound comp;
|
||||
builder.MakeCompound(comp);
|
||||
for(TopExp_Explorer it(s.shape, TopAbs_SOLID); it.More(); it.Next()) {
|
||||
BRepAlgoAPI_Section section(it.Current().Moved(loc),face);
|
||||
if(!section.IsDone()) {
|
||||
++error;
|
||||
continue;
|
||||
}
|
||||
const TopoDS_Shape &shape = section.Shape();
|
||||
if(shape.IsNull()) continue;
|
||||
|
||||
Part::FaceMakerBullseye mkFace;
|
||||
mkFace.setPlane(pln);
|
||||
|
||||
std::list<TopoDS_Edge> edges;
|
||||
for(TopExp_Explorer it(shape, TopAbs_EDGE); it.More(); it.Next())
|
||||
edges.push_back(TopoDS::Edge(it.Current()));
|
||||
bool open = false;
|
||||
std::list<TopoDS_Wire> wires;
|
||||
while(edges.size()) {
|
||||
const std::list<TopoDS_Edge> sorted =
|
||||
Part::sort_Edges(Precision::Confusion(),edges);
|
||||
BRepBuilderAPI_MakeWire mkWire;
|
||||
for(const TopoDS_Edge &e : sorted)
|
||||
mkWire.Add(e);
|
||||
const TopoDS_Wire &wire = mkWire.Wire();
|
||||
if(!BRep_Tool::IsClosed(wire))
|
||||
open = true;
|
||||
wires.push_back(wire);
|
||||
}
|
||||
if(!open) {
|
||||
for(const TopoDS_Wire &wire : wires)
|
||||
mkFace.addWire(wire);
|
||||
try {
|
||||
mkFace.Build();
|
||||
if (mkFace.Shape().IsNull())
|
||||
continue;
|
||||
builder.Add(comp,mkFace.Shape());
|
||||
continue;
|
||||
}catch (Base::Exception &e){
|
||||
Base::Console().Warning("FaceMakerBullseye failed: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
//Shouldn't have any open wire, so count as error
|
||||
++error;
|
||||
for(const TopoDS_Wire &wire : wires)
|
||||
builder.Add(comp,wire);
|
||||
}
|
||||
if(comp.IsNull()) continue;
|
||||
area->add(comp,s.op);
|
||||
}
|
||||
mySections.push_back(area);
|
||||
}
|
||||
if(error)
|
||||
Base::Console().Warning("Some errors occured during operation\n");
|
||||
mySections = makeSections(myParams.SectionMode);
|
||||
return;
|
||||
}
|
||||
|
||||
getPlane();
|
||||
|
||||
try {
|
||||
myArea.reset(new CArea());
|
||||
myAreaOpen.reset(new CArea());
|
||||
|
@ -549,13 +702,11 @@ void Area::build() {
|
|||
mySkippedShapes = 0;
|
||||
short op = OperationUnion;
|
||||
bool pending = false;
|
||||
bool explode = myParams.Explode;
|
||||
bool exploding = myParams.Explode;
|
||||
for(const Shape &s : myShapes) {
|
||||
if(explode) {
|
||||
explode = false;
|
||||
for (TopExp_Explorer it(s.shape, TopAbs_EDGE); it.More(); it.Next())
|
||||
add(*myArea,BRepBuilderAPI_MakeWire(
|
||||
TopoDS::Edge(it.Current())).Wire(),&myTrsf,myParams.Deflection,true);
|
||||
if(exploding) {
|
||||
exploding = false;
|
||||
explode(s.shape);
|
||||
continue;
|
||||
}else if(op!=s.op) {
|
||||
if(myParams.OpenMode!=OpenModeNone)
|
||||
|
@ -604,8 +755,7 @@ void Area::build() {
|
|||
Area area(&myParams);
|
||||
area.myParams.Explode = false;
|
||||
area.myParams.Coplanar = CoplanarNone;
|
||||
area.myWorkPlane = myWorkPlane.IsNull()?myShapePlane:myWorkPlane;
|
||||
area.myTrsf = myTrsf;
|
||||
area.myWorkPlane = getPlane(&area.myTrsf);
|
||||
while(edges.size()) {
|
||||
BRepBuilderAPI_MakeWire mkWire;
|
||||
for(const auto &e : Part::sort_Edges(myParams.Tolerance,edges))
|
||||
|
@ -706,6 +856,7 @@ TopoDS_Shape Area::toShape(CArea &area, short fill) {
|
|||
return toShape(area,bFill,&trsf);
|
||||
}
|
||||
|
||||
|
||||
#define AREA_SECTION(_op,_index,...) do {\
|
||||
if(mySections.size()) {\
|
||||
if(_index>=(int)mySections.size())\
|
||||
|
@ -720,9 +871,14 @@ TopoDS_Shape Area::toShape(CArea &area, short fill) {
|
|||
if(s.IsNull()) continue;\
|
||||
builder.Add(compound,s.Moved(loc));\
|
||||
}\
|
||||
return compound;\
|
||||
for(TopExp_Explorer it(compound,TopAbs_EDGE);it.More();)\
|
||||
return compound;\
|
||||
return TopoDS_Shape();\
|
||||
}\
|
||||
return mySections[_index]->_op(-1, ## __VA_ARGS__).Moved(loc);\
|
||||
const TopoDS_Shape &shape = mySections[_index]->_op(-1, ## __VA_ARGS__);\
|
||||
if(!shape.IsNull())\
|
||||
return shape.Moved(loc);\
|
||||
return shape;\
|
||||
}\
|
||||
}while(0)
|
||||
|
||||
|
@ -732,6 +888,8 @@ TopoDS_Shape Area::getShape(int index) {
|
|||
|
||||
if(myShapeDone) return myShape;
|
||||
|
||||
if(!myArea) return TopoDS_Shape();
|
||||
|
||||
CAreaConfig conf(myParams);
|
||||
|
||||
#define AREA_MY(_param) myParams.PARAM_FNAME(_param)
|
||||
|
@ -793,9 +951,13 @@ TopoDS_Shape Area::getShape(int index) {
|
|||
const TopoDS_Shape &shape = toShape(*area,fill);
|
||||
builder.Add(compound,toShape(*area,fill));
|
||||
}
|
||||
builder.Add(compound,areaPocket.makePocket(
|
||||
-1,PARAM_FIELDS(AREA_MY,AREA_PARAMS_POCKET)));
|
||||
myShape = compound;
|
||||
// make sure the compound has at least one edge
|
||||
for(TopExp_Explorer it(compound,TopAbs_EDGE);it.More();) {
|
||||
builder.Add(compound,areaPocket.makePocket(
|
||||
-1,PARAM_FIELDS(AREA_MY,AREA_PARAMS_POCKET)));
|
||||
myShape = compound;
|
||||
break;
|
||||
}
|
||||
myShapeDone = true;
|
||||
return myShape;
|
||||
}
|
||||
|
@ -826,9 +988,13 @@ TopoDS_Shape Area::makeOffset(int index,PARAM_ARGS(PARAM_FARG,AREA_PARAMS_OFFSET
|
|||
fill = myParams.Fill;
|
||||
else
|
||||
fill = FillNone;
|
||||
builder.Add(compound,toShape(*area,fill));
|
||||
const TopoDS_Shape &shape = toShape(*area,fill);
|
||||
if(shape.IsNull()) continue;
|
||||
builder.Add(compound,shape);
|
||||
}
|
||||
return compound;
|
||||
for(TopExp_Explorer it(compound,TopAbs_EDGE);it.More();)
|
||||
return compound;
|
||||
return TopoDS_Shape();
|
||||
}
|
||||
|
||||
void Area::makeOffset(list<shared_ptr<CArea> > &areas,
|
||||
|
@ -855,7 +1021,7 @@ void Area::makeOffset(list<shared_ptr<CArea> > &areas,
|
|||
#ifdef AREA_OFFSET_ALGO
|
||||
PARAM_ENUM_CONVERT(AREA_SRC,PARAM_FNAME,PARAM_ENUM_EXCEPT,AREA_PARAMS_CLIPPER_FILL);
|
||||
#endif
|
||||
|
||||
|
||||
for(int i=0;count<0||i<count;++i,offset+=stepover) {
|
||||
areas.push_back(make_shared<CArea>());
|
||||
CArea &area = *areas.back();
|
||||
|
@ -1032,23 +1198,25 @@ TopoDS_Shape Area::toShape(const CArea &area, bool fill, const gp_Trsf *trsf) {
|
|||
if(!wire.IsNull())
|
||||
builder.Add(compound,wire);
|
||||
}
|
||||
|
||||
if(!compound.IsNull() && fill) {
|
||||
try{
|
||||
Part::FaceMakerBullseye mkFace;
|
||||
if(trsf)
|
||||
mkFace.setPlane(gp_Pln().Transformed(*trsf));
|
||||
for(TopExp_Explorer it(compound, TopAbs_WIRE); it.More(); it.Next())
|
||||
mkFace.addWire(TopoDS::Wire(it.Current()));
|
||||
mkFace.Build();
|
||||
if (mkFace.Shape().IsNull())
|
||||
Base::Console().Warning("FaceMakerBullseye returns null shape\n");
|
||||
return mkFace.Shape();
|
||||
}catch (Base::Exception &e){
|
||||
Base::Console().Warning("FaceMakerBullseye failed: %s\n", e.what());
|
||||
for(TopExp_Explorer it(compound,TopAbs_EDGE);it.More();) {
|
||||
if(fill) {
|
||||
try{
|
||||
Part::FaceMakerBullseye mkFace;
|
||||
if(trsf)
|
||||
mkFace.setPlane(gp_Pln().Transformed(*trsf));
|
||||
for(TopExp_Explorer it(compound, TopAbs_WIRE); it.More(); it.Next())
|
||||
mkFace.addWire(TopoDS::Wire(it.Current()));
|
||||
mkFace.Build();
|
||||
if (mkFace.Shape().IsNull())
|
||||
Base::Console().Warning("FaceMakerBullseye returns null shape\n");
|
||||
return mkFace.Shape();
|
||||
}catch (Base::Exception &e){
|
||||
Base::Console().Warning("FaceMakerBullseye failed: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
return compound;
|
||||
}
|
||||
return compound;
|
||||
return TopoDS_Shape();
|
||||
}
|
||||
|
||||
std::list<TopoDS_Shape> Area::sortWires(const std::list<TopoDS_Shape> &shapes,
|
||||
|
@ -1220,13 +1388,20 @@ void Area::toPath(Toolpath &path, const std::list<TopoDS_Shape> &shapes,
|
|||
break;
|
||||
} default: {
|
||||
// Discretize all other type of curves
|
||||
GCPnts_UniformDeflection discretizer(curve, deflection,
|
||||
GCPnts_QuasiUniformDeflection discretizer(curve, deflection,
|
||||
curve.FirstParameter(), curve.LastParameter());
|
||||
if (discretizer.IsDone () && discretizer.NbPoints () > 0) {
|
||||
if (discretizer.IsDone () && discretizer.NbPoints () > 1) {
|
||||
int nbPoints = discretizer.NbPoints ();
|
||||
for (int i=1; i<=nbPoints; i++) {
|
||||
gp_Pnt pt = discretizer.Value (i);
|
||||
addCommand(path,pt);
|
||||
if(reversed) {
|
||||
for (int i=nbPoints-1; i>=1; --i) {
|
||||
gp_Pnt pt = discretizer.Value (i);
|
||||
addCommand(path,pt);
|
||||
}
|
||||
}else{
|
||||
for (int i=2; i<=nbPoints; i++) {
|
||||
gp_Pnt pt = discretizer.Value (i);
|
||||
addCommand(path,pt);
|
||||
}
|
||||
}
|
||||
}else
|
||||
Standard_Failure::Raise("Curve discretization failed");
|
||||
|
|
|
@ -54,7 +54,8 @@ struct PathExport AreaParams: CAreaParams {
|
|||
bool operator==(const AreaParams &other) const {
|
||||
#define AREA_COMPARE(_param) \
|
||||
if(PARAM_FIELD(NAME,_param)!=other.PARAM_FIELD(NAME,_param)) return false;
|
||||
PARAM_FOREACH(AREA_COMPARE,AREA_PARAMS_CONF)
|
||||
PARAM_FOREACH(AREA_COMPARE,AREA_PARAMS_CAREA)
|
||||
PARAM_FOREACH(AREA_COMPARE,AREA_PARAMS_AREA)
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const AreaParams &other) const {
|
||||
|
@ -71,12 +72,9 @@ struct PathExport AreaParams: CAreaParams {
|
|||
*/
|
||||
struct PathExport CAreaConfig {
|
||||
|
||||
/** Stores current libarea settings */
|
||||
/** For saving current libarea settings */
|
||||
PARAM_DECLARE(PARAM_FNAME,AREA_PARAMS_CAREA)
|
||||
|
||||
/** Stores user defined setting */
|
||||
CAreaParams params;
|
||||
|
||||
/** The constructor automatically saves current setting and apply user defined ones
|
||||
*
|
||||
* \arg \c p user defined configurations
|
||||
|
@ -96,8 +94,7 @@ class PathExport Area: public Base::BaseClass {
|
|||
|
||||
TYPESYSTEM_HEADER();
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
struct Shape {
|
||||
short op;
|
||||
TopoDS_Shape shape;
|
||||
|
@ -108,6 +105,7 @@ protected:
|
|||
{}
|
||||
};
|
||||
|
||||
protected:
|
||||
std::list<Shape> myShapes;
|
||||
std::unique_ptr<CArea> myArea;
|
||||
std::unique_ptr<CArea> myAreaOpen;
|
||||
|
@ -146,6 +144,10 @@ protected:
|
|||
*/
|
||||
TopoDS_Shape makePocket();
|
||||
|
||||
void explode(const TopoDS_Shape &shape);
|
||||
|
||||
bool isBuilt() const;
|
||||
|
||||
public:
|
||||
/** Declare all parameters defined in #AREA_PARAMS_ALL as member variable */
|
||||
PARAM_ENUM_DECLARE(AREA_PARAMS_ALL)
|
||||
|
@ -156,18 +158,27 @@ public:
|
|||
|
||||
/** Set a working plane
|
||||
*
|
||||
* If no working plane are set, Area will try to find a working plane from
|
||||
* individual children faces, wires or edges. By right, we should create a
|
||||
* compound of all shapes and then findplane on it. However, because we
|
||||
* supports solid, and also because OCC may hang for a long time if
|
||||
* something goes a bit off, we opt to find plane on each individual shape.
|
||||
* If you intend to pass individual edges, you must supply a workplane shape
|
||||
* manually
|
||||
* \arg \c shape: a shape defining a working plane.
|
||||
*
|
||||
* \arg \c shape: a shape defining a working plane
|
||||
* The supplied shape does not need to be planar. Area will try to find planar
|
||||
* sub-shape (face, wire or edge). If more than one planar sub-shape is found,
|
||||
* it will prefer the top plane parallel to XY0 plane.
|
||||
*
|
||||
* If no working plane are set, Area will try to find a working plane from
|
||||
* the added children shape using the same algorithm
|
||||
*/
|
||||
void setPlane(const TopoDS_Shape &shape);
|
||||
|
||||
/** Return the current active workplane
|
||||
*
|
||||
* \arg \c trsf: optional return of a transformation matrix that will bring the
|
||||
* found plane to XY0 plane.
|
||||
*
|
||||
* If no workplane is set using setPlane(), the active workplane is derived from
|
||||
* the added children shapes using the same algorithm empolyed by setPlane().
|
||||
*/
|
||||
TopoDS_Shape getPlane(gp_Trsf *trsf = NULL);
|
||||
|
||||
/** Add a child shape with given operation code
|
||||
*
|
||||
* No validation is done at this point. Exception will be thrown when asking
|
||||
|
@ -195,10 +206,19 @@ public:
|
|||
TopoDS_Shape makePocket(int index=-1, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_POCKET));
|
||||
|
||||
|
||||
std::vector<std::shared_ptr<Area> > makeSections(
|
||||
PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SECTION_EXTRA),
|
||||
const std::vector<double> &_heights = std::vector<double>(),
|
||||
const TopoDS_Shape &plane = TopoDS_Shape());
|
||||
|
||||
/** Config this Area object */
|
||||
void setParams(const AreaParams ¶ms);
|
||||
|
||||
|
||||
const std::list<Shape> getChildren() const {
|
||||
return myShapes;
|
||||
}
|
||||
|
||||
/** Get the current configuration */
|
||||
const AreaParams &getParams() const {
|
||||
return myParams;
|
||||
|
@ -330,6 +350,42 @@ public:
|
|||
static void toPath(Toolpath &path, const std::list<TopoDS_Shape> &shapes,
|
||||
const gp_Pnt *pstart=NULL, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_PATH));
|
||||
|
||||
|
||||
/** Explore the shape to find a planar element, and return its transformation
|
||||
*
|
||||
* \arg \c shape: shape to explore
|
||||
* \arg \c type: OCC shape type (TopAbs_ShapeEnum) to explore
|
||||
* \arg \c plane: returns the sub planar shape found
|
||||
* \arg \c trsf: the transformation of the plane which will transform the
|
||||
* plane into XY0 plane.
|
||||
*
|
||||
* If there are multiple subshapes on different planes. It will prefer the
|
||||
* top XY plane. If there is no XY parallel plane, the first planar shape
|
||||
* encountered will be returned
|
||||
*
|
||||
* \return Returns true is there is any subshape of the give type found.
|
||||
* You should use plane.IsNull() to see if there is any planar shape found.
|
||||
*/
|
||||
static bool findPlane(const TopoDS_Shape &shape, int type,
|
||||
TopoDS_Shape &plane, gp_Trsf &trsf);
|
||||
|
||||
/** Explore the shape with subtype FACE, WIRE and EDGE to find a planar
|
||||
* subshape
|
||||
*
|
||||
* \arg \c shape: shape to explore
|
||||
* \arg \c plane: returns the sub planar shape found
|
||||
* \arg \c trsf: the transformation of the plane which will transform the
|
||||
* plane into XY0 plane.
|
||||
*
|
||||
* If there are multiple subshapes on different planes. It will prefer the
|
||||
* top XY plane. If there is no XY parallel plane, the first planar shape
|
||||
* encountered will be returned
|
||||
*
|
||||
* \return Returns true is there is any subshape is found. You should use
|
||||
* plane.IsNull() to see if there is any planar shape found.
|
||||
*/
|
||||
static bool findPlane(const TopoDS_Shape &shape,
|
||||
TopoDS_Shape &plane, gp_Trsf &trsf);
|
||||
};
|
||||
|
||||
} //namespace Path
|
||||
|
|
|
@ -104,9 +104,9 @@
|
|||
|
||||
/** Operation code */
|
||||
#define AREA_PARAMS_OPCODE \
|
||||
((enum,op,Operation,0,\
|
||||
"Boolean operation. For the first four operation, see https://goo.gl/Gj8RUu.\n"\
|
||||
"'Compound' means no operation, normal used to do Area.sortWires().",\
|
||||
((enum,op,Operation,0,"Boolean operation.\n"\
|
||||
"For the first four operations, see https://goo.gl/Gj8RUu.\n"\
|
||||
"'Compound' means no operation, normally used to do Area.sortWires().",\
|
||||
(Union)(Difference)(Intersection)(Xor)(Compound)))
|
||||
|
||||
/** Offset parameters */
|
||||
|
@ -115,11 +115,22 @@
|
|||
((long,extra_pass,ExtraPass,0,"Number of extra offset pass to generate."))\
|
||||
((double,stepover,Stepover,0.0,"Cutter diameter to step over on each pass. If =0, use Offset"))
|
||||
|
||||
#define AREA_PARAMS_SECTION_EXTRA \
|
||||
((enum,mode,SectionMode,2,"Section offset coordinate mode.\n"\
|
||||
"'Absolute' means the absolute Z height to start section.\n"\
|
||||
"'BoundBox' means relative Z height to the bounding box of all the children shape. Only\n"\
|
||||
"positive value is allowed, which specifies the offset below the top Z of the bounding box.\n"\
|
||||
"Note that OCC has trouble getting the minimumi bounding box of some solids, particually\n"\
|
||||
"those with non-planar surface.\n"\
|
||||
"'Workplane' means relative to workplane.",\
|
||||
(Absolute)(BoundBox)(Workplane)))
|
||||
|
||||
/** Section parameters */
|
||||
#define AREA_PARAMS_SECTION \
|
||||
((long,count,SectionCount,0,"Number of sections to generate. -1 means full sections."))\
|
||||
((double,stepdown,Stepdown,1.0,"Step down distance for each section"))\
|
||||
((double,offset,SectionOffset,0.0,"Offset for the first section"))
|
||||
((double,offset,SectionOffset,0.0,"Offset for the first section"))\
|
||||
AREA_PARAMS_SECTION_EXTRA
|
||||
|
||||
#ifdef AREA_OFFSET_ALGO
|
||||
# define AREA_PARAMS_OFFSET_ALGO \
|
||||
|
|
|
@ -25,8 +25,12 @@ All arguments are optional.</UserDocu>
|
|||
</Methode>
|
||||
<Methode Name="setPlane">
|
||||
<Documentation>
|
||||
<UserDocu>setPlane(shape): Set the working plane. The shape will not be used for
|
||||
any operation</UserDocu>
|
||||
<UserDocu>setPlane(shape): Set the working plane.\n
|
||||
The supplied shape does not need to be planar. Area will try to find planar
|
||||
sub-shape (face, wire or edge). If more than one planar sub-shape is found, it
|
||||
will prefer the top plane parallel to XY0 plane. If no working plane are set,
|
||||
Area will try to find a working plane from the added children shape using the
|
||||
same algorithm</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="getShape" Keyword='true'>
|
||||
|
@ -46,6 +50,11 @@ any operation</UserDocu>
|
|||
<UserDocu></UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="makeSections" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu></UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="setParams" Keyword="true">
|
||||
<Documentation>
|
||||
<UserDocu></UserDocu>
|
||||
|
@ -74,5 +83,17 @@ any operation</UserDocu>
|
|||
</Documentation>
|
||||
<Parameter Name="Sections" Type="List"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Workplane" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>The current workplane. If no plane is set, it is derived from the added shapes.</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Workplane" Type="Object"/>
|
||||
</Attribute>
|
||||
<Attribute Name="Shapes" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>A list of tuple: [(shape,op), ...] containing the added shapes together with their operation code</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Shapes" Type="List"/>
|
||||
</Attribute>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
|
|
@ -54,11 +54,11 @@ static const AreaDoc myDocs[] = {
|
|||
"add((shape...)," PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_OPCODE) "):\n"
|
||||
"Add TopoShape(s) with given operation code\n"
|
||||
PARAM_PY_DOC(ARG,AREA_PARAMS_OPCODE)
|
||||
"\nThe first shape's wires will be fused together regardless of the op code given.\n"
|
||||
"Subsequent shape's wire will be combined using the op code. All shape wires\n"
|
||||
"shall be coplanar, and are used to determine a working plane for face making and\n"
|
||||
"offseting. You can call setPlane() to supply a reference shape to determin the\n"
|
||||
"working plane in case the added shapes are all colinear lines.\n",
|
||||
"\nThe first shape's wires will be unioned together regardless of the op code given\n"
|
||||
"(except for 'Compound'). Subsequent shape's wire will be combined using the op code.\n"
|
||||
"All shape wires shall be coplanar, and are used to determine a working plane for face\n"
|
||||
"making and offseting. You can call setPlane() to supply a reference shape to determin\n"
|
||||
"the workplane in case the added shapes are all colinear lines.\n",
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -77,6 +77,17 @@ static const AreaDoc myDocs[] = {
|
|||
"\n* index (-1): the index of the section. -1 means all sections. No effect on planar shape.\n"
|
||||
PARAM_PY_DOC(ARG,AREA_PARAMS_POCKET),
|
||||
},
|
||||
{
|
||||
"makeSections",
|
||||
|
||||
"makeSections(" PARAM_PY_ARGS_DOC(ARG,AREA_PARAMS_SECTION_EXTRA) ", heights=[], plane=None):\n"
|
||||
"Make a list of area holding the sectioned children shapes on given heights\n"
|
||||
PARAM_PY_DOC(ARG,AREA_PARAMS_SECTION_EXTRA)
|
||||
"\n* heights ([]): a list of section heights, the meaning of the value is determined by 'mode'.\n"
|
||||
"If not specified, the current SectionCount, and SectionOffset of this Area is used.\n"
|
||||
"\n* plane (None): optional shape to specify a section plane. If not give, the current workplane\n"
|
||||
"of this Area is used.",
|
||||
},
|
||||
{
|
||||
"sortWires",
|
||||
|
||||
|
@ -270,6 +281,53 @@ PyObject* AreaPy::makePocket(PyObject *args, PyObject *keywds)
|
|||
return Py::new_reference_to(Part::shape2pyshape(resultShape));
|
||||
}
|
||||
|
||||
PyObject* AreaPy::makeSections(PyObject *args, PyObject *keywds)
|
||||
{
|
||||
static char *kwlist[] = {PARAM_FIELD_STRINGS(ARG,AREA_PARAMS_SECTION_EXTRA),
|
||||
"heights", "plane", NULL};
|
||||
PyObject *heights = NULL;
|
||||
PyObject *plane = NULL;
|
||||
|
||||
PARAM_PY_DECLARE_INIT(PARAM_FARG,AREA_PARAMS_SECTION_EXTRA)
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds,
|
||||
"|" PARAM_PY_KWDS(AREA_PARAMS_SECTION_EXTRA) "OO!", kwlist,
|
||||
PARAM_REF(PARAM_FARG,AREA_PARAMS_SECTION_EXTRA),
|
||||
&heights, &(Part::TopoShapePy::Type), &plane))
|
||||
return 0;
|
||||
|
||||
std::vector<double> h;
|
||||
if(heights) {
|
||||
if (PyObject_TypeCheck(heights, &(PyFloat_Type)))
|
||||
h.push_back(PyFloat_AsDouble(heights));
|
||||
else if (PyObject_TypeCheck(heights, &(PyList_Type)) ||
|
||||
PyObject_TypeCheck(heights, &(PyTuple_Type))) {
|
||||
Py::Sequence shapeSeq(heights);
|
||||
h.reserve(shapeSeq.size());
|
||||
for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it) {
|
||||
PyObject* item = (*it).ptr();
|
||||
if(!PyObject_TypeCheck(item, &(PyFloat_Type))) {
|
||||
PyErr_SetString(PyExc_TypeError, "heights must only contain float type");
|
||||
return 0;
|
||||
}
|
||||
h.push_back(PyFloat_AsDouble(item));
|
||||
}
|
||||
}else{
|
||||
PyErr_SetString(PyExc_TypeError, "heights must be of type float or list/tuple of float");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Area> > sections = getAreaPtr()->makeSections(
|
||||
PARAM_PY_FIELDS(PARAM_FARG,AREA_PARAMS_SECTION_EXTRA),
|
||||
h,plane?GET_TOPOSHAPE(plane):TopoDS_Shape());
|
||||
|
||||
Py::List ret;
|
||||
for(auto &area : sections)
|
||||
ret.append(Py::asObject(new AreaPy(new Area(*area,false))));
|
||||
return Py::new_reference_to(ret);
|
||||
}
|
||||
|
||||
PyObject* AreaPy::setParams(PyObject *args, PyObject *keywds)
|
||||
{
|
||||
static char *kwlist[] = {PARAM_FIELD_STRINGS(NAME,AREA_PARAMS_CONF),NULL};
|
||||
|
@ -337,6 +395,20 @@ Py::List AreaPy::getSections(void) const {
|
|||
return ret;
|
||||
}
|
||||
|
||||
Py::List AreaPy::getShapes(void) const {
|
||||
Py::List ret;
|
||||
Area *area = getAreaPtr();
|
||||
const std::list<Area::Shape> &shapes = area->getChildren();
|
||||
for(auto &s : shapes)
|
||||
ret.append(Py::TupleN(Part::shape2pyshape(s.shape),Py::Int(s.op)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
Py::Object AreaPy::getWorkplane(void) const {
|
||||
return Part::shape2pyshape(getAreaPtr()->getPlane());
|
||||
}
|
||||
|
||||
|
||||
// custom attributes get/set
|
||||
|
||||
PyObject *AreaPy::getCustomAttributes(const char* /*attr*/) const
|
||||
|
|
|
@ -49,11 +49,11 @@ FeatureArea::FeatureArea()
|
|||
PARAM_PROP_ADD("Area",AREA_PARAMS_OPCODE);
|
||||
PARAM_PROP_ADD("Area",AREA_PARAMS_BASE);
|
||||
PARAM_PROP_ADD("Offset",AREA_PARAMS_OFFSET);
|
||||
PARAM_PROP_ADD("Offset", AREA_PARAMS_OFFSET_CONF);
|
||||
PARAM_PROP_ADD("Pocket",AREA_PARAMS_POCKET);
|
||||
PARAM_PROP_ADD("Pocket",AREA_PARAMS_POCKET_CONF);
|
||||
PARAM_PROP_ADD("Section",AREA_PARAMS_SECTION);
|
||||
PARAM_PROP_ADD("Offset Settings", AREA_PARAMS_OFFSET_CONF);
|
||||
PARAM_PROP_ADD("libarea Settings",AREA_PARAMS_CAREA);
|
||||
PARAM_PROP_ADD("libarea",AREA_PARAMS_CAREA);
|
||||
|
||||
PARAM_PROP_SET_ENUM(Enums,AREA_PARAMS_ALL);
|
||||
PocketMode.setValue((long)0);
|
||||
|
|
Loading…
Reference in New Issue
Block a user