/****************************************************************************** * Copyright (c)2012 Jan Rheinlaender * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ******************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ # include # include # include # include # include # include # include # include # include #endif #include "FeatureTransformed.h" #include "FeatureMultiTransform.h" #include "FeatureAdditive.h" #include "FeatureSubtractive.h" #include "FeatureMirrored.h" #include using namespace PartDesign; namespace PartDesign { PROPERTY_SOURCE(PartDesign::Transformed, PartDesign::Feature) Transformed::Transformed() : rejected(0) { ADD_PROPERTY(Originals,(0)); Originals.setSize(0); } void Transformed::positionBySupport(void) { Part::Feature *support = static_cast(getSupportObject()); if ((support != NULL) && support->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) this->Placement.setValue(support->Placement.getValue()); } App::DocumentObject* Transformed::getSupportObject() const { if (!Originals.getValues().empty()) return Originals.getValues().front(); else return NULL; } short Transformed::mustExecute() const { if (Originals.isTouched()) return 1; return PartDesign::Feature::mustExecute(); } App::DocumentObjectExecReturn *Transformed::execute(void) { rejected.clear(); std::vector originals = Originals.getValues(); if (originals.empty()) // typically InsideMultiTransform return App::DocumentObject::StdReturn; this->positionBySupport(); // get transformations from subclass by calling virtual method std::list transformations; try { transformations = getTransformations(originals); } catch (Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } if (transformations.empty()) return App::DocumentObject::StdReturn; // No transformations defined, exit silently // Get the support // NOTE: Because of the way we define the support, FeatureTransformed can only work on // one Body feature at a time // TODO: Currently, the support is simply the first Original. Change this to the Body feature later Part::Feature* supportFeature = static_cast(getSupportObject()); const Part::TopoShape& supportTopShape = supportFeature->Shape.getShape(); if (supportTopShape._Shape.IsNull()) return new App::DocumentObjectExecReturn("Cannot transform invalid support shape"); // create an untransformed copy of the support shape Part::TopoShape supportShape(supportTopShape); supportShape.setTransform(Base::Matrix4D()); TopoDS_Shape support = supportShape._Shape; // 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 (std::vector::const_iterator o = originals.begin(); o != originals.end(); o++) { TopoDS_Shape shape; bool fuse; if ((*o)->getTypeId().isDerivedFrom(PartDesign::Additive::getClassTypeId())) { PartDesign::Additive* addFeature = static_cast(*o); shape = addFeature->AddShape.getShape()._Shape; fuse = true; } else if ((*o)->getTypeId().isDerivedFrom(PartDesign::Subtractive::getClassTypeId())) { PartDesign::Subtractive* subFeature = static_cast(*o); shape = subFeature->SubShape.getShape()._Shape; fuse = false; } else { return new App::DocumentObjectExecReturn("Only additive and subtractive features can be transformed"); } // Transform the add/subshape and build a compound from the transformations, BRep_Builder builder; TopoDS_Compound transformedShapes; builder.MakeCompound(transformedShapes); std::vector v_transformedShapes; // collect all the transformed shapes for intersection testing std::list::const_iterator t = transformations.begin(); t++; // Skip first transformation, which is always the identity transformation for (; t != transformations.end(); t++) { // Make an explicit copy of the shape because the "true" parameter to BRepBuilderAPI_Transform // seems to be pretty broken BRepBuilderAPI_Copy copy(shape); shape = copy.Shape(); if (shape.IsNull()) throw Base::Exception("Transformed: Linked shape object is empty"); BRepBuilderAPI_Transform mkTrf(shape, *t, false); // No need to copy, now if (!mkTrf.IsDone()) return new App::DocumentObjectExecReturn("Transformation failed", (*o)); // Check for intersection with support if (!Part::checkIntersection(support, mkTrf.Shape(), false)) { Base::Console().Warning("Transformed shape does not intersect support %s: Removed\n", (*o)->getNameInDocument()); // Note: The removal happens in getSolid() after the fuse rejected.push_back(*t); } builder.Add(transformedShapes, mkTrf.Shape()); v_transformedShapes.push_back(mkTrf.Shape()); /* // Note: This method is only stable for Linear and Polar transformations. No need to // make an explicit copy of the shape, either TopoDS_Shape trfShape = shape.Moved(TopLoc_Location(*t)); // Check for intersection with support Bnd_Box transformed_bb; BRepBndLib::Add(trfShape, transformed_bb); if (support_bb.Distance(transformed_bb) > Precision::Confusion()) { Base::Console().Warning("Transformed shape does not intersect support %s: Removed\n", (*o)->getNameInDocument()); // Note: The removal happens in getSolid() after the fuse } builder.Add(transformedShapes, trfShape); v_transformedShapes.push_back(trfShape); */ } // Check for intersection of the original and the transformed shape for (std::vector::const_iterator s = v_transformedShapes.begin(); s != v_transformedShapes.end(); s++) { // If there is only one transformed feature, this check is not necessary (though it might seem // illogical to the user why we allow overlapping shapes in this case! if (v_transformedShapes.size() == 1) break; if (Part::checkIntersection(shape, *s, false)) return new App::DocumentObjectExecReturn("Transformed objects are overlapping, try using a higher length or reducing the number of occurrences", (*o)); // Note: This limitation could be overcome by fusing the transformed features instead of // compounding them, probably at the expense of quite a bit of performance and complexity // in this code // For MultiTransform, just checking the first transformed shape is not sufficient - any two // features might overlap, even if the original and the first shape don't overlap! if (this->getTypeId() != PartDesign::MultiTransform::getClassTypeId()) break; else { // Check intersection with all other transformed shapes as well std::vector::const_iterator s2 = s; s2++; for (; s2 != v_transformedShapes.end(); s2++) if (Part::checkIntersection(*s, *s2, false)) return new App::DocumentObjectExecReturn("Transformed objects are overlapping, try using a higher length or reducing the number of occurrences", (*o)); } } // Fuse/Cut the compounded transformed shapes with the support TopoDS_Shape result; if (fuse) { BRepAlgoAPI_Fuse mkFuse(support, transformedShapes); if (!mkFuse.IsDone()) return new App::DocumentObjectExecReturn("Fusion with support failed", *o); // we have to get the solids (fuse sometimes creates compounds) // Note: Because getSolid() only returns the first solid in the explorer, all // solids that are outside the support automatically disappear! result = this->getSolid(mkFuse.Shape()); // lets check if the result is a solid if (result.IsNull()) return new App::DocumentObjectExecReturn("Resulting shape is not a solid", *o); // check if mkFuse created more than one solids TopExp_Explorer xp; xp.Init(mkFuse.Shape(),TopAbs_SOLID); if (xp.More()) xp.Next(); if (!xp.More()) // There are no rejected transformations even rejected.clear(); // if the bb check guessed that there would be } else { BRepAlgoAPI_Cut mkCut(support, transformedShapes); if (!mkCut.IsDone()) return new App::DocumentObjectExecReturn("Cut out of support failed", *o); result = mkCut.Shape(); } support = result; // Use result of this operation for fuse/cut of next original } this->Shape.setValue(support); return App::DocumentObject::StdReturn; } }