Merge pull request #12589 from NomAnor/pd-trans-body
Add a new mode to PD patterns that will transform the base feature's shape instead of the tool shapes
This commit is contained in:
@@ -184,35 +184,22 @@ bool Body::isAfterInsertPoint(App::DocumentObject* feature) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Body::isMemberOfMultiTransform(const App::DocumentObject* obj)
|
||||
{
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
// ORIGINAL COMMENT:
|
||||
// This can be recognized because the Originals property is empty (it is contained
|
||||
// in the MultiTransform instead)
|
||||
// COMMENT ON THE COMMENT:
|
||||
// This is wrong because at the creation (addObject) and before assigning the originals, that
|
||||
// is when this code is executed, the originals property is indeed empty.
|
||||
//
|
||||
// However, for the purpose of setting the base feature, the transform feature has been modified
|
||||
// to auto set it when the originals are not null. See:
|
||||
// App::DocumentObjectExecReturn *Transformed::execute(void)
|
||||
//
|
||||
return (obj->isDerivedFrom<PartDesign::Transformed>() &&
|
||||
static_cast<const PartDesign::Transformed*>(obj)->Originals.getValues().empty());
|
||||
}
|
||||
|
||||
bool Body::isSolidFeature(const App::DocumentObject *obj)
|
||||
{
|
||||
if (!obj)
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj->isDerivedFrom<PartDesign::Feature>() &&
|
||||
!PartDesign::Feature::isDatum(obj)) {
|
||||
// Transformed Features inside a MultiTransform are not solid features
|
||||
return !isMemberOfMultiTransform(obj);
|
||||
if (obj->isDerivedFrom<PartDesign::Feature>()) {
|
||||
if (PartDesign::Feature::isDatum(obj)) {
|
||||
// Datum objects are not solid
|
||||
return false;
|
||||
}
|
||||
if (auto transFeature = Base::freecad_dynamic_cast<PartDesign::Transformed>(obj)) {
|
||||
// Transformed Features inside a MultiTransform are not solid features
|
||||
return !transFeature->isMultiTransformChild();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;//DeepSOIC: work-in-progress?
|
||||
}
|
||||
|
||||
@@ -91,9 +91,6 @@ public:
|
||||
*/
|
||||
bool isAfterInsertPoint(App::DocumentObject* feature);
|
||||
|
||||
/// Return true if the given feature is member of a MultiTransform feature
|
||||
static bool isMemberOfMultiTransform(const App::DocumentObject *obj);
|
||||
|
||||
/**
|
||||
* Return true if the given feature is a solid feature allowed in a Body. Currently this is only valid
|
||||
* for features derived from PartDesign::Feature
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <BRepGProp.hxx>
|
||||
# include <GProp_GProps.hxx>
|
||||
# include <Precision.hxx>
|
||||
#include <BRepGProp.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <Precision.hxx>
|
||||
#endif
|
||||
|
||||
#include "FeatureMultiTransform.h"
|
||||
@@ -35,14 +35,15 @@
|
||||
|
||||
using namespace PartDesign;
|
||||
|
||||
namespace PartDesign {
|
||||
namespace PartDesign
|
||||
{
|
||||
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::MultiTransform, PartDesign::Transformed)
|
||||
|
||||
MultiTransform::MultiTransform()
|
||||
{
|
||||
ADD_PROPERTY(Transformations,(nullptr));
|
||||
ADD_PROPERTY(Transformations, (nullptr));
|
||||
Transformations.setSize(0);
|
||||
}
|
||||
|
||||
@@ -51,9 +52,11 @@ void MultiTransform::positionBySupport()
|
||||
PartDesign::Transformed::positionBySupport();
|
||||
std::vector<App::DocumentObject*> transFeatures = Transformations.getValues();
|
||||
for (auto f : transFeatures) {
|
||||
if (!(f->isDerivedFrom<PartDesign::Transformed>()))
|
||||
auto transFeature = Base::freecad_dynamic_cast<PartDesign::Transformed>(f);
|
||||
if (!transFeature) {
|
||||
throw Base::TypeError("Transformation features must be subclasses of Transformed");
|
||||
PartDesign::Transformed* transFeature = static_cast<PartDesign::Transformed*>(f);
|
||||
}
|
||||
|
||||
transFeature->Placement.setValue(this->Placement.getValue());
|
||||
|
||||
// To avoid that a linked transform feature stays touched after a recompute
|
||||
@@ -66,57 +69,57 @@ void MultiTransform::positionBySupport()
|
||||
|
||||
short MultiTransform::mustExecute() const
|
||||
{
|
||||
if (Transformations.isTouched())
|
||||
if (Transformations.isTouched()) {
|
||||
return 1;
|
||||
}
|
||||
return Transformed::mustExecute();
|
||||
}
|
||||
|
||||
const std::list<gp_Trsf> MultiTransform::getTransformations(const std::vector<App::DocumentObject*> originals)
|
||||
const std::list<gp_Trsf>
|
||||
MultiTransform::getTransformations(const std::vector<App::DocumentObject*> originals)
|
||||
{
|
||||
std::vector<App::DocumentObject*> transFeatures = Transformations.getValues();
|
||||
|
||||
// Find centre of gravity of first original
|
||||
// FIXME: This method will NOT give the expected result for more than one original!
|
||||
Part::Feature* originalFeature = static_cast<Part::Feature*>(originals.front());
|
||||
TopoDS_Shape original;
|
||||
gp_Pnt cog;
|
||||
if (!originals.empty()) {
|
||||
// Find centre of gravity of first original
|
||||
// FIXME: This method will NOT give the expected result for more than one original!
|
||||
if (auto addFeature =
|
||||
Base::freecad_dynamic_cast<PartDesign::FeatureAddSub>(originals.front())) {
|
||||
TopoDS_Shape original = addFeature->AddSubShape.getShape().getShape();
|
||||
|
||||
if (originalFeature->isDerivedFrom<PartDesign::FeatureAddSub>()) {
|
||||
PartDesign::FeatureAddSub* addFeature = static_cast<PartDesign::FeatureAddSub*>(originalFeature);
|
||||
//if (addFeature->getAddSubType() == FeatureAddSub::Additive)
|
||||
// original = addFeature->AddSubShape.getShape().getShape();
|
||||
//else
|
||||
original = addFeature->AddSubShape.getShape().getShape();
|
||||
GProp_GProps props;
|
||||
BRepGProp::VolumeProperties(original, props);
|
||||
cog = props.CentreOfMass();
|
||||
}
|
||||
}
|
||||
|
||||
GProp_GProps props;
|
||||
BRepGProp::VolumeProperties(original,props);
|
||||
gp_Pnt cog = props.CentreOfMass();
|
||||
|
||||
std::list<gp_Trsf> result;
|
||||
std::list<gp_Pnt> cogs;
|
||||
std::vector<App::DocumentObject*>::const_iterator f;
|
||||
|
||||
for (f = transFeatures.begin(); f != transFeatures.end(); ++f) {
|
||||
if (!((*f)->isDerivedFrom<PartDesign::Transformed>()))
|
||||
for (auto const& f : transFeatures) {
|
||||
auto transFeature = Base::freecad_dynamic_cast<PartDesign::Transformed>(f);
|
||||
if (!transFeature) {
|
||||
throw Base::TypeError("Transformation features must be subclasses of Transformed");
|
||||
PartDesign::Transformed* transFeature = static_cast<PartDesign::Transformed*>(*f);
|
||||
std::list<gp_Trsf> newTransformations = transFeature->getTransformations(originals);
|
||||
}
|
||||
|
||||
std::list<gp_Trsf> newTransformations = transFeature->getTransformations(originals);
|
||||
if (result.empty()) {
|
||||
// First transformation Feature
|
||||
result = newTransformations;
|
||||
for (std::list<gp_Trsf>::const_iterator nt = newTransformations.begin(); nt != newTransformations.end(); ++nt) {
|
||||
cogs.push_back(cog.Transformed(*nt));
|
||||
for (auto nt : newTransformations) {
|
||||
cogs.push_back(cog.Transformed(nt));
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Retain a copy of the first set of transformations for iterator ot
|
||||
// We can't iterate through result if we are also adding elements with push_back()!
|
||||
std::list<gp_Trsf> oldTransformations;
|
||||
result.swap(oldTransformations); // empty result to receive new transformations
|
||||
result.swap(oldTransformations); // empty result to receive new transformations
|
||||
std::list<gp_Pnt> oldCogs;
|
||||
cogs.swap(oldCogs); // empty cogs to receive new cogs
|
||||
cogs.swap(oldCogs); // empty cogs to receive new cogs
|
||||
|
||||
if ((*f)->is<PartDesign::Scaled>()) {
|
||||
if (transFeature->is<PartDesign::Scaled>()) {
|
||||
// Diagonal method
|
||||
// Multiply every element in the old transformations' slices with the corresponding
|
||||
// element in the newTransformations. Example:
|
||||
@@ -126,57 +129,64 @@ const std::list<gp_Trsf> MultiTransform::getTransformations(const std::vector<Ap
|
||||
// In other words, the length of the result vector is equal to the length of the
|
||||
// oldTransformations vector
|
||||
|
||||
if (newTransformations.empty())
|
||||
throw Base::ValueError("Number of occurrences must be a divisor of previous number of occurrences");
|
||||
if (oldTransformations.size() % newTransformations.size() != 0)
|
||||
throw Base::ValueError("Number of occurrences must be a divisor of previous number of occurrences");
|
||||
if (newTransformations.empty()) {
|
||||
throw Base::ValueError("Number of occurrences must be a divisor of previous "
|
||||
"number of occurrences");
|
||||
}
|
||||
if (oldTransformations.size() % newTransformations.size() != 0) {
|
||||
throw Base::ValueError("Number of occurrences must be a divisor of previous "
|
||||
"number of occurrences");
|
||||
}
|
||||
|
||||
unsigned sliceLength = oldTransformations.size() / newTransformations.size();
|
||||
std::list<gp_Trsf>::const_iterator ot = oldTransformations.begin();
|
||||
std::list<gp_Pnt>::const_iterator oc = oldCogs.begin();
|
||||
auto ot = oldTransformations.begin();
|
||||
auto oc = oldCogs.begin();
|
||||
|
||||
for (std::list<gp_Trsf>::const_iterator nt = newTransformations.begin(); nt != newTransformations.end(); ++nt) {
|
||||
for (auto const& nt : newTransformations) {
|
||||
for (unsigned s = 0; s < sliceLength; s++) {
|
||||
gp_Trsf trans;
|
||||
double factor = nt->ScaleFactor(); // extract scale factor
|
||||
double factor = nt.ScaleFactor(); // extract scale factor
|
||||
|
||||
if (factor > Precision::Confusion()) {
|
||||
trans.SetScale(*oc, factor); // recreate the scaled transformation to use the correct COG
|
||||
trans.SetScale(*oc, factor); // recreate the scaled transformation to
|
||||
// use the correct COG
|
||||
trans = trans * (*ot);
|
||||
cogs.push_back(*oc); // Scaling does not affect the COG
|
||||
} else {
|
||||
trans = (*nt) * (*ot);
|
||||
cogs.push_back(oc->Transformed(*nt));
|
||||
cogs.push_back(*oc); // Scaling does not affect the COG
|
||||
}
|
||||
else {
|
||||
trans = nt * (*ot);
|
||||
cogs.push_back(oc->Transformed(nt));
|
||||
}
|
||||
result.push_back(trans);
|
||||
++ot;
|
||||
++oc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Multiplication method: Combine the new transformations with the old ones.
|
||||
// All old transformations are multiplied with all new ones, so that the length of the
|
||||
// result vector is the length of the old and new transformations multiplied.
|
||||
// All old transformations are multiplied with all new ones, so that the length of
|
||||
// the result vector is the length of the old and new transformations multiplied.
|
||||
// a11 a12 b1 a11*b1 a12*b1 a11*b2 a12*b2 a11*b3 a12*b3
|
||||
// a21 a22 mul b2 = a21*b1 a22*b1 a21*b2 a22*b2 a21*b3 a22*b3
|
||||
// b3
|
||||
for (std::list<gp_Trsf>::const_iterator nt = newTransformations.begin(); nt != newTransformations.end(); ++nt) {
|
||||
std::list<gp_Pnt>::const_iterator oc = oldCogs.begin();
|
||||
for (auto const& nt : newTransformations) {
|
||||
auto oc = oldCogs.begin();
|
||||
|
||||
for (std::list<gp_Trsf>::const_iterator ot = oldTransformations.begin(); ot != oldTransformations.end(); ++ot) {
|
||||
result.push_back((*nt) * (*ot));
|
||||
cogs.push_back(oc->Transformed(*nt));
|
||||
for (auto const& ot : oldTransformations) {
|
||||
result.push_back(nt * ot);
|
||||
cogs.push_back(oc->Transformed(nt));
|
||||
++oc;
|
||||
}
|
||||
}
|
||||
}
|
||||
// What about the Additive method: Take the last (set of) transformations and use them as
|
||||
// "originals" for the next transformationFeature, so that something similar to a sweep
|
||||
// for transformations could be put together?
|
||||
// What about the Additive method: Take the last (set of) transformations and use them
|
||||
// as "originals" for the next transformationFeature, so that something similar to a
|
||||
// sweep for transformations could be put together?
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace PartDesign
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
namespace PartDesign
|
||||
{
|
||||
|
||||
class PartDesignExport MultiTransform : public PartDesign::Transformed
|
||||
class PartDesignExport MultiTransform: public PartDesign::Transformed
|
||||
{
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::MultiTransform);
|
||||
|
||||
@@ -39,30 +39,35 @@ public:
|
||||
|
||||
App::PropertyLinkList Transformations;
|
||||
|
||||
/** @name methods override feature */
|
||||
/** @name methods override feature */
|
||||
//@{
|
||||
short mustExecute() const override;
|
||||
|
||||
/// returns the type name of the view provider
|
||||
const char* getViewProviderName() const override {
|
||||
const char* getViewProviderName() const override
|
||||
{
|
||||
return "PartDesignGui::ViewProviderMultiTransform";
|
||||
}
|
||||
//@}
|
||||
|
||||
std::vector<App::DocumentObject*> getOriginals() const { return Originals.getValues(); }
|
||||
std::vector<App::DocumentObject*> getOriginals() const
|
||||
{
|
||||
return Originals.getValues();
|
||||
}
|
||||
|
||||
/** Create transformations
|
||||
* Returns a list containing the product of all transformations of the subfeatures given
|
||||
* by the Transformations property. Subfeatures can be Mirrored, LinearPattern, PolarPattern and
|
||||
* Scaled.
|
||||
*/
|
||||
const std::list<gp_Trsf> getTransformations(const std::vector<App::DocumentObject*> originals) override;
|
||||
* Returns a list containing the product of all transformations of the subfeatures given
|
||||
* by the Transformations property. Subfeatures can be Mirrored, LinearPattern, PolarPattern and
|
||||
* Scaled.
|
||||
*/
|
||||
const std::list<gp_Trsf>
|
||||
getTransformations(const std::vector<App::DocumentObject*> originals) override;
|
||||
|
||||
protected:
|
||||
void positionBySupport() override;
|
||||
};
|
||||
|
||||
} //namespace PartDesign
|
||||
} // namespace PartDesign
|
||||
|
||||
|
||||
#endif // PARTDESIGN_FeatureMultiTransform_H
|
||||
#endif // PARTDESIGN_FeatureMultiTransform_H
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <BRepGProp.hxx>
|
||||
# include <GProp_GProps.hxx>
|
||||
# include <Precision.hxx>
|
||||
#include <BRepGProp.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <Precision.hxx>
|
||||
#endif
|
||||
|
||||
#include "FeatureScaled.h"
|
||||
@@ -34,58 +34,58 @@
|
||||
|
||||
using namespace PartDesign;
|
||||
|
||||
namespace PartDesign {
|
||||
namespace PartDesign
|
||||
{
|
||||
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::Scaled, PartDesign::Transformed)
|
||||
|
||||
Scaled::Scaled()
|
||||
{
|
||||
ADD_PROPERTY(Factor,(2.0));
|
||||
ADD_PROPERTY(Occurrences,(2));
|
||||
ADD_PROPERTY(Factor, (2.0));
|
||||
ADD_PROPERTY(Occurrences, (2));
|
||||
}
|
||||
|
||||
short Scaled::mustExecute() const
|
||||
{
|
||||
if (Factor.isTouched() ||
|
||||
Occurrences.isTouched())
|
||||
if (Factor.isTouched() || Occurrences.isTouched()) {
|
||||
return 1;
|
||||
}
|
||||
return Transformed::mustExecute();
|
||||
}
|
||||
|
||||
const std::list<gp_Trsf> Scaled::getTransformations(const std::vector<App::DocumentObject*> originals)
|
||||
const std::list<gp_Trsf>
|
||||
Scaled::getTransformations(const std::vector<App::DocumentObject*> originals)
|
||||
{
|
||||
double factor = Factor.getValue();
|
||||
if (factor < Precision::Confusion())
|
||||
double const factor = Factor.getValue();
|
||||
if (factor < Precision::Confusion()) {
|
||||
throw Base::ValueError("Scaling factor too small");
|
||||
int occurrences = Occurrences.getValue();
|
||||
if (occurrences < 2)
|
||||
}
|
||||
int const occurrences = Occurrences.getValue();
|
||||
if (occurrences < 2) {
|
||||
throw Base::ValueError("At least two occurrences required");
|
||||
}
|
||||
|
||||
double f = (factor - 1.0) / double(occurrences - 1);
|
||||
|
||||
// Find centre of gravity of first original
|
||||
// FIXME: This method will NOT give the expected result for more than one original!
|
||||
Part::Feature* originalFeature = static_cast<Part::Feature*>(originals.front());
|
||||
TopoDS_Shape original;
|
||||
gp_Pnt cog;
|
||||
if (!originals.empty()) {
|
||||
// Find centre of gravity of first original
|
||||
// FIXME: This method will NOT give the expected result for more than one original!
|
||||
if (auto feature = Base::freecad_dynamic_cast<PartDesign::FeatureAddSub>(originals.front())) {
|
||||
TopoDS_Shape original = feature->AddSubShape.getShape().getShape();
|
||||
|
||||
if (originalFeature->isDerivedFrom<PartDesign::FeatureAddSub>()) {
|
||||
PartDesign::FeatureAddSub* Feature = static_cast<PartDesign::FeatureAddSub*>(originalFeature);
|
||||
//if(Feature->getAddSubType() == FeatureAddSub::Additive)
|
||||
// original = Feature->AddSubShape.getShape().getShape();
|
||||
//else
|
||||
original = Feature->AddSubShape.getShape().getShape();
|
||||
GProp_GProps props;
|
||||
BRepGProp::VolumeProperties(original, props);
|
||||
cog = props.CentreOfMass();
|
||||
}
|
||||
}
|
||||
|
||||
GProp_GProps props;
|
||||
BRepGProp::VolumeProperties(original,props);
|
||||
gp_Pnt cog = props.CentreOfMass();
|
||||
|
||||
// Note: The original feature is NOT included in the list of transformations! Therefore
|
||||
// we start with occurrence number 1, not number 0
|
||||
std::list<gp_Trsf> transformations;
|
||||
gp_Trsf trans;
|
||||
transformations.push_back(trans); // identity transformation
|
||||
transformations.push_back(trans); // identity transformation
|
||||
|
||||
for (int i = 1; i < occurrences; i++) {
|
||||
trans.SetScale(cog, 1.0 + double(i) * f);
|
||||
@@ -95,4 +95,4 @@ const std::list<gp_Trsf> Scaled::getTransformations(const std::vector<App::Docum
|
||||
return transformations;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace PartDesign
|
||||
|
||||
@@ -31,39 +31,41 @@
|
||||
namespace PartDesign
|
||||
{
|
||||
|
||||
class PartDesignExport Scaled : public PartDesign::Transformed
|
||||
class PartDesignExport Scaled: public PartDesign::Transformed
|
||||
{
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::Scaled);
|
||||
|
||||
public:
|
||||
Scaled();
|
||||
|
||||
App::PropertyFloat Factor;
|
||||
App::PropertyFloat Factor;
|
||||
App::PropertyInteger Occurrences;
|
||||
|
||||
/** @name methods override feature */
|
||||
/** @name methods override feature */
|
||||
//@{
|
||||
short mustExecute() const override;
|
||||
|
||||
/// returns the type name of the view provider
|
||||
const char* getViewProviderName() const override {
|
||||
const char* getViewProviderName() const override
|
||||
{
|
||||
return "PartDesignGui::ViewProviderScaled";
|
||||
}
|
||||
//@}
|
||||
|
||||
/** Create transformations
|
||||
* Returns a list containing (Occurrences-1) transformation since the first, untransformed instance
|
||||
* is not counted. Each transformation will scale the shape it is applied to by the factor
|
||||
* (Factor / (Occurrences - 1))
|
||||
* The centre point of the scaling (currently) is the centre of gravity of the first original shape
|
||||
* for this reason we need to pass the list of originals into the feature
|
||||
*/
|
||||
* Returns a list containing (Occurrences-1) transformation since the first, untransformed
|
||||
* instance is not counted. Each transformation will scale the shape it is applied to by the
|
||||
* factor (Factor / (Occurrences - 1)) The centre point of the scaling (currently) is the centre
|
||||
* of gravity of the first original shape for this reason we need to pass the list of originals
|
||||
* into the feature
|
||||
*/
|
||||
// Note: We can't just use the Originals property because this will fail if the Scaled feature
|
||||
// is being used inside a MultiTransform feature
|
||||
const std::list<gp_Trsf> getTransformations(const std::vector<App::DocumentObject*> originals) override;
|
||||
const std::list<gp_Trsf>
|
||||
getTransformations(const std::vector<App::DocumentObject*> originals) override;
|
||||
};
|
||||
|
||||
} //namespace PartDesign
|
||||
} // namespace PartDesign
|
||||
|
||||
|
||||
#endif // PARTDESIGN_FeatureScaled_H
|
||||
#endif // PARTDESIGN_FeatureScaled_H
|
||||
|
||||
@@ -22,17 +22,19 @@
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <Bnd_Box.hxx>
|
||||
# include <BRep_Builder.hxx>
|
||||
# include <BRepAlgoAPI_Cut.hxx>
|
||||
# include <BRepAlgoAPI_Fuse.hxx>
|
||||
# include <BRepBndLib.hxx>
|
||||
# include <BRepBuilderAPI_Copy.hxx>
|
||||
# include <BRepBuilderAPI_Transform.hxx>
|
||||
# include <Precision.hxx>
|
||||
# include <TopExp_Explorer.hxx>
|
||||
#include <Bnd_Box.hxx>
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <BRepAlgoAPI_Cut.hxx>
|
||||
#include <BRepAlgoAPI_Fuse.hxx>
|
||||
#include <BRepBndLib.hxx>
|
||||
#include <BRepBuilderAPI_Copy.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
@@ -43,6 +45,7 @@
|
||||
#include "FeatureTransformed.h"
|
||||
#include "Body.h"
|
||||
#include "FeatureAddSub.h"
|
||||
#include "FeatureMultiTransform.h"
|
||||
#include "FeatureMirrored.h"
|
||||
#include "FeatureLinearPattern.h"
|
||||
#include "FeaturePolarPattern.h"
|
||||
@@ -51,49 +54,68 @@
|
||||
|
||||
using namespace PartDesign;
|
||||
|
||||
namespace PartDesign {
|
||||
namespace PartDesign
|
||||
{
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::Transformed, PartDesign::Feature)
|
||||
|
||||
std::array<char const*, 3> transformModeEnums = {"Transform tool shapes",
|
||||
"Transform body",
|
||||
nullptr};
|
||||
|
||||
Transformed::Transformed()
|
||||
{
|
||||
ADD_PROPERTY(Originals,(nullptr));
|
||||
ADD_PROPERTY(Originals, (nullptr));
|
||||
Originals.setSize(0);
|
||||
Placement.setStatus(App::Property::ReadOnly, true);
|
||||
|
||||
ADD_PROPERTY_TYPE(Refine,(0),"Part Design",(App::PropertyType)(App::Prop_None),"Refine shape (clean up redundant edges) after adding/subtracting");
|
||||
ADD_PROPERTY(TransformMode, (static_cast<long>(Mode::TransformToolShapes)));
|
||||
TransformMode.setEnums(transformModeEnums.data());
|
||||
|
||||
//init Refine property
|
||||
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
|
||||
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/PartDesign");
|
||||
ADD_PROPERTY_TYPE(Refine,
|
||||
(0),
|
||||
"Part Design",
|
||||
(App::PropertyType)(App::Prop_None),
|
||||
"Refine shape (clean up redundant edges) after adding/subtracting");
|
||||
|
||||
// init Refine property
|
||||
Base::Reference<ParameterGrp> hGrp = App::GetApplication()
|
||||
.GetUserParameter()
|
||||
.GetGroup("BaseApp")
|
||||
->GetGroup("Preferences")
|
||||
->GetGroup("Mod/PartDesign");
|
||||
this->Refine.setValue(hGrp->GetBool("RefineModel", false));
|
||||
}
|
||||
|
||||
void Transformed::positionBySupport()
|
||||
{
|
||||
// TODO May be here better to throw exception (silent=false) (2015-07-27, Fat-Zer)
|
||||
Part::Feature *support = getBaseObject(/* silent =*/ true);
|
||||
if (support)
|
||||
Part::Feature* support = getBaseObject(/* silent =*/true);
|
||||
if (support) {
|
||||
this->Placement.setValue(support->Placement.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
Part::Feature* Transformed::getBaseObject(bool silent) const {
|
||||
Part::Feature *rv = Feature::getBaseObject(/* silent = */ true);
|
||||
Part::Feature* Transformed::getBaseObject(bool silent) const
|
||||
{
|
||||
Part::Feature* rv = Feature::getBaseObject(/* silent = */ true);
|
||||
if (rv) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
const char* err = nullptr;
|
||||
const std::vector<App::DocumentObject*> & originals = Originals.getValues();
|
||||
// NOTE: may be here supposed to be last origin but in order to keep the old behaviour keep here first
|
||||
const std::vector<App::DocumentObject*>& originals = Originals.getValues();
|
||||
// NOTE: may be here supposed to be last origin but in order to keep the old behaviour keep here
|
||||
// first
|
||||
App::DocumentObject* firstOriginal = originals.empty() ? nullptr : originals.front();
|
||||
if (firstOriginal) {
|
||||
if(firstOriginal->isDerivedFrom(Part::Feature::getClassTypeId())) {
|
||||
rv = static_cast<Part::Feature*>(firstOriginal);
|
||||
} else {
|
||||
err = QT_TRANSLATE_NOOP("Exception", "Transformation feature Linked object is not a Part object");
|
||||
rv = Base::freecad_dynamic_cast<Part::Feature>(firstOriginal);
|
||||
if (!rv) {
|
||||
err = QT_TRANSLATE_NOOP("Exception",
|
||||
"Transformation feature Linked object is not a Part object");
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
err = QT_TRANSLATE_NOOP("Exception", "No originals linked to the transformed feature.");
|
||||
}
|
||||
|
||||
@@ -107,49 +129,73 @@ Part::Feature* Transformed::getBaseObject(bool silent) const {
|
||||
App::DocumentObject* Transformed::getSketchObject() const
|
||||
{
|
||||
std::vector<DocumentObject*> originals = Originals.getValues();
|
||||
if (!originals.empty() && originals.front()->isDerivedFrom<PartDesign::ProfileBased>()) {
|
||||
return (static_cast<PartDesign::ProfileBased*>(originals.front()))->getVerifiedSketch(true);
|
||||
DocumentObject const* firstOriginal = !originals.empty() ? originals.front() : nullptr;
|
||||
|
||||
if (auto feature = Base::freecad_dynamic_cast<PartDesign::ProfileBased>(firstOriginal)) {
|
||||
return feature->getVerifiedSketch(true);
|
||||
}
|
||||
else if (!originals.empty() && originals.front()->isDerivedFrom<PartDesign::FeatureAddSub>()) {
|
||||
if (Base::freecad_dynamic_cast<PartDesign::FeatureAddSub>(firstOriginal)) {
|
||||
return nullptr;
|
||||
}
|
||||
else if (this->isDerivedFrom<LinearPattern>()) {
|
||||
// if Originals is empty then try the linear pattern's Direction property
|
||||
const LinearPattern* pattern = static_cast<const LinearPattern*>(this);
|
||||
if (auto pattern = Base::freecad_dynamic_cast<LinearPattern>(this)) {
|
||||
return pattern->Direction.getValue();
|
||||
}
|
||||
else if (this->isDerivedFrom<PolarPattern>()) {
|
||||
// if Originals is empty then try the polar pattern's Axis property
|
||||
const PolarPattern* pattern = static_cast<const PolarPattern*>(this);
|
||||
if (auto pattern = Base::freecad_dynamic_cast<PolarPattern>(this)) {
|
||||
return pattern->Axis.getValue();
|
||||
}
|
||||
else if (this->isDerivedFrom<Mirrored>()) {
|
||||
// if Originals is empty then try the mirror pattern's MirrorPlane property
|
||||
const Mirrored* pattern = static_cast<const Mirrored*>(this);
|
||||
if (auto pattern = Base::freecad_dynamic_cast<Mirrored>(this)) {
|
||||
return pattern->MirrorPlane.getValue();
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Transformed::Restore(Base::XMLReader &reader)
|
||||
void Transformed::Restore(Base::XMLReader& reader)
|
||||
{
|
||||
PartDesign::Feature::Restore(reader);
|
||||
}
|
||||
|
||||
void Transformed::handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, App::Property * prop)
|
||||
bool Transformed::isMultiTransformChild() const
|
||||
{
|
||||
// Checking for a MultiTransform in the dependency list is not reliable on initialization
|
||||
// because the dependencies are only established after creation.
|
||||
/*
|
||||
for (auto const* obj : getInList()) {
|
||||
auto mt = Base::freecad_dynamic_cast<PartDesign::MultiTransform>(obj);
|
||||
if (!mt) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const transfmt = mt->Transformations.getValues();
|
||||
if (std::find(transfmt.begin(), transfmt.end(), this) != transfmt.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// instead check for default property values because these are invalid for a standalone transform feature.
|
||||
// This will mislabel standalone features during the initialization phase.
|
||||
if (TransformMode.getValue() == 0 && Originals.getValue().empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Transformed::handleChangedPropertyType(Base::XMLReader& reader,
|
||||
const char* TypeName,
|
||||
App::Property* prop)
|
||||
{
|
||||
// The property 'Angle' of PolarPattern has changed from PropertyFloat
|
||||
// to PropertyAngle and the property 'Length' has changed to PropertyLength.
|
||||
Base::Type inputType = Base::Type::fromName(TypeName);
|
||||
if (prop->isDerivedFrom<App::PropertyFloat>() &&
|
||||
inputType.isDerivedFrom(App::PropertyFloat::getClassTypeId())) {
|
||||
if (auto property = Base::freecad_dynamic_cast<App::PropertyFloat>(prop);
|
||||
property != nullptr && inputType.isDerivedFrom(App::PropertyFloat::getClassTypeId())) {
|
||||
// Do not directly call the property's Restore method in case the implementation
|
||||
// has changed. So, create a temporary PropertyFloat object and assign the value.
|
||||
App::PropertyFloat floatProp;
|
||||
floatProp.Restore(reader);
|
||||
static_cast<App::PropertyFloat*>(prop)->setValue(floatProp.getValue());
|
||||
property->setValue(floatProp.getValue());
|
||||
}
|
||||
else {
|
||||
PartDesign::Feature::handleChangedPropertyType(reader, TypeName, prop);
|
||||
@@ -158,35 +204,48 @@ void Transformed::handleChangedPropertyType(Base::XMLReader &reader, const char
|
||||
|
||||
short Transformed::mustExecute() const
|
||||
{
|
||||
if (Originals.isTouched())
|
||||
if (Originals.isTouched() || TransformMode.isTouched()) {
|
||||
return 1;
|
||||
}
|
||||
return PartDesign::Feature::mustExecute();
|
||||
}
|
||||
|
||||
App::DocumentObjectExecReturn *Transformed::execute()
|
||||
App::DocumentObjectExecReturn* Transformed::execute()
|
||||
{
|
||||
std::vector<App::DocumentObject*> originals = Originals.getValues();
|
||||
if (originals.empty()) // typically InsideMultiTransform
|
||||
if (isMultiTransformChild()) {
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
if(!this->BaseFeature.getValue()) {
|
||||
auto const mode = static_cast<Mode>(TransformMode.getValue());
|
||||
if (mode == Mode::TransformBody) {
|
||||
Originals.setValues({});
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> originals = Originals.getValues();
|
||||
// Remove suppressed features from the list so the transformations behave as if they are not
|
||||
// there
|
||||
{
|
||||
auto eraseIter =
|
||||
std::remove_if(originals.begin(), originals.end(), [](App::DocumentObject const* obj) {
|
||||
auto feature = Base::freecad_dynamic_cast<PartDesign::Feature>(obj);
|
||||
return feature != nullptr && feature->Suppressed.getValue();
|
||||
});
|
||||
originals.erase(eraseIter, originals.end());
|
||||
}
|
||||
|
||||
if (mode == Mode::TransformToolShapes && originals.empty()) {
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
if (!this->BaseFeature.getValue()) {
|
||||
auto body = getFeatureBody();
|
||||
if(body) {
|
||||
if (body) {
|
||||
body->setBaseProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
this->positionBySupport();
|
||||
|
||||
// Remove suppressed features from the list so the transformations behave as if they are not there
|
||||
{
|
||||
auto eraseIter = std::remove_if(originals.begin(), originals.end(), [](App::DocumentObject const* obj) {
|
||||
auto feature = Base::freecad_dynamic_cast<PartDesign::Feature>(obj);
|
||||
return feature != nullptr && feature->Suppressed.getValue();
|
||||
});
|
||||
originals.erase(eraseIter, originals.end());
|
||||
}
|
||||
|
||||
// get transformations from subclass by calling virtual method
|
||||
std::vector<gp_Trsf> transformations;
|
||||
try {
|
||||
@@ -200,21 +259,25 @@ App::DocumentObjectExecReturn *Transformed::execute()
|
||||
return new App::DocumentObjectExecReturn(e.GetMessageString());
|
||||
}
|
||||
|
||||
if (transformations.empty())
|
||||
return App::DocumentObject::StdReturn; // No transformations defined, exit silently
|
||||
if (transformations.empty()) {
|
||||
return App::DocumentObject::StdReturn; // No transformations defined, exit silently
|
||||
}
|
||||
|
||||
// Get the support
|
||||
Part::Feature* supportFeature;
|
||||
Part::Feature* supportFeature = nullptr;
|
||||
|
||||
try {
|
||||
supportFeature = getBaseObject();
|
||||
} catch (Base::Exception& e) {
|
||||
}
|
||||
catch (Base::Exception& e) {
|
||||
return new App::DocumentObjectExecReturn(e.what());
|
||||
}
|
||||
|
||||
const Part::TopoShape& supportTopShape = supportFeature->Shape.getShape();
|
||||
if (supportTopShape.getShape().IsNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Cannot transform invalid support shape"));
|
||||
if (supportTopShape.getShape().IsNull()) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Cannot transform invalid support shape"));
|
||||
}
|
||||
|
||||
// create an untransformed copy of the support shape
|
||||
Part::TopoShape supportShape(supportTopShape);
|
||||
@@ -224,24 +287,23 @@ App::DocumentObjectExecReturn *Transformed::execute()
|
||||
supportShape.setTransform(Base::Matrix4D());
|
||||
TopoDS_Shape support = supportShape.getShape();
|
||||
|
||||
auto getTransformedCompShape = [&](const auto& origShape)
|
||||
{
|
||||
auto getTransformedCompShape = [&](const auto& origShape) {
|
||||
TopTools_ListOfShape shapeTools;
|
||||
std::vector<TopoDS_Shape> shapes;
|
||||
|
||||
std::vector<gp_Trsf>::const_iterator transformIter = transformations.begin();
|
||||
auto transformIter = transformations.cbegin();
|
||||
|
||||
// First transformation is skipped since it should not be part of the toolShape.
|
||||
++transformIter;
|
||||
|
||||
for (; transformIter != transformations.end(); ++transformIter) {
|
||||
// Make an explicit copy of the shape because the "true" parameter to BRepBuilderAPI_Transform
|
||||
// seems to be pretty broken
|
||||
// Make an explicit copy of the shape because the "true" parameter to
|
||||
// BRepBuilderAPI_Transform seems to be pretty broken
|
||||
BRepBuilderAPI_Copy copy(origShape);
|
||||
|
||||
TopoDS_Shape shape = copy.Shape();
|
||||
|
||||
BRepBuilderAPI_Transform mkTrf(shape, *transformIter, false); // No need to copy, now
|
||||
BRepBuilderAPI_Transform mkTrf(shape, *transformIter, false); // No need to copy, now
|
||||
if (!mkTrf.IsDone()) {
|
||||
throw Base::CADKernelError(QT_TRANSLATE_NOOP("Exception", "Transformation failed"));
|
||||
}
|
||||
@@ -250,79 +312,111 @@ App::DocumentObjectExecReturn *Transformed::execute()
|
||||
shapes.emplace_back(shape);
|
||||
}
|
||||
|
||||
for (const auto& shape : shapes)
|
||||
for (const auto& shape : shapes) {
|
||||
shapeTools.Append(shape);
|
||||
}
|
||||
|
||||
return shapeTools;
|
||||
};
|
||||
|
||||
// NOTE: It would be possible to build a compound from all original addShapes/subShapes and then
|
||||
// transform the compounds as a whole. But we choose to apply the transformations to each
|
||||
// Original separately. This way it is easier to discover what feature causes a fuse/cut
|
||||
// to fail. The downside is that performance suffers when there are many originals. But it seems
|
||||
// safe to assume that in most cases there are few originals and many transformations
|
||||
for (auto original : originals)
|
||||
{
|
||||
// Extract the original shape and determine whether to cut or to fuse
|
||||
Part::TopoShape fuseShape;
|
||||
Part::TopoShape cutShape;
|
||||
switch (mode) {
|
||||
case Mode::TransformToolShapes:
|
||||
// NOTE: It would be possible to build a compound from all original addShapes/subShapes
|
||||
// and then transform the compounds as a whole. But we choose to apply the
|
||||
// transformations to each Original separately. This way it is easier to discover what
|
||||
// feature causes a fuse/cut to fail. The downside is that performance suffers when
|
||||
// there are many originals. But it seems safe to assume that in most cases there are
|
||||
// few originals and many transformations
|
||||
for (auto original : originals) {
|
||||
// Extract the original shape and determine whether to cut or to fuse
|
||||
Part::TopoShape fuseShape;
|
||||
Part::TopoShape cutShape;
|
||||
|
||||
if (original->isDerivedFrom<PartDesign::FeatureAddSub>()) {
|
||||
PartDesign::FeatureAddSub* feature = static_cast<PartDesign::FeatureAddSub*>(original);
|
||||
feature->getAddSubShape(fuseShape, cutShape);
|
||||
if (fuseShape.isNull() && cutShape.isNull())
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Shape of additive/subtractive feature is empty"));
|
||||
gp_Trsf trsf = feature->getLocation().Transformation().Multiplied(trsfInv);
|
||||
auto feature = Base::freecad_dynamic_cast<PartDesign::FeatureAddSub>(original);
|
||||
if (!feature) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP(
|
||||
"Exception",
|
||||
"Only additive and subtractive features can be transformed"));
|
||||
}
|
||||
|
||||
feature->getAddSubShape(fuseShape, cutShape);
|
||||
if (fuseShape.isNull() && cutShape.isNull()) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception",
|
||||
"Shape of additive/subtractive feature is empty"));
|
||||
}
|
||||
gp_Trsf trsf = feature->getLocation().Transformation().Multiplied(trsfInv);
|
||||
#ifdef FC_USE_TNP_FIX
|
||||
if (!fuseShape.isNull())
|
||||
fuseShape = fuseShape.makeElementTransform(trsf);
|
||||
if (!cutShape.isNull())
|
||||
cutShape = cutShape.makeElementTransform(trsf);
|
||||
if (!fuseShape.isNull()) {
|
||||
fuseShape = fuseShape.makeElementTransform(trsf);
|
||||
}
|
||||
if (!cutShape.isNull()) {
|
||||
cutShape = cutShape.makeElementTransform(trsf);
|
||||
}
|
||||
#else
|
||||
if (!fuseShape.isNull())
|
||||
fuseShape = fuseShape.makeTransform(trsf);
|
||||
if (!cutShape.isNull())
|
||||
cutShape = cutShape.makeTransform(trsf);
|
||||
if (!fuseShape.isNull()) {
|
||||
fuseShape = fuseShape.makeTransform(trsf);
|
||||
}
|
||||
if (!cutShape.isNull()) {
|
||||
cutShape = cutShape.makeTransform(trsf);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Only additive and subtractive features can be transformed"));
|
||||
}
|
||||
|
||||
TopoDS_Shape current = support;
|
||||
if (!fuseShape.isNull()) {
|
||||
TopTools_ListOfShape shapeArguments;
|
||||
shapeArguments.Append(current);
|
||||
TopTools_ListOfShape shapeTools = getTransformedCompShape(fuseShape.getShape());
|
||||
if (!shapeTools.IsEmpty()) {
|
||||
std::unique_ptr<BRepAlgoAPI_BooleanOperation> mkBool(new BRepAlgoAPI_Fuse());
|
||||
mkBool->SetArguments(shapeArguments);
|
||||
mkBool->SetTools(shapeTools);
|
||||
mkBool->Build();
|
||||
if (!mkBool->IsDone()) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed"));
|
||||
TopoDS_Shape current = support;
|
||||
if (!fuseShape.isNull()) {
|
||||
TopTools_ListOfShape shapeArguments;
|
||||
shapeArguments.Append(current);
|
||||
TopTools_ListOfShape shapeTools = getTransformedCompShape(fuseShape.getShape());
|
||||
if (!shapeTools.IsEmpty()) {
|
||||
BRepAlgoAPI_Fuse mkBool;
|
||||
mkBool.SetArguments(shapeArguments);
|
||||
mkBool.SetTools(shapeTools);
|
||||
mkBool.Build();
|
||||
if (!mkBool.IsDone()) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Boolean operation failed"));
|
||||
}
|
||||
current = mkBool.Shape();
|
||||
}
|
||||
}
|
||||
current = mkBool->Shape();
|
||||
}
|
||||
}
|
||||
if (!cutShape.isNull()) {
|
||||
TopTools_ListOfShape shapeArguments;
|
||||
shapeArguments.Append(current);
|
||||
TopTools_ListOfShape shapeTools = getTransformedCompShape(cutShape.getShape());
|
||||
if (!shapeTools.IsEmpty()) {
|
||||
std::unique_ptr<BRepAlgoAPI_BooleanOperation> mkBool(new BRepAlgoAPI_Cut());
|
||||
mkBool->SetArguments(shapeArguments);
|
||||
mkBool->SetTools(shapeTools);
|
||||
mkBool->Build();
|
||||
if (!mkBool->IsDone()) {
|
||||
return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed"));
|
||||
if (!cutShape.isNull()) {
|
||||
TopTools_ListOfShape shapeArguments;
|
||||
shapeArguments.Append(current);
|
||||
TopTools_ListOfShape shapeTools = getTransformedCompShape(cutShape.getShape());
|
||||
if (!shapeTools.IsEmpty()) {
|
||||
BRepAlgoAPI_Cut mkBool;
|
||||
mkBool.SetArguments(shapeArguments);
|
||||
mkBool.SetTools(shapeTools);
|
||||
mkBool.Build();
|
||||
if (!mkBool.IsDone()) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Boolean operation failed"));
|
||||
}
|
||||
current = mkBool.Shape();
|
||||
}
|
||||
}
|
||||
current = mkBool->Shape();
|
||||
}
|
||||
}
|
||||
|
||||
support = current; // Use result of this operation for fuse/cut of next original
|
||||
support = current; // Use result of this operation for fuse/cut of next original
|
||||
}
|
||||
break;
|
||||
case Mode::TransformBody: {
|
||||
TopTools_ListOfShape shapeArguments;
|
||||
shapeArguments.Append(support);
|
||||
TopTools_ListOfShape shapeTools = getTransformedCompShape(support);
|
||||
if (!shapeTools.IsEmpty()) {
|
||||
BRepAlgoAPI_Fuse mkBool;
|
||||
mkBool.SetArguments(shapeArguments);
|
||||
mkBool.SetTools(shapeTools);
|
||||
mkBool.Build();
|
||||
if (!mkBool.IsDone()) {
|
||||
return new App::DocumentObjectExecReturn(
|
||||
QT_TRANSLATE_NOOP("Exception", "Boolean operation failed"));
|
||||
}
|
||||
support = mkBool.Shape();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
support = refineShapeIfActive(support);
|
||||
@@ -356,78 +450,24 @@ TopoDS_Shape Transformed::refineShapeIfActive(const TopoDS_Shape& oldShape) cons
|
||||
return oldShape;
|
||||
}
|
||||
|
||||
void Transformed::divideTools(const std::vector<TopoDS_Shape> &toolsIn, std::vector<TopoDS_Shape> &individualsOut,
|
||||
TopoDS_Compound &compoundOut) const
|
||||
{
|
||||
using ShapeBoundPair = std::pair<TopoDS_Shape, Bnd_Box>;
|
||||
using PairList = std::list<ShapeBoundPair>;
|
||||
using PairVector = std::vector<ShapeBoundPair>;
|
||||
|
||||
PairList pairList;
|
||||
|
||||
std::vector<TopoDS_Shape>::const_iterator it;
|
||||
for (it = toolsIn.begin(); it != toolsIn.end(); ++it) {
|
||||
Bnd_Box bound;
|
||||
BRepBndLib::Add(*it, bound);
|
||||
bound.SetGap(0.0);
|
||||
ShapeBoundPair temp = std::make_pair(*it, bound);
|
||||
pairList.push_back(temp);
|
||||
}
|
||||
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompound(compoundOut);
|
||||
|
||||
while(!pairList.empty()) {
|
||||
PairVector currentGroup;
|
||||
currentGroup.push_back(pairList.front());
|
||||
pairList.pop_front();
|
||||
PairList::iterator it = pairList.begin();
|
||||
while(it != pairList.end()) {
|
||||
PairVector::const_iterator groupIt;
|
||||
bool found(false);
|
||||
for (groupIt = currentGroup.begin(); groupIt != currentGroup.end(); ++groupIt) {
|
||||
if (!(*it).second.IsOut((*groupIt).second)) {//touching means is out.
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
currentGroup.push_back(*it);
|
||||
pairList.erase(it);
|
||||
it=pairList.begin();
|
||||
continue;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
if (currentGroup.size() == 1) {
|
||||
builder.Add(compoundOut, currentGroup.front().first);
|
||||
}
|
||||
else {
|
||||
PairVector::const_iterator groupIt;
|
||||
for (groupIt = currentGroup.begin(); groupIt != currentGroup.end(); ++groupIt)
|
||||
individualsOut.push_back((*groupIt).first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TopoDS_Shape Transformed::getRemainingSolids(const TopoDS_Shape& shape)
|
||||
{
|
||||
BRep_Builder builder;
|
||||
TopoDS_Compound compShape;
|
||||
builder.MakeCompound(compShape);
|
||||
|
||||
if (shape.IsNull())
|
||||
if (shape.IsNull()) {
|
||||
Standard_Failure::Raise("Shape is null");
|
||||
}
|
||||
TopExp_Explorer xp;
|
||||
xp.Init(shape,TopAbs_SOLID);
|
||||
xp.Init(shape, TopAbs_SOLID);
|
||||
xp.Next(); // skip the first
|
||||
|
||||
for (; xp.More(); xp.Next()) {
|
||||
builder.Add(compShape, xp.Current());
|
||||
}
|
||||
|
||||
return TopoDS_Shape(std::move(compShape));
|
||||
return {std::move(compShape)};
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace PartDesign
|
||||
|
||||
@@ -37,18 +37,25 @@ namespace PartDesign
|
||||
* Abstract superclass of all features that are created by transformation of another feature
|
||||
* Transformations are translation, rotation and mirroring
|
||||
*/
|
||||
class PartDesignExport Transformed : public PartDesign::Feature
|
||||
class PartDesignExport Transformed: public PartDesign::Feature
|
||||
{
|
||||
PROPERTY_HEADER_WITH_OVERRIDE(PartDesign::Transformed);
|
||||
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
TransformToolShapes,
|
||||
TransformBody
|
||||
};
|
||||
|
||||
Transformed();
|
||||
|
||||
/** The shapes to be transformed
|
||||
* if Originals is empty the instance is just a container for storing transformation data
|
||||
*/
|
||||
/** The features to be transformed
|
||||
*/
|
||||
App::PropertyLinkList Originals;
|
||||
|
||||
App::PropertyEnumeration TransformMode;
|
||||
|
||||
App::PropertyBool Refine;
|
||||
|
||||
/**
|
||||
@@ -58,48 +65,54 @@ public:
|
||||
* silently return a nullptr, otherwise throw Base::Exception.
|
||||
* Default is false.
|
||||
*/
|
||||
Part::Feature* getBaseObject(bool silent=false) const override;
|
||||
Part::Feature* getBaseObject(bool silent = false) const override;
|
||||
|
||||
/// Return the sketch of the first original
|
||||
App::DocumentObject* getSketchObject() const;
|
||||
|
||||
/// Return true if this feature is a child of a MultiTransform
|
||||
bool isMultiTransformChild() const;
|
||||
|
||||
/// Get the list of transformations describing the members of the pattern
|
||||
// Note: Only the Scaled feature requires the originals
|
||||
virtual const std::list<gp_Trsf> getTransformations(const std::vector<App::DocumentObject*> /*originals*/) {
|
||||
return std::list<gp_Trsf>(); // Default method
|
||||
virtual const std::list<gp_Trsf>
|
||||
getTransformations(const std::vector<App::DocumentObject*> /*originals*/)
|
||||
{
|
||||
return std::list<gp_Trsf>(); // Default method
|
||||
}
|
||||
|
||||
/** @name methods override feature */
|
||||
/** @name methods override feature */
|
||||
//@{
|
||||
/** Recalculate the feature
|
||||
* Gets the transformations from the virtual getTransformations() method of the sub class
|
||||
* and applies them to every member of Originals. The total number of copies including
|
||||
* the untransformed Originals will be sizeof(Originals) times sizeof(getTransformations())
|
||||
* If Originals is empty, execute() returns immediately without doing anything as
|
||||
* the actual processing will happen in the MultiTransform feature
|
||||
*/
|
||||
App::DocumentObjectExecReturn *execute() override;
|
||||
* Gets the transformations from the virtual getTransformations() method of the sub class
|
||||
* and applies them to every member of Originals. The total number of copies including
|
||||
* the untransformed Originals will be sizeof(Originals) times sizeof(getTransformations())
|
||||
* If Originals is empty, execute() returns immediately without doing anything as
|
||||
* the actual processing will happen in the MultiTransform feature
|
||||
*/
|
||||
App::DocumentObjectExecReturn* execute() override;
|
||||
short mustExecute() const override;
|
||||
//@}
|
||||
|
||||
/** returns the compound of the shapes that were rejected during the last execute
|
||||
* because they did not overlap with the support
|
||||
*/
|
||||
* because they did not overlap with the support
|
||||
*/
|
||||
TopoDS_Shape rejected;
|
||||
|
||||
protected:
|
||||
void Restore(Base::XMLReader &reader) override;
|
||||
void handleChangedPropertyType(Base::XMLReader &reader, const char * TypeName, App::Property * prop) override;
|
||||
void Restore(Base::XMLReader& reader) override;
|
||||
void handleChangedPropertyType(Base::XMLReader& reader,
|
||||
const char* TypeName,
|
||||
App::Property* prop) override;
|
||||
|
||||
virtual void positionBySupport();
|
||||
TopoDS_Shape refineShapeIfActive(const TopoDS_Shape&) const;
|
||||
void divideTools(const std::vector<TopoDS_Shape> &toolsIn, std::vector<TopoDS_Shape> &individualsOut,
|
||||
TopoDS_Compound &compoundOut) const;
|
||||
static TopoDS_Shape getRemainingSolids(const TopoDS_Shape&);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} //namespace PartDesign
|
||||
} // namespace PartDesign
|
||||
|
||||
|
||||
#endif // PARTDESIGN_FeatureTransformed_H
|
||||
#endif // PARTDESIGN_FeatureTransformed_H
|
||||
|
||||
@@ -1981,22 +1981,7 @@ void prepareTransformed(PartDesign::Body *pcActiveBody, Gui::Command* cmd, const
|
||||
{
|
||||
std::string FeatName = cmd->getUniqueObjectName(which.c_str(), pcActiveBody);
|
||||
|
||||
auto accepter = [=](std::vector<App::DocumentObject*> features) -> bool {
|
||||
|
||||
if (features.empty())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto worker = [=](std::vector<App::DocumentObject*> features) {
|
||||
std::stringstream str;
|
||||
str << cmd->getObjectCmd(FeatName.c_str(), pcActiveBody->getDocument()) << ".Originals = [";
|
||||
for (auto feature : features) {
|
||||
str << cmd->getObjectCmd(feature) << ",";
|
||||
}
|
||||
str << "]";
|
||||
|
||||
std::string msg("Make ");
|
||||
msg += which;
|
||||
Gui::Command::openCommand(msg.c_str());
|
||||
@@ -2004,10 +1989,21 @@ void prepareTransformed(PartDesign::Body *pcActiveBody, Gui::Command* cmd, const
|
||||
// FIXME: There seems to be kind of a race condition here, leading to sporadic errors like
|
||||
// Exception (Thu Sep 6 11:52:01 2012): 'App.Document' object has no attribute 'Mirrored'
|
||||
Gui::Command::updateActive(); // Helps to ensure that the object already exists when the next command comes up
|
||||
Gui::Command::doCommand(Gui::Command::Doc, str.str().c_str());
|
||||
|
||||
auto Feat = pcActiveBody->getDocument()->getObject(FeatName.c_str());
|
||||
|
||||
if (features.empty()) {
|
||||
FCMD_OBJ_CMD(Feat, "TransformMode = \"Transform body\"");
|
||||
} else {
|
||||
std::stringstream str;
|
||||
str << "Originals = [";
|
||||
for (auto feature : features) {
|
||||
str << cmd->getObjectCmd(feature) << ",";
|
||||
}
|
||||
str << "]";
|
||||
FCMD_OBJ_CMD(Feat, str.str().c_str());
|
||||
}
|
||||
|
||||
// TODO What is this function supposed to do? (2015-08-05, Fat-Zer)
|
||||
func(Feat, features);
|
||||
|
||||
@@ -2017,44 +2013,7 @@ void prepareTransformed(PartDesign::Body *pcActiveBody, Gui::Command* cmd, const
|
||||
};
|
||||
|
||||
// Get a valid original from the user
|
||||
// First check selections
|
||||
std::vector<App::DocumentObject*> features = cmd->getSelection().getObjectsOfType(PartDesign::Feature::getClassTypeId());
|
||||
// Next create a list of all eligible objects
|
||||
if (features.empty()) {
|
||||
features = cmd->getDocument()->getObjectsOfType(PartDesign::Feature::getClassTypeId());
|
||||
// If there is more than one selected or eligible object, show dialog and let user pick one
|
||||
if (features.size() > 1) {
|
||||
std::vector<PartDesignGui::TaskFeaturePick::featureStatus> status;
|
||||
for (unsigned i = 0; i < features.size(); i++)
|
||||
status.push_back(PartDesignGui::TaskFeaturePick::validFeature);
|
||||
|
||||
Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog();
|
||||
PartDesignGui::TaskDlgFeaturePick* pickDlg = qobject_cast<PartDesignGui::TaskDlgFeaturePick*>(dlg);
|
||||
if (dlg && !pickDlg) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(QObject::tr("A dialog is already open in the task panel"));
|
||||
msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?"));
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::Yes);
|
||||
int ret = msgBox.exec();
|
||||
if (ret == QMessageBox::Yes)
|
||||
Gui::Control().closeDialog();
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
if (dlg)
|
||||
Gui::Control().closeDialog();
|
||||
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Control().showDialog(new PartDesignGui::TaskDlgFeaturePick(features, status, accepter, worker, false));
|
||||
return;
|
||||
} else if (features.empty()) {
|
||||
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No valid features in this document"),
|
||||
QObject::tr("Please create a feature first."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PartDesign::Body* activeBody = PartDesignGui::getBody(true);
|
||||
for (auto feature : features) {
|
||||
@@ -2103,24 +2062,17 @@ void CmdPartDesignMirrored::activated(int iMsg)
|
||||
return;
|
||||
|
||||
Gui::Command* cmd = this;
|
||||
auto worker = [cmd](App::DocumentObject *Feat, std::vector<App::DocumentObject*> features) {
|
||||
|
||||
if (features.empty())
|
||||
return;
|
||||
|
||||
auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector<App::DocumentObject*> features) {
|
||||
bool direction = false;
|
||||
if (features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) {
|
||||
Part::Part2DObject *sketch = (static_cast<PartDesign::ProfileBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
|
||||
if (!features.empty() && features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) {
|
||||
Part::Part2DObject* sketch = (static_cast<PartDesign::ProfileBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
|
||||
if (sketch) {
|
||||
FCMD_OBJ_CMD(Feat,"MirrorPlane = ("<<getObjectCmd(sketch)<<", ['V_Axis'])");
|
||||
direction = true;
|
||||
}
|
||||
}
|
||||
if (!direction) {
|
||||
auto body = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(features.front()));
|
||||
if (body) {
|
||||
FCMD_OBJ_CMD(Feat,"MirrorPlane = ("<<getObjectCmd(body->getOrigin()->getXY())<<", [''])");
|
||||
}
|
||||
FCMD_OBJ_CMD(Feat,"MirrorPlane = ("<<getObjectCmd(pcActiveBody->getOrigin()->getXY())<<", [''])");
|
||||
}
|
||||
|
||||
finishTransformed(cmd, Feat);
|
||||
@@ -2165,13 +2117,9 @@ void CmdPartDesignLinearPattern::activated(int iMsg)
|
||||
return;
|
||||
|
||||
Gui::Command* cmd = this;
|
||||
auto worker = [cmd](App::DocumentObject *Feat, std::vector<App::DocumentObject*> features) {
|
||||
|
||||
if (!Feat || features.empty())
|
||||
return;
|
||||
|
||||
auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector<App::DocumentObject*> features) {
|
||||
bool direction = false;
|
||||
if (features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) {
|
||||
if (!features.empty() && features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) {
|
||||
Part::Part2DObject *sketch = (static_cast<PartDesign::ProfileBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
|
||||
if (sketch) {
|
||||
FCMD_OBJ_CMD(Feat,"Direction = ("<<Gui::Command::getObjectCmd(sketch)<<", ['H_Axis'])");
|
||||
@@ -2179,10 +2127,7 @@ void CmdPartDesignLinearPattern::activated(int iMsg)
|
||||
}
|
||||
}
|
||||
if (!direction) {
|
||||
auto body = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(features.front()));
|
||||
if (body) {
|
||||
FCMD_OBJ_CMD(Feat,"Direction = ("<<Gui::Command::getObjectCmd(body->getOrigin()->getX())<<",[''])");
|
||||
}
|
||||
FCMD_OBJ_CMD(Feat,"Direction = ("<<Gui::Command::getObjectCmd(pcActiveBody->getOrigin()->getX())<<",[''])");
|
||||
}
|
||||
FCMD_OBJ_CMD(Feat,"Length = 100");
|
||||
FCMD_OBJ_CMD(Feat,"Occurrences = 2");
|
||||
@@ -2229,13 +2174,10 @@ void CmdPartDesignPolarPattern::activated(int iMsg)
|
||||
return;
|
||||
|
||||
Gui::Command* cmd = this;
|
||||
auto worker = [cmd](App::DocumentObject *Feat, std::vector<App::DocumentObject*> features) {
|
||||
|
||||
if (!Feat || features.empty())
|
||||
return;
|
||||
auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector<App::DocumentObject*> features) {
|
||||
|
||||
bool direction = false;
|
||||
if (features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) {
|
||||
if (!features.empty() && features.front()->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) {
|
||||
Part::Part2DObject *sketch = (static_cast<PartDesign::ProfileBased*>(features.front()))->getVerifiedSketch(/* silent =*/ true);
|
||||
if (sketch) {
|
||||
FCMD_OBJ_CMD(Feat,"Axis = ("<<Gui::Command::getObjectCmd(sketch)<<",['N_Axis'])");
|
||||
@@ -2243,10 +2185,7 @@ void CmdPartDesignPolarPattern::activated(int iMsg)
|
||||
}
|
||||
}
|
||||
if (!direction) {
|
||||
auto body = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(features.front()));
|
||||
if (body) {
|
||||
FCMD_OBJ_CMD(Feat,"Axis = ("<<Gui::Command::getObjectCmd(body->getOrigin()->getZ())<<",[''])");
|
||||
}
|
||||
FCMD_OBJ_CMD(Feat,"Axis = ("<<Gui::Command::getObjectCmd(pcActiveBody->getOrigin()->getZ())<<",[''])");
|
||||
}
|
||||
|
||||
FCMD_OBJ_CMD(Feat,"Angle = 360");
|
||||
@@ -2293,11 +2232,7 @@ void CmdPartDesignScaled::activated(int iMsg)
|
||||
return;
|
||||
|
||||
Gui::Command* cmd = this;
|
||||
auto worker = [cmd](App::DocumentObject *Feat, std::vector<App::DocumentObject*> features) {
|
||||
|
||||
if (!Feat || features.empty())
|
||||
return;
|
||||
|
||||
auto worker = [cmd](App::DocumentObject *Feat, std::vector<App::DocumentObject*> /*features*/) {
|
||||
FCMD_OBJ_CMD(Feat,"Factor = 2");
|
||||
FCMD_OBJ_CMD(Feat,"Occurrences = 2");
|
||||
|
||||
@@ -2411,11 +2346,7 @@ void CmdPartDesignMultiTransform::activated(int iMsg)
|
||||
} else {
|
||||
|
||||
Gui::Command* cmd = this;
|
||||
auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector<App::DocumentObject*> features) {
|
||||
|
||||
if (!Feat || features.empty())
|
||||
return;
|
||||
|
||||
auto worker = [cmd, pcActiveBody](App::DocumentObject *Feat, std::vector<App::DocumentObject*> /*features*/) {
|
||||
// Make sure the user isn't presented with an empty screen because no transformations are defined yet...
|
||||
App::DocumentObject* prevSolid = pcActiveBody->Tip.getValue();
|
||||
if (prevSolid) {
|
||||
|
||||
@@ -120,8 +120,33 @@ void TaskTransformedParameters::setupUI()
|
||||
|
||||
// Get the feature data
|
||||
auto pcTransformed = static_cast<PartDesign::Transformed*>(getObject());
|
||||
std::vector<App::DocumentObject*> originals = pcTransformed->Originals.getValues();
|
||||
|
||||
using Mode = PartDesign::Transformed::Mode;
|
||||
|
||||
ui->buttonGroupMode->setId(ui->radioTransformBody, static_cast<int>(Mode::TransformBody));
|
||||
ui->buttonGroupMode->setId(ui->radioTransformToolShapes, static_cast<int>(Mode::TransformToolShapes));
|
||||
|
||||
connect(ui->buttonGroupMode,
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
qOverload<int>(&QButtonGroup::buttonClicked),
|
||||
#else
|
||||
&QButtonGroup::idClicked,
|
||||
#endif
|
||||
this,
|
||||
&TaskTransformedParameters::onModeChanged);
|
||||
|
||||
auto const mode = static_cast<Mode>(pcTransformed->TransformMode.getValue());
|
||||
ui->groupFeatureList->setEnabled(mode == Mode::TransformToolShapes);
|
||||
switch (mode) {
|
||||
case Mode::TransformBody:
|
||||
ui->radioTransformBody->setChecked(true);
|
||||
break;
|
||||
case Mode::TransformToolShapes:
|
||||
ui->radioTransformToolShapes->setChecked(true);
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> originals = pcTransformed->Originals.getValues();
|
||||
// Fill data into dialog elements
|
||||
for (auto obj : originals) {
|
||||
if (obj) {
|
||||
@@ -272,6 +297,26 @@ bool TaskTransformedParameters::isEnabledTransaction() const
|
||||
return enableTransaction;
|
||||
}
|
||||
|
||||
void TaskTransformedParameters::onModeChanged(int mode_id)
|
||||
{
|
||||
if (mode_id < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pcTransformed = static_cast<PartDesign::Transformed*>(getObject());
|
||||
pcTransformed->TransformMode.setValue(mode_id);
|
||||
|
||||
using Mode = PartDesign::Transformed::Mode;
|
||||
Mode const mode = static_cast<Mode>(mode_id);
|
||||
|
||||
ui->groupFeatureList->setEnabled(mode == Mode::TransformToolShapes);
|
||||
if (mode == Mode::TransformBody) {
|
||||
ui->listWidgetFeatures->clear();
|
||||
}
|
||||
setupTransaction();
|
||||
recomputeFeature();
|
||||
}
|
||||
|
||||
void TaskTransformedParameters::onButtonAddFeature(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
|
||||
@@ -228,6 +228,7 @@ private Q_SLOTS:
|
||||
void onButtonRemoveFeature(bool checked);
|
||||
void onFeatureDeleted();
|
||||
void indexesMoved();
|
||||
void onModeChanged(int mode_id);
|
||||
|
||||
private:
|
||||
/** Setup the parameter UI.
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>262</width>
|
||||
<height>207</height>
|
||||
<width>297</width>
|
||||
<height>248</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -15,43 +15,102 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonAddFeature">
|
||||
<property name="text">
|
||||
<string>Add feature</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonRemoveFeature">
|
||||
<property name="text">
|
||||
<string>Remove feature</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioTransformBody">
|
||||
<property name="text">
|
||||
<string>Transform body</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroupMode</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioTransformToolShapes">
|
||||
<property name="text">
|
||||
<string>Transform tool shapes</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroupMode</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listWidgetFeatures">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>120</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>List can be reordered by dragging</string>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="groupFeatureList" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonAddFeature">
|
||||
<property name="text">
|
||||
<string>Add feature</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonRemoveFeature">
|
||||
<property name="text">
|
||||
<string>Remove feature</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listWidgetFeatures">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>120</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>List can be reordered by dragging</string>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -70,6 +129,8 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>radioTransformBody</tabstop>
|
||||
<tabstop>radioTransformToolShapes</tabstop>
|
||||
<tabstop>buttonAddFeature</tabstop>
|
||||
<tabstop>buttonRemoveFeature</tabstop>
|
||||
<tabstop>listWidgetFeatures</tabstop>
|
||||
@@ -77,4 +138,7 @@
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="buttonGroupMode"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
||||
Reference in New Issue
Block a user