Path.Area: support negative stepdown

Negative stepdown means going from bottom up. Some machine may have
reversed Z axis.
This commit is contained in:
Zheng, Lei
2017-03-09 14:27:09 +08:00
committed by wmayer
parent d1978a075a
commit f94bc81987
6 changed files with 109 additions and 55 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -66,9 +66,9 @@ public:
}
private:
bool myBuild;
Area myArea;
std::vector<TopoDS_Shape> myShapes;
bool myInited;
};
typedef App::FeaturePythonT<FeatureArea> FeatureAreaPython;