| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| |
|
| | #include <Mod/Part/App/FCBRepAlgoAPI_Common.h>
|
| | #include <Mod/Part/App/FCBRepAlgoAPI_Cut.h>
|
| | #include <Mod/Part/App/FCBRepAlgoAPI_Fuse.h>
|
| | #include <Standard_Failure.hxx>
|
| |
|
| |
|
| | #include <App/DocumentObject.h>
|
| | #include <Mod/Part/App/modelRefine.h>
|
| | #include <Mod/Part/App/TopoShapeOpCode.h>
|
| |
|
| | #include "FeatureBoolean.h"
|
| | #include "Body.h"
|
| |
|
| | FC_LOG_LEVEL_INIT("PartDesign", true, true);
|
| |
|
| | using namespace PartDesign;
|
| |
|
| | namespace PartDesign
|
| | {
|
| | extern bool getPDRefineModelParameter();
|
| |
|
| | PROPERTY_SOURCE_WITH_EXTENSIONS(PartDesign::Boolean, PartDesign::FeatureRefine)
|
| |
|
| | const char* Boolean::TypeEnums[] = {"Fuse", "Cut", "Common", nullptr};
|
| |
|
| | Boolean::Boolean()
|
| | {
|
| | ADD_PROPERTY(Type, ((long)0));
|
| | Type.setEnums(TypeEnums);
|
| |
|
| | App::GeoFeatureGroupExtension::initExtension(this);
|
| | }
|
| |
|
| | short Boolean::mustExecute() const
|
| | {
|
| | if (Group.isTouched()) {
|
| | return 1;
|
| | }
|
| | return PartDesign::Feature::mustExecute();
|
| | }
|
| |
|
| | App::DocumentObjectExecReturn* Boolean::execute()
|
| | {
|
| |
|
| | std::string type = Type.getValueAsString();
|
| |
|
| |
|
| | const Part::Feature* baseFeature = this->getBaseObject( true);
|
| |
|
| | if (!baseFeature && type == "Cut") {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Cannot do boolean cut without BaseFeature")
|
| | );
|
| | }
|
| |
|
| | std::vector<App::DocumentObject*> tools = Group.getValues();
|
| | if (tools.empty()) {
|
| | return App::DocumentObject::StdReturn;
|
| | }
|
| |
|
| |
|
| | Part::TopoShape baseTopShape;
|
| | if (baseFeature) {
|
| | baseTopShape = baseFeature->Shape.getShape();
|
| | }
|
| | else {
|
| | auto feature = tools.back();
|
| | if (!feature->isDerivedFrom<Part::Feature>()) {
|
| | return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP(
|
| | "Exception",
|
| | "Cannot do boolean with anything but Part::Feature and its derivatives"
|
| | ));
|
| | }
|
| |
|
| | baseTopShape = static_cast<Part::Feature*>(feature)->Shape.getShape();
|
| | tools.pop_back();
|
| | }
|
| |
|
| | if (baseTopShape.getShape().IsNull()) {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Cannot do boolean operation with invalid base shape")
|
| | );
|
| | }
|
| |
|
| | std::vector<TopoShape> shapes;
|
| | shapes.push_back(baseTopShape);
|
| | for (auto it = tools.begin(); it < tools.end(); ++it) {
|
| | auto shape = getTopoShape(*it, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform);
|
| | if (shape.isNull()) {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Tool shape is null")
|
| | );
|
| | }
|
| | shapes.push_back(shape);
|
| | }
|
| | TopoShape result(baseTopShape);
|
| |
|
| | if (!tools.empty()) {
|
| | const char* op = nullptr;
|
| |
|
| | if (type == "Fuse") {
|
| | op = Part::OpCodes::Fuse;
|
| | }
|
| | else if (type == "Cut") {
|
| | op = Part::OpCodes::Cut;
|
| | }
|
| | else if (type == "Common") {
|
| | op = Part::OpCodes::Common;
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | else {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Unsupported boolean operation")
|
| | );
|
| | }
|
| |
|
| | try {
|
| | result.makeElementBoolean(op, shapes);
|
| | }
|
| | catch (Standard_Failure& e) {
|
| | FC_ERR("Boolean operation failed: " << e.GetMessageString());
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")
|
| | );
|
| | }
|
| | }
|
| |
|
| | result = refineShapeIfActive(result);
|
| |
|
| | if (!isSingleSolidRuleSatisfied(result.getShape())) {
|
| | return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP(
|
| | "Exception",
|
| | "Result has multiple solids: enable 'Allow Compound' in the active body."
|
| | ));
|
| | }
|
| |
|
| | this->Shape.setValue(getSolid(result));
|
| |
|
| | return StdReturn;
|
| | }
|
| |
|
| | void Boolean::updatePreviewShape()
|
| | {
|
| | if (strcmp(Type.getValueAsString(), "Cut") == 0) {
|
| | TopoShape base = getBaseTopoShape(true).moved(getLocation().Inverted());
|
| | TopoShape result = Shape.getShape();
|
| |
|
| | PreviewShape.setValue(base.makeElementCut(result.getShape()));
|
| | return;
|
| | }
|
| |
|
| | if (strcmp(Type.getValueAsString(), "Fuse") == 0) {
|
| |
|
| | if (Group.getValues().empty()) {
|
| | PreviewShape.setValue(Shape.getShape());
|
| | return;
|
| | }
|
| |
|
| | std::vector<TopoShape> shapes;
|
| |
|
| | for (auto& obj : Group.getValues()) {
|
| | shapes.push_back(
|
| | getTopoShape(obj, Part::ShapeOption::ResolveLink | Part::ShapeOption::Transform)
|
| | );
|
| | }
|
| |
|
| | TopoShape result;
|
| | result.makeCompound(shapes);
|
| |
|
| | PreviewShape.setValue(result.getShape());
|
| | return;
|
| | }
|
| |
|
| | PreviewShape.setValue(Shape.getShape());
|
| | }
|
| |
|
| | void Boolean::onChanged(const App::Property* prop)
|
| | {
|
| |
|
| | if (strcmp(prop->getName(), "Group") == 0) {
|
| | touch();
|
| | }
|
| |
|
| | Feature::onChanged(prop);
|
| | }
|
| |
|
| | void Boolean::handleChangedPropertyName(Base::XMLReader& reader, const char* TypeName, const char* PropName)
|
| | {
|
| |
|
| | Base::Type type = Base::Type::fromName(TypeName);
|
| |
|
| | if (Group.getClassTypeId() == type && strcmp(PropName, "Bodies") == 0) {
|
| | Group.Restore(reader);
|
| | }
|
| | }
|
| |
|
| | }
|
| |
|