void ProfileBased::getUpToFace(TopoDS_Face& upToFace, const TopoDS_Shape& support, const TopoDS_Face& supportface, const TopoDS_Shape& sketchshape, const std::string& method, const gp_Dir& dir, const double offset) { if ((method == "UpToLast") || (method == "UpToFirst")) { // Check for valid support object if (support.IsNull()) throw Base::ValueError("SketchBased: Up to face: No support in Sketch and no base feature!"); std::vector<Part::cutFaces> cfaces = Part::findAllFacesCutBy(support, sketchshape, dir); if (cfaces.empty()) throw Base::ValueError("SketchBased: Up to face: No faces found in this direction"); // Find nearest/furthest face std::vector<Part::cutFaces>::const_iterator it, it_near, it_far; it_near = it_far = cfaces.begin(); for (it = cfaces.begin(); it != cfaces.end(); it++) if (it->distsq > it_far->distsq) it_far = it; else if (it->distsq < it_near->distsq) it_near = it; upToFace = (method == "UpToLast" ? it_far->face : it_near->face); } // Check whether the face has limits or not. Unlimited faces have no wire // Note: Datum planes are always unlimited TopExp_Explorer Ex(upToFace,TopAbs_WIRE); if (Ex.More()) { // Remove the limits of the upToFace so that the extrusion works even if sketchshape is larger // than the upToFace bool remove_limits = false; for (Ex.Init(sketchshape,TopAbs_FACE); Ex.More(); Ex.Next()) { // Get outermost wire of sketch face TopoDS_Face sketchface = TopoDS::Face(Ex.Current()); TopoDS_Wire outerWire = ShapeAnalysis::OuterWire(sketchface); if (!checkWireInsideFace(outerWire, upToFace, dir)) { remove_limits = true; break; } } // It must also be checked that all projected inner wires of the upToFace // lie outside the sketch shape. If this is not the case then the sketch // shape is not completely covered by the upToFace. See #0003141 if (!remove_limits) { TopoDS_Wire outerWire = ShapeAnalysis::OuterWire(upToFace); for (Ex.Init(upToFace, TopAbs_WIRE); Ex.More(); Ex.Next()) { if (!outerWire.IsSame(Ex.Current())) { BRepProj_Projection proj(TopoDS::Wire(Ex.Current()), sketchshape, -dir); if (proj.More()) { remove_limits = true; break; } } } } if (remove_limits) { // Note: Using an unlimited face every time gives unnecessary failures for concave faces TopLoc_Location loc = upToFace.Location(); BRepAdaptor_Surface adapt(upToFace, Standard_False); // use the placement of the adapter, not of the upToFace loc = TopLoc_Location(adapt.Trsf()); BRepBuilderAPI_MakeFace mkFace(adapt.Surface().Surface() #if OCC_VERSION_HEX >= 0x060502 , Precision::Confusion() #endif ); if (!mkFace.IsDone()) throw Base::ValueError("SketchBased: Up To Face: Failed to create unlimited face"); upToFace = TopoDS::Face(mkFace.Shape()); upToFace.Location(loc); } } // Check that the upToFace does not intersect the sketch face and // is not parallel to the extrusion direction (for simplicity, supportface is used instead of sketchshape) BRepAdaptor_Surface adapt1(TopoDS::Face(supportface)); BRepAdaptor_Surface adapt2(TopoDS::Face(upToFace)); if (adapt2.GetType() == GeomAbs_Plane) { if (adapt1.Plane().Axis().IsNormal(adapt2.Plane().Axis(), Precision::Confusion())) throw Base::ValueError("SketchBased: Up to face: Must not be parallel to extrusion direction!"); } // We must measure from sketchshape, not supportface, here BRepExtrema_DistShapeShape distSS(sketchshape, upToFace); if (distSS.Value() < Precision::Confusion()) throw Base::ValueError("SketchBased: Up to face: Must not intersect sketch!"); // Move the face in the extrusion direction // TODO: For non-planar faces, we could consider offsetting the surface if (fabs(offset) > Precision::Confusion()) { if (adapt2.GetType() == GeomAbs_Plane) { gp_Trsf mov; mov.SetTranslation(offset * gp_Vec(dir)); TopLoc_Location loc(mov); upToFace.Move(loc); } else { throw Base::TypeError("SketchBased: Up to Face: Offset not supported yet for non-planar faces"); } } }
App::DocumentObjectExecReturn *Pocket::execute(void) { // Handle legacy features, these typically have Type set to 3 (previously NULL, now UpToFace), // empty FaceName (because it didn't exist) and a value for Length if (std::string(Type.getValueAsString()) == "UpToFace" && (UpToFace.getValue() == NULL && Length.getValue() > Precision::Confusion())) Type.setValue("Length"); // Validate parameters double L = Length.getValue(); if ((std::string(Type.getValueAsString()) == "Length") && (L < Precision::Confusion())) return new App::DocumentObjectExecReturn("Pocket: Length of pocket too small"); Part::Part2DObject* sketch = 0; std::vector<TopoDS_Wire> wires; TopoDS_Shape support; try { sketch = getVerifiedSketch(); wires = getSketchWires(); support = getSupportShape(); } catch (const Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } // get the Sketch plane Base::Placement SketchPos = sketch->Placement.getValue(); Base::Rotation SketchOrientation = SketchPos.getRotation(); Base::Vector3d SketchVector(0,0,1); SketchOrientation.multVec(SketchVector,SketchVector); // turn around for pockets SketchVector *= -1; this->positionBySketch(); TopLoc_Location invObjLoc = this->getLocation().Inverted(); try { support.Move(invObjLoc); gp_Dir dir(SketchVector.x,SketchVector.y,SketchVector.z); dir.Transform(invObjLoc.Transformation()); TopoDS_Shape sketchshape = makeFace(wires); if (sketchshape.IsNull()) return new App::DocumentObjectExecReturn("Pocket: Creating a face from sketch failed"); sketchshape.Move(invObjLoc); std::string method(Type.getValueAsString()); if (method == "UpToFirst" || method == "UpToFace") { TopoDS_Face supportface = getSupportFace(); supportface.Move(invObjLoc); // Find a valid face to extrude up to TopoDS_Face upToFace; if (method == "UpToFace") { getUpToFaceFromLinkSub(upToFace, UpToFace); upToFace.Move(invObjLoc); } getUpToFace(upToFace, support, supportface, sketchshape, method, dir); // Special treatment because often the created stand-alone prism is invalid (empty) because // BRepFeat_MakePrism(..., 2, 1) is buggy BRepFeat_MakePrism PrismMaker; PrismMaker.Init(support, sketchshape, supportface, dir, 0, 1); PrismMaker.Perform(upToFace); if (!PrismMaker.IsDone()) return new App::DocumentObjectExecReturn("Pocket: Up to face: Could not extrude the sketch!"); TopoDS_Shape prism = PrismMaker.Shape(); prism = refineShapeIfActive(prism); // And the really expensive way to get the SubShape... BRepAlgoAPI_Cut mkCut(support, prism); if (!mkCut.IsDone()) return new App::DocumentObjectExecReturn("Pocket: Up to face: Could not get SubShape!"); // FIXME: In some cases this affects the Shape property: It is set to the same shape as the SubShape!!!! TopoDS_Shape result = refineShapeIfActive(mkCut.Shape()); this->SubShape.setValue(result); this->Shape.setValue(prism); } else { TopoDS_Shape prism; generatePrism(prism, sketchshape, method, dir, L, 0.0, Midplane.getValue(), Reversed.getValue()); if (prism.IsNull()) return new App::DocumentObjectExecReturn("Pocket: Resulting shape is empty"); // set the subtractive shape property for later usage in e.g. pattern prism = refineShapeIfActive(prism); this->SubShape.setValue(prism); // Cut the SubShape out of the support BRepAlgoAPI_Cut mkCut(support, prism); if (!mkCut.IsDone()) return new App::DocumentObjectExecReturn("Pocket: Cut out of support failed"); TopoDS_Shape result = mkCut.Shape(); // we have to get the solids (fuse sometimes creates compounds) TopoDS_Shape solRes = this->getSolid(result); if (solRes.IsNull()) return new App::DocumentObjectExecReturn("Pocket: Resulting shape is not a solid"); solRes = refineShapeIfActive(solRes); remapSupportShape(solRes); this->Shape.setValue(solRes); } return App::DocumentObject::StdReturn; } catch (Standard_Failure) { Handle_Standard_Failure e = Standard_Failure::Caught(); if (std::string(e->GetMessageString()) == "TopoDS::Face" && (std::string(Type.getValueAsString()) == "UpToFirst" || std::string(Type.getValueAsString()) == "UpToFace")) return new App::DocumentObjectExecReturn("Could not create face from sketch.\n" "Intersecting sketch entities or multiple faces in a sketch are not allowed " "for making a pocket up to a face."); else return new App::DocumentObjectExecReturn(e->GetMessageString()); } catch (Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } }
App::DocumentObjectExecReturn *Pad::execute(void) { // Validate parameters double L = Length.getValue(); if ((std::string(Type.getValueAsString()) == "Length") && (L < Precision::Confusion())) return new App::DocumentObjectExecReturn("Length of pad too small"); double L2 = Length2.getValue(); if ((std::string(Type.getValueAsString()) == "TwoLengths") && (L < Precision::Confusion())) return new App::DocumentObjectExecReturn("Second length of pad too small"); Part::Part2DObject* sketch = 0; std::vector<TopoDS_Wire> wires; try { sketch = getVerifiedSketch(); wires = getSketchWires(); } catch (const Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } TopoDS_Shape support; try { support = getSupportShape(); } catch (const Base::Exception&) { // ignore, because support isn't mandatory support = TopoDS_Shape(); } // get the Sketch plane Base::Placement SketchPos = sketch->Placement.getValue(); Base::Rotation SketchOrientation = SketchPos.getRotation(); Base::Vector3d SketchVector(0,0,1); SketchOrientation.multVec(SketchVector,SketchVector); this->positionBySketch(); TopLoc_Location invObjLoc = this->getLocation().Inverted(); try { support.Move(invObjLoc); gp_Dir dir(SketchVector.x,SketchVector.y,SketchVector.z); dir.Transform(invObjLoc.Transformation()); TopoDS_Shape sketchshape = makeFace(wires); if (sketchshape.IsNull()) return new App::DocumentObjectExecReturn("Pad: Creating a face from sketch failed"); sketchshape.Move(invObjLoc); TopoDS_Shape prism; std::string method(Type.getValueAsString()); if (method == "UpToFirst" || method == "UpToLast" || method == "UpToFace") { TopoDS_Face supportface = getSupportFace(); supportface.Move(invObjLoc); if (Reversed.getValue()) dir.Reverse(); // Find a valid face to extrude up to TopoDS_Face upToFace; if (method == "UpToFace") { getUpToFaceFromLinkSub(upToFace, UpToFace); upToFace.Move(invObjLoc); } getUpToFace(upToFace, support, supportface, sketchshape, method, dir); // A support object is always required and we need to use BRepFeat_MakePrism // Problem: For Pocket/UpToFirst (or an equivalent Pocket/UpToFace) the resulting shape is invalid // because the feature does not add any material. This only happens with the "2" option, though // Note: It might be possible to pass a shell or a compound containing multiple faces // as the Until parameter of Perform() BRepFeat_MakePrism PrismMaker; PrismMaker.Init(support, sketchshape, supportface, dir, 2, 1); PrismMaker.Perform(upToFace); if (!PrismMaker.IsDone()) return new App::DocumentObjectExecReturn("Pad: Up to face: Could not extrude the sketch!"); prism = PrismMaker.Shape(); } else { generatePrism(prism, sketchshape, method, dir, L, L2, Midplane.getValue(), Reversed.getValue()); } if (prism.IsNull()) return new App::DocumentObjectExecReturn("Pad: Resulting shape is empty"); // set the additive shape property for later usage in e.g. pattern this->AddShape.setValue(prism); // if the sketch has a support fuse them to get one result object if (!support.IsNull()) { // Let's call algorithm computing a fuse operation: BRepAlgoAPI_Fuse mkFuse(support, prism); // Let's check if the fusion has been successful if (!mkFuse.IsDone()) return new App::DocumentObjectExecReturn("Pad: Fusion with support failed"); TopoDS_Shape result = mkFuse.Shape(); // we have to get the solids (fuse sometimes creates compounds) TopoDS_Shape solRes = this->getSolid(result); // lets check if the result is a solid if (solRes.IsNull()) return new App::DocumentObjectExecReturn("Pad: Resulting shape is not a solid"); this->Shape.setValue(solRes); } else { this->Shape.setValue(prism); } return App::DocumentObject::StdReturn; } catch (Standard_Failure) { Handle_Standard_Failure e = Standard_Failure::Caught(); if (std::string(e->GetMessageString()) == "TopoDS::Face") return new App::DocumentObjectExecReturn("Could not create face from sketch.\n" "Intersecting sketch entities or multiple faces in a sketch are not allowed."); else return new App::DocumentObjectExecReturn(e->GetMessageString()); } catch (Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } }
App::DocumentObjectExecReturn *Pocket::execute(void) { // Handle legacy features, these typically have Type set to 3 (previously NULL, now UpToFace), // empty FaceName (because it didn't exist) and a value for Length if (std::string(Type.getValueAsString()) == "UpToFace" && (UpToFace.getValue() == NULL && Length.getValue() > Precision::Confusion())) Type.setValue("Length"); // Validate parameters double L = Length.getValue(); if ((std::string(Type.getValueAsString()) == "Length") && (L < Precision::Confusion())) return new App::DocumentObjectExecReturn("Pocket: Length of pocket too small"); Part::Feature* obj = 0; TopoDS_Face face; try { obj = getVerifiedObject(); face = getVerifiedFace(); } catch (const Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } // if the Base property has a valid shape, fuse the prism into it TopoDS_Shape base; try { base = getBaseShape(); } catch (const Base::Exception&) { return new App::DocumentObjectExecReturn("No sketch support and no base shape: Please tell me where to remove the material of the pocket!"); } // get the Sketch plane Base::Placement SketchPos = obj->Placement.getValue(); Base::Vector3d SketchVector = getProfileNormal(); // turn around for pockets SketchVector *= -1; try { this->positionByPrevious(); TopLoc_Location invObjLoc = this->getLocation().Inverted(); base.Move(invObjLoc); gp_Dir dir(SketchVector.x,SketchVector.y,SketchVector.z); dir.Transform(invObjLoc.Transformation()); if (face.IsNull()) return new App::DocumentObjectExecReturn("Pocket: Creating a face from sketch failed"); face.Move(invObjLoc); std::string method(Type.getValueAsString()); if (method == "UpToFirst" || method == "UpToFace") { if (base.IsNull()) return new App::DocumentObjectExecReturn("Pocket: Extruding up to a face is only possible if the sketch is located on a face"); // Note: This will return an unlimited planar face if support is a datum plane TopoDS_Face supportface = getSupportFace(); supportface.Move(invObjLoc); if (Reversed.getValue()) dir.Reverse(); // Find a valid face or datum plane to extrude up to TopoDS_Face upToFace; if (method == "UpToFace") { getUpToFaceFromLinkSub(upToFace, UpToFace); upToFace.Move(invObjLoc); } getUpToFace(upToFace, base, supportface, face, method, dir, Offset.getValue()); // BRepFeat_MakePrism(..., 2, 1) in combination with PerForm(upToFace) is buggy when the // prism that is being created is contained completely inside the base solid // In this case the resulting shape is empty. This is not a problem for the Pad or Pocket itself // but it leads to an invalid SubShape // The bug only occurs when the upToFace is limited (by a wire), not for unlimited upToFace. But // other problems occur with unlimited concave upToFace so it is not an option to always unlimit upToFace // Check supportface for limits, otherwise Perform() throws an exception TopExp_Explorer Ex(supportface,TopAbs_WIRE); if (!Ex.More()) supportface = TopoDS_Face(); BRepFeat_MakePrism PrismMaker; PrismMaker.Init(base, face, supportface, dir, 0, 1); PrismMaker.Perform(upToFace); if (!PrismMaker.IsDone()) return new App::DocumentObjectExecReturn("Pocket: Up to face: Could not extrude the sketch!"); TopoDS_Shape prism = PrismMaker.Shape(); // And the really expensive way to get the SubShape... BRepAlgoAPI_Cut mkCut(base, prism); if (!mkCut.IsDone()) return new App::DocumentObjectExecReturn("Pocket: Up to face: Could not get SubShape!"); // FIXME: In some cases this affects the Shape property: It is set to the same shape as the SubShape!!!! TopoDS_Shape result = refineShapeIfActive(mkCut.Shape()); this->AddSubShape.setValue(result); this->Shape.setValue(prism); } else { TopoDS_Shape prism; generatePrism(prism, face, method, dir, L, 0.0, Midplane.getValue(), Reversed.getValue()); if (prism.IsNull()) return new App::DocumentObjectExecReturn("Pocket: Resulting shape is empty"); // set the subtractive shape property for later usage in e.g. pattern prism = refineShapeIfActive(prism); this->AddSubShape.setValue(prism); // Cut the SubShape out of the base feature BRepAlgoAPI_Cut mkCut(base, prism); if (!mkCut.IsDone()) return new App::DocumentObjectExecReturn("Pocket: Cut out of base feature failed"); TopoDS_Shape result = mkCut.Shape(); // we have to get the solids (fuse sometimes creates compounds) TopoDS_Shape solRes = this->getSolid(result); if (solRes.IsNull()) return new App::DocumentObjectExecReturn("Pocket: Resulting shape is not a solid"); solRes = refineShapeIfActive(solRes); remapSupportShape(solRes); this->Shape.setValue(solRes); } return App::DocumentObject::StdReturn; } catch (Standard_Failure) { Handle_Standard_Failure e = Standard_Failure::Caught(); if (std::string(e->GetMessageString()) == "TopoDS::Face" && (std::string(Type.getValueAsString()) == "UpToFirst" || std::string(Type.getValueAsString()) == "UpToFace")) return new App::DocumentObjectExecReturn("Could not create face from sketch.\n" "Intersecting sketch entities or multiple faces in a sketch are not allowed " "for making a pocket up to a face."); else return new App::DocumentObjectExecReturn(e->GetMessageString()); } catch (Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } }