Merge branch 'master' into RRF-PP-for-Path

This commit is contained in:
P-C-R
2021-11-12 19:06:52 +01:00
committed by GitHub
51 changed files with 2652 additions and 889 deletions

View File

@@ -162,9 +162,22 @@ def getPreferences():
'EXPORT_MODEL': ['arch', 'struct', 'hybrid'][p.GetInt("ifcExportModel", 0)]
}
# get ifcopenshell version
ifcos_version = 0.0
if hasattr(ifcopenshell, "version"):
if ifcopenshell.version.startswith("0"):
ifcos_version = float(ifcopenshell.version[:3]) # < 0.6
elif ifcopenshell.version.startswith("v"):
ifcos_version = float(ifcopenshell.version[1:4]) # 0.7
else:
print("Could not retrieve IfcOpenShell version. Version is set to {}".format(ifcos_version))
else:
print("Could not retrieve IfcOpenShell version. Version is set to {}".format(ifcos_version))
# set schema
if hasattr(ifcopenshell, "schema_identifier"):
schema = ifcopenshell.schema_identifier
elif hasattr(ifcopenshell, "version") and (float(ifcopenshell.version[:3]) >= 0.6):
elif ifcos_version >= 0.6:
# v0.6 onwards allows to set our own schema
schema = ["IFC4", "IFC2X3"][p.GetInt("IfcVersion", 0)]
else:

View File

@@ -151,34 +151,25 @@ def getTeighaConverter():
-------
str
The full path of the converter executable
'/usr/bin/TeighaFileConverter'
'/usr/bin/ODAFileConverter'
"""
import FreeCAD, os, platform
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
p = p.GetString("TeighaFileConverter")
if p:
path = p.GetString("TeighaFileConverter")
if path:
# path set manually
teigha = p
else:
# try to find teigha
teigha = None
if platform.system() == "Linux":
teigha = "/usr/bin/TeighaFileConverter"
if not os.path.exists(teigha):
teigha = "/usr/bin/ODAFileConverter"
elif platform.system() == "Windows":
odadir = os.path.expandvars("%ProgramFiles%\ODA")
if os.path.exists(odadir):
subdirs = os.walk(odadir).next()[1]
for sub in subdirs:
t = (odadir + os.sep + sub + os.sep
+ "TeighaFileConverter.exe")
t = os.path.join(odadir, sub, "TeighaFileConverter.exe")
if os.path.exists(t):
teigha = t
if teigha:
if os.path.exists(teigha):
return teigha
return path
elif platform.system() == "Linux":
path = "/usr/bin/ODAFileConverter"
if os.path.exists(path):
return path
elif platform.system() == "Windows":
odadir = os.path.expandvars("%ProgramFiles%\ODA")
if os.path.exists(odadir):
for sub in os.listdir(odadir):
path = os.path.join(odadir, sub, "ODAFileConverter.exe")
if os.path.exists(path):
return path
return None
@@ -249,7 +240,7 @@ def convertToDxf(dwgfilename):
outdir = tempfile.mkdtemp()
basename = os.path.basename(dwgfilename)
result = outdir + os.sep + os.path.splitext(basename)[0] + ".dxf"
proc = subprocess.Popen((path, "-o", result, dwgfilename))
proc = subprocess.Popen((path, "-f", "-o", result, dwgfilename))
proc.communicate()
return result
except Exception:

View File

@@ -44,6 +44,9 @@ public:
QIcon getIcon(void) const;
void updateData(const App::Property*);
bool onDelete(const std::vector<std::string> &);
virtual bool allowTreeOrderSwap(const App::DocumentObject *, const App::DocumentObject *) const { return false; }
};
/// ViewProvider for the MultiFuse feature

View File

@@ -126,6 +126,8 @@ public:
/// grouping handling
std::vector<App::DocumentObject*> claimChildren(void)const;
bool onDelete(const std::vector<std::string> &);
virtual bool allowTreeOrderSwap(const App::DocumentObject *, const App::DocumentObject *) const { return false; }
};
class ViewProviderSweep : public ViewProviderPart
@@ -141,6 +143,8 @@ public:
/// grouping handling
std::vector<App::DocumentObject*> claimChildren(void)const;
bool onDelete(const std::vector<std::string> &);
virtual bool allowTreeOrderSwap(const App::DocumentObject *, const App::DocumentObject *) const { return false; }
};
class ViewProviderOffset : public ViewProviderPart
@@ -188,6 +192,8 @@ public:
void setupContextMenu(QMenu*, QObject*, const char*);
bool onDelete(const std::vector<std::string> &);
virtual bool allowTreeOrderSwap(const App::DocumentObject *, const App::DocumentObject *) const { return false; }
protected:
virtual bool setEdit(int ModNum);
virtual void unsetEdit(int ModNum);

View File

@@ -26,6 +26,7 @@
#include <App/PropertyStandard.h>
#include <Mod/Part/App/PartFeature.h>
#include <Mod/PartDesign/PartDesignGlobal.h>
class gp_Pnt;
class gp_Pln;

View File

@@ -55,7 +55,7 @@ PROPERTY_SOURCE(PartDesign::Loft, PartDesign::ProfileBased)
Loft::Loft()
{
ADD_PROPERTY_TYPE(Sections,(0),"Loft",App::Prop_None,"List of sections");
Sections.setSize(0);
Sections.setValue(0);
ADD_PROPERTY_TYPE(Ruled,(false),"Loft",App::Prop_None,"Create ruled surface");
ADD_PROPERTY_TYPE(Closed,(false),"Loft",App::Prop_None,"Close Last to First Profile");
}
@@ -95,44 +95,56 @@ App::DocumentObjectExecReturn *Loft::execute(void)
}
try {
//setup the location
// setup the location
this->positionByPrevious();
TopLoc_Location invObjLoc = this->getLocation().Inverted();
if(!base.IsNull())
if (!base.IsNull())
base.Move(invObjLoc);
//build up multisections
// build up multisections
auto multisections = Sections.getValues();
if(multisections.empty())
if (multisections.empty())
return new App::DocumentObjectExecReturn("Loft: At least one section is needed");
std::vector<std::vector<TopoDS_Wire>> wiresections;
for(TopoDS_Wire& wire : wires)
for (TopoDS_Wire& wire : wires)
wiresections.emplace_back(1, wire);
for(App::DocumentObject* obj : multisections) {
if(!obj->isDerivedFrom(Part::Feature::getClassTypeId()))
for (auto obj : multisections) {
if (!obj->isDerivedFrom(Part::Feature::getClassTypeId()))
return new App::DocumentObjectExecReturn("Loft: All sections need to be part features");
// if the section is an object's face then take just the face
TopoDS_Shape shape;
if (obj->isDerivedFrom(Part::Part2DObject::getClassTypeId()))
shape = static_cast<Part::Part2DObject*>(obj)->Shape.getValue();
else {
auto subValues = Sections.getSubValues(obj);
if (subValues.empty())
throw Base::ValueError("Loft: No valid subelement linked in Part::Feature");
shape = static_cast<Part::Feature*>(obj)->Shape.getShape(). getSubShape(subValues[0].c_str());
}
TopExp_Explorer ex;
size_t i=0;
for (ex.Init(static_cast<Part::Feature*>(obj)->Shape.getValue(), TopAbs_WIRE); ex.More(); ex.Next(), ++i) {
if(i>=wiresections.size())
for (ex.Init(shape, TopAbs_WIRE); ex.More(); ex.Next(), ++i) {
if (i>=wiresections.size())
return new App::DocumentObjectExecReturn("Loft: Sections need to have the same amount of inner wires as the base section");
wiresections[i].push_back(TopoDS::Wire(ex.Current()));
}
if(i<wiresections.size())
if (i<wiresections.size())
return new App::DocumentObjectExecReturn("Loft: Sections need to have the same amount of inner wires as the base section");
}
//build all shells
// build all shells
std::vector<TopoDS_Shape> shells;
for(std::vector<TopoDS_Wire>& wires : wiresections) {
for (std::vector<TopoDS_Wire>& wires : wiresections) {
BRepOffsetAPI_ThruSections mkTS(false, Ruled.getValue(), Precision::Confusion());
for(TopoDS_Wire& wire : wires) {
for (TopoDS_Wire& wire : wires) {
wire.Move(invObjLoc);
mkTS.AddWire(wire);
}
@@ -149,7 +161,7 @@ App::DocumentObjectExecReturn *Loft::execute(void)
TopoDS_Shape front = getVerifiedFace();
front.Move(invObjLoc);
std::vector<TopoDS_Wire> backwires;
for(std::vector<TopoDS_Wire>& wires : wiresections)
for (std::vector<TopoDS_Wire>& wires : wiresections)
backwires.push_back(wires.back());
TopoDS_Shape back = Part::FaceMakerCheese::makeFace(backwires);
@@ -158,7 +170,7 @@ App::DocumentObjectExecReturn *Loft::execute(void)
sewer.SetTolerance(Precision::Confusion());
sewer.Add(front);
sewer.Add(back);
for(TopoDS_Shape& s : shells)
for (TopoDS_Shape& s : shells)
sewer.Add(s);
sewer.Perform();
@@ -166,7 +178,7 @@ App::DocumentObjectExecReturn *Loft::execute(void)
//build the solid
BRepBuilderAPI_MakeSolid mkSolid;
mkSolid.Add(TopoDS::Shell(sewer.SewedShape()));
if(!mkSolid.IsDone())
if (!mkSolid.IsDone())
return new App::DocumentObjectExecReturn("Loft: Result is not a solid");
TopoDS_Shape result = mkSolid.Shape();
@@ -178,7 +190,7 @@ App::DocumentObjectExecReturn *Loft::execute(void)
AddSubShape.setValue(result);
if(base.IsNull()) {
if (base.IsNull()) {
if (getAddSubType() == FeatureAddSub::Subtractive)
return new App::DocumentObjectExecReturn("Loft: There is nothing to subtract from\n");
@@ -186,7 +198,7 @@ App::DocumentObjectExecReturn *Loft::execute(void)
return App::DocumentObject::StdReturn;
}
if(getAddSubType() == FeatureAddSub::Additive) {
if (getAddSubType() == FeatureAddSub::Additive) {
BRepAlgoAPI_Fuse mkFuse(base, result);
if (!mkFuse.IsDone())
@@ -204,7 +216,7 @@ App::DocumentObjectExecReturn *Loft::execute(void)
boolOp = refineShapeIfActive(boolOp);
Shape.setValue(getSolid(boolOp));
}
else if(getAddSubType() == FeatureAddSub::Subtractive) {
else if (getAddSubType() == FeatureAddSub::Subtractive) {
BRepAlgoAPI_Cut mkCut(base, result);
if (!mkCut.IsDone())
@@ -244,3 +256,14 @@ PROPERTY_SOURCE(PartDesign::SubtractiveLoft, PartDesign::Loft)
SubtractiveLoft::SubtractiveLoft() {
addSubType = Subtractive;
}
void Loft::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop)
{
// property Sections had the App::PropertyLinkList and was changed to App::PropertyXLinkSubList
if (prop == &Sections && strcmp(TypeName, "App::PropertyLinkList") == 0) {
Sections.upgrade(reader, TypeName);
}
else {
ProfileBased::handleChangedPropertyType(reader, TypeName, prop);
}
}

View File

@@ -40,9 +40,9 @@ class PartDesignExport Loft : public ProfileBased
public:
Loft();
App::PropertyLinkList Sections;
App::PropertyBool Ruled;
App::PropertyBool Closed;
App::PropertyXLinkSubList Sections;
App::PropertyBool Ruled;
App::PropertyBool Closed;
/** @name methods override feature */
//@{
@@ -54,20 +54,24 @@ public:
}
//@}
protected:
// handle changed property
virtual void handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop);
private:
//static const char* TypeEnums[];
//static const char* SideEnums[];
};
class PartDesignExport AdditiveLoft : public Loft {
PROPERTY_HEADER(PartDesign::AdditiveLoft);
public:
AdditiveLoft();
};
class PartDesignExport SubtractiveLoft : public Loft {
PROPERTY_HEADER(PartDesign::SubtractiveLoft);
public:
SubtractiveLoft();

View File

@@ -147,7 +147,7 @@ App::DocumentObjectExecReturn *Pad::execute(void)
Base::Vector3d paddingDirection;
if (!UseCustomVector.getValue()) {
if (ReferenceAxis.getValue() == nullptr) {
if (!ReferenceAxis.getValue()) {
// use sketch's normal vector for direction
paddingDirection = SketchVector;
AlongSketchNormal.setReadOnly(true);

View File

@@ -82,7 +82,7 @@ PROPERTY_SOURCE(PartDesign::Pipe, PartDesign::ProfileBased)
Pipe::Pipe()
{
ADD_PROPERTY_TYPE(Sections,(0),"Sweep",App::Prop_None,"List of sections");
Sections.setSize(0);
Sections.setValue(0);
ADD_PROPERTY_TYPE(Spine,(0),"Sweep",App::Prop_None,"Path to sweep along");
ADD_PROPERTY_TYPE(SpineTangent,(false),"Sweep",App::Prop_None,"Include tangent edges into path");
ADD_PROPERTY_TYPE(AuxillerySpine,(0),"Sweep",App::Prop_None,"Secondary path to orient sweep");
@@ -129,7 +129,7 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
//We would need a method to translate the front shape to match the shell starting position somehow...
TopoDS_Face face = TopoDS::Face(sketchshape);
BRepAdaptor_Surface adapt(face);
if(adapt.GetType() != GeomAbs_Plane)
if (adapt.GetType() != GeomAbs_Plane)
return new App::DocumentObjectExecReturn("Pipe: Only planar faces supported");
}
@@ -145,7 +145,7 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
//setup the location
this->positionByPrevious();
TopLoc_Location invObjLoc = this->getLocation().Inverted();
if(!base.IsNull())
if (!base.IsNull())
base.Move(invObjLoc);
//build the paths
@@ -162,7 +162,7 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
// auxiliary
TopoDS_Shape auxpath;
if(Mode.getValue()==3) {
if (Mode.getValue()==3) {
App::DocumentObject* auxspine = AuxillerySpine.getValue();
if (!(auxspine && auxspine->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())))
return new App::DocumentObjectExecReturn("No auxiliary spine linked.");
@@ -176,38 +176,50 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
//build up multisections
auto multisections = Sections.getValues();
std::vector<std::vector<TopoDS_Wire>> wiresections;
for(TopoDS_Wire& wire : wires)
for (TopoDS_Wire& wire : wires)
wiresections.emplace_back(1, wire);
//maybe we need a sacling law
Handle(Law_Function) scalinglaw;
//see if we shall use multiple sections
if(Transformation.getValue() == 1) {
if (Transformation.getValue() == 1) {
//TODO: we need to order the sections to prevent occ from crahsing, as makepieshell connects
//the sections in the order of adding
for(App::DocumentObject* obj : multisections) {
if(!obj->isDerivedFrom(Part::Feature::getClassTypeId()))
for (App::DocumentObject* obj : multisections) {
if (!obj->isDerivedFrom(Part::Feature::getClassTypeId()))
return new App::DocumentObjectExecReturn("All sections need to be part features");
// if the section is an object's face then take just the face
TopoDS_Shape shape;
if (obj->isDerivedFrom(Part::Part2DObject::getClassTypeId()))
shape = static_cast<Part::Part2DObject*>(obj)->Shape.getValue();
else {
auto subValues = Sections.getSubValues(obj);
if (subValues.empty())
throw Base::ValueError("Pipe: No valid subelement in multisection");
shape = static_cast<Part::Feature*>(obj)->Shape.getShape(). getSubShape(subValues[0].c_str());
}
TopExp_Explorer ex;
size_t i=0;
for (ex.Init(static_cast<Part::Feature*>(obj)->Shape.getValue(), TopAbs_WIRE); ex.More(); ex.Next()) {
if(i>=wiresections.size())
for (ex.Init(shape, TopAbs_WIRE); ex.More(); ex.Next()) {
if (i>=wiresections.size())
return new App::DocumentObjectExecReturn("Multisections need to have the same amount of inner wires as the base section");
wiresections[i].push_back(TopoDS::Wire(ex.Current()));
++i;
}
if(i<wiresections.size())
if (i<wiresections.size())
return new App::DocumentObjectExecReturn("Multisections need to have the same amount of inner wires as the base section");
}
}
/*//build the law functions instead
else if(Transformation.getValue() == 2) {
if(ScalingData.getValues().size()<1)
else if (Transformation.getValue() == 2) {
if (ScalingData.getValues().size()<1)
return new App::DocumentObjectExecReturn("No valid data given for linear scaling mode");
Handle(Law_Linear) lin = new Law_Linear();
@@ -215,8 +227,8 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
scalinglaw = lin;
}
else if(Transformation.getValue() == 3) {
if(ScalingData.getValues().size()<1)
else if (Transformation.getValue() == 3) {
if (ScalingData.getValues().size()<1)
return new App::DocumentObjectExecReturn("No valid data given for S-shape scaling mode");
Handle(Law_S) s = new Law_S();
@@ -229,22 +241,22 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
if (path.IsNull())
return new App::DocumentObjectExecReturn("Path must not be a null shape");
//build all shells
// build all shells
std::vector<TopoDS_Shape> shells;
std::vector<TopoDS_Wire> frontwires, backwires;
for(std::vector<TopoDS_Wire>& wires : wiresections) {
for (std::vector<TopoDS_Wire>& wires : wiresections) {
BRepOffsetAPI_MakePipeShell mkPS(TopoDS::Wire(path));
setupAlgorithm(mkPS, auxpath);
if(!scalinglaw) {
for(TopoDS_Wire& wire : wires) {
if (!scalinglaw) {
for (TopoDS_Wire& wire : wires) {
wire.Move(invObjLoc);
mkPS.Add(wire);
}
}
else {
for(TopoDS_Wire& wire : wires) {
for (TopoDS_Wire& wire : wires) {
wire.Move(invObjLoc);
mkPS.SetLaw(wire, scalinglaw);
}
@@ -277,7 +289,7 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
sewer.Add(front);
sewer.Add(back);
for(TopoDS_Shape& s : shells)
for (TopoDS_Shape& s : shells)
sewer.Add(s);
sewer.Perform();
@@ -289,7 +301,7 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
}
}
if(!mkSolid.IsDone())
if (!mkSolid.IsDone())
return new App::DocumentObjectExecReturn("Result is not a solid");
TopoDS_Shape result = mkSolid.Shape();
@@ -302,7 +314,7 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
//result.Move(invObjLoc);
AddSubShape.setValue(result);
if(base.IsNull()) {
if (base.IsNull()) {
if (getAddSubType() == FeatureAddSub::Subtractive)
return new App::DocumentObjectExecReturn("Pipe: There is nothing to subtract from\n");
@@ -311,7 +323,7 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
return App::DocumentObject::StdReturn;
}
if(getAddSubType() == FeatureAddSub::Additive) {
if (getAddSubType() == FeatureAddSub::Additive) {
BRepAlgoAPI_Fuse mkFuse(base, result);
if (!mkFuse.IsDone())
@@ -330,7 +342,7 @@ App::DocumentObjectExecReturn *Pipe::execute(void)
boolOp = refineShapeIfActive(boolOp);
Shape.setValue(getSolid(boolOp));
}
else if(getAddSubType() == FeatureAddSub::Subtractive) {
else if (getAddSubType() == FeatureAddSub::Subtractive) {
BRepAlgoAPI_Cut mkCut(base, result);
if (!mkCut.IsDone())
@@ -394,7 +406,7 @@ void Pipe::setupAlgorithm(BRepOffsetAPI_MakePipeShell& mkPipeShell, TopoDS_Shape
break;
}
if(auxiliary) {
if (auxiliary) {
mkPipeShell.SetMode(TopoDS::Wire(auxshape), AuxilleryCurvelinear.getValue());
//mkPipeShell.SetMode(TopoDS::Wire(auxshape), AuxilleryCurvelinear.getValue(), BRepFill_ContactOnBorder);
}
@@ -410,7 +422,7 @@ void Pipe::getContinuousEdges(Part::TopoShape /*TopShape*/, std::vector< std::st
TopExp::MapShapes(TopShape.getShape(), TopAbs_EDGE, mapOfEdges);
Base::Console().Message("Initial edges:\n");
for(int i=0; i<SubNames.size(); ++i)
for (int i=0; i<SubNames.size(); ++i)
Base::Console().Message("Subname: %s\n", SubNames[i].c_str());
unsigned int i = 0;
@@ -422,7 +434,7 @@ void Pipe::getContinuousEdges(Part::TopoShape /*TopShape*/, std::vector< std::st
TopoDS_Edge edge = TopoDS::Edge(TopShape.getSubShape(aSubName.c_str()));
const TopTools_ListOfShape& los = mapEdgeEdge.FindFromKey(edge);
if(los.Extent() != 2)
if (los.Extent() != 2)
{
SubNames.erase(SubNames.begin()+i);
continue;
@@ -447,7 +459,7 @@ void Pipe::getContinuousEdges(Part::TopoShape /*TopShape*/, std::vector< std::st
}
Base::Console().Message("Final edges:\n");
for(int i=0; i<SubNames.size(); ++i)
for (int i=0; i<SubNames.size(); ++i)
Base::Console().Message("Subname: %s\n", SubNames[i].c_str());
*/
}
@@ -457,7 +469,7 @@ void Pipe::buildPipePath(const Part::TopoShape& shape, const std::vector< std::s
if (!shape.getShape().IsNull()) {
try {
if (!subedge.empty()) {
//if(SpineTangent.getValue())
//if (SpineTangent.getValue())
//getContinuousEdges(shape, subedge);
BRepBuilderAPI_MakeWire mkWire;
@@ -506,8 +518,6 @@ void Pipe::buildPipePath(const Part::TopoShape& shape, const std::vector< std::s
}
}
PROPERTY_SOURCE(PartDesign::AdditivePipe, PartDesign::Pipe)
AdditivePipe::AdditivePipe() {
addSubType = Additive;
@@ -517,3 +527,14 @@ PROPERTY_SOURCE(PartDesign::SubtractivePipe, PartDesign::Pipe)
SubtractivePipe::SubtractivePipe() {
addSubType = Subtractive;
}
void Pipe::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop)
{
// property Sections had the App::PropertyLinkList and was changed to App::PropertyXLinkSubList
if (prop == &Sections && strcmp(TypeName, "App::PropertyLinkList") == 0) {
Sections.upgrade(reader, TypeName);
}
else {
ProfileBased::handleChangedPropertyType(reader, TypeName, prop);
}
}

View File

@@ -38,17 +38,16 @@ class PartDesignExport Pipe : public ProfileBased
public:
Pipe();
App::PropertyLinkSub Spine;
App::PropertyBool SpineTangent;
App::PropertyLinkSub AuxillerySpine;
App::PropertyBool AuxillerySpineTangent;
App::PropertyBool AuxilleryCurvelinear;
App::PropertyLinkSub Spine;
App::PropertyBool SpineTangent;
App::PropertyLinkSub AuxillerySpine;
App::PropertyBool AuxillerySpineTangent;
App::PropertyBool AuxilleryCurvelinear;
App::PropertyEnumeration Mode;
App::PropertyVector Binormal;
App::PropertyVector Binormal;
App::PropertyEnumeration Transition;
App::PropertyEnumeration Transformation;
App::PropertyLinkList Sections;
App::PropertyXLinkSubList Sections;
App::DocumentObjectExecReturn *execute(void);
short mustExecute() const;
@@ -56,13 +55,14 @@ public:
const char* getViewProviderName(void) const {
return "PartDesignGui::ViewProviderPipe";
}
//@}
protected:
///get the given edges and all their tangent ones
/// get the given edges and all their tangent ones
void getContinuousEdges(Part::TopoShape TopShape, std::vector< std::string >& SubNames);
void buildPipePath(const Part::TopoShape& input, const std::vector<std::string>& edges, TopoDS_Shape& result);
void setupAlgorithm(BRepOffsetAPI_MakePipeShell& mkPipeShell, TopoDS_Shape& auxshape);
/// handle changed property
virtual void handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop);
private:
static const char* TypeEnums[];

View File

@@ -67,6 +67,10 @@ Pocket::Pocket()
Type.setEnums(TypeEnums);
ADD_PROPERTY_TYPE(Length,(100.0),"Pocket",App::Prop_None,"Pocket length");
ADD_PROPERTY_TYPE(Length2,(100.0),"Pocket",App::Prop_None,"P");
ADD_PROPERTY_TYPE(UseCustomVector, (false), "Pocket", App::Prop_None, "Use custom vector for pocket direction");
ADD_PROPERTY_TYPE(Direction, (Base::Vector3d(1.0, 1.0, 1.0)), "Pocket", App::Prop_None, "Pocket direction vector");
ADD_PROPERTY_TYPE(ReferenceAxis, (0), "Pocket", App::Prop_None, "Reference axis of direction");
ADD_PROPERTY_TYPE(AlongSketchNormal, (true), "Pocket", App::Prop_None, "Measure pocket length along the sketch normal direction");
ADD_PROPERTY_TYPE(UpToFace,(0),"Pocket",App::Prop_None,"Face where pocket will end");
ADD_PROPERTY_TYPE(Offset,(0.0),"Pocket",App::Prop_None,"Offset from face in which pocket will end");
static const App::PropertyQuantityConstraint::Constraints signedLengthConstraint = {-DBL_MAX, DBL_MAX, 1.0};
@@ -84,6 +88,10 @@ short Pocket::mustExecute() const
Length.isTouched() ||
Length2.isTouched() ||
Offset.isTouched() ||
UseCustomVector.isTouched() ||
Direction.isTouched() ||
ReferenceAxis.isTouched() ||
AlongSketchNormal.isTouched() ||
UpToFace.isTouched())
return 1;
return ProfileBased::mustExecute();
@@ -130,6 +138,7 @@ App::DocumentObjectExecReturn *Pocket::execute(void)
// get the Sketch plane
Base::Placement SketchPos = obj->Placement.getValue();
// get the normal vector of the sketch
Base::Vector3d SketchVector = getProfileNormal();
// turn around for pockets
@@ -141,7 +150,78 @@ App::DocumentObjectExecReturn *Pocket::execute(void)
base.Move(invObjLoc);
gp_Dir dir(SketchVector.x,SketchVector.y,SketchVector.z);
Base::Vector3d pocketDirection;
if (!UseCustomVector.getValue()) {
if (!ReferenceAxis.getValue()) {
// use sketch's normal vector for direction
pocketDirection = SketchVector;
AlongSketchNormal.setReadOnly(true);
}
else {
// update Direction from ReferenceAxis
try {
App::DocumentObject* pcReferenceAxis = ReferenceAxis.getValue();
const std::vector<std::string>& subReferenceAxis = ReferenceAxis.getSubValues();
Base::Vector3d base;
Base::Vector3d dir;
getAxis(pcReferenceAxis, subReferenceAxis, base, dir, false);
pocketDirection = dir;
}
catch (const Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
}
}
}
else {
// use the given vector
// if null vector, use SketchVector
if ((fabs(Direction.getValue().x) < Precision::Confusion())
&& (fabs(Direction.getValue().y) < Precision::Confusion())
&& (fabs(Direction.getValue().z) < Precision::Confusion())) {
Direction.setValue(SketchVector);
}
pocketDirection = Direction.getValue();
}
// disable options of UseCustomVector
Direction.setReadOnly(!UseCustomVector.getValue());
ReferenceAxis.setReadOnly(UseCustomVector.getValue());
// UseCustomVector allows AlongSketchNormal but !UseCustomVector does not forbid it
if (UseCustomVector.getValue())
AlongSketchNormal.setReadOnly(false);
// create vector in pocketing direction with length 1
gp_Dir dir(pocketDirection.x, pocketDirection.y, pocketDirection.z);
// store the finally used direction to display it in the dialog
Direction.setValue(dir.X(), dir.Y(), dir.Z());
// The length of a gp_Dir is 1 so the resulting pocket would have
// the length L in the direction of dir. But we want to have its height in the
// direction of the normal vector.
// Therefore we must multiply L by the factor that is necessary
// to make dir as long that its projection to the SketchVector
// equals the SketchVector.
// This is the scalar product of both vectors.
// Since the pocket length cannot be negative, the factor must not be negative.
double factor = fabs(dir * gp_Dir(SketchVector.x, SketchVector.y, SketchVector.z));
// factor would be zero if vectors are orthogonal
if (factor < Precision::Confusion())
return new App::DocumentObjectExecReturn("Pocket: Creation failed because direction is orthogonal to sketch's normal vector");
// perform the length correction if not along custom vector
if (AlongSketchNormal.getValue()) {
L = L / factor;
L2 = L2 / factor;
}
// explicitly set the Direction so that the dialog shows also the used direction
// if the sketch's normal vector was used
Direction.setValue(pocketDirection);
dir.Transform(invObjLoc.Transformation());
if (profileshape.IsNull())

View File

@@ -37,10 +37,14 @@ class PartDesignExport Pocket : public ProfileBased
public:
Pocket();
App::PropertyEnumeration Type;
App::PropertyLength Length;
App::PropertyLength Length2;
App::PropertyLength Offset;
App::PropertyEnumeration Type;
App::PropertyLength Length;
App::PropertyLength Length2;
App::PropertyLength Offset;
App::PropertyBool UseCustomVector;
App::PropertyVector Direction;
App::PropertyBool AlongSketchNormal;
App::PropertyLinkSub ReferenceAxis;
/** @name methods override feature */
//@{

View File

@@ -1019,10 +1019,12 @@ void prepareProfileBased(PartDesign::Body *pcActiveBody, Gui::Command* cmd, cons
std::vector<Gui::SelectionObject> selection = cmd->getSelection().getSelectionEx();
if (selection.size() > 1) { //treat additional selected objects as sections
for (std::vector<Gui::SelectionObject>::size_type ii = 1; ii < selection.size(); ii++) {
if (selection[ii].getObject()->isDerivedFrom(Part::Part2DObject::getClassTypeId())) {
auto objCmdSection = Gui::Command::getObjectCmd(selection[ii].getObject());
FCMD_OBJ_CMD(Feat, "Sections += [" << objCmdSection << "]");
}
// Add subvalues even for sketches in case we just want points
auto objCmdSection = Gui::Command::getObjectCmd(selection[ii].getObject());
std::ostringstream ss;
for (auto &s : selection[ii].getSubNames())
ss << "'" << s << "',";
FCMD_OBJ_CMD(Feat, "Sections += [(" << objCmdSection << ", [" << ss.str() << "])]");
}
}
}

View File

@@ -140,46 +140,54 @@ bool ReferenceSelection::allow(App::Document* pDoc, App::DocumentObject* pObj, c
// Handle selection of geometry elements
if (!sSubName || sSubName[0] == '\0')
return whole;
std::string subName(sSubName);
if (edge && subName.size() > 4 && subName.substr(0,4) == "Edge") {
const Part::TopoShape &shape = static_cast<const Part::Feature*>(pObj)->Shape.getValue();
TopoDS_Shape sh = shape.getSubShape(subName.c_str());
const TopoDS_Edge& edgeShape = TopoDS::Edge(sh);
if (!edgeShape.IsNull()) {
if (planar) {
BRepAdaptor_Curve adapt(edgeShape);
if (adapt.GetType() == GeomAbs_Line)
// resolve links if needed
if (!pObj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
pObj = Part::Feature::getShapeOwner(pObj, sSubName);
}
if (pObj && pObj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
std::string subName(sSubName);
if (edge && subName.size() > 4 && subName.substr(0,4) == "Edge") {
const Part::TopoShape &shape = static_cast<const Part::Feature*>(pObj)->Shape.getValue();
TopoDS_Shape sh = shape.getSubShape(subName.c_str());
const TopoDS_Edge& edgeShape = TopoDS::Edge(sh);
if (!edgeShape.IsNull()) {
if (planar) {
BRepAdaptor_Curve adapt(edgeShape);
if (adapt.GetType() == GeomAbs_Line)
return true;
} else {
return true;
} else {
return true;
}
}
}
}
if (plane && subName.size() > 4 && subName.substr(0,4) == "Face") {
const Part::TopoShape &shape = static_cast<const Part::Feature*>(pObj)->Shape.getValue();
TopoDS_Shape sh = shape.getSubShape(subName.c_str());
const TopoDS_Face& face = TopoDS::Face(sh);
if (!face.IsNull()) {
if (planar) {
BRepAdaptor_Surface adapt(face);
if (adapt.GetType() == GeomAbs_Plane)
if (plane && subName.size() > 4 && subName.substr(0,4) == "Face") {
const Part::TopoShape &shape = static_cast<const Part::Feature*>(pObj)->Shape.getValue();
TopoDS_Shape sh = shape.getSubShape(subName.c_str());
const TopoDS_Face& face = TopoDS::Face(sh);
if (!face.IsNull()) {
if (planar) {
BRepAdaptor_Surface adapt(face);
if (adapt.GetType() == GeomAbs_Plane)
return true;
} else {
return true;
} else {
return true;
}
}
}
}
if (point && subName.size() > 6 && subName.substr(0,6) == "Vertex") {
return true;
}
if (circle && subName.size() > 4 && subName.substr(0,4) == "Edge") {
const Part::TopoShape &shape = static_cast<const Part::Feature*>(pObj)->Shape.getValue();
TopoDS_Shape sh = shape.getSubShape(subName.c_str());
const TopoDS_Edge& edgeShape = TopoDS::Edge(sh);
BRepAdaptor_Curve adapt(edgeShape);
if (adapt.GetType() == GeomAbs_Circle) {
if (point && subName.size() > 6 && subName.substr(0,6) == "Vertex") {
return true;
}
if (circle && subName.size() > 4 && subName.substr(0,4) == "Edge") {
const Part::TopoShape &shape = static_cast<const Part::Feature*>(pObj)->Shape.getValue();
TopoDS_Shape sh = shape.getSubShape(subName.c_str());
const TopoDS_Edge& edgeShape = TopoDS::Edge(sh);
BRepAdaptor_Curve adapt(edgeShape);
if (adapt.GetType() == GeomAbs_Circle) {
return true;
}
}
}
return false;
}

View File

@@ -103,14 +103,14 @@ TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView, bool /*newObj
if (profile) {
Gui::Application::Instance->showViewProvider(profile);
QString label = QString::fromUtf8(profile->Label.getValue());
QString label = make2DLabel(profile, loft->Profile.getSubValues());
ui->profileBaseEdit->setText(label);
}
for (auto obj : loft->Sections.getValues()) {
Gui::Application::Instance->showViewProvider(obj);
QString label = QString::fromUtf8(obj->Label.getValue());
QString label = make2DLabel(obj, loft->Sections.getSubValues(obj));
QListWidgetItem* item = new QListWidgetItem();
item->setText(label);
item->setData(Qt::UserRole, QByteArray(obj->getNameInDocument()));
@@ -121,10 +121,6 @@ TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView, bool /*newObj
ui->checkBoxRuled->setChecked(loft->Ruled.getValue());
ui->checkBoxClosed->setChecked(loft->Closed.getValue());
if (!loft->Sections.getValues().empty()) {
LoftView->makeTemporaryVisible(true);
}
// activate and de-activate dialog elements as appropriate
for (QWidget* child : proxy->findChildren<QWidget*>())
child->blockSignals(false);
@@ -138,6 +134,10 @@ TaskLoftParameters::~TaskLoftParameters()
void TaskLoftParameters::updateUI()
{
// we must assure the changed loft is kept visible on section changes,
// see https://forum.freecadweb.org/viewtopic.php?f=3&t=63252
PartDesign::Loft* loft = static_cast<PartDesign::Loft*>(vp->getObject());
vp->makeTemporaryVisible(!loft->Sections.getValues().empty());
}
void TaskLoftParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
@@ -150,7 +150,7 @@ void TaskLoftParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
App::Document* document = App::GetApplication().getDocument(msg.pDocName);
App::DocumentObject* object = document ? document->getObject(msg.pObjectName) : nullptr;
if (object) {
QString label = QString::fromUtf8(object->Label.getValue());
QString label = make2DLabel(object, {msg.pSubName});
if (selectionMode == refProfile) {
ui->profileBaseEdit->setText(label);
}
@@ -172,6 +172,7 @@ void TaskLoftParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
clearButtons();
exitSelectionMode();
updateUI();
}
}
@@ -195,7 +196,7 @@ bool TaskLoftParameters::referenceSelected(const Gui::SelectionChanges& msg) con
App::DocumentObject* obj = loft->getDocument()->getObject(msg.pObjectName);
if (selectionMode == refProfile) {
loft->Profile.setValue(obj);
loft->Profile.setValue(obj, {msg.pSubName});
return true;
}
else if (selectionMode == refAdd || selectionMode == refRemove) {
@@ -205,18 +206,19 @@ bool TaskLoftParameters::referenceSelected(const Gui::SelectionChanges& msg) con
if (selectionMode == refAdd) {
if (f == refs.end())
refs.push_back(obj);
loft->Sections.addValue(obj, {msg.pSubName});
else
return false; // duplicate selection
}
else if (selectionMode == refRemove) {
if (f != refs.end())
refs.erase(f);
// Removing just the object this way instead of `refs.erase` and
// `setValues(ref)` cleanly ensures subnames are preserved.
loft->Sections.removeValue(obj);
else
return false;
}
static_cast<PartDesign::Loft*>(vp->getObject())->Sections.setValues(refs);
return true;
}
}
@@ -250,11 +252,13 @@ void TaskLoftParameters::onDeleteSection()
App::DocumentObject* obj = loft->getDocument()->getObject(data.constData());
std::vector<App::DocumentObject*>::iterator f = std::find(refs.begin(), refs.end(), obj);
if (f != refs.end()) {
refs.erase(f);
loft->Sections.setValues(refs);
// Removing just the object this way instead of `refs.erase` and
// `setValues(ref)` cleanly ensures subnames are preserved.
loft->Sections.removeValue(obj);
//static_cast<ViewProviderLoft*>(vp)->highlightReferences(false, true);
recomputeFeature();
updateUI();
}
}
}
@@ -278,16 +282,21 @@ void TaskLoftParameters::indexesMoved()
loft->Sections.setValues(originals);
recomputeFeature();
updateUI();
}
void TaskLoftParameters::clearButtons() {
ui->buttonRefAdd->setChecked(false);
ui->buttonRefRemove->setChecked(false);
void TaskLoftParameters::clearButtons(const selectionModes notThis)
{
if (notThis != refProfile)
ui->buttonProfileBase->setChecked(false);
if (notThis != refAdd)
ui->buttonRefAdd->setChecked(false);
if (notThis != refRemove)
ui->buttonRefRemove->setChecked(false);
}
void TaskLoftParameters::exitSelectionMode() {
void TaskLoftParameters::exitSelectionMode()
{
selectionMode = none;
Gui::Selection().clearSelection();
}
@@ -309,23 +318,27 @@ void TaskLoftParameters::onRuled(bool val) {
void TaskLoftParameters::onProfileButton(bool checked)
{
if (checked) {
clearButtons(refProfile);
Gui::Selection().clearSelection();
selectionMode = refProfile;
//static_cast<ViewProviderLoft*>(vp)->highlightReferences(true, true);
}
}
void TaskLoftParameters::onRefButtonAdd(bool checked) {
void TaskLoftParameters::onRefButtonAdd(bool checked)
{
if (checked) {
clearButtons(refAdd);
Gui::Selection().clearSelection();
selectionMode = refAdd;
//static_cast<ViewProviderLoft*>(vp)->highlightReferences(true, true);
}
}
void TaskLoftParameters::onRefButtonRemove(bool checked) {
void TaskLoftParameters::onRefButtonRemove(bool checked)
{
if (checked) {
clearButtons(refRemove);
Gui::Selection().clearSelection();
selectionMode = refRemove;
//static_cast<ViewProviderLoft*>(vp)->highlightReferences(true, true);

View File

@@ -50,7 +50,7 @@ class TaskLoftParameters : public TaskSketchBasedParameters
Q_OBJECT
public:
TaskLoftParameters(ViewProviderLoft *LoftView,bool newObj=false,QWidget *parent = 0);
TaskLoftParameters(ViewProviderLoft *LoftView, bool newObj=false, QWidget *parent = 0);
~TaskLoftParameters();
private Q_SLOTS:
@@ -63,6 +63,8 @@ private Q_SLOTS:
void indexesMoved();
protected:
enum selectionModes { none, refAdd, refRemove, refProfile };
void changeEvent(QEvent *e);
private:
@@ -70,14 +72,13 @@ private:
void updateUI();
bool referenceSelected(const Gui::SelectionChanges& msg) const;
void removeFromListWidget(QListWidget*w, QString name);
void clearButtons();
void clearButtons(const selectionModes notThis=none);
void exitSelectionMode();
private:
QWidget* proxy;
std::unique_ptr<Ui_TaskLoftParameters> ui;
enum selectionModes { none, refAdd, refRemove, refProfile };
selectionModes selectionMode = none;
};

View File

@@ -361,12 +361,12 @@ void TaskPadParameters::fillDirectionCombo()
PartDesign::ProfileBased* pcFeat = static_cast<PartDesign::ProfileBased*>(vp->getObject());
Part::Part2DObject* pcSketch = dynamic_cast<Part::Part2DObject*>(pcFeat->Profile.getValue());
if (pcSketch)
addAxisToCombo(pcSketch, "N_Axis", QObject::tr("Sketch normal"));
addAxisToCombo(pcSketch, "N_Axis", tr("Sketch normal"));
// add the other entries
addAxisToCombo(0, std::string(), tr("Select reference..."));
// we start with the sketch normal as proposal for the custom direction
if (pcSketch)
addAxisToCombo(pcSketch, "N_Axis", QObject::tr("Custom direction"));
addAxisToCombo(pcSketch, "N_Axis", tr("Custom direction"));
}
// add current link, if not in list
@@ -420,7 +420,7 @@ void TaskPadParameters::onDirectionCBChanged(int num)
{
PartDesign::Pad* pcPad = static_cast<PartDesign::Pad*>(vp->getObject());
if (axesInList.empty() || !pcPad)
if (axesInList.empty())
return;
App::PropertyLinkSub& lnk = *(axesInList[num]);
@@ -457,7 +457,6 @@ void TaskPadParameters::onDirectionCBChanged(int num)
// if custom direction is used, show it
if (num == 2) {
ui->checkBoxDirection->setChecked(true);
PartDesign::Pad* pcPad = static_cast<PartDesign::Pad*>(vp->getObject());
pcPad->UseCustomVector.setValue(true);
}
else {

View File

@@ -108,20 +108,29 @@ TaskPipeParameters::TaskPipeParameters(ViewProviderPipe *PipeView, bool /*newObj
PartDesign::Pipe* pipe = static_cast<PartDesign::Pipe*>(PipeView->getObject());
Gui::Document* doc = PipeView->getDocument();
//make sure the user sees all important things: the
//spine/auxiliary spine he already selected
// make sure the user sees all important things and load the values
// also save visibility state to reset it later when pipe is closed
// first the spine
if (pipe->Spine.getValue()) {
auto* svp = doc->getViewProvider(pipe->Spine.getValue());
spineShow = svp->isShow();
svp->setVisible(true);
}
//add initial values
if (pipe->Profile.getValue())
ui->profileBaseEdit->setText(QString::fromUtf8(pipe->Profile.getValue()->Label.getValue()));
if (pipe->Spine.getValue())
auto* spineVP = doc->getViewProvider(pipe->Spine.getValue());
spineShow = spineVP->isShow();
spineVP->setVisible(true);
ui->spineBaseEdit->setText(QString::fromUtf8(pipe->Spine.getValue()->Label.getValue()));
}
// the profile
if (pipe->Profile.getValue()) {
auto* profileVP = doc->getViewProvider(pipe->Profile.getValue());
profileShow = profileVP->isShow();
profileVP->setVisible(true);
ui->profileBaseEdit->setText(make2DLabel(pipe->Profile.getValue(), pipe->Profile.getSubValues()));
}
// the auxiliary spine
if (pipe->AuxillerySpine.getValue()) {
auto* svp = doc->getViewProvider(pipe->AuxillerySpine.getValue());
auxSpineShow = svp->isShow();
svp->show();
}
// the spine edges
std::vector<std::string> strings = pipe->Spine.getSubValues();
for (std::vector<std::string>::const_iterator it = strings.begin(); it != strings.end(); ++it) {
QString label = QString::fromStdString(*it);
@@ -145,19 +154,11 @@ TaskPipeParameters::~TaskPipeParameters()
try {
if (vp) {
PartDesign::Pipe* pipe = static_cast<PartDesign::Pipe*>(vp->getObject());
Gui::Document* doc = vp->getDocument();
//make sure the user sees all important things: the
//spine/auxiliary spine he already selected
if (pipe->Spine.getValue()) {
auto* svp = doc->getViewProvider(pipe->Spine.getValue());
svp->setVisible(spineShow);
spineShow = false;
}
//setting visibility to true is needed when preselecting profile and path prior to invoking sweep
// setting visibility to true is needed when preselecting profile and path prior to invoking sweep
Gui::cmdGuiObject(pipe, "Visibility = True");
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::Spine, false);
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::Profile, false);
}
}
catch (const Base::Exception& e) {
@@ -186,7 +187,7 @@ void TaskPipeParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
App::Document* document = App::GetApplication().getDocument(msg.pDocName);
App::DocumentObject* object = document ? document->getObject(msg.pObjectName) : nullptr;
if (object) {
QString label = QString::fromUtf8(object->Label.getValue());
QString label = make2DLabel(object, {msg.pSubName});
ui->profileBaseEdit->setText(label);
}
}
@@ -215,7 +216,7 @@ void TaskPipeParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
ui->spineBaseEdit->clear();
}
}
else if(selectionMode == refObjAdd) {
else if (selectionMode == refObjAdd) {
ui->listWidgetReferences->clear();
App::Document* document = App::GetApplication().getDocument(msg.pDocName);
@@ -235,16 +236,16 @@ void TaskPipeParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
}
}
void TaskPipeParameters::onTransitionChanged(int idx) {
void TaskPipeParameters::onTransitionChanged(int idx)
{
static_cast<PartDesign::Pipe*>(vp->getObject())->Transition.setValue(idx);
recomputeFeature();
}
void TaskPipeParameters::onButtonRefAdd(bool checked) {
void TaskPipeParameters::onButtonRefAdd(bool checked)
{
if (checked) {
//clearButtons(refAdd);
clearButtons(refAdd);
//hideObject();
Gui::Selection().clearSelection();
selectionMode = refAdd;
@@ -257,10 +258,10 @@ void TaskPipeParameters::onButtonRefAdd(bool checked) {
}
}
void TaskPipeParameters::onButtonRefRemove(bool checked) {
void TaskPipeParameters::onButtonRefRemove(bool checked)
{
if (checked) {
//clearButtons(refRemove);
clearButtons(refRemove);
//hideObject();
Gui::Selection().clearSelection();
selectionMode = refRemove;
@@ -273,10 +274,10 @@ void TaskPipeParameters::onButtonRefRemove(bool checked) {
}
}
void TaskPipeParameters::onBaseButton(bool checked) {
void TaskPipeParameters::onBaseButton(bool checked)
{
if (checked) {
//clearButtons(refRemove);
clearButtons(refObjAdd);
//hideObject();
Gui::Selection().clearSelection();
selectionMode = refObjAdd;
@@ -300,7 +301,7 @@ void TaskPipeParameters::onProfileButton(bool checked)
pvp->setVisible(true);
}
//clearButtons(refRemove);
clearButtons(refProfile);
//hideObject();
Gui::Selection().clearSelection();
selectionMode = refProfile;
@@ -313,14 +314,14 @@ void TaskPipeParameters::onProfileButton(bool checked)
}
}
void TaskPipeParameters::onTangentChanged(bool checked) {
void TaskPipeParameters::onTangentChanged(bool checked)
{
static_cast<PartDesign::Pipe*>(vp->getObject())->SpineTangent.setValue(checked);
recomputeFeature();
}
void TaskPipeParameters::removeFromListWidget(QListWidget* widget, QString itemstr) {
void TaskPipeParameters::removeFromListWidget(QListWidget* widget, QString itemstr)
{
QList<QListWidgetItem*> items = widget->findItems(itemstr, Qt::MatchExactly);
if (!items.empty()) {
for (QList<QListWidgetItem*>::const_iterator i = items.begin(); i != items.end(); i++) {
@@ -355,8 +356,8 @@ void TaskPipeParameters::onDeleteEdge()
}
}
bool TaskPipeParameters::referenceSelected(const SelectionChanges& msg) const {
bool TaskPipeParameters::referenceSelected(const SelectionChanges& msg) const
{
if (msg.Type == Gui::SelectionChanges::AddSelection && selectionMode != none) {
if (strcmp(msg.pDocName, vp->getObject()->getDocument()->getName()) != 0)
return false;
@@ -382,12 +383,13 @@ bool TaskPipeParameters::referenceSelected(const SelectionChanges& msg) const {
success = false;
}
else {
pipe->Profile.setValue(profile);
pipe->Profile.setValue(profile, {msg.pSubName});
}
// hide the old or new profile again
auto* pvp = doc->getViewProvider(pipe->Profile.getValue());
if(pvp) pvp->setVisible(false);
if (pvp)
pvp->setVisible(false);
}
return success;
}
@@ -423,32 +425,66 @@ bool TaskPipeParameters::referenceSelected(const SelectionChanges& msg) const {
return false;
}
void TaskPipeParameters::clearButtons() {
ui->buttonProfileBase->setChecked(false);
ui->buttonRefAdd->setChecked(false);
ui->buttonRefRemove->setChecked(false);
ui->buttonSpineBase->setChecked(false);
void TaskPipeParameters::clearButtons(const selectionModes notThis)
{
// TODO: Clear buttons in the other pipe taskboxes as well
if (notThis != refProfile)
ui->buttonProfileBase->setChecked(false);
if (notThis != refAdd)
ui->buttonRefAdd->setChecked(false);
if (notThis != refRemove)
ui->buttonRefRemove->setChecked(false);
if (notThis != refObjAdd)
ui->buttonSpineBase->setChecked(false);
}
void TaskPipeParameters::exitSelectionMode() {
void TaskPipeParameters::exitSelectionMode()
{
selectionMode = none;
Gui::Selection().clearSelection();
}
void TaskPipeParameters::setVisibilityOfSpineAndProfile()
{
if (vp) {
PartDesign::Pipe* pipe = static_cast<PartDesign::Pipe*>(vp->getObject());
Gui::Document* doc = vp->getDocument();
// set visibility to the state when the pipe was opened
for (auto obj : pipe->Sections.getValues()) {
auto* sectionVP = doc->getViewProvider(obj);
sectionVP->setVisible(profileShow);
}
if (pipe->Spine.getValue()) {
auto* spineVP = doc->getViewProvider(pipe->Spine.getValue());
spineVP->setVisible(spineShow);
spineShow = false;
}
if (pipe->Profile.getValue()) {
auto* profileVP = doc->getViewProvider(pipe->Profile.getValue());
profileVP->setVisible(profileShow);
profileShow = false;
}
if (pipe->AuxillerySpine.getValue()) {
auto* svp = doc->getViewProvider(pipe->AuxillerySpine.getValue());
svp->setVisible(auxSpineShow);
auxSpineShow = false;
}
}
}
bool TaskPipeParameters::accept()
{
//see what to do with external references
//check the prerequisites for the selected objects
//the user has to decide which option we should take if external references are used
PartDesign::Pipe* pcPipe = static_cast<PartDesign::Pipe*>(getPipeView()->getObject());
auto pcActiveBody = PartDesignGui::getBodyFor(pcPipe, false);
auto pcActiveBody = PartDesignGui::getBodyFor (pcPipe, false);
if (!pcActiveBody) {
QMessageBox::warning(this, tr("Input error"), tr("No active body"));
return false;
}
//auto pcActivePart = PartDesignGui::getPartFor(pcActiveBody, false);
//auto pcActivePart = PartDesignGui::getPartFor (pcActiveBody, false);
std::vector<App::DocumentObject*> copies;
bool extReference = false;
@@ -473,7 +509,7 @@ bool TaskPipeParameters::accept()
extReference = true;
}
else {
for(App::DocumentObject* obj : pcPipe->Sections.getValues()) {
for (App::DocumentObject* obj : pcPipe->Sections.getValues()) {
if (!pcActiveBody->hasObject(obj) && !pcActiveBody->getOrigin()->hasObject(obj)) {
extReference = true;
break;
@@ -491,7 +527,6 @@ bool TaskPipeParameters::accept()
return false;
if (!dlg.radioXRef->isChecked()) {
if (!pcActiveBody->hasObject(spine) && !pcActiveBody->getOrigin()->hasObject(spine)) {
pcPipe->Spine.setValue(PartDesignGui::TaskFeaturePick::makeCopy(spine, "",
dlg.radioIndependent->isChecked()),
@@ -507,9 +542,8 @@ bool TaskPipeParameters::accept()
std::vector<App::DocumentObject*> objs;
int index = 0;
for(App::DocumentObject* obj : pcPipe->Sections.getValues()) {
if(!pcActiveBody->hasObject(obj) && !pcActiveBody->getOrigin()->hasObject(obj)) {
for (App::DocumentObject* obj : pcPipe->Sections.getValues()) {
if (!pcActiveBody->hasObject(obj) && !pcActiveBody->getOrigin()->hasObject(obj)) {
objs.push_back(PartDesignGui::TaskFeaturePick::makeCopy(obj, "", dlg.radioIndependent->isChecked()));
copies.push_back(objs.back());
}
@@ -525,6 +559,8 @@ bool TaskPipeParameters::accept()
}
try {
setVisibilityOfSpineAndProfile();
App::DocumentObject* spine = pcPipe->Spine.getValue();
std::vector<std::string> subNames = pcPipe->Spine.getSubValues();
App::PropertyLinkT propT(spine, subNames);
@@ -600,15 +636,6 @@ TaskPipeOrientation::TaskPipeOrientation(ViewProviderPipe* PipeView, bool /*newO
this->groupLayout()->addWidget(proxy);
PartDesign::Pipe* pipe = static_cast<PartDesign::Pipe*>(PipeView->getObject());
Gui::Document* doc = Gui::Application::Instance->getDocument(pipe->getDocument());
//make sure the user sees an important things: the base feature to select edges and the
//spine/auxiliary spine he already selected
if(pipe->AuxillerySpine.getValue()) {
auto* svp = doc->getViewProvider(pipe->AuxillerySpine.getValue());
auxSpineShow = svp->isShow();
svp->show();
}
//add initial values
if (pipe->AuxillerySpine.getValue())
@@ -633,49 +660,38 @@ TaskPipeOrientation::TaskPipeOrientation(ViewProviderPipe* PipeView, bool /*newO
TaskPipeOrientation::~TaskPipeOrientation()
{
try {
if (vp) {
PartDesign::Pipe* pipe = static_cast<PartDesign::Pipe*>(vp->getObject());
Gui::Document* doc = vp->getDocument();
//make sure the user sees al important things: the base feature to select edges and the
//spine/auxiliary spine he already selected
if (pipe->AuxillerySpine.getValue()) {
auto* svp = doc->getViewProvider(pipe->AuxillerySpine.getValue());
svp->setVisible(auxSpineShow);
auxSpineShow = false;
}
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::AuxiliarySpine, false);
}
}
catch (const Base::RuntimeError&) {
// getDocument() may raise an exception
if (vp) {
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::AuxiliarySpine, false);
}
}
void TaskPipeOrientation::onOrientationChanged(int idx) {
void TaskPipeOrientation::onOrientationChanged(int idx)
{
static_cast<PartDesign::Pipe*>(vp->getObject())->Mode.setValue(idx);
recomputeFeature();
}
void TaskPipeOrientation::clearButtons() {
ui->buttonRefAdd->setChecked(false);
ui->buttonRefRemove->setChecked(false);
ui->buttonProfileBase->setChecked(false);
void TaskPipeOrientation::clearButtons(const selectionModes notThis)
{
// TODO: Clear buttons in the other pipe taskboxes as well
if (notThis != refAdd)
ui->buttonRefAdd->setChecked(false);
if (notThis != refRemove)
ui->buttonRefRemove->setChecked(false);
if (notThis != refObjAdd)
ui->buttonProfileBase->setChecked(false);
}
void TaskPipeOrientation::exitSelectionMode() {
void TaskPipeOrientation::exitSelectionMode()
{
selectionMode = none;
Gui::Selection().clearSelection();
}
void TaskPipeOrientation::onButtonRefAdd(bool checked) {
void TaskPipeOrientation::onButtonRefAdd(bool checked)
{
if (checked) {
clearButtons(refAdd);
Gui::Selection().clearSelection();
selectionMode = refAdd;
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::AuxiliarySpine, true);
@@ -687,9 +703,10 @@ void TaskPipeOrientation::onButtonRefAdd(bool checked) {
}
}
void TaskPipeOrientation::onButtonRefRemove(bool checked) {
void TaskPipeOrientation::onButtonRefRemove(bool checked)
{
if (checked) {
clearButtons(refRemove);
Gui::Selection().clearSelection();
selectionMode = refRemove;
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::AuxiliarySpine, true);
@@ -704,6 +721,7 @@ void TaskPipeOrientation::onButtonRefRemove(bool checked) {
void TaskPipeOrientation::onBaseButton(bool checked)
{
if (checked) {
clearButtons(refObjAdd);
Gui::Selection().clearSelection();
selectionMode = refObjAdd;
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::AuxiliarySpine, true);
@@ -740,8 +758,8 @@ void TaskPipeOrientation::onBinormalChanged(double)
recomputeFeature();
}
void TaskPipeOrientation::onSelectionChanged(const SelectionChanges& msg) {
void TaskPipeOrientation::onSelectionChanged(const SelectionChanges& msg)
{
if (selectionMode == none)
return;
@@ -771,7 +789,7 @@ void TaskPipeOrientation::onSelectionChanged(const SelectionChanges& msg) {
ui->profileBaseEdit->clear();
}
}
else if(selectionMode == refObjAdd) {
else if (selectionMode == refObjAdd) {
ui->listWidgetReferences->clear();
App::Document* document = App::GetApplication().getDocument(msg.pDocName);
@@ -792,8 +810,8 @@ void TaskPipeOrientation::onSelectionChanged(const SelectionChanges& msg) {
}
}
bool TaskPipeOrientation::referenceSelected(const SelectionChanges& msg) const {
bool TaskPipeOrientation::referenceSelected(const SelectionChanges& msg) const
{
if (msg.Type == Gui::SelectionChanges::AddSelection && selectionMode != none) {
if (strcmp(msg.pDocName, vp->getObject()->getDocument()->getName()) != 0)
return false;
@@ -832,8 +850,8 @@ bool TaskPipeOrientation::referenceSelected(const SelectionChanges& msg) const {
return false;
}
void TaskPipeOrientation::removeFromListWidget(QListWidget* widget, QString name) {
void TaskPipeOrientation::removeFromListWidget(QListWidget* widget, QString name)
{
QList<QListWidgetItem*> items = widget->findItems(name, Qt::MatchExactly);
if (!items.empty()) {
for (QList<QListWidgetItem*>::const_iterator i = items.begin(); i != items.end(); i++) {
@@ -868,8 +886,8 @@ void TaskPipeOrientation::onDeleteItem()
}
}
void TaskPipeOrientation::updateUI(int idx) {
void TaskPipeOrientation::updateUI(int idx)
{
//make sure we resize to the size of the current page
for (int i=0; i<ui->stackedWidget->count(); ++i)
ui->stackedWidget->widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
@@ -918,7 +936,7 @@ TaskPipeScaling::TaskPipeScaling(ViewProviderPipe* PipeView, bool /*newObj*/, QW
PartDesign::Pipe* pipe = static_cast<PartDesign::Pipe*>(PipeView->getObject());
for (auto obj : pipe->Sections.getValues()) {
Gui::Application::Instance->showViewProvider(obj);
QString label = QString::fromUtf8(obj->Label.getValue());
QString label = make2DLabel(obj, pipe->Sections.getSubValues(obj));
QListWidgetItem* item = new QListWidgetItem();
item->setText(label);
item->setData(Qt::UserRole, QByteArray(obj->getNameInDocument()));
@@ -932,25 +950,32 @@ TaskPipeScaling::TaskPipeScaling(ViewProviderPipe* PipeView, bool /*newObj*/, QW
QGenericReturnArgument(), Q_ARG(int,pipe->Transformation.getValue()));
}
TaskPipeScaling::~TaskPipeScaling() {
TaskPipeScaling::~TaskPipeScaling()
{
if (vp) {
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::Section, false);
}
}
void TaskPipeScaling::clearButtons() {
ui->buttonRefAdd->setChecked(false);
ui->buttonRefRemove->setChecked(false);
void TaskPipeScaling::clearButtons(const selectionModes notThis)
{
// TODO: Clear buttons in the other pipe taskboxes as well
if (notThis != refRemove)
ui->buttonRefRemove->setChecked(false);
if (notThis != refAdd)
ui->buttonRefAdd->setChecked(false);
}
void TaskPipeScaling::exitSelectionMode() {
void TaskPipeScaling::exitSelectionMode()
{
selectionMode = none;
Gui::Selection().clearSelection();
}
void TaskPipeScaling::onButtonRefAdd(bool checked) {
void TaskPipeScaling::onButtonRefAdd(bool checked)
{
if (checked) {
clearButtons(refAdd);
Gui::Selection().clearSelection();
selectionMode = refAdd;
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::Section, true);
@@ -962,9 +987,10 @@ void TaskPipeScaling::onButtonRefAdd(bool checked) {
}
}
void TaskPipeScaling::onButtonRefRemove(bool checked) {
void TaskPipeScaling::onButtonRefRemove(bool checked)
{
if (checked) {
clearButtons(refRemove);
Gui::Selection().clearSelection();
selectionMode = refRemove;
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::Section, true);
@@ -976,14 +1002,14 @@ void TaskPipeScaling::onButtonRefRemove(bool checked) {
}
}
void TaskPipeScaling::onScalingChanged(int idx) {
void TaskPipeScaling::onScalingChanged(int idx)
{
updateUI(idx);
static_cast<PartDesign::Pipe*>(vp->getObject())->Transformation.setValue(idx);
}
void TaskPipeScaling::onSelectionChanged(const SelectionChanges& msg) {
void TaskPipeScaling::onSelectionChanged(const SelectionChanges& msg)
{
if (selectionMode == none)
return;
@@ -992,7 +1018,7 @@ void TaskPipeScaling::onSelectionChanged(const SelectionChanges& msg) {
App::Document* document = App::GetApplication().getDocument(msg.pDocName);
App::DocumentObject* object = document ? document->getObject(msg.pObjectName) : nullptr;
if (object) {
QString label = QString::fromUtf8(object->Label.getValue());
QString label = make2DLabel(object, {msg.pSubName});
if (selectionMode == refAdd) {
QListWidgetItem* item = new QListWidgetItem();
item->setText(label);
@@ -1012,11 +1038,10 @@ void TaskPipeScaling::onSelectionChanged(const SelectionChanges& msg) {
}
}
bool TaskPipeScaling::referenceSelected(const SelectionChanges& msg) const {
bool TaskPipeScaling::referenceSelected(const SelectionChanges& msg) const
{
if ((msg.Type == Gui::SelectionChanges::AddSelection) && (
(selectionMode == refAdd) || (selectionMode == refRemove))) {
if (strcmp(msg.pDocName, vp->getObject()->getDocument()->getName()) != 0)
return false;
@@ -1029,33 +1054,35 @@ bool TaskPipeScaling::referenceSelected(const SelectionChanges& msg) const {
//supported, not individual edges of a part
//change the references
std::vector<App::DocumentObject*> refs = static_cast<PartDesign::Pipe*>(vp->getObject())->Sections.getValues();
PartDesign::Pipe* pipe = static_cast<PartDesign::Pipe*>(vp->getObject());
std::vector<App::DocumentObject*> refs = pipe->Sections.getValues();
App::DocumentObject* obj = vp->getObject()->getDocument()->getObject(msg.pObjectName);
std::vector<App::DocumentObject*>::iterator f = std::find(refs.begin(), refs.end(), obj);
if (selectionMode == refAdd) {
if (f == refs.end())
refs.push_back(obj);
pipe->Sections.addValue(obj, {msg.pSubName});
else
return false; // duplicate selection
}
else {
if (f != refs.end())
refs.erase(f);
// Removing just the object this way instead of `refs.erase` and
// `setValues(ref)` cleanly ensures subnames are preserved.
pipe->Sections.removeValue(obj);
else
return false;
}
static_cast<ViewProviderPipe*>(vp)->highlightReferences(ViewProviderPipe::Section, false);
static_cast<PartDesign::Pipe*>(vp->getObject())->Sections.setValues(refs);
return true;
}
return false;
}
void TaskPipeScaling::removeFromListWidget(QListWidget* widget, QString name) {
void TaskPipeScaling::removeFromListWidget(QListWidget* widget, QString name)
{
QList<QListWidgetItem*> items = widget->findItems(name, Qt::MatchExactly);
if (!items.empty()) {
for (QList<QListWidgetItem*>::const_iterator i = items.begin(); i != items.end(); i++) {
@@ -1082,18 +1109,19 @@ void TaskPipeScaling::onDeleteSection()
// if something was found, delete it and update the section list
if (f != refs.end()) {
refs.erase(f);
pipe->Sections.setValues(refs);
// Removing just the object this way instead of `refs.erase` and
// `setValues(ref)` cleanly ensures subnames are preserved.
pipe->Sections.removeValue(obj);
clearButtons();
recomputeFeature();
}
}
}
void TaskPipeScaling::updateUI(int idx) {
void TaskPipeScaling::updateUI(int idx)
{
//make sure we resize to the size of the current page
for(int i=0; i<ui->stackedWidget->count(); ++i)
for (int i=0; i<ui->stackedWidget->count(); ++i)
ui->stackedWidget->widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
if (idx < ui->stackedWidget->count())

View File

@@ -41,7 +41,7 @@ namespace Gui {
class ViewProvider;
}
namespace PartDesignGui {
namespace PartDesignGui {
class Ui_TaskPipeParameters;
class Ui_TaskPipeOrientation;
@@ -53,7 +53,7 @@ class TaskPipeParameters : public TaskSketchBasedParameters
Q_OBJECT
public:
TaskPipeParameters(ViewProviderPipe *PipeView,bool newObj=false,QWidget *parent = 0);
TaskPipeParameters(ViewProviderPipe *PipeView, bool newObj=false, QWidget *parent = 0);
~TaskPipeParameters();
bool accept();
@@ -66,25 +66,28 @@ private Q_SLOTS:
void onBaseButton(bool checked);
void onProfileButton(bool checked);
void onDeleteEdge();
protected:
enum selectionModes { none, refAdd, refRemove, refObjAdd, refProfile };
selectionModes selectionMode = none;
void removeFromListWidget(QListWidget*w, QString name);
bool referenceSelected(const Gui::SelectionChanges& msg) const;
private:
void onSelectionChanged(const Gui::SelectionChanges& msg);
void updateUI();
void clearButtons();
void clearButtons(const selectionModes notThis=none);
void exitSelectionMode();
void setVisibilityOfSpineAndProfile();
ViewProviderPipe* getPipeView() const
{ return static_cast<ViewProviderPipe*>(vp); }
bool spineShow = false;
bool profileShow = false;
bool auxSpineShow = false;
private:
QWidget* proxy;
std::unique_ptr<Ui_TaskPipeParameters> ui;
@@ -95,10 +98,10 @@ class TaskPipeOrientation : public TaskSketchBasedParameters
Q_OBJECT
public:
TaskPipeOrientation(ViewProviderPipe *PipeView,bool newObj=false,QWidget *parent = 0);
TaskPipeOrientation(ViewProviderPipe *PipeView, bool newObj=false, QWidget *parent = 0);
virtual ~TaskPipeOrientation();
private Q_SLOTS:
void onOrientationChanged(int);
void onButtonRefAdd(bool checked);
@@ -109,20 +112,18 @@ private Q_SLOTS:
void onCurvelinearChanged(bool checked);
void onBinormalChanged(double);
void onDeleteItem();
protected:
enum selectionModes { none, refAdd, refRemove, refObjAdd };
selectionModes selectionMode = none;
void removeFromListWidget(QListWidget*w, QString name);
bool referenceSelected(const Gui::SelectionChanges& msg) const;
private:
void onSelectionChanged(const Gui::SelectionChanges& msg);
void clearButtons();
void clearButtons(const selectionModes notThis=none);
void exitSelectionMode();
bool auxSpineShow = false;
private:
QWidget* proxy;
@@ -144,17 +145,17 @@ private Q_SLOTS:
void onButtonRefRemove(bool checked);
void updateUI(int idx);
void onDeleteSection();
protected:
enum selectionModes { none, refAdd, refRemove };
selectionModes selectionMode = none;
void removeFromListWidget(QListWidget*w, QString name);
bool referenceSelected(const Gui::SelectionChanges& msg) const;
private:
void onSelectionChanged(const Gui::SelectionChanges& msg);
void clearButtons();
void clearButtons(const selectionModes notThis=none);
void exitSelectionMode();
private:

View File

@@ -34,14 +34,15 @@
#include "TaskPocketParameters.h"
#include <App/Application.h>
#include <App/Document.h>
#include <Base/Console.h>
#include <Base/UnitsApi.h>
#include <Gui/Application.h>
#include <Gui/Document.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Command.h>
#include <Gui/Document.h>
#include <Gui/Selection.h>
#include <Gui/ViewProvider.h>
#include <Gui/WaitCursor.h>
#include <Base/Console.h>
#include <Gui/Selection.h>
#include <Gui/Command.h>
#include <Mod/PartDesign/App/FeaturePocket.h>
#include <Mod/Sketcher/App/SketchObject.h>
#include "TaskSketchBasedParameters.h"
@@ -76,6 +77,11 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
Base::Quantity l = pcPocket->Length.getQuantityValue();
Base::Quantity l2 = pcPocket->Length2.getQuantityValue();
Base::Quantity off = pcPocket->Offset.getQuantityValue();
bool alongNormal = pcPocket->AlongSketchNormal.getValue();
bool useCustom = pcPocket->UseCustomVector.getValue();
double xs = pcPocket->Direction.getValue().x;
double ys = pcPocket->Direction.getValue().y;
double zs = pcPocket->Direction.getValue().z;
bool midplane = pcPocket->Midplane.getValue();
bool reversed = pcPocket->Reversed.getValue();
int index = pcPocket->Type.getValue(); // must extract value here, clear() kills it!
@@ -89,10 +95,30 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
faceId = std::atoi(&upToFace[4]);
}
// set decimals for the direction edits
// do this here before the edits are filed to avoid rounding mistakes
int UserDecimals = Base::UnitsApi::getDecimals();
ui->XDirectionEdit->setDecimals(UserDecimals);
ui->YDirectionEdit->setDecimals(UserDecimals);
ui->ZDirectionEdit->setDecimals(UserDecimals);
// Fill data into dialog elements
// the direction combobox is later filled in updateUI()
ui->lengthEdit->setValue(l);
ui->lengthEdit2->setValue(l2);
ui->offsetEdit->setValue(off);
ui->checkBoxAlongDirection->setChecked(alongNormal);
ui->checkBoxDirection->setChecked(useCustom);
onDirectionToggled(useCustom);
// disable to change the direction if not custom
if (!useCustom) {
ui->XDirectionEdit->setEnabled(false);
ui->YDirectionEdit->setEnabled(false);
ui->ZDirectionEdit->setEnabled(false);
}
ui->XDirectionEdit->setValue(xs);
ui->YDirectionEdit->setValue(ys);
ui->ZDirectionEdit->setValue(zs);
ui->checkBoxMidplane->setChecked(midplane);
ui->checkBoxReversed->setChecked(reversed);
@@ -127,6 +153,9 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
ui->lengthEdit->bind(pcPocket->Length);
ui->lengthEdit2->bind(pcPocket->Length2);
ui->offsetEdit->bind(pcPocket->Offset);
ui->XDirectionEdit->bind(App::ObjectIdentifier::parse(pcPocket, std::string("Direction.x")));
ui->YDirectionEdit->bind(App::ObjectIdentifier::parse(pcPocket, std::string("Direction.y")));
ui->ZDirectionEdit->bind(App::ObjectIdentifier::parse(pcPocket, std::string("Direction.z")));
QMetaObject::connectSlotsByName(this);
@@ -136,6 +165,18 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
this, SLOT(onLength2Changed(double)));
connect(ui->offsetEdit, SIGNAL(valueChanged(double)),
this, SLOT(onOffsetChanged(double)));
connect(ui->directionCB, SIGNAL(activated(int)),
this, SLOT(onDirectionCBChanged(int)));
connect(ui->checkBoxAlongDirection, SIGNAL(toggled(bool)),
this, SLOT(onAlongSketchNormalChanged(bool)));
connect(ui->checkBoxDirection, SIGNAL(toggled(bool)),
this, SLOT(onDirectionToggled(bool)));
connect(ui->XDirectionEdit, SIGNAL(valueChanged(double)),
this, SLOT(onXDirectionEditChanged(double)));
connect(ui->YDirectionEdit, SIGNAL(valueChanged(double)),
this, SLOT(onYDirectionEditChanged(double)));
connect(ui->ZDirectionEdit, SIGNAL(valueChanged(double)),
this, SLOT(onZDirectionEditChanged(double)));
connect(ui->checkBoxMidplane, SIGNAL(toggled(bool)),
this, SLOT(onMidplaneChanged(bool)));
connect(ui->checkBoxReversed, SIGNAL(toggled(bool)),
@@ -149,12 +190,14 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
connect(ui->checkBoxUpdateView, SIGNAL(toggled(bool)),
this, SLOT(onUpdateView(bool)));
this->propReferenceAxis = &(pcPocket->ReferenceAxis);
// Due to signals attached after changes took took into effect we should update the UI now.
updateUI(index);
// if it is a newly created object use the last value of the history
// TODO: newObj doesn't supplied normally by any caller (2015-07-24, Fat-Zer)
if(newObj){
if (newObj){
ui->lengthEdit->setToLastUsedValue();
ui->lengthEdit->selectNumber();
ui->lengthEdit2->setToLastUsedValue();
@@ -166,10 +209,13 @@ TaskPocketParameters::TaskPocketParameters(ViewProviderPocket *PocketView,QWidge
void TaskPocketParameters::updateUI(int index)
{
// update direction combobox
fillDirectionCombo();
// disable/hide everything unless we are sure we don't need it
bool isLengthEditVisable = false;
bool isLengthEdit2Visable = false;
bool isOffsetEditVisable = false;
bool isLengthEditVisible = false;
bool isLengthEdit2Visible = false;
bool isOffsetEditVisible = false;
bool isOffsetEditEnabled = true;
bool isMidplateEnabled = false;
bool isReversedEnabled = false;
@@ -177,7 +223,7 @@ void TaskPocketParameters::updateUI(int index)
// dimension
if (index == 0) {
isLengthEditVisable = true;
isLengthEditVisible = true;
ui->lengthEdit->selectNumber();
// Make sure that the spin box has the focus to get key events
// Calling setFocus() directly doesn't work because the spin box is not
@@ -189,14 +235,14 @@ void TaskPocketParameters::updateUI(int index)
}
// through all
else if (index == 1) {
isOffsetEditVisable = true;
isOffsetEditVisible = true;
isOffsetEditEnabled = false; // offset may have some meaning for through all but it doesn't work
isMidplateEnabled = true;
isReversedEnabled = !ui->checkBoxMidplane->isChecked();
}
// up to first
else if (index == 2) {
isOffsetEditVisable = true;
isOffsetEditVisible = true;
isReversedEnabled = true; // Will change the direction it seeks for its first face?
// It may work not quite as expected but useful if sketch oriented upside-down.
// (may happen in bodies)
@@ -204,7 +250,7 @@ void TaskPocketParameters::updateUI(int index)
}
// up to face
else if (index == 3) {
isOffsetEditVisable = true;
isOffsetEditVisible = true;
isReversedEnabled = true;
isFaceEditEnabled = true;
QMetaObject::invokeMethod(ui->lineFaceName, "setFocus", Qt::QueuedConnection);
@@ -214,22 +260,23 @@ void TaskPocketParameters::updateUI(int index)
}
// two dimensions
else {
isLengthEditVisable = true;
isLengthEdit2Visable = true;
isLengthEditVisible = true;
isLengthEdit2Visible = true;
isReversedEnabled = true;
}
ui->lengthEdit->setVisible( isLengthEditVisable );
ui->lengthEdit->setEnabled( isLengthEditVisable );
ui->labelLength->setVisible( isLengthEditVisable );
ui->lengthEdit->setVisible( isLengthEditVisible );
ui->lengthEdit->setEnabled( isLengthEditVisible );
ui->labelLength->setVisible( isLengthEditVisible );
ui->checkBoxAlongDirection->setVisible(isLengthEditVisible);
ui->lengthEdit2->setVisible( isLengthEdit2Visable );
ui->lengthEdit2->setEnabled( isLengthEdit2Visable );
ui->labelLength2->setVisible( isLengthEdit2Visable );
ui->lengthEdit2->setVisible( isLengthEdit2Visible );
ui->lengthEdit2->setEnabled( isLengthEdit2Visible );
ui->labelLength2->setVisible( isLengthEdit2Visible );
ui->offsetEdit->setVisible( isOffsetEditVisable );
ui->offsetEdit->setEnabled( isOffsetEditVisable && isOffsetEditEnabled );
ui->labelOffset->setVisible( isOffsetEditVisable );
ui->offsetEdit->setVisible( isOffsetEditVisible );
ui->offsetEdit->setEnabled( isOffsetEditVisible && isOffsetEditEnabled );
ui->labelOffset->setVisible( isOffsetEditVisible );
ui->checkBoxMidplane->setEnabled( isMidplateEnabled );
@@ -245,21 +292,36 @@ void TaskPocketParameters::updateUI(int index)
void TaskPocketParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
{
if (msg.Type == Gui::SelectionChanges::AddSelection) {
QString refText = onAddSelection(msg);
if (refText.length() > 0) {
ui->lineFaceName->blockSignals(true);
ui->lineFaceName->setText(refText);
ui->lineFaceName->setProperty("FeatureName", QByteArray(msg.pObjectName));
ui->lineFaceName->setProperty("FaceName", QByteArray(msg.pSubName));
ui->lineFaceName->blockSignals(false);
// Turn off reference selection mode
onButtonFace(false);
} else {
ui->lineFaceName->blockSignals(true);
ui->lineFaceName->clear();
ui->lineFaceName->setProperty("FeatureName", QVariant());
ui->lineFaceName->setProperty("FaceName", QVariant());
ui->lineFaceName->blockSignals(false);
// if we have an edge selection for the pocket direction
if (!selectionFace) {
std::vector<std::string> edge;
App::DocumentObject* selObj;
if (getReferencedSelection(vp->getObject(), msg, selObj, edge) && selObj) {
exitSelectionMode();
propReferenceAxis->setValue(selObj, edge);
recomputeFeature();
// update direction combobox
fillDirectionCombo();
}
}
else { // if we have a selection of a face
QString refText = onAddSelection(msg);
if (refText.length() > 0) {
ui->lineFaceName->blockSignals(true);
ui->lineFaceName->setText(refText);
ui->lineFaceName->setProperty("FeatureName", QByteArray(msg.pObjectName));
ui->lineFaceName->setProperty("FaceName", QByteArray(msg.pSubName));
ui->lineFaceName->blockSignals(false);
// Turn off reference selection mode
onButtonFace(false);
}
else {
ui->lineFaceName->blockSignals(true);
ui->lineFaceName->clear();
ui->lineFaceName->setProperty("FeatureName", QVariant());
ui->lineFaceName->setProperty("FaceName", QVariant());
ui->lineFaceName->blockSignals(false);
}
}
} else if (msg.Type == Gui::SelectionChanges::ClrSelection) {
ui->lineFaceName->blockSignals(true);
@@ -291,6 +353,193 @@ void TaskPocketParameters::onOffsetChanged(double len)
recomputeFeature();
}
void TaskPocketParameters::fillDirectionCombo()
{
bool oldVal_blockUpdate = blockUpdate;
blockUpdate = true;
if (axesInList.empty()) {
ui->directionCB->clear();
// add sketch normal
PartDesign::ProfileBased* pcFeat = static_cast<PartDesign::ProfileBased*>(vp->getObject());
Part::Part2DObject* pcSketch = dynamic_cast<Part::Part2DObject*>(pcFeat->Profile.getValue());
if (pcSketch)
addAxisToCombo(pcSketch, "N_Axis", QObject::tr("Sketch normal"));
// add the other entries
addAxisToCombo(0, std::string(), tr("Select reference..."));
// we start with the sketch normal as proposal for the custom direction
if (pcSketch)
addAxisToCombo(pcSketch, "N_Axis", QObject::tr("Custom direction"));
}
// add current link, if not in list
// first, figure out the item number for current axis
int indexOfCurrent = -1;
App::DocumentObject* ax = propReferenceAxis->getValue();
const std::vector<std::string>& subList = propReferenceAxis->getSubValues();
for (size_t i = 0; i < axesInList.size(); i++) {
if (ax == axesInList[i]->getValue() && subList == axesInList[i]->getSubValues()) {
indexOfCurrent = i;
break;
}
}
// if the axis is not yet listed in the combobox
if (indexOfCurrent == -1 && ax) {
assert(subList.size() <= 1);
std::string sub;
if (!subList.empty())
sub = subList[0];
addAxisToCombo(ax, sub, getRefStr(ax, subList));
indexOfCurrent = axesInList.size() - 1;
// the axis is not the normal, thus enable along direction
ui->checkBoxAlongDirection->setEnabled(true);
// we don't have custom direction thus disable its settings
ui->XDirectionEdit->setEnabled(false);
ui->YDirectionEdit->setEnabled(false);
ui->ZDirectionEdit->setEnabled(false);
}
// highlight either current index or set custom direction
PartDesign::Pocket* pcPocket = static_cast<PartDesign::Pocket*>(vp->getObject());
bool hasCustom = pcPocket->UseCustomVector.getValue();
if (indexOfCurrent != -1 && !hasCustom)
ui->directionCB->setCurrentIndex(indexOfCurrent);
if (hasCustom)
ui->directionCB->setCurrentIndex(2);
blockUpdate = oldVal_blockUpdate;
}
void TaskPocketParameters::addAxisToCombo(App::DocumentObject* linkObj,
std::string linkSubname, QString itemText)
{
this->ui->directionCB->addItem(itemText);
this->axesInList.emplace_back(new App::PropertyLinkSub);
App::PropertyLinkSub& lnk = *(axesInList.back());
lnk.setValue(linkObj, std::vector<std::string>(1, linkSubname));
}
void TaskPocketParameters::onDirectionCBChanged(int num)
{
PartDesign::Pocket* pcPocket = static_cast<PartDesign::Pocket*>(vp->getObject());
if (axesInList.empty() || !pcPocket)
return;
App::PropertyLinkSub& lnk = *(axesInList[num]);
if (lnk.getValue() == 0) {
// enter reference selection mode
this->blockConnection(false);
// to distinguish that this is the direction selection
selectionFace = false;
TaskSketchBasedParameters::onSelectReference(true, true, false, true, true);
return;
}
else {
if (!pcPocket->getDocument()->isIn(lnk.getValue())) {
Base::Console().Error("Object was deleted\n");
return;
}
propReferenceAxis->Paste(lnk);
// in case user is in selection mode, but changed his mind before selecting anything
exitSelectionMode();
}
try {
recomputeFeature();
}
catch (const Base::Exception& e) {
e.ReportException();
}
// disable AlongSketchNormal when the direction is already normal
if (num == 0)
ui->checkBoxAlongDirection->setEnabled(false);
else
ui->checkBoxAlongDirection->setEnabled(true);
// if custom direction is used, show it
if (num == 2) {
ui->checkBoxDirection->setChecked(true);
PartDesign::Pocket* pcPocket = static_cast<PartDesign::Pocket*>(vp->getObject());
pcPocket->UseCustomVector.setValue(true);
}
else {
ui->checkBoxDirection->setChecked(false);
pcPocket->UseCustomVector.setValue(false);
}
// if we dont use custom direction, only allow to show its direction
if (num != 2) {
ui->XDirectionEdit->setEnabled(false);
ui->YDirectionEdit->setEnabled(false);
ui->ZDirectionEdit->setEnabled(false);
}
else {
ui->XDirectionEdit->setEnabled(true);
ui->YDirectionEdit->setEnabled(true);
ui->ZDirectionEdit->setEnabled(true);
}
// recompute and update the direction
recomputeFeature();
updateDirectionEdits();
}
void TaskPocketParameters::onAlongSketchNormalChanged(bool on)
{
PartDesign::Pocket* pcPocket = static_cast<PartDesign::Pocket*>(vp->getObject());
pcPocket->AlongSketchNormal.setValue(on);
recomputeFeature();
}
void TaskPocketParameters::onDirectionToggled(bool on)
{
if (on)
ui->groupBoxDirection->show();
else
ui->groupBoxDirection->hide();
}
void TaskPocketParameters::onXDirectionEditChanged(double len)
{
PartDesign::Pocket* pcPocket = static_cast<PartDesign::Pocket*>(vp->getObject());
pcPocket->Direction.setValue(len, pcPocket->Direction.getValue().y, pcPocket->Direction.getValue().z);
recomputeFeature();
// checking for case of a null vector is done in FeaturePocket.cpp
// if there was a null vector, the normal vector of the sketch is used.
// therefore the vector component edits must be updated
updateDirectionEdits();
}
void TaskPocketParameters::onYDirectionEditChanged(double len)
{
PartDesign::Pocket* pcPocket = static_cast<PartDesign::Pocket*>(vp->getObject());
pcPocket->Direction.setValue(pcPocket->Direction.getValue().x, len, pcPocket->Direction.getValue().z);
recomputeFeature();
updateDirectionEdits();
}
void TaskPocketParameters::onZDirectionEditChanged(double len)
{
PartDesign::Pocket* pcPocket = static_cast<PartDesign::Pocket*>(vp->getObject());
pcPocket->Direction.setValue(pcPocket->Direction.getValue().x, pcPocket->Direction.getValue().y, len);
recomputeFeature();
updateDirectionEdits();
}
void TaskPocketParameters::updateDirectionEdits(void)
{
PartDesign::Pocket* pcPocket = static_cast<PartDesign::Pocket*>(vp->getObject());
// we don't want to execute the onChanged edits, but just update their contents
ui->XDirectionEdit->blockSignals(true);
ui->YDirectionEdit->blockSignals(true);
ui->ZDirectionEdit->blockSignals(true);
ui->XDirectionEdit->setValue(pcPocket->Direction.getValue().x);
ui->YDirectionEdit->setValue(pcPocket->Direction.getValue().y);
ui->ZDirectionEdit->setValue(pcPocket->Direction.getValue().z);
ui->XDirectionEdit->blockSignals(false);
ui->YDirectionEdit->blockSignals(false);
ui->ZDirectionEdit->blockSignals(false);
}
void TaskPocketParameters::onMidplaneChanged(bool on)
{
PartDesign::Pocket* pcPocket = static_cast<PartDesign::Pocket*>(vp->getObject());
@@ -394,6 +643,39 @@ double TaskPocketParameters::getOffset(void) const
return ui->offsetEdit->value().getValue();
}
bool TaskPocketParameters::getAlongSketchNormal(void) const
{
return ui->checkBoxAlongDirection->isChecked();
}
bool TaskPocketParameters::getCustom(void) const
{
return ui->checkBoxDirection->isChecked();
}
std::string TaskPocketParameters::getReferenceAxis(void) const
{
std::vector<std::string> sub;
App::DocumentObject* obj;
getReferenceAxis(obj, sub);
return buildLinkSingleSubPythonStr(obj, sub);
}
double TaskPocketParameters::getXDirection(void) const
{
return ui->XDirectionEdit->value();
}
double TaskPocketParameters::getYDirection(void) const
{
return ui->YDirectionEdit->value();
}
double TaskPocketParameters::getZDirection(void) const
{
return ui->ZDirectionEdit->value();
}
bool TaskPocketParameters::getReversed(void) const
{
return ui->checkBoxReversed->isChecked();
@@ -433,9 +715,19 @@ void TaskPocketParameters::changeEvent(QEvent *e)
ui->lengthEdit->blockSignals(true);
ui->lengthEdit2->blockSignals(true);
ui->offsetEdit->blockSignals(true);
ui->XDirectionEdit->blockSignals(true);
ui->YDirectionEdit->blockSignals(true);
ui->ZDirectionEdit->blockSignals(true);
ui->directionCB->blockSignals(true);
int index = ui->directionCB->currentIndex();
ui->directionCB->clear();
ui->directionCB->addItem(tr("Sketch normal"));
ui->directionCB->addItem(tr("Select reference..."));
ui->directionCB->addItem(tr("Custom direction"));
ui->directionCB->setCurrentIndex(index);
ui->lineFaceName->blockSignals(true);
ui->changeMode->blockSignals(true);
int index = ui->changeMode->currentIndex();
index = ui->changeMode->currentIndex();
ui->retranslateUi(proxy);
ui->changeMode->clear();
ui->changeMode->addItem(tr("Dimension"));
@@ -477,6 +769,29 @@ void TaskPocketParameters::changeEvent(QEvent *e)
}
}
void TaskPocketParameters::getReferenceAxis(App::DocumentObject*& obj, std::vector<std::string>& sub) const
{
if (axesInList.empty())
throw Base::RuntimeError("Not initialized!");
int num = ui->directionCB->currentIndex();
const App::PropertyLinkSub& lnk = *(axesInList[num]);
if (lnk.getValue() == 0) {
// Note: Is is possible that a face of an object is directly pocketed without defining a profile shape
obj = nullptr;
sub.clear();
//throw Base::RuntimeError("Still in reference selection mode; reference wasn't selected yet");
}
else {
PartDesign::ProfileBased* pcDirection = static_cast<PartDesign::ProfileBased*>(vp->getObject());
if (!pcDirection->getDocument()->isIn(lnk.getValue()))
throw Base::RuntimeError("Object was deleted");
obj = lnk.getValue();
sub = lnk.getSubValues();
}
}
void TaskPocketParameters::saveHistory(void)
{
// save the user values to history

View File

@@ -55,10 +55,19 @@ public:
virtual void saveHistory() override;
virtual void apply() override;
void fillDirectionCombo();
void addAxisToCombo(App::DocumentObject* linkObj, std::string linkSubname, QString itemText);
private Q_SLOTS:
void onLengthChanged(double);
void onLength2Changed(double);
void onOffsetChanged(double);
void onDirectionCBChanged(int);
void onAlongSketchNormalChanged(bool);
void onDirectionToggled(bool);
void onXDirectionEditChanged(double);
void onYDirectionEditChanged(double);
void onZDirectionEditChanged(double);
void onMidplaneChanged(bool);
void onReversedChanged(bool);
void onButtonFace(const bool pressed = true);
@@ -67,11 +76,19 @@ private Q_SLOTS:
protected:
void changeEvent(QEvent *e) override;
App::PropertyLinkSub* propReferenceAxis;
void getReferenceAxis(App::DocumentObject*& obj, std::vector<std::string>& sub) const;
private:
double getLength(void) const;
double getLength2(void) const;
double getOffset(void) const;
bool getAlongSketchNormal(void) const;
bool getCustom(void) const;
std::string getReferenceAxis(void) const;
double getXDirection(void) const;
double getYDirection(void) const;
double getZDirection(void) const;
int getMode(void) const;
bool getMidplane(void) const;
bool getReversed(void) const;
@@ -79,11 +96,14 @@ private:
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
void updateUI(int index);
void updateDirectionEdits(void);
private:
QWidget* proxy;
std::unique_ptr<Ui_TaskPocketParameters> ui;
double oldLength;
bool selectionFace;
std::vector<std::unique_ptr<App::PropertyLinkSub>> axesInList;
};
/// simulation dialog for the TaskView

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>193</width>
<height>272</height>
<width>300</width>
<height>436</height>
</rect>
</property>
<property name="windowTitle">
@@ -65,6 +65,179 @@
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Direction</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="labelEdge">
<property name="text">
<string>Direction/edge:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="directionCB">
<property name="toolTip">
<string>Set a direction or select an edge
from the model as reference</string>
</property>
<item>
<property name="text">
<string>Sketch normal</string>
</property>
</item>
<item>
<property name="text">
<string>Select reference...</string>
</property>
</item>
<item>
<property name="text">
<string>Custom direction</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxDirection">
<property name="text">
<string>Show direction</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBoxDirection">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Use custom vector for pad direction, otherwise
the sketch plane's normal vector will be used</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<widget class="Gui::DoubleSpinBox" name="XDirectionEdit">
<property name="toolTip">
<string>x-component of direction vector</string>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<double>-100.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelZSkew">
<property name="text">
<string>z</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::DoubleSpinBox" name="YDirectionEdit">
<property name="toolTip">
<string>y-component of direction vector</string>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<double>-100.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelYSkew">
<property name="text">
<string>y</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::DoubleSpinBox" name="ZDirectionEdit">
<property name="toolTip">
<string>z-component of direction vector</string>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="minimum">
<double>-100.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelXSkew">
<property name="text">
<string>x</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxAlongDirection">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>If unchecked, the length will be
measured along the specified direction</string>
</property>
<property name="text">
<string>Length along sketch normal</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxMidplane">
<property name="enabled">
@@ -139,6 +312,11 @@
<extends>QWidget</extends>
<header>Gui/QuantitySpinBox.h</header>
</customwidget>
<customwidget>
<class>Gui::DoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>Gui/SpinBox.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefQuantitySpinBox</class>
<extends>Gui::QuantitySpinBox</extends>

View File

@@ -220,6 +220,20 @@ QString TaskSketchBasedParameters::getFaceReference(const QString& obj, const QS
.arg(QString::fromLatin1(doc->getName()), o, sub);
}
QString TaskSketchBasedParameters::make2DLabel(const App::DocumentObject* section,
const std::vector<std::string>& subValues)
{
if(section->isDerivedFrom(Part::Part2DObject::getClassTypeId()))
return QString::fromUtf8(section->Label.getValue());
else {
if(subValues.empty())
throw Base::ValueError("No valid subelement linked in Part::Feature");
return QString::fromUtf8((std::string(section->getNameInDocument())
+ ":" + subValues[0]).c_str());
}
}
TaskSketchBasedParameters::~TaskSketchBasedParameters()
{
Gui::Selection().rmvSelectionGate();

View File

@@ -61,6 +61,10 @@ protected:
QVariant objectNameByLabel(const QString& label, const QVariant& suggest) const;
QString getFaceReference(const QString& obj, const QString& sub) const;
/// Create a label for the 2D feature: the objects name if it's already 2D,
/// or the subelement's name if the object is a solid.
QString make2DLabel(const App::DocumentObject* section,
const std::vector<std::string>& subValues);
};
class TaskDlgSketchBasedParameters : public PartDesignGui::TaskDlgFeatureParameters

View File

@@ -235,7 +235,7 @@ void ViewProviderAddSub::updateAddSubShapeIndicator() {
void ViewProviderAddSub::updateData(const App::Property* p) {
if(strcmp(p->getName(), "AddSubShape")==0)
if(p->getName() && strcmp(p->getName(), "AddSubShape")==0)
updateAddSubShapeIndicator();
PartDesignGui::ViewProvider::updateData(p);

View File

@@ -44,7 +44,9 @@ public:
virtual bool onDelete(const std::vector<std::string> &);
void highlightReferences(const bool on, bool auxiliary);
virtual bool allowTreeOrderSwap(const App::DocumentObject *, const App::DocumentObject *) const { return false; }
protected:
virtual bool setEdit(int ModNum);
virtual void unsetEdit(int ModNum);

View File

@@ -52,7 +52,9 @@ public:
virtual bool onDelete(const std::vector<std::string> &);
void highlightReferences(Reference mode, bool on);
virtual bool allowTreeOrderSwap(const App::DocumentObject *, const App::DocumentObject *) const { return false; }
protected:
virtual QIcon getIcon(void) const;
virtual bool setEdit(int ModNum);

View File

@@ -0,0 +1,48 @@
/***************************************************************************
* Copyright (c) 2021 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 PARTDESIGN_GLOBAL_H
#define PARTDESIGN_GLOBAL_H
#include <FCGlobal.h>
// PartDesign
#ifndef PartDesignExport
#ifdef PartDesign_EXPORTS
# define PartDesignExport FREECAD_DECL_EXPORT
#else
# define PartDesignExport FREECAD_DECL_IMPORT
#endif
#endif
// PartDesignGui
#ifndef PartDesignGuiExport
#ifdef PartDesignGui_EXPORTS
# define PartDesignGuiExport FREECAD_DECL_EXPORT
#else
# define PartDesignGuiExport FREECAD_DECL_IMPORT
#endif
#endif
#endif //PARTDESIGN_GLOBAL_H

View File

@@ -129,7 +129,7 @@ This is done by analyzing the sketch geometries and constraints</string>
<item row="1" column="0" colspan="2">
<widget class="QPushButton" name="delConstrExtr">
<property name="toolTip">
<string>Deletes constrains refering to external geometry</string>
<string>Deletes constraints referring to external geometry</string>
</property>
<property name="text">
<string>Delete constraints to external geom.</string>

View File

@@ -39,14 +39,17 @@ using namespace TechDrawGui;
qApp->translate("Workbench", "Add Lines");
qApp->translate("Workbench", "Add Vertices");
qApp->translate("Workbench", "TechDraw");
// Translations for View > Toolbars
qApp->translate("Workbench", "TechDraw Pages");
qApp->translate("Workbench", "TechDraw Views");
qApp->translate("Workbench", "TechDraw Clips");
qApp->translate("Workbench", "TechDraw Dimensions");
qApp->translate("Workbench", "TechDraw Tool Attributes");
qApp->translate("Workbench", "TechDraw File Access");
qApp->translate("Workbench", "TechDraw Decoration");
qApp->translate("Workbench", "TechDraw Annotation");
qApp->translate("Workbench", "Views");
qApp->translate("Workbench", "Extensions: Centerlines/Threading");
#endif
TYPESYSTEM_SOURCE(TechDrawGui::Workbench, Gui::StdWorkbench)
@@ -82,7 +85,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const
// toolattributes
Gui::MenuItem* toolattrib = new Gui::MenuItem;
toolattrib->setCommand("Extensions: centerlines and threading");
toolattrib->setCommand("Extensions: Centerlines/Threading");
*toolattrib << "TechDraw_ExtensionCircleCenterLines";
*toolattrib << "TechDraw_ExtensionThreadHoleSide";
*toolattrib << "TechDraw_ExtensionThreadBoltSide";
@@ -198,7 +201,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
// *dims << "TechDraw_Dimension"
Gui::ToolBarItem *attribs = new Gui::ToolBarItem(root);
attribs->setCommand("TechDraw Toolattributes");
attribs->setCommand("TechDraw Tool Attributes");
*attribs << "TechDraw_ExtensionCircleCenterLines";
*attribs << "TechDraw_ExtensionThreadHoleSide";
*attribs << "TechDraw_ExtensionThreadBoltSide";
@@ -280,7 +283,7 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const
// *dims << "TechDraw_Dimension";
Gui::ToolBarItem *attribs = new Gui::ToolBarItem(root);
attribs->setCommand("TechDraw Toolattributes");
attribs->setCommand("TechDraw Tool Attributes");
*attribs << "TechDraw_ExtensionCircleCenterLines";
*attribs << "TechDraw_ExtensionThreadHoleSide";
*attribs << "TechDraw_ExtensionThreadBoltSide";

View File

@@ -13,6 +13,7 @@ SET(Test_SRCS
unittestgui.py
testmakeWireString.py
TestPythonSyntax.py
TreeView.py
)
SOURCE_GROUP("" FILES ${Test_SRCS})

View File

@@ -77,4 +77,5 @@ Gui.addWorkbench(TestWorkbench())
FreeCAD.__unit_test__ += [ "Workbench",
"Menu",
"Menu.MenuDeleteCases",
"Menu.MenuCreateCases" ]
"Menu.MenuCreateCases",
"TreeView"]

244
src/Mod/Test/TreeView.py Normal file
View File

@@ -0,0 +1,244 @@
# TreeView test module
import os
import time
import tempfile
import unittest
import FreeCAD
from PySide import QtCore, QtGui
import FreeCADGui
class TreeViewTestCase(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def getTreeWidget(self):
mainWnd = FreeCADGui.getMainWindow()
treeDock = mainWnd.findChild(QtGui.QDockWidget, "Tree View")
if treeDock is None:
treeDock = mainWnd.findChild(QtGui.QDockWidget, "Combo View")
if not treeDock is None:
tabWidget = treeDock.findChild(QtGui.QTabWidget, "combiTab")
if not tabWidget is None:
tabWidget.setCurrentIndex(0)
self.assertTrue(not treeDock is None, "No Tree View docks available")
treeView = treeDock.findChild(QtGui.QTreeWidget)
self.assertTrue(not treeView is None, "No Tree View widget found")
return treeView
def waitForTreeViewSync(self):
start = time.time()
while time.time() < start + 0.5:
FreeCADGui.updateGui()
def selectDocItem(self, docItem):
treeView = self.getTreeWidget()
if docItem.TypeId == "App::Document":
appNode = treeView.topLevelItem(0)
self.assertTrue(not appNode is None, "No Application top level node")
docNode = next((appNode.child(i) for i in range(appNode.childCount())
if appNode.child(i).text(0) == docItem.Label), None)
self.assertTrue(not docNode is None, "No test Document node")
treeView.setCurrentItem(docNode)
else:
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(docItem)
self.waitForTreeViewSync()
def trySwapOuterTreeViewItems(self, docItem, transposable):
self.selectDocItem(docItem)
treeView = self.getTreeWidget()
selected = treeView.selectedItems()
self.assertTrue(len(selected) == 1,
"Unexpected count of selected items: " + str(len(selected)))
selected = selected[0]
originalState = [ selected.child(i).text(0) for i in range(selected.childCount()) ]
self.assertTrue(len(originalState) >= 1,
"No children found in item " + selected.text(0))
targetState = originalState.copy()
if transposable:
targetState[0], targetState[-1] = targetState[-1], targetState[0]
treeView.setCurrentItem(selected.child(0))
self.waitForTreeViewSync()
# One move down attempt more to test boundary behaviour
for i in range(len(originalState)):
FreeCADGui.runCommand("Std_GroupMoveDown")
self.waitForTreeViewSync()
treeView.setCurrentItem(selected.child(len(originalState) - 2 if len(originalState) > 1 else 0))
self.waitForTreeViewSync()
# One move up attempt more to test boundary behaviour
for i in range(len(originalState) - 1):
FreeCADGui.runCommand("Std_GroupMoveUp")
self.waitForTreeViewSync()
finalState = [ selected.child(i).text(0) for i in range(selected.childCount()) ]
self.assertTrue(targetState == finalState,
"Unexpected final state: %s\nExpected: %s" % (finalState, targetState))
def getChildrenOf(self, docItem):
self.selectDocItem(docItem)
treeView = self.getTreeWidget()
selected = treeView.selectedItems()
self.assertTrue(len(selected) == 1,
"Unexpected count of selected items: " + str(len(selected)))
selected = selected[0]
return [ selected.child(i).text(0) for i in range(selected.childCount()) ]
def testMoveTransposableItems(self):
# Makes sense only if Gui is shown
if not FreeCAD.GuiUp:
return
FreeCAD.TestEnvironment = True
doc = FreeCAD.newDocument("TreeViewTest1")
FreeCAD.setActiveDocument(doc.Name)
box = doc.addObject("Part::Box", "Box")
cyl = doc.addObject("Part::Cylinder", "Cylinder")
sph = doc.addObject("Part::Sphere", "Sphere")
con = doc.addObject("Part::Cone", "Cone")
doc.recompute()
self.trySwapOuterTreeViewItems(doc, True)
grp = doc.addObject("App::DocumentObjectGroup", "Group")
grp.addObjects([ box, cyl, sph, con ])
doc.recompute()
self.trySwapOuterTreeViewItems(grp, True)
del FreeCAD.TestEnvironment
def testMoveUnmovableItems(self):
# Makes sense only if Gui is shown
if not FreeCAD.GuiUp:
return
FreeCAD.TestEnvironment = True
doc = FreeCAD.newDocument("TreeViewTest2")
FreeCAD.setActiveDocument(doc.Name)
sph = doc.addObject("Part::Sphere", "Sphere")
con = doc.addObject("Part::Cone", "Cone")
doc.recompute()
cut = doc.addObject("Part::Cut", "Cut")
cut.Base = sph
cut.Tool = con
doc.recompute()
self.trySwapOuterTreeViewItems(cut, False)
bdy = doc.addObject("PartDesign::Body", "Body")
box = doc.addObject("PartDesign::AdditiveBox", "Box")
bdy.addObject(box)
doc.recompute()
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(bdy)
self.waitForTreeViewSync()
treeView = self.getTreeWidget()
treeView.selectedItems()[0].setExpanded(True)
self.waitForTreeViewSync()
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(doc.Name, bdy.Name, box.Name + ".Face6")
self.waitForTreeViewSync()
cha = bdy.newObject("PartDesign::Chamfer", "Chamfer")
cha.Base = (box, ["Face6"])
doc.recompute()
cyl = doc.addObject("PartDesign::SubtractiveCylinder", "Cylinder")
bdy.addObject(cyl)
doc.recompute()
self.trySwapOuterTreeViewItems(bdy, False)
del FreeCAD.TestEnvironment
def testItemOrderSaveAndRestore(self):
# Makes sense only if Gui is shown
if not FreeCAD.GuiUp:
return
FreeCAD.TestEnvironment = True
doc = FreeCAD.newDocument("TreeViewTest3")
FreeCAD.setActiveDocument(doc.Name)
grp = doc.addObject("App::DocumentObjectGroup", "Group")
box = doc.addObject("Part::Box", "Box")
cyl = doc.addObject("Part::Cylinder", "Cylinder")
sph = doc.addObject("Part::Sphere", "Sphere")
con = doc.addObject("Part::Cone", "Cone")
doc.recompute()
origOrder = self.getChildrenOf(doc)
self.assertTrue(origOrder == ["Group", "Box", "Cylinder", "Sphere", "Cone"])
origOrderFile = tempfile.gettempdir() + os.sep + "TreeViewTest3_1.fcstd"
doc.saveAs(origOrderFile)
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(con)
FreeCADGui.Selection.addSelection(cyl)
self.waitForTreeViewSync()
FreeCADGui.runCommand("Std_GroupMoveUp")
self.waitForTreeViewSync()
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(grp)
FreeCADGui.Selection.addSelection(box)
self.waitForTreeViewSync()
FreeCADGui.runCommand("Std_GroupMoveDown")
self.waitForTreeViewSync()
newOrder = self.getChildrenOf(doc)
self.assertTrue(newOrder == ["Cylinder", "Cone", "Sphere", "Group", "Box"])
newOrderFile = tempfile.gettempdir() + os.sep + "TreeViewTest3_2.fcstd"
doc.saveAs(newOrderFile)
FreeCAD.closeDocument(doc.Name)
self.waitForTreeViewSync()
doc = FreeCAD.open(origOrderFile)
order = self.getChildrenOf(doc)
self.assertTrue(order == origOrder)
doc = FreeCAD.open(newOrderFile)
order = self.getChildrenOf(doc)
self.assertTrue(order == newOrder)
del FreeCAD.TestEnvironment