/****************************************************************************** * Copyright (c) 2012 Jan Rheinländer * * * * 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 #endif #include "DatumLine.h" #include #include #include #include #include #include #include "FeaturePolarPattern.h" using namespace PartDesign; namespace PartDesign { PROPERTY_SOURCE(PartDesign::PolarPattern, PartDesign::Transformed) const App::PropertyIntegerConstraint::Constraints PolarPattern::intOccurrences = { 1, INT_MAX, 1 }; const App::PropertyAngle::Constraints PolarPattern::floatAngle = { Base::toDegrees(Precision::Angular()), 360.0, 1.0 }; PolarPattern::PolarPattern() { ADD_PROPERTY_TYPE(Axis, (nullptr), "PolarPattern", (App::PropertyType)(App::Prop_None), "Direction"); ADD_PROPERTY(Reversed, (0)); ADD_PROPERTY(Angle, (360.0)); Angle.setConstraints(&floatAngle); ADD_PROPERTY(Occurrences, (3)); Occurrences.setConstraints(&intOccurrences); } short PolarPattern::mustExecute() const { if (Axis.isTouched() || Reversed.isTouched() || Angle.isTouched() || Occurrences.isTouched()) return 1; return Transformed::mustExecute(); } const std::list PolarPattern::getTransformations(const std::vector) { double angle = Angle.getValue(); double radians = Base::toRadians(angle); if (radians < Precision::Angular()) throw Base::ValueError("Pattern angle too small"); int occurrences = Occurrences.getValue(); if (occurrences < 1) throw Base::ValueError("At least one occurrence required"); // Note: The original feature is NOT included in the list of transformations! Therefore // we start with occurrence number 1, not number 0 std::list transformations; gp_Trsf trans; transformations.push_back(trans); // identity transformation if (occurrences < 2) return transformations; bool reversed = Reversed.getValue(); double offset; if (std::fabs(angle - 360.0) < Precision::Confusion()) offset = radians / occurrences; // Because e.g. two occurrences in 360 degrees need to be 180 degrees apart else offset = radians / (occurrences - 1); App::DocumentObject* refObject = Axis.getValue(); if (refObject == nullptr) throw Base::ValueError("No axis reference specified"); std::vector subStrings = Axis.getSubValues(); if (subStrings.empty()) throw Base::ValueError("No axis reference specified"); gp_Pnt axbase; gp_Dir axdir; if (refObject->getTypeId().isDerivedFrom(Part::Part2DObject::getClassTypeId())) { Part::Part2DObject* refSketch = static_cast(refObject); Base::Axis axis; if (subStrings[0] == "H_Axis") axis = refSketch->getAxis(Part::Part2DObject::H_Axis); else if (subStrings[0] == "V_Axis") axis = refSketch->getAxis(Part::Part2DObject::V_Axis); else if (subStrings[0] == "N_Axis") axis = refSketch->getAxis(Part::Part2DObject::N_Axis); else if (subStrings[0].compare(0, 4, "Axis") == 0) { int AxId = std::atoi(subStrings[0].substr(4,4000).c_str()); if (AxId >= 0 && AxId < refSketch->getAxisCount()) axis = refSketch->getAxis(AxId); } axis *= refSketch->Placement.getValue(); axbase = gp_Pnt(axis.getBase().x, axis.getBase().y, axis.getBase().z); axdir = gp_Dir(axis.getDirection().x, axis.getDirection().y, axis.getDirection().z); } else if (refObject->getTypeId().isDerivedFrom(PartDesign::Line::getClassTypeId())) { PartDesign::Line* line = static_cast(refObject); Base::Vector3d base = line->getBasePoint(); axbase = gp_Pnt(base.x, base.y, base.z); Base::Vector3d dir = line->getDirection(); axdir = gp_Dir(dir.x, dir.y, dir.z); } else if (refObject->getTypeId().isDerivedFrom(App::Line::getClassTypeId())) { App::Line* line = static_cast(refObject); Base::Rotation rot = line->Placement.getValue().getRotation(); Base::Vector3d d(1,0,0); rot.multVec(d, d); axdir = gp_Dir(d.x, d.y, d.z); } else if (refObject->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { if (subStrings[0].empty()) throw Base::ValueError("No axis reference specified"); Part::Feature* refFeature = static_cast(refObject); Part::TopoShape refShape = refFeature->Shape.getShape(); TopoDS_Shape ref = refShape.getSubShape(subStrings[0].c_str()); if (ref.ShapeType() == TopAbs_EDGE) { TopoDS_Edge refEdge = TopoDS::Edge(ref); if (refEdge.IsNull()) throw Base::ValueError("Failed to extract axis edge"); BRepAdaptor_Curve adapt(refEdge); if (adapt.GetType() == GeomAbs_Line) { axbase = adapt.Line().Location(); axdir = adapt.Line().Direction(); } else if (adapt.GetType() == GeomAbs_Circle) { axbase = adapt.Circle().Location(); axdir = adapt.Circle().Axis().Direction(); } else { throw Base::TypeError("Rotation edge must be a straight line, circle or arc of circle"); } } else { throw Base::TypeError("Axis reference must be an edge"); } } else { throw Base::TypeError("Axis reference must be edge of a feature or datum line"); } TopLoc_Location invObjLoc = this->getLocation().Inverted(); axbase.Transform(invObjLoc.Transformation()); axdir.Transform(invObjLoc.Transformation()); gp_Ax2 axis(axbase, axdir); if (reversed) axis.SetDirection(axis.Direction().Reversed()); for (int i = 1; i < occurrences; i++) { trans.SetRotation(axis.Axis(), i * offset); transformations.push_back(trans); } return transformations; } void PolarPattern::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop) // transforms properties that had been changed { // property Occurrences had the App::PropertyInteger and was changed to App::PropertyIntegerConstraint if (prop == &Occurrences && strcmp(TypeName, "App::PropertyInteger") == 0) { App::PropertyInteger OccurrencesProperty; // restore the PropertyInteger to be able to set its value OccurrencesProperty.Restore(reader); Occurrences.setValue(OccurrencesProperty.getValue()); } else { Transformed::handleChangedPropertyType(reader, TypeName, prop); } } }