| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| |
|
| | #include <App/Document.h>
|
| | #include <App/VarSet.h>
|
| | #include <App/Origin.h>
|
| | #include <Base/Placement.h>
|
| |
|
| | #include "Body.h"
|
| | #include "BodyPy.h"
|
| | #include "FeatureBase.h"
|
| | #include "FeatureSketchBased.h"
|
| | #include "FeatureSolid.h"
|
| | #include "FeatureTransformed.h"
|
| | #include "ShapeBinder.h"
|
| |
|
| | using namespace PartDesign;
|
| |
|
| |
|
| | PROPERTY_SOURCE(PartDesign::Body, Part::BodyBase)
|
| |
|
| | Body::Body()
|
| | {
|
| | ADD_PROPERTY_TYPE(AllowCompound, (true), "Base", App::Prop_None, "Allow multiple solids in Body");
|
| |
|
| | _GroupTouched.setStatus(App::Property::Output, true);
|
| | }
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | short Body::mustExecute() const
|
| | {
|
| | if (Tip.isTouched()) {
|
| | return 1;
|
| | }
|
| | return Part::BodyBase::mustExecute();
|
| | }
|
| |
|
| | App::DocumentObject* Body::getPrevSolidFeature(App::DocumentObject* start)
|
| | {
|
| | if (!start) {
|
| | start = Tip.getValue();
|
| | }
|
| |
|
| | if (!start) {
|
| | return nullptr;
|
| | }
|
| |
|
| | if (!hasObject(start)) {
|
| | return nullptr;
|
| | }
|
| |
|
| | const std::vector<App::DocumentObject*>& features = Group.getValues();
|
| |
|
| | auto startIt = std::find(features.rbegin(), features.rend(), start);
|
| | if (startIt == features.rend()) {
|
| | return nullptr;
|
| | }
|
| |
|
| | auto rvIt = std::find_if(startIt + 1, features.rend(), isSolidFeature);
|
| | if (rvIt != features.rend()) {
|
| | return *rvIt;
|
| | }
|
| | return nullptr;
|
| | }
|
| |
|
| | App::DocumentObject* Body::getNextSolidFeature(App::DocumentObject* start)
|
| | {
|
| | if (!start) {
|
| | start = Tip.getValue();
|
| | }
|
| |
|
| | if (!start || !hasObject(start)) {
|
| | return nullptr;
|
| | }
|
| |
|
| | const std::vector<App::DocumentObject*>& features = Group.getValues();
|
| | std::vector<App::DocumentObject*>::const_iterator startIt;
|
| |
|
| | startIt = std::find(features.begin(), features.end(), start);
|
| | if (startIt == features.end()) {
|
| | return nullptr;
|
| | }
|
| |
|
| | startIt++;
|
| | if (startIt == features.end()) {
|
| | return nullptr;
|
| | }
|
| |
|
| | auto rvIt = std::find_if(startIt, features.end(), isSolidFeature);
|
| | if (rvIt != features.end()) {
|
| | return *rvIt;
|
| | }
|
| | return nullptr;
|
| | }
|
| |
|
| | bool Body::isAfterInsertPoint(App::DocumentObject* feature)
|
| | {
|
| | App::DocumentObject* nextSolid = getNextSolidFeature();
|
| | assert(feature);
|
| |
|
| | if (feature == nextSolid) {
|
| | return true;
|
| | }
|
| | else if (!nextSolid) {
|
| | return false;
|
| | }
|
| | else {
|
| | return isAfter(feature, nextSolid);
|
| | }
|
| | }
|
| |
|
| | bool Body::isSolidFeature(const App::DocumentObject* obj)
|
| | {
|
| | if (!obj) {
|
| | return false;
|
| | }
|
| |
|
| | if (obj->isDerivedFrom<PartDesign::Feature>()) {
|
| | if (PartDesign::Feature::isDatum(obj)) {
|
| |
|
| | return false;
|
| | }
|
| | if (auto transFeature = freecad_cast<PartDesign::Transformed*>(obj)) {
|
| |
|
| | return !transFeature->isMultiTransformChild();
|
| | }
|
| | return true;
|
| | }
|
| | return false;
|
| | }
|
| |
|
| | bool Body::isAllowed(const App::DocumentObject* obj)
|
| | {
|
| | if (!obj) {
|
| | return false;
|
| | }
|
| |
|
| |
|
| |
|
| | return (
|
| | obj->isDerivedFrom<PartDesign::Feature>() || obj->isDerivedFrom<Part::Datum>() ||
|
| |
|
| | obj->isDerivedFrom<Part::Part2DObject>() || obj->isDerivedFrom<PartDesign::ShapeBinder>()
|
| | || obj->isDerivedFrom<PartDesign::SubShapeBinder>() ||
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | obj->isDerivedFrom<App::VarSet>() || obj->isDerivedFrom<App::DatumElement>()
|
| | || obj->isDerivedFrom<App::LocalCoordinateSystem>()
|
| | );
|
| | }
|
| |
|
| |
|
| | Body* Body::findBodyOf(const App::DocumentObject* feature)
|
| | {
|
| | if (!feature) {
|
| | return nullptr;
|
| | }
|
| |
|
| | return static_cast<Body*>(BodyBase::findBodyOf(feature));
|
| | }
|
| |
|
| |
|
| | std::vector<App::DocumentObject*> Body::addObject(App::DocumentObject* feature)
|
| | {
|
| | if (!isAllowed(feature)) {
|
| | throw Base::ValueError("Body: object is not allowed");
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | auto* group = App::GroupExtension::getGroupOfObject(feature);
|
| | if (group && group != getExtendedObject()) {
|
| | group->getExtensionByType<GroupExtension>()->removeObject(feature);
|
| | }
|
| |
|
| |
|
| | insertObject(feature, getNextSolidFeature(), false);
|
| |
|
| | if (isSolidFeature(feature)) {
|
| | Tip.setValue(feature);
|
| | }
|
| |
|
| | if (feature->Visibility.getValue() && feature->isDerivedFrom<PartDesign::Feature>()) {
|
| | for (auto obj : Group.getValues()) {
|
| | if (obj->Visibility.getValue() && obj != feature
|
| | && obj->isDerivedFrom<PartDesign::Feature>()) {
|
| | obj->Visibility.setValue(false);
|
| | }
|
| | }
|
| | }
|
| |
|
| | std::vector<App::DocumentObject*> result = {feature};
|
| | return result;
|
| | }
|
| |
|
| | std::vector<App::DocumentObject*> Body::addObjects(std::vector<App::DocumentObject*> objs)
|
| | {
|
| |
|
| | for (auto obj : objs) {
|
| | addObject(obj);
|
| | }
|
| |
|
| | return objs;
|
| | }
|
| |
|
| |
|
| | void Body::insertObject(App::DocumentObject* feature, App::DocumentObject* target, bool after)
|
| | {
|
| | if (target && !hasObject(target)) {
|
| | throw Base::ValueError(
|
| | "Body: the feature we should insert relative to is not part of that body"
|
| | );
|
| | }
|
| |
|
| |
|
| | relinkToOrigin(feature);
|
| |
|
| | std::vector<App::DocumentObject*> model = Group.getValues();
|
| | std::vector<App::DocumentObject*>::iterator insertInto;
|
| |
|
| |
|
| | if (!target) {
|
| | if (after) {
|
| | insertInto = model.begin();
|
| | }
|
| | else {
|
| | insertInto = model.end();
|
| | }
|
| | }
|
| | else {
|
| | std::vector<App::DocumentObject*>::iterator targetIt
|
| | = std::find(model.begin(), model.end(), target);
|
| | assert(targetIt != model.end());
|
| | if (after) {
|
| | insertInto = targetIt + 1;
|
| | }
|
| | else {
|
| | insertInto = targetIt;
|
| | }
|
| | }
|
| |
|
| |
|
| | model.insert(insertInto, feature);
|
| |
|
| | Group.setValues(model);
|
| |
|
| | if (feature->isDerivedFrom<PartDesign::Feature>()) {
|
| | static_cast<PartDesign::Feature*>(feature)->_Body.setValue(this);
|
| | }
|
| |
|
| |
|
| | setBaseProperty(feature);
|
| | }
|
| |
|
| | void Body::setBaseProperty(App::DocumentObject* feature)
|
| | {
|
| | if (Body::isSolidFeature(feature)) {
|
| |
|
| | App::DocumentObject* prevSolidFeature = getPrevSolidFeature(feature);
|
| |
|
| | static_cast<PartDesign::Feature*>(feature)->BaseFeature.setValue(prevSolidFeature);
|
| |
|
| |
|
| | App::DocumentObject* nextSolidFeature = getNextSolidFeature(feature);
|
| | if (nextSolidFeature) {
|
| | assert(nextSolidFeature->isDerivedFrom(PartDesign::Feature::getClassTypeId()));
|
| | static_cast<PartDesign::Feature*>(nextSolidFeature)->BaseFeature.setValue(feature);
|
| | }
|
| | }
|
| | }
|
| |
|
| | std::vector<App::DocumentObject*> Body::removeObject(App::DocumentObject* feature)
|
| | {
|
| |
|
| |
|
| | App::DocumentObject* nextSolidFeature = getNextSolidFeature(feature);
|
| | App::DocumentObject* prevSolidFeature = getPrevSolidFeature(feature);
|
| |
|
| |
|
| |
|
| | if (nextSolidFeature && nextSolidFeature->isDerivedFrom(PartDesign::Feature::getClassTypeId())) {
|
| | auto* nextPD = static_cast<PartDesign::Feature*>(nextSolidFeature);
|
| |
|
| | if (nextPD->BaseFeature.getValue() == feature) {
|
| | nextPD->BaseFeature.setValue(prevSolidFeature);
|
| | }
|
| | }
|
| |
|
| | std::vector<App::DocumentObject*> model = Group.getValues();
|
| | const auto it = std::ranges::find(model, feature);
|
| |
|
| |
|
| | if (Tip.getValue() == feature) {
|
| | if (prevSolidFeature) {
|
| | Tip.setValue(prevSolidFeature);
|
| | }
|
| | else {
|
| | Tip.setValue(nextSolidFeature);
|
| | }
|
| | }
|
| |
|
| |
|
| | if (it != model.end()) {
|
| | model.erase(it);
|
| | Group.setValues(model);
|
| | }
|
| | std::vector<App::DocumentObject*> result = {feature};
|
| | return result;
|
| | }
|
| |
|
| |
|
| | App::DocumentObjectExecReturn* Body::execute()
|
| | {
|
| | Part::BodyBase::execute();
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | App::DocumentObject* tip = Tip.getValue();
|
| |
|
| | Part::TopoShape tipShape;
|
| | if (tip) {
|
| | if (!tip->isDerivedFrom<PartDesign::Feature>()) {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Linked object is not a PartDesign feature")
|
| | );
|
| | }
|
| |
|
| |
|
| | tipShape = static_cast<Part::Feature*>(tip)->Shape.getShape();
|
| |
|
| | if (tipShape.getShape().IsNull()) {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Tip shape is empty")
|
| | );
|
| | }
|
| |
|
| |
|
| | tipShape.transformShape(tipShape.getTransform(), true);
|
| | }
|
| | else {
|
| | tipShape = Part::TopoShape();
|
| | }
|
| |
|
| | Shape.setValue(tipShape);
|
| | return App::DocumentObject::StdReturn;
|
| | }
|
| |
|
| | void Body::onSettingDocument()
|
| | {
|
| |
|
| | if (connection.connected()) {
|
| | connection.disconnect();
|
| | }
|
| |
|
| | Part::BodyBase::onSettingDocument();
|
| | }
|
| |
|
| | void Body::onChanged(const App::Property* prop)
|
| | {
|
| |
|
| | if (!this->isRestoring() && this->getDocument()
|
| | && !this->getDocument()->isPerformingTransaction()) {
|
| | if (prop == &BaseFeature) {
|
| | FeatureBase* bf = nullptr;
|
| | auto first = Group.getValues().empty() ? nullptr : Group.getValues().front();
|
| |
|
| | if (BaseFeature.getValue()) {
|
| |
|
| | if (!first || !first->isDerivedFrom<FeatureBase>()) {
|
| | bf = getDocument()->addObject<FeatureBase>("BaseFeature");
|
| | insertObject(bf, first, false);
|
| |
|
| | if (!Tip.getValue()) {
|
| | Tip.setValue(bf);
|
| | }
|
| | }
|
| | else {
|
| | bf = static_cast<FeatureBase*>(first);
|
| | }
|
| | }
|
| |
|
| | if (bf && (bf->BaseFeature.getValue() != BaseFeature.getValue())) {
|
| | bf->BaseFeature.setValue(BaseFeature.getValue());
|
| | }
|
| | }
|
| | else if (prop == &Group) {
|
| |
|
| | if (BaseFeature.getValue()
|
| | && (Group.getValues().empty()
|
| | || !Group.getValues().front()->isDerivedFrom<FeatureBase>())) {
|
| | BaseFeature.setValue(nullptr);
|
| | }
|
| | }
|
| | else if (prop == &AllowCompound) {
|
| |
|
| |
|
| |
|
| | for (auto feature : getFullModel()) {
|
| | feature->enforceRecompute();
|
| | }
|
| | }
|
| | else if (prop == &ShapeMaterial) {
|
| | std::vector<App::DocumentObject*> features = Group.getValues();
|
| | if (!features.empty()) {
|
| | for (auto it : features) {
|
| | auto feature = dynamic_cast<Part::Feature*>(it);
|
| | if (feature) {
|
| | if (feature->ShapeMaterial.getValue().getUUID()
|
| | != ShapeMaterial.getValue().getUUID()) {
|
| | feature->ShapeMaterial.setValue(ShapeMaterial.getValue());
|
| | }
|
| | }
|
| | }
|
| | }
|
| | }
|
| | }
|
| |
|
| | Part::BodyBase::onChanged(prop);
|
| | }
|
| |
|
| | void Body::setupObject()
|
| | {
|
| | Part::BodyBase::setupObject();
|
| | }
|
| |
|
| | void Body::unsetupObject()
|
| | {
|
| | Part::BodyBase::unsetupObject();
|
| | }
|
| |
|
| | PyObject* Body::getPyObject()
|
| | {
|
| | if (PythonObject.is(Py::_None())) {
|
| |
|
| | PythonObject = Py::Object(new BodyPy(this), true);
|
| | }
|
| | return Py::new_reference_to(PythonObject);
|
| | }
|
| |
|
| | std::vector<std::string> Body::getSubObjects(int reason) const
|
| | {
|
| | if (reason == GS_SELECT && !showTip) {
|
| | return Part::BodyBase::getSubObjects(reason);
|
| | }
|
| | return {};
|
| | }
|
| |
|
| | App::DocumentObject* Body::getSubObject(
|
| | const char* subname,
|
| | PyObject** pyObj,
|
| | Base::Matrix4D* pmat,
|
| | bool transform,
|
| | int depth
|
| | ) const
|
| | {
|
| | while (subname && *subname == '.') {
|
| | ++subname;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | if (subname) {
|
| | const char* firstDot = strchr(subname, '.');
|
| | if (firstDot) {
|
| | const char* secondDot = strchr(firstDot + 1, '.');
|
| | if (secondDot) {
|
| | auto firstObj = Group.find(std::string(subname, firstDot).c_str());
|
| | if (!firstObj || firstObj->isDerivedFrom<PartDesign::Feature>()) {
|
| | auto secondObj = Group.find(std::string(firstDot + 1, secondDot).c_str());
|
| | if (secondObj) {
|
| |
|
| |
|
| | return Part::BodyBase::getSubObject(
|
| | firstDot + 1,
|
| | pyObj,
|
| | pmat,
|
| | transform,
|
| | depth + 1
|
| | );
|
| | }
|
| | }
|
| | }
|
| | }
|
| | }
|
| | #if 1
|
| | return Part::BodyBase::getSubObject(subname, pyObj, pmat, transform, depth);
|
| | #else
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | if (!pyObj || showTip
|
| | || (subname && !Data::ComplexGeoData::isMappedElement(subname) && strchr(subname, '.'))) {
|
| | return Part::BodyBase::getSubObject(subname, pyObj, pmat, transform, depth);
|
| | }
|
| |
|
| |
|
| | for (auto obj : Group.getValues()) {
|
| | if (obj->Visibility.getValue() && obj->isDerivedFrom<PartDesign::Feature>()) {
|
| | return Part::BodyBase::getSubObject(subname, pyObj, pmat, transform, depth);
|
| | }
|
| | }
|
| | if (pmat && transform) {
|
| | *pmat *= Placement.getValue().toMatrix();
|
| | }
|
| | return const_cast<Body*>(this);
|
| | #endif
|
| | }
|
| |
|
| | void Body::onDocumentRestored()
|
| | {
|
| | for (auto obj : Group.getValues()) {
|
| | if (obj->isDerivedFrom<PartDesign::Feature>()) {
|
| | static_cast<PartDesign::Feature*>(obj)->_Body.setValue(this);
|
| | }
|
| | }
|
| | _GroupTouched.setStatus(App::Property::Output, true);
|
| |
|
| |
|
| | if (Tip.getValue()) {
|
| | Tip.touch();
|
| | }
|
| |
|
| | DocumentObject::onDocumentRestored();
|
| | }
|
| |
|
| |
|
| | bool Body::isSolid()
|
| | {
|
| | std::vector<App::DocumentObject*> features = getFullModel();
|
| | for (auto feature : features) {
|
| | if (isSolidFeature(feature)) {
|
| | return true;
|
| | }
|
| | }
|
| | return false;
|
| | }
|
| |
|