Path.Area: support negative stepdown
Negative stepdown means going from bottom up. Some machine may have reversed Z axis.
This commit is contained in:
@@ -558,43 +558,65 @@ std::vector<shared_ptr<Area> > Area::makeSections(
|
||||
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())
|
||||
double z;
|
||||
double d = fabs(myParams.Stepdown);
|
||||
if(myParams.SectionCount>1 && d<Precision::Confusion())
|
||||
throw Base::ValueError("invalid stepdown");
|
||||
|
||||
if(mode == SectionModeBoundBox)
|
||||
zMax -= myParams.SectionOffset;
|
||||
else if(mode == SectionModeWorkplane)
|
||||
zMax = -myParams.SectionOffset;
|
||||
else {
|
||||
if(mode == SectionModeBoundBox) {
|
||||
if(myParams.Stepdown > 0.0)
|
||||
z = zMax-myParams.SectionOffset;
|
||||
else
|
||||
z = zMin+myParams.SectionOffset;
|
||||
}else if(mode == SectionModeWorkplane){
|
||||
// Because we've transformed the shapes using the work plane so
|
||||
// that the work plane is aligned with xy0 plane, the starting Z
|
||||
// value shall be 0 minus the given section offset. Note the
|
||||
// section offset is relative to the starting Z
|
||||
if(myParams.Stepdown > 0.0)
|
||||
z = -myParams.SectionOffset;
|
||||
else
|
||||
z = myParams.SectionOffset;
|
||||
} else {
|
||||
gp_Pnt pt(0,0,myParams.SectionOffset);
|
||||
double z = pt.Transformed(loc).Z();
|
||||
if(z < zMax)
|
||||
zMax = z;
|
||||
z = pt.Transformed(loc).Z();
|
||||
}
|
||||
if(zMax <= zMin)
|
||||
throw Base::ValueError("section offset too big");
|
||||
if(z > zMax)
|
||||
z = zMax;
|
||||
else if(z < zMin)
|
||||
z = zMin;
|
||||
double dz;
|
||||
if(myParams.Stepdown>0.0)
|
||||
dz = z - zMin;
|
||||
else
|
||||
dz = zMax - z;
|
||||
|
||||
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;
|
||||
}
|
||||
if(count<0 || count*d > dz)
|
||||
count = floor(dz/d)+1;
|
||||
heights.reserve(count);
|
||||
for(int i=0;i<count;++i,zMax-=myParams.Stepdown) {
|
||||
if(zMax < zMin) {
|
||||
hit_bottom = true;
|
||||
for(int i=0;i<count;++i,z-=myParams.Stepdown) {
|
||||
if(myParams.Stepdown>0.0) {
|
||||
if(z-zMin<myParams.SectionTolerance){
|
||||
double zNew = zMin+myParams.SectionTolerance;
|
||||
AREA_TRACE("hit bottom " <<z<<','<<zMin<<','<<zNew);
|
||||
heights.push_back(zNew);
|
||||
break;
|
||||
}
|
||||
}else if(zMax-z<myParams.SectionTolerance) {
|
||||
double zNew = zMax-myParams.SectionTolerance;
|
||||
AREA_TRACE("hit top " <<z<<','<<zMin<<','<<zNew);
|
||||
heights.push_back(zNew);
|
||||
break;
|
||||
}
|
||||
heights.push_back(zMax);
|
||||
heights.push_back(z);
|
||||
}
|
||||
}else{
|
||||
heights.reserve(_heights.size());
|
||||
|
||||
bool hitMax = false, hitMin = false;
|
||||
for(double z : _heights) {
|
||||
switch(mode) {
|
||||
case SectionModeAbsolute: {
|
||||
@@ -610,19 +632,24 @@ std::vector<shared_ptr<Area> > Area::makeSections(
|
||||
default:
|
||||
throw Base::ValueError("invalid section mode");
|
||||
}
|
||||
if((zMin-z)>Precision::Confusion()) {
|
||||
hit_bottom = true;
|
||||
continue;
|
||||
}else if ((z-zMax)>Precision::Confusion())
|
||||
continue;
|
||||
if(z-zMin<myParams.SectionTolerance) {
|
||||
if(hitMin) continue;
|
||||
hitMin = true;
|
||||
double zNew = zMin+myParams.SectionTolerance;
|
||||
AREA_TRACE("hit bottom " <<z<<','<<zMin<<','<<zNew);
|
||||
z = zNew;
|
||||
}else if (zMax-z<myParams.SectionTolerance) {
|
||||
if(hitMax) continue;
|
||||
double zNew = zMax-myParams.SectionTolerance;
|
||||
AREA_TRACE("hit top " <<z<<','<<zMin<<','<<zNew);
|
||||
z = zNew;
|
||||
}
|
||||
heights.push_back(z);
|
||||
}
|
||||
}
|
||||
|
||||
if(hit_bottom)
|
||||
heights.push_back(zMin);
|
||||
else if(heights.empty())
|
||||
heights.push_back(zMax);
|
||||
if(heights.empty())
|
||||
throw Base::ValueError("no sections");
|
||||
|
||||
std::vector<shared_ptr<Area> > sections;
|
||||
sections.reserve(heights.size());
|
||||
|
||||
@@ -303,10 +303,21 @@ public:
|
||||
*/
|
||||
TopoDS_Shape makePocket(int index=-1, PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_POCKET));
|
||||
|
||||
|
||||
/** Make a pocket of the combined shape
|
||||
*
|
||||
* \arg \c heights: optional customized heights of each section. The
|
||||
* meaning of each height depends on section mode. If none is given,
|
||||
* the section heights is determined by the section settings in this
|
||||
* Area object (configured through setParams()).
|
||||
* \arg \c plane: the section plane if the section mode is
|
||||
* SectionModeWorkplane, otherwise ignored
|
||||
*
|
||||
* See #AREA_PARAMS_EXTRA for description of the arguments. Currently, there
|
||||
* is only one argument, namely \c mode for section mode.
|
||||
*/
|
||||
std::vector<std::shared_ptr<Area> > makeSections(
|
||||
PARAM_ARGS_DEF(PARAM_FARG,AREA_PARAMS_SECTION_EXTRA),
|
||||
const std::vector<double> &_heights = std::vector<double>(),
|
||||
const std::vector<double> &heights = std::vector<double>(),
|
||||
const TopoDS_Shape &plane = TopoDS_Shape());
|
||||
|
||||
/** Config this Area object */
|
||||
|
||||
@@ -120,18 +120,19 @@
|
||||
|
||||
#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, particularly\n"\
|
||||
"those with non-planar surface.\n"\
|
||||
"'Workplane' means relative to workplane.",\
|
||||
"'Absolute' means the absolute Z height (given in SectionOffset) to start slicing.\n"\
|
||||
"'BoundBox' means relative Z height to the bounding box of all the children shape.\n"\
|
||||
"'Workplane' means relative to workplane, minus SectionOffset.\n"\
|
||||
"Note that OCC has trouble getting the minimum bounding box of some solids, particularly\n"\
|
||||
"those with non-planar surface. It is recommended to use Workplane to specifiy the intended\n"\
|
||||
"starting z height.",\
|
||||
(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,stepdown,Stepdown,1.0,"Step down distance for each section.\n"\
|
||||
"Positive value means going from top down, and negative the other way round"))\
|
||||
((double,offset,SectionOffset,0.0,"Offset for the first section"))\
|
||||
AREA_PARAMS_SECTION_EXTRA
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ static const PyMethodDef areaOverrides[] = {
|
||||
"\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.",
|
||||
"of this Area is used if section mode is 'Workplane'.",
|
||||
},
|
||||
{
|
||||
"sortWires",NULL,0,
|
||||
|
||||
@@ -41,7 +41,7 @@ PROPERTY_SOURCE(Path::FeatureArea, Part::Feature)
|
||||
PARAM_ENUM_STRING_DECLARE(static const char *Enums,AREA_PARAMS_ALL)
|
||||
|
||||
FeatureArea::FeatureArea()
|
||||
:myBuild(false)
|
||||
:myInited(false)
|
||||
{
|
||||
ADD_PROPERTY(Sources,(0));
|
||||
ADD_PROPERTY(WorkPlane,(TopoDS_Shape()));
|
||||
@@ -64,13 +64,14 @@ FeatureArea::~FeatureArea()
|
||||
}
|
||||
|
||||
Area &FeatureArea::getArea() {
|
||||
if(!myBuild)
|
||||
execute();
|
||||
if(!myInited) execute();
|
||||
return myArea;
|
||||
}
|
||||
|
||||
App::DocumentObjectExecReturn *FeatureArea::execute(void)
|
||||
{
|
||||
myInited = true;
|
||||
|
||||
std::vector<App::DocumentObject*> links = Sources.getValues();
|
||||
if (links.empty())
|
||||
return new App::DocumentObjectExecReturn("No shapes linked");
|
||||
@@ -85,7 +86,6 @@ App::DocumentObjectExecReturn *FeatureArea::execute(void)
|
||||
|
||||
TIME_INIT(t);
|
||||
|
||||
myBuild = true;
|
||||
AreaParams params;
|
||||
|
||||
#define AREA_PROP_GET(_param) \
|
||||
@@ -112,6 +112,7 @@ App::DocumentObjectExecReturn *FeatureArea::execute(void)
|
||||
myShapes.push_back(myArea.getShape(i));
|
||||
}
|
||||
|
||||
bool hasShape = false;
|
||||
if(myShapes.empty())
|
||||
Shape.setValue(TopoDS_Shape());
|
||||
else{
|
||||
@@ -120,13 +121,20 @@ App::DocumentObjectExecReturn *FeatureArea::execute(void)
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound compound;
|
||||
builder.MakeCompound(compound);
|
||||
for(auto &shape : myShapes)
|
||||
for(auto &shape : myShapes) {
|
||||
if(shape.IsNull()) continue;
|
||||
hasShape = true;
|
||||
builder.Add(compound,shape);
|
||||
}
|
||||
Shape.setValue(compound);
|
||||
}
|
||||
|
||||
TIME_PRINT(t,"feature execute");
|
||||
return Part::Feature::execute();
|
||||
|
||||
if(!hasShape)
|
||||
return new App::DocumentObjectExecReturn("no output shape");
|
||||
|
||||
return DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
const std::vector<TopoDS_Shape> &FeatureArea::getShapes() {
|
||||
@@ -136,7 +144,9 @@ const std::vector<TopoDS_Shape> &FeatureArea::getShapes() {
|
||||
|
||||
short FeatureArea::mustExecute(void) const
|
||||
{
|
||||
return !myArea.isBuilt() || Part::Feature::mustExecute();
|
||||
if(!myArea.isBuilt())
|
||||
return 1;
|
||||
return Part::Feature::mustExecute();
|
||||
}
|
||||
|
||||
PyObject *FeatureArea::getPyObject()
|
||||
@@ -202,21 +212,26 @@ App::DocumentObjectExecReturn *FeatureAreaView::execute(void)
|
||||
if(!pObj->isDerivedFrom(FeatureArea::getClassTypeId()))
|
||||
return new App::DocumentObjectExecReturn("Linked object is not a FeatureArea");
|
||||
|
||||
bool hasShape = false;
|
||||
std::list<TopoDS_Shape> shapes = getShapes();
|
||||
if(shapes.empty())
|
||||
Shape.setValue(TopoDS_Shape());
|
||||
else if(shapes.size()==1) {
|
||||
Shape.setValue(shapes.front());
|
||||
}else{
|
||||
else{
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound compound;
|
||||
builder.MakeCompound(compound);
|
||||
for(auto &shape : shapes)
|
||||
for(auto &shape : shapes) {
|
||||
if(shape.IsNull()) continue;
|
||||
hasShape = true;
|
||||
builder.Add(compound,shape);
|
||||
}
|
||||
Shape.setValue(compound);
|
||||
}
|
||||
|
||||
return Part::Feature::execute();
|
||||
if(!hasShape)
|
||||
return new App::DocumentObjectExecReturn("no output shape");
|
||||
|
||||
return DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
// Python feature ---------------------------------------------------------
|
||||
|
||||
@@ -66,9 +66,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool myBuild;
|
||||
Area myArea;
|
||||
std::vector<TopoDS_Shape> myShapes;
|
||||
bool myInited;
|
||||
};
|
||||
|
||||
typedef App::FeaturePythonT<FeatureArea> FeatureAreaPython;
|
||||
|
||||
Reference in New Issue
Block a user