diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index 3b581f4aa..5973dd402 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -91,6 +91,7 @@ #include "BRepOffsetAPI_MakePipeShellPy.h" #include "PartFeaturePy.h" #include "PropertyGeometryList.h" +#include "DatumFeature.h" namespace Part { extern PyObject* initModule(); @@ -301,7 +302,7 @@ PyMODINIT_FUNC initPart() Part::GeomTrimmedSurface ::init(); Part::GeomSurfaceOfRevolution ::init(); Part::GeomSurfaceOfExtrusion ::init(); - + Part::Datum ::init(); IGESControl_Controller::Init(); STEPControl_Controller::Init(); diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 1328cac34..888930440 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -137,6 +137,8 @@ SET(Features_SRCS CustomFeature.h BodyBase.h BodyBase.cpp + DatumFeature.cpp + DatumFeature.h ) SOURCE_GROUP("Features" FILES ${Features_SRCS}) diff --git a/src/Mod/Part/App/DatumFeature.cpp b/src/Mod/Part/App/DatumFeature.cpp new file mode 100644 index 000000000..a4fe088c3 --- /dev/null +++ b/src/Mod/Part/App/DatumFeature.cpp @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (c) 2013 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 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include +#include "DatumFeature.h" +#include +#include +#include +#include "Mod/Part/App/PrimitiveFeature.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +using namespace Part; + +PROPERTY_SOURCE_ABSTRACT(Part::Datum, Part::Feature) + +Datum::Datum(void) +{ + ADD_PROPERTY_TYPE(References,(0,0),"References",(App::PropertyType)(App::Prop_None),"References defining the datum feature"); + ADD_PROPERTY(Offset,(0.0)); + ADD_PROPERTY(Angle,(0.0)); + touch(); +} + +Datum::~Datum() +{ +} + +void Datum::onChanged (const App::Property* prop) +{ + Part::Feature::onChanged(prop); +} + +App::DocumentObjectExecReturn *Datum::execute(void) +{ + References.touch(); + return StdReturn; +} + +void Datum::onDocumentRestored() +{ + // This seems to be the only way to make the ViewProvider display the datum feature + References.touch(); + Part::Feature::onDocumentRestored(); +} + diff --git a/src/Mod/Part/App/DatumFeature.h b/src/Mod/Part/App/DatumFeature.h new file mode 100644 index 000000000..c10126138 --- /dev/null +++ b/src/Mod/Part/App/DatumFeature.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (c) 2013 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 * + * * + ***************************************************************************/ + + +#ifndef PART_DATUMFEATURE_H +#define PART_DATUMFEATURE_H + +#include +#include + +#include "PartFeature.h" + +namespace Part +{ + +// This generic class is defined here so that the Sketcher module can access datum features +// without creating a dependency on PartDesign + +class PartExport Datum : public Part::Feature +{ + PROPERTY_HEADER(Part::Datum); + +public: + Datum(); + virtual ~Datum(); + + /// The references defining the datum object, e.g. three planes for a point, two planes for a line + App::PropertyLinkSubList References; + /// Offset and angle for defining planes + App::PropertyFloat Offset; + App::PropertyFloat Angle; + + /// recalculate the feature + App::DocumentObjectExecReturn *execute(void); + + /// returns the type name of the view provider + virtual const char* getViewProviderName(void) const = 0; + + virtual const std::set getHint() = 0; + + /// Return a shape representing the datum feature + //virtual const TopoDS_Shape getShape() const = 0; + +protected: + void onChanged (const App::Property* prop); + void onDocumentRestored(); + +protected: + std::multiset refTypes; + +}; + +} //namespace Part + + +#endif // PART_DATUMFEATURE_H diff --git a/src/Mod/Part/App/Part2DObject.cpp b/src/Mod/Part/App/Part2DObject.cpp index b70c337bb..e6122d69f 100644 --- a/src/Mod/Part/App/Part2DObject.cpp +++ b/src/Mod/Part/App/Part2DObject.cpp @@ -48,6 +48,7 @@ #include #include "Part2DObject.h" #include "Geometry.h" +#include "DatumFeature.h" #include #include "Part2DObjectPy.h" @@ -96,7 +97,7 @@ void Part2DObject::positionBySupport(void) // Set placement identical to the way it used to be done in the Sketcher::SketchOrientationDialog if (dir == Base::Vector3d(0,0,1)) { - Place = Base::Placement(Base::Vector3d(0,0,0),Base::Rotation(Reverse ? -1.0 : 0.0,0.0,0.0,0.0)); + Place = Base::Placement(Base::Vector3d(0,0,0),Base::Rotation(Reverse ? -1.0 : 0.0, 0.0,0.0,0.0)); } else if (dir == Base::Vector3d(0,1,0)) { if (Reverse) Place = Base::Placement(Base::Vector3d(0,0,0),Base::Rotation(Base::Vector3d(0,sqrt(2.0)/2.0,sqrt(2.0)/2.0),M_PI)); @@ -111,6 +112,18 @@ void Part2DObject::positionBySupport(void) Place.getRotation().multVec(Base::Vector3d(0,0,1),dir); Base::Vector3d pos = Place.getPosition(); plane = gp_Pln(gp_Pnt(pos.x, pos.y, pos.z), gp_Dir(dir.x, dir.y, dir.z)); + } else if (support->getTypeId() == Part::Datum::getClassTypeId()) { + Part::Datum* pcDatum = static_cast(support); + TopoDS_Shape plane = pcDatum->Shape.getValue(); + if (plane.ShapeType() != TopAbs_FACE) + return; + BRepAdaptor_Surface adapt(TopoDS::Face(plane)); + if (adapt.GetType() != GeomAbs_Plane) + return; + gp_Pln pl = adapt.Plane(); + Base::Vector3d pos(pl.Location().X(), pl.Location().Y(), pl.Location().Z()); + Base::Vector3d normal(pl.Axis().Direction().X(), pl.Axis().Direction().Y(), pl.Axis().Direction().Z()); + this->Placement.setValue(Base::Placement(pos, Base::Rotation(Base::Vector3d(0,0,1), normal))); } else { Part::Feature *part = static_cast(support); if (!part || !part->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) diff --git a/src/Mod/PartDesign/App/AppPartDesign.cpp b/src/Mod/PartDesign/App/AppPartDesign.cpp index 98782e7a2..55e52e5ec 100644 --- a/src/Mod/PartDesign/App/AppPartDesign.cpp +++ b/src/Mod/PartDesign/App/AppPartDesign.cpp @@ -49,7 +49,9 @@ #include "FeatureScaled.h" #include "FeatureMultiTransform.h" #include "FeatureHole.h" -#include "DatumFeature.h" +#include "DatumPlane.h" +#include "DatumLine.h" +#include "DatumPoint.h" namespace PartDesign { extern PyObject* initModule(); @@ -97,7 +99,6 @@ PyMODINIT_FUNC init_PartDesign() PartDesign::Groove ::init(); PartDesign::Chamfer ::init(); PartDesign::Draft ::init(); - PartDesign::Datum ::init(); PartDesign::Plane ::init(); PartDesign::Line ::init(); PartDesign::Point ::init(); diff --git a/src/Mod/PartDesign/App/Body.cpp b/src/Mod/PartDesign/App/Body.cpp index fb6632772..4ed343a75 100644 --- a/src/Mod/PartDesign/App/Body.cpp +++ b/src/Mod/PartDesign/App/Body.cpp @@ -28,14 +28,14 @@ #include #include "Feature.h" -#include "DatumFeature.h" #include "Body.h" #include "BodyPy.h" +#include "FeatureSketchBased.h" #include #include #include -#include "FeatureSketchBased.h" +#include using namespace PartDesign; @@ -192,7 +192,7 @@ const bool Body::isAllowed(const App::DocumentObject* f) return false; return (f->getTypeId().isDerivedFrom(PartDesign::Feature::getClassTypeId()) || - f->getTypeId().isDerivedFrom(PartDesign::Datum::getClassTypeId()) || + f->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId()) || f->getTypeId().isDerivedFrom(Part::Part2DObject::getClassTypeId())); } @@ -214,7 +214,6 @@ Body* Body::findBodyOf(const App::DocumentObject* f) void Body::addFeature(App::DocumentObject *feature) { // Set the BaseFeature property - // Note: This is not strictly necessary for Datum features if (feature->getTypeId().isDerivedFrom(PartDesign::Feature::getClassTypeId())) { App::DocumentObject* prevSolidFeature = getPrevSolidFeature(NULL, true); if (prevSolidFeature != NULL) diff --git a/src/Mod/PartDesign/App/Body.h b/src/Mod/PartDesign/App/Body.h index 18b58d84e..519f7486a 100644 --- a/src/Mod/PartDesign/App/Body.h +++ b/src/Mod/PartDesign/App/Body.h @@ -91,13 +91,13 @@ public: /** * Return true if the given feature is a solid feature allowed in a Body. Currently this is only valid * for features derived from PartDesign::Feature - * Return false if the given feature is a Sketch or a PartDesign::Datum feature + * Return false if the given feature is a Sketch or a Part::Datum feature */ static const bool isSolidFeature(const App::DocumentObject* f); /** * Return true if the given feature is allowed in a Body. Currently allowed are - * all features derived from PartDesign::Feature and PartDesign::Datum and sketches + * all features derived from PartDesign::Feature and Part::Datum and sketches */ static const bool isAllowed(const App::DocumentObject* f); diff --git a/src/Mod/PartDesign/App/BodyPyImp.cpp b/src/Mod/PartDesign/App/BodyPyImp.cpp index f66754810..195b88fd4 100644 --- a/src/Mod/PartDesign/App/BodyPyImp.cpp +++ b/src/Mod/PartDesign/App/BodyPyImp.cpp @@ -3,7 +3,6 @@ #include "Mod/Part/App/Part2DObject.h" #include "Mod/PartDesign/App/Body.h" -#include "Mod/PartDesign/App/DatumFeature.h" // inclusion of the generated files (generated out of ItemPy.xml) #include "BodyPy.h" diff --git a/src/Mod/PartDesign/App/CMakeLists.txt b/src/Mod/PartDesign/App/CMakeLists.txt index 725c2cc8a..9578300e5 100644 --- a/src/Mod/PartDesign/App/CMakeLists.txt +++ b/src/Mod/PartDesign/App/CMakeLists.txt @@ -39,8 +39,12 @@ SET(Features_SRCS SOURCE_GROUP("Features" FILES ${Features_SRCS}) SET(DatumFeatures_SRCS - DatumFeature.cpp - DatumFeature.h + DatumPlane.cpp + DatumPlane.h + DatumLine.cpp + DatumLine.h + DatumPoint.cpp + DatumPoint.h ) SOURCE_GROUP("DatumFeatures" FILES ${DatumFeatures_SRCS}) diff --git a/src/Mod/PartDesign/App/DatumFeature.cpp b/src/Mod/PartDesign/App/DatumFeature.cpp deleted file mode 100644 index e9268c050..000000000 --- a/src/Mod/PartDesign/App/DatumFeature.cpp +++ /dev/null @@ -1,763 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2013 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 -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -#endif - -#include -#include -#include "DatumFeature.h" -#include -#include -#include -#include "Mod/Part/App/PrimitiveFeature.h" - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -using namespace PartDesign; - -PROPERTY_SOURCE_ABSTRACT(PartDesign::Datum, App::GeoFeature) - -Datum::Datum(void) -{ - ADD_PROPERTY_TYPE(References,(0,0),"References",(App::PropertyType)(App::Prop_None),"References defining the datum feature"); - ADD_PROPERTY(Offset,(0.0)); - ADD_PROPERTY(Angle,(0.0)); - touch(); -} - -Datum::~Datum() -{ -} - - -App::DocumentObjectExecReturn *Datum::execute(void) -{ - References.touch(); - return StdReturn; -} - -// Note: We don't distinguish between e.g. datum lines and edges here -#define PLANE QObject::tr("DPLANE") -#define LINE QObject::tr("DLINE") -#define POINT QObject::tr("DPOINT") -#define ANGLE QObject::tr("Angle") - -void Datum::onChanged(const App::Property* prop) -{ - - if (prop == &References) { - refTypes.clear(); - std::vector refs = References.getValues(); - std::vector refnames = References.getSubValues(); - - for (int r = 0; r < refs.size(); r++) - refTypes.insert(getRefType(refs[r], refnames[r])); - - if (fabs(Angle.getValue()) > Precision::Confusion()) - refTypes.insert(ANGLE); - } else if (prop == &Angle) { - // Zero value counts as angle not defined - if (fabs(Angle.getValue()) > Precision::Confusion()) - refTypes.insert(ANGLE); - else - refTypes.erase(ANGLE); - } - - App::GeoFeature::onChanged(prop); -} - -void Datum::onDocumentRestored() -{ - // This seems to be the only way to make the ViewProvider display the datum feature - References.touch(); - App::GeoFeature::onDocumentRestored(); -} - -const QString Datum::getRefType(const App::DocumentObject* obj, const std::string& subname) -{ - Base::Type type = obj->getTypeId(); - - if ((type == App::Plane::getClassTypeId()) || (type == PartDesign::Plane::getClassTypeId())) - return PLANE; - else if (type == PartDesign::Line::getClassTypeId()) - return LINE; - else if (type == PartDesign::Point::getClassTypeId()) - return POINT; - else if (type.isDerivedFrom(Part::Feature::getClassTypeId())) { - // Note: For now, only planar references are possible - if (subname.size() > 4 && subname.substr(0,4) == "Face") - return PLANE; - else if (subname.size() > 4 && subname.substr(0,4) == "Edge") - return LINE; - else if (subname.size() > 6 && subname.substr(0,6) == "Vertex") - return POINT; - } - - throw Base::Exception("PartDesign::Datum::getRefType(): Illegal object type"); -} - -// ================================ Initialize the hints ===================== - -std::map, std::set > Point::hints = std::map, std::set >(); - -void Point::initHints() -{ - std::set DONE; - DONE.insert(QObject::tr("Done")); - - std::multiset key; - std::set value; - key.insert(POINT); - hints[key] = DONE; // POINT -> DONE. Point from another point or vertex - - key.clear(); value.clear(); - key.insert(LINE); - value.insert(LINE); value.insert(PLANE); - hints[key] = value; // LINE -> LINE or PLANE - - key.clear(); value.clear(); - key.insert(LINE); key.insert(LINE); - hints[key] = DONE; // {LINE, LINE} -> DONE. Point from two lines or edges - - key.clear(); value.clear(); - key.insert(LINE); key.insert(PLANE); - hints[key] = DONE; // {LINE, PLANE} -> DONE. Point from line and plane - - key.clear(); value.clear(); - key.insert(PLANE); - value.insert(PLANE); value.insert(LINE); - hints[key] = value; // PLANE -> PLANE or LINE - - key.clear(); value.clear(); - key.insert(PLANE); key.insert(PLANE); - value.insert(PLANE); - hints[key] = value; // {PLANE, PLANE} -> PLANE - - key.clear(); value.clear(); - key.insert(PLANE); key.insert(PLANE); key.insert(PLANE); - hints[key] = DONE; // {PLANE, PLANE, PLANE} -> DONE. Point from three planes - - key.clear(); value.clear(); - value.insert(POINT); value.insert(LINE); value.insert(PLANE); - hints[key] = value; -} - -std::map, std::set > Line::hints = std::map, std::set >(); - -void Line::initHints() -{ - std::set DONE; - DONE.insert(QObject::tr("Done")); - - std::multiset key; - std::set value; - key.insert(LINE); - hints[key] = DONE; // LINE -> DONE. Line from another line or edge - - key.clear(); value.clear(); - key.insert(POINT); - value.insert(POINT); - hints[key] = value; // POINT -> POINT - - key.clear(); value.clear(); - key.insert(POINT); key.insert(POINT); - hints[key] = DONE; // {POINT, POINT} -> DONE. Line from two points or vertices - - key.clear(); value.clear(); - key.insert(PLANE); - value.insert(PLANE); - hints[key] = value; // PLANE -> PLANE - - key.clear(); value.clear(); - key.insert(PLANE); key.insert(PLANE); - hints[key] = DONE; // {PLANE, PLANE} -> DONE. Line from two planes or faces - - key.clear(); value.clear(); - value.insert(POINT); value.insert(LINE); value.insert(PLANE); - hints[key] = value; -} - -std::map, std::set > Plane::hints = std::map, std::set >(); - -void Plane::initHints() -{ - std::set DONE; - DONE.insert(QObject::tr("Done")); - - std::multiset key; - std::set value; - key.insert(PLANE); - hints[key] = DONE; // PLANE -> DONE. Plane from another plane or face - - key.clear(); value.clear(); - key.insert(POINT); - value.insert(POINT); value.insert(LINE); - hints[key] = value; // POINT -> POINT or LINE - - key.clear(); value.clear(); - key.insert(POINT); key.insert(LINE); - hints[key] = DONE; // {POINT, LINE} -> DONE. Plane from point/vertex and line/edge - - key.clear(); value.clear(); - key.insert(POINT); key.insert(POINT); - value.insert(POINT); - hints[key] = value; // {POINT, POINT} -> POINT - - key.clear(); value.clear(); - key.insert(POINT); key.insert(POINT); key.insert(POINT); - hints[key] = DONE; // {POINT, POINT, POINT} -> DONE. Plane from 3 points or vertices - - key.clear(); value.clear(); - key.insert(LINE); - value.insert(POINT); value.insert(PLANE); value.insert(ANGLE); - hints[key] = value; // LINE -> POINT or PLANE or ANGLE - - key.clear(); value.clear(); - key.insert(PLANE); key.insert(LINE); - value.insert(ANGLE); - hints[key] = value; // {PLANE, LINE} -> ANGLE - - key.clear(); value.clear(); - key.insert(PLANE); key.insert(ANGLE); - value.insert(LINE); - hints[key] = value; // {PLANE, ANGLE} -> LINE - - key.clear(); value.clear(); - key.insert(ANGLE); key.insert(LINE); - value.insert(PLANE); - hints[key] = value; // {ANGLE, LINE} -> PLANE - - key.clear(); value.clear(); - key.insert(LINE); key.insert(PLANE); key.insert(ANGLE); - hints[key] = DONE; // {LINE, PLANE, ANGLE} -> DONE. Plane through line with angle to other plane - - - key.clear(); value.clear(); - value.insert(POINT); value.insert(LINE); value.insert(PLANE); value.insert(ANGLE); - hints[key] = value; -} - -// ============================================================================ - -PROPERTY_SOURCE(PartDesign::Point, PartDesign::Datum) - -Point::Point() -{ - ADD_PROPERTY_TYPE(_Point,(Base::Vector3d(0,0,0)),"DatumPoint", - App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), - "Coordinates of the datum point"); -} - -Point::~Point() -{ -} - -void Point::onChanged(const App::Property* prop) -{ - Datum::onChanged(prop); - - if (prop == &References) { - std::set hint = getHint(); - if (!((hint.size() == 1) && (hint.find(QObject::tr("Done")) != hint.end()))) - return; // incomplete references - - // Extract the geometry of the references - std::vector refs = References.getValues(); - std::vector refnames = References.getSubValues(); - Base::Vector3d* point = NULL; - Handle_Geom_Curve c1 = NULL; - Handle_Geom_Curve c2 = NULL; - Handle_Geom_Surface s1 = NULL; - Handle_Geom_Surface s2 = NULL; - Handle_Geom_Surface s3 = NULL; - - for (int i = 0; i < refs.size(); i++) { - if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Point::getClassTypeId())) { - PartDesign::Point* p = static_cast(refs[i]); - point = new Base::Vector3d (p->_Point.getValue()); - } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Line::getClassTypeId())) { - PartDesign::Line* l = static_cast(refs[i]); - Base::Vector3d base = l->_Base.getValue(); - Base::Vector3d dir = l->_Direction.getValue(); - if (c1.IsNull()) - c1 = new Geom_Line(gp_Pnt(base.x, base.y, base.z), gp_Dir(dir.x, dir.y, dir.z)); - else - c2 = new Geom_Line(gp_Pnt(base.x, base.y, base.z), gp_Dir(dir.x, dir.y, dir.z)); - } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Plane::getClassTypeId())) { - PartDesign::Plane* p = static_cast(refs[i]); - Base::Vector3d base = p->_Base.getValue(); - Base::Vector3d normal = p->_Normal.getValue(); - if (s1.IsNull()) - s1 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); - else if (s2.IsNull()) - s2 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); - else - s3 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); - } else if (refs[i]->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { - App::Plane* p = static_cast(refs[i]); - // Note: We only handle the three base planes here - gp_Pnt base(0,0,0); - gp_Dir normal; - if (strcmp(p->getNameInDocument(), "BaseplaneXY") == 0) - normal = gp_Dir(0,0,1); - else if (strcmp(p->getNameInDocument(), "BaseplaneYZ") == 0) - normal = gp_Dir(1,0,0); - else if (strcmp(p->getNameInDocument(), "BaseplaneXZ") == 0) - normal = gp_Dir(0,1,0); - - if (s1.IsNull()) - s1 = new Geom_Plane(base, normal); - else if (s2.IsNull()) - s2 = new Geom_Plane(base, normal); - else - s3 = new Geom_Plane(base, normal); - } else if (refs[i]->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { - Part::Feature* feature = static_cast(refs[i]); - const TopoDS_Shape& sh = feature->Shape.getValue(); - if (sh.IsNull()) - return; // "PartDesign::Point: Reference has NULL shape" - // Get subshape - TopoDS_Shape subshape = feature->Shape.getShape().getSubShape(refnames[i].c_str()); - if (subshape.IsNull()) - return; // "PartDesign::Point: Reference has NULL subshape"; - - if (subshape.ShapeType() == TopAbs_VERTEX) { - TopoDS_Vertex v = TopoDS::Vertex(subshape); - gp_Pnt p = BRep_Tool::Pnt(v); - point = new Base::Vector3d(p.X(), p.Y(), p.Z()); - } else if (subshape.ShapeType() == TopAbs_EDGE) { - TopoDS_Edge e = TopoDS::Edge(subshape); - Standard_Real first, last; - if (c1.IsNull()) - c1 = BRep_Tool::Curve(e, first, last); - else - c2 = BRep_Tool::Curve(e, first, last); - } else if (subshape.ShapeType() == TopAbs_FACE) { - TopoDS_Face f = TopoDS::Face(subshape); - if (s1.IsNull()) - s1 = BRep_Tool::Surface(f); - else if (s2.IsNull()) - s2 = BRep_Tool::Surface(f); - else - s3 = BRep_Tool::Surface(f); - } - } else { - return; //"PartDesign::Point: Invalid reference type" - } - } - - if (point != NULL) { - // Point from vertex or other point. Nothing to be done - } else if (!c1.IsNull()) { - if (!c2.IsNull()) { - // Point from intersection of two curves - GeomAPI_ExtremaCurveCurve intersector(c1, c2); - if ((intersector.LowerDistance() > Precision::Confusion()) || (intersector.NbExtrema() == 0)) - return; // No intersection - // Note: We don't check for multiple intersection points - gp_Pnt p, p2; - intersector.Points(1, p, p2); - point = new Base::Vector3d(p.X(), p.Y(), p.Z()); - } else if (!s1.IsNull()) { - GeomAPI_IntCS intersector(c1, s1); - if (!intersector.IsDone() || (intersector.NbPoints() == 0)) - return; - if (intersector.NbPoints() > 1) - Base::Console().Warning("More than one intersection point for datum point from curve and surface"); - - gp_Pnt p = intersector.Point(1); - point = new Base::Vector3d(p.X(), p.Y(), p.Z()); - } else - return; - } else if (!s1.IsNull() && !s2.IsNull() && !s3.IsNull()) { - GeomAPI_IntSS intersectorSS(s1, s2, Precision::Confusion()); - if (!intersectorSS.IsDone() || (intersectorSS.NbLines() == 0)) - return; - if (intersectorSS.NbLines() > 1) - Base::Console().Warning("More than one intersection line for datum point from surfaces"); - Handle_Geom_Curve line = intersectorSS.Line(1); - - GeomAPI_IntCS intersector(line, s3); - if (!intersector.IsDone() || (intersector.NbPoints() == 0)) - return; - if (intersector.NbPoints() > 1) - Base::Console().Warning("More than one intersection point for datum point from surfaces"); - - gp_Pnt p = intersector.Point(1); - point = new Base::Vector3d(p.X(), p.Y(), p.Z()); - } else { - return; - } - - _Point.setValue(*point); - _Point.touch(); // This triggers ViewProvider::updateData() - delete point; - } -} - - -const std::set Point::getHint() -{ - if (hints.find(refTypes) != hints.end()) - return hints[refTypes]; - else - return std::set(); -} - - -PROPERTY_SOURCE(PartDesign::Line, PartDesign::Datum) - -Line::Line() -{ - ADD_PROPERTY_TYPE(_Base,(Base::Vector3d(0,0,0)),"DatumLine", - App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), - "Coordinates of the line base point"); - ADD_PROPERTY_TYPE(_Direction,(Base::Vector3d(1,1,1)),"DatumLine", - App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), - "Coordinates of the line direction"); -} - -Line::~Line() -{ -} - -void Line::onChanged(const App::Property *prop) -{ - Datum::onChanged(prop); - - if (prop == &References) { - std::set hint = getHint(); - if (!((hint.size() == 1) && (hint.find(QObject::tr("Done")) != hint.end()))) - return; // incomplete references - - // Extract the geometry of the references - std::vector refs = References.getValues(); - std::vector refnames = References.getSubValues(); - Base::Vector3d* base = NULL; - Base::Vector3d* direction = NULL; - Base::Vector3d* p1 = NULL; - Base::Vector3d* p2 = NULL; - gp_Lin* line = NULL; - Handle_Geom_Surface s1 = NULL; - Handle_Geom_Surface s2 = NULL; - - for (int i = 0; i < refs.size(); i++) { - if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Point::getClassTypeId())) { - PartDesign::Point* p = static_cast(refs[i]); - if (p1 == NULL) - p1 = new Base::Vector3d (p->_Point.getValue()); - else - p2 = new Base::Vector3d (p->_Point.getValue()); - } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Line::getClassTypeId())) { - PartDesign::Line* l = static_cast(refs[i]); - base = new Base::Vector3d (l->_Base.getValue()); - direction = new Base::Vector3d (l->_Direction.getValue()); - } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Plane::getClassTypeId())) { - PartDesign::Plane* p = static_cast(refs[i]); - Base::Vector3d base = p->_Base.getValue(); - Base::Vector3d normal = p->_Normal.getValue(); - if (s1.IsNull()) - s1 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); - else - s2 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); - } else if (refs[i]->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { - App::Plane* p = static_cast(refs[i]); - // Note: We only handle the three base planes here - gp_Pnt base(0,0,0); - gp_Dir normal; - if (strcmp(p->getNameInDocument(), "BaseplaneXY") == 0) - normal = gp_Dir(0,0,1); - else if (strcmp(p->getNameInDocument(), "BaseplaneYZ") == 0) - normal = gp_Dir(1,0,0); - else if (strcmp(p->getNameInDocument(), "BaseplaneXZ") == 0) - normal = gp_Dir(0,1,0); - - if (s1.IsNull()) - s1 = new Geom_Plane(base, normal); - else - s2 = new Geom_Plane(base, normal); - } else if (refs[i]->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { - Part::Feature* feature = static_cast(refs[i]); - const TopoDS_Shape& sh = feature->Shape.getValue(); - if (sh.IsNull()) - return; // "PartDesign::Line: Reference has NULL shape" - // Get subshape - TopoDS_Shape subshape = feature->Shape.getShape().getSubShape(refnames[i].c_str()); - if (subshape.IsNull()) - return; // "PartDesign::Line: Reference has NULL subshape"; - - if (subshape.ShapeType() == TopAbs_VERTEX) { - TopoDS_Vertex v = TopoDS::Vertex(subshape); - gp_Pnt p = BRep_Tool::Pnt(v); - if (p1 == NULL) - p1 = new Base::Vector3d(p.X(), p.Y(), p.Z()); - else - p2 = new Base::Vector3d(p.X(), p.Y(), p.Z()); - } else if (subshape.ShapeType() == TopAbs_EDGE) { - TopoDS_Edge e = TopoDS::Edge(subshape); - BRepAdaptor_Curve adapt(e); - if (adapt.GetType() != GeomAbs_Line) - return; // Non-linear edge - line = new gp_Lin(adapt.Line()); - } else if (subshape.ShapeType() == TopAbs_FACE) { - TopoDS_Face f = TopoDS::Face(subshape); - if (s1.IsNull()) - s1 = BRep_Tool::Surface(f); - else - s2 = BRep_Tool::Surface(f); - } - } else { - return; //"PartDesign::Point: Invalid reference type" - } - } - - if ((base != NULL) && (direction != NULL)) { - // Line from other datum line. Nothing to be done - } else if ((p1 != NULL) && (p2 != NULL)) { - // Line from two points - base = new Base::Vector3d(*p1); - direction = new Base::Vector3d(*p2 - *p1); - } else if (line != NULL) { - // Line from gp_lin - base = new Base::Vector3d(line->Location().X(), line->Location().Y(), line->Location().Z()); - direction = new Base::Vector3d(line->Direction().X(), line->Direction().Y(), line->Direction().Z()); - } else if (!s1.IsNull() && !s2.IsNull()) { - GeomAPI_IntSS intersectorSS(s1, s2, Precision::Confusion()); - if (!intersectorSS.IsDone() || (intersectorSS.NbLines() == 0)) - return; - if (intersectorSS.NbLines() > 1) - Base::Console().Warning("More than one intersection line for datum point from surfaces"); - Handle_Geom_Line l = Handle_Geom_Line::DownCast(intersectorSS.Line(1)); - if (l.IsNull()) - return; // non-linear intersection curve - gp_Lin lin = l->Lin(); - base = new Base::Vector3d(lin.Location().X(), lin.Location().Y(), lin.Location().Z()); - direction = new Base::Vector3d(lin.Direction().X(), lin.Direction().Y(), lin.Direction().Z()); - } else { - return; - } - - _Base.setValue(*base); - _Direction.setValue(*direction); - _Base.touch(); // This triggers ViewProvider::updateData() - delete base; - delete direction; - if (p1 != NULL) delete p1; - if (p2 != NULL) delete p2; - if (line != NULL) delete line; - } -} - - -const std::set Line::getHint() -{ - if (hints.find(refTypes) != hints.end()) - return hints[refTypes]; - else - return std::set(); -} - - -PROPERTY_SOURCE(PartDesign::Plane, PartDesign::Datum) - -Plane::Plane() -{ -ADD_PROPERTY_TYPE(_Base,(Base::Vector3d(0,0,0)),"DatumPlane", - App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), - "Coordinates of the plane base point"); -ADD_PROPERTY_TYPE(_Normal,(Base::Vector3d(1,1,1)),"DatumPlane", - App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), - "Coordinates of the plane normal"); -} - -void Plane::onChanged(const App::Property *prop) -{ - Datum::onChanged(prop); - - if (prop == &References) { - std::set hint = getHint(); - if (!((hint.size() == 1) && (hint.find(QObject::tr("Done")) != hint.end()))) - return; // incomplete references - - // Extract the geometry of the references - std::vector refs = References.getValues(); - std::vector refnames = References.getSubValues(); - Base::Vector3d* p1 = NULL; - Base::Vector3d* p2 = NULL; - Base::Vector3d* p3 = NULL; - Base::Vector3d* normal = NULL; - gp_Lin* line = NULL; - - for (int i = 0; i < refs.size(); i++) { - if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Point::getClassTypeId())) { - PartDesign::Point* p = static_cast(refs[i]); - if (p1 == NULL) - p1 = new Base::Vector3d (p->_Point.getValue()); - else if (p2 == NULL) - p2 = new Base::Vector3d (p->_Point.getValue()); - else - p3 = new Base::Vector3d (p->_Point.getValue()); - } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Line::getClassTypeId())) { - PartDesign::Line* l = static_cast(refs[i]); - Base::Vector3d base = l->_Base.getValue(); - Base::Vector3d dir = l->_Direction.getValue(); - line = new gp_Lin(gp_Pnt(base.x, base.y, base.z), gp_Dir(dir.x, dir.y, dir.z)); - } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Plane::getClassTypeId())) { - PartDesign::Plane* p = static_cast(refs[i]); - p1 = new Base::Vector3d(p->_Base.getValue()); - normal = new Base::Vector3d(p->_Normal.getValue()); - } else if (refs[i]->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { - App::Plane* p = static_cast(refs[i]); - // Note: We only handle the three base planes here - p1 = new Base::Vector3d(0,0,0); - normal = new Base::Vector3d; - if (strcmp(p->getNameInDocument(), "BaseplaneXY") == 0) - *normal = Base::Vector3d(0,0,1); - else if (strcmp(p->getNameInDocument(), "BaseplaneYZ") == 0) - *normal = Base::Vector3d(1,0,0); - else if (strcmp(p->getNameInDocument(), "BaseplaneXZ") == 0) - *normal = Base::Vector3d(0,1,0); - } else if (refs[i]->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { - Part::Feature* feature = static_cast(refs[i]); - const TopoDS_Shape& sh = feature->Shape.getValue(); - if (sh.IsNull()) - return; // "PartDesign::Plane: Reference has NULL shape" - // Get subshape - TopoDS_Shape subshape = feature->Shape.getShape().getSubShape(refnames[i].c_str()); - if (subshape.IsNull()) - return; // "PartDesign::Plane: Reference has NULL subshape"; - - if (subshape.ShapeType() == TopAbs_VERTEX) { - TopoDS_Vertex v = TopoDS::Vertex(subshape); - gp_Pnt p = BRep_Tool::Pnt(v); - if (p1 == NULL) - p1 = new Base::Vector3d(p.X(), p.Y(), p.Z()); - else if (p2 == NULL) - p2 = new Base::Vector3d(p.X(), p.Y(), p.Z()); - else - p3 = new Base::Vector3d(p.X(), p.Y(), p.Z()); - } else if (subshape.ShapeType() == TopAbs_EDGE) { - TopoDS_Edge e = TopoDS::Edge(subshape); - BRepAdaptor_Curve adapt(e); - if (adapt.GetType() != GeomAbs_Line) - return; // Non-linear edge - line = new gp_Lin(adapt.Line()); - } else if (subshape.ShapeType() == TopAbs_FACE) { - TopoDS_Face f = TopoDS::Face(subshape); - BRepAdaptor_Surface adapt(f); - if (adapt.GetType() != GeomAbs_Plane) - return; // Non-planar face - gp_Pnt b = adapt.Plane().Location(); - gp_Dir d = adapt.Plane().Axis().Direction(); - p1 = new Base::Vector3d(b.X(), b.Y(), b.Z()); - normal = new Base::Vector3d(d.X(), d.Y(), d.Z()); - } - } else { - return; //"PartDesign::Plane: Invalid reference type" - } - } - - *normal = normal->Normalize(); - - if ((line != NULL) && (normal != NULL) && (p1 != NULL) && (fabs(Angle.getValue()) > Precision::Confusion())) { - // plane from line, plane, and angle to plane - gp_Pnt p = line->Location(); - *p1 = Base::Vector3d(p.X(), p.Y(), p.Z()); - gp_Dir dir = line->Direction(); - Base::Rotation rot(Base::Vector3d(dir.X(), dir.Y(), dir.Z()), Angle.getValue() / 180.0 * M_PI); - rot.multVec(*normal, *normal); - } else if ((p1 != NULL) && (normal != NULL)) { - // plane from other plane. Nothing to be done - } else if ((p1 != NULL) && (p2 != NULL) && (p3 != NULL)) { - // Plane from three points - Base::Vector3d vec1 = *p2 - *p1; - Base::Vector3d vec2 = *p3 - *p1; - normal = new Base::Vector3d(vec1 % vec2); - } else if ((line != NULL) && (p1 != NULL)) { - // Plane from point and line - p2 = new Base::Vector3d(line->Location().X(), line->Location().Y(), line->Location().Z()); - gp_Pnt p(line->Location().X() + line->Direction().X(), line->Location().Y() + line->Direction().Y(), line->Location().Z() + line->Direction().Z()); - p3 = new Base::Vector3d(p.X(), p.Y(), p.Z()); - Base::Vector3d vec1 = *p2 - *p1; - Base::Vector3d vec2 = *p3 - *p1; - normal = new Base::Vector3d(vec1 % vec2); - } else { - return; - } - - if (fabs(Offset.getValue()) > Precision::Confusion()) - *p1 += Offset.getValue() * *normal; - - _Base.setValue(*p1); - _Normal.setValue(*normal); - _Base.touch(); // This triggers ViewProvider::updateData() - delete p1; - delete normal; - if (p2 != NULL) delete p2; - if (p3 != NULL) delete p3; - if (line != NULL) delete line; - } -} - - -const std::set Plane::getHint() -{ - if (hints.find(refTypes) != hints.end()) - return hints[refTypes]; - else - return std::set(); -} diff --git a/src/Mod/PartDesign/App/DatumFeature.h b/src/Mod/PartDesign/App/DatumFeature.h deleted file mode 100644 index ab4e357b9..000000000 --- a/src/Mod/PartDesign/App/DatumFeature.h +++ /dev/null @@ -1,148 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2013 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 * - * * - ***************************************************************************/ - - -#ifndef PARTDESIGN_DATUMFEATURE_H -#define PARTDESIGN_DATUMFEATURE_H - -#include -#include -#include - -namespace PartDesign -{ - -class PartDesignExport Datum : public App::GeoFeature -{ - PROPERTY_HEADER(PartDesign::Datum); - -public: - Datum(); - virtual ~Datum(); - - /// The references defining the datum object, e.g. three planes for a point, two planes for a line - App::PropertyLinkSubList References; - /// Offset and angle for defining planes - App::PropertyFloat Offset; - App::PropertyFloat Angle; - - /// recalculate the feature - App::DocumentObjectExecReturn *execute(void); - - /// returns the type name of the view provider - const char* getViewProviderName(void) const { - return "PartDesignGui::ViewProviderDatum"; - } - - virtual const std::set getHint() = 0; - -protected: - void onChanged (const App::Property* prop); - void onDocumentRestored(); - -protected: - std::multiset refTypes; - - static const QString getRefType(const App::DocumentObject* obj, const std::string& subname); - -}; - -class PartDesignExport Point : public PartDesign::Datum -{ - PROPERTY_HEADER(PartDesign::Point); - -public: - App::PropertyVector _Point; - - Point(); - virtual ~Point(); - - const char* getViewProviderName(void) const { - return "PartDesignGui::ViewProviderDatumPoint"; - } - - static void initHints(); - const std::set getHint(); - -protected: - virtual void onChanged(const App::Property* prop); - -private: - // Hints on what further references are required/possible on this feature for a given set of references - static std::map, std::set > hints; -}; - -class PartDesignExport Line : public PartDesign::Datum -{ - PROPERTY_HEADER(PartDesign::Line); - -public: - App::PropertyVector _Base; - App::PropertyVector _Direction; - - Line(); - virtual ~Line(); - - const char* getViewProviderName(void) const { - return "PartDesignGui::ViewProviderDatumLine"; - } - - static void initHints(); - const std::set getHint(); - -protected: - virtual void onChanged(const App::Property* prop); - -private: - // Hints on what further references are required/possible on this feature for a given set of references - static std::map, std::set > hints; -}; - -class PartDesignExport Plane : public PartDesign::Datum -{ - PROPERTY_HEADER(PartDesign::Plane); - -public: - App::PropertyVector _Base; - App::PropertyVector _Normal; - - Plane(); - - const char* getViewProviderName(void) const { - return "PartDesignGui::ViewProviderDatumPlane"; - } - - static void initHints(); - const std::set getHint(); - -protected: - virtual void onChanged(const App::Property* prop); - -private: - // Hints on what further references are required/possible on this feature for a given set of references - static std::map, std::set > hints; -}; - -} //namespace PartDesign - - -#endif // PARTDESIGN_DATUMFEATURE_H diff --git a/src/Mod/PartDesign/App/DatumLine.cpp b/src/Mod/PartDesign/App/DatumLine.cpp new file mode 100644 index 000000000..f59e93220 --- /dev/null +++ b/src/Mod/PartDesign/App/DatumLine.cpp @@ -0,0 +1,280 @@ +/*************************************************************************** + * Copyright (c) 2013 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 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include +#include "DatumPoint.h" +#include "DatumLine.h" +#include "DatumPlane.h" +#include +#include +#include +#include "Mod/Part/App/PrimitiveFeature.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +using namespace PartDesign; + +// Note: We don't distinguish between e.g. datum lines and edges here +#define PLANE QObject::tr("DPLANE") +#define LINE QObject::tr("DLINE") +#define POINT QObject::tr("DPOINT") +#define ANGLE QObject::tr("Angle") + +std::map, std::set > Line::hints = std::map, std::set >(); + +void Line::initHints() +{ + std::set DONE; + DONE.insert(QObject::tr("Done")); + + std::multiset key; + std::set value; + key.insert(LINE); + hints[key] = DONE; // LINE -> DONE. Line from another line or edge + + key.clear(); value.clear(); + key.insert(POINT); + value.insert(POINT); + hints[key] = value; // POINT -> POINT + + key.clear(); value.clear(); + key.insert(POINT); key.insert(POINT); + hints[key] = DONE; // {POINT, POINT} -> DONE. Line from two points or vertices + + key.clear(); value.clear(); + key.insert(PLANE); + value.insert(PLANE); + hints[key] = value; // PLANE -> PLANE + + key.clear(); value.clear(); + key.insert(PLANE); key.insert(PLANE); + hints[key] = DONE; // {PLANE, PLANE} -> DONE. Line from two planes or faces + + key.clear(); value.clear(); + value.insert(POINT); value.insert(LINE); value.insert(PLANE); + hints[key] = value; +} + +PROPERTY_SOURCE(PartDesign::Line, Part::Datum) + +Line::Line() +{ + ADD_PROPERTY_TYPE(_Base,(Base::Vector3d(0,0,0)),"DatumLine", + App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Coordinates of the line base point"); + ADD_PROPERTY_TYPE(_Direction,(Base::Vector3d(1,1,1)),"DatumLine", + App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Coordinates of the line direction"); +} + +Line::~Line() +{ +} + +void Line::onChanged(const App::Property *prop) +{ + Datum::onChanged(prop); + + if (prop == &References) { + refTypes.clear(); + std::vector refs = References.getValues(); + std::vector refnames = References.getSubValues(); + + for (int r = 0; r < refs.size(); r++) + refTypes.insert(getRefType(refs[r], refnames[r])); + + std::set hint = getHint(); + if (!((hint.size() == 1) && (hint.find(QObject::tr("Done")) != hint.end()))) + return; // incomplete references + + // Extract the geometry of the references + Base::Vector3d* base = NULL; + Base::Vector3d* direction = NULL; + Base::Vector3d* p1 = NULL; + Base::Vector3d* p2 = NULL; + gp_Lin* line = NULL; + Handle_Geom_Surface s1 = NULL; + Handle_Geom_Surface s2 = NULL; + + for (int i = 0; i < refs.size(); i++) { + if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Point::getClassTypeId())) { + PartDesign::Point* p = static_cast(refs[i]); + if (p1 == NULL) + p1 = new Base::Vector3d (p->_Point.getValue()); + else + p2 = new Base::Vector3d (p->_Point.getValue()); + } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Line::getClassTypeId())) { + PartDesign::Line* l = static_cast(refs[i]); + base = new Base::Vector3d (l->_Base.getValue()); + direction = new Base::Vector3d (l->_Direction.getValue()); + } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Plane::getClassTypeId())) { + PartDesign::Plane* p = static_cast(refs[i]); + Base::Vector3d base = p->_Base.getValue(); + Base::Vector3d normal = p->_Normal.getValue(); + if (s1.IsNull()) + s1 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); + else + s2 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); + } else if (refs[i]->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { + App::Plane* p = static_cast(refs[i]); + // Note: We only handle the three base planes here + gp_Pnt base(0,0,0); + gp_Dir normal; + if (strcmp(p->getNameInDocument(), "BaseplaneXY") == 0) + normal = gp_Dir(0,0,1); + else if (strcmp(p->getNameInDocument(), "BaseplaneYZ") == 0) + normal = gp_Dir(1,0,0); + else if (strcmp(p->getNameInDocument(), "BaseplaneXZ") == 0) + normal = gp_Dir(0,1,0); + + if (s1.IsNull()) + s1 = new Geom_Plane(base, normal); + else + s2 = new Geom_Plane(base, normal); + } else if (refs[i]->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { + Part::Feature* feature = static_cast(refs[i]); + const TopoDS_Shape& sh = feature->Shape.getValue(); + if (sh.IsNull()) + return; // "PartDesign::Line: Reference has NULL shape" + // Get subshape + TopoDS_Shape subshape = feature->Shape.getShape().getSubShape(refnames[i].c_str()); + if (subshape.IsNull()) + return; // "PartDesign::Line: Reference has NULL subshape"; + + if (subshape.ShapeType() == TopAbs_VERTEX) { + TopoDS_Vertex v = TopoDS::Vertex(subshape); + gp_Pnt p = BRep_Tool::Pnt(v); + if (p1 == NULL) + p1 = new Base::Vector3d(p.X(), p.Y(), p.Z()); + else + p2 = new Base::Vector3d(p.X(), p.Y(), p.Z()); + } else if (subshape.ShapeType() == TopAbs_EDGE) { + TopoDS_Edge e = TopoDS::Edge(subshape); + BRepAdaptor_Curve adapt(e); + if (adapt.GetType() != GeomAbs_Line) + return; // Non-linear edge + line = new gp_Lin(adapt.Line()); + } else if (subshape.ShapeType() == TopAbs_FACE) { + TopoDS_Face f = TopoDS::Face(subshape); + if (s1.IsNull()) + s1 = BRep_Tool::Surface(f); + else + s2 = BRep_Tool::Surface(f); + } + } else { + return; //"PartDesign::Point: Invalid reference type" + } + } + + if ((base != NULL) && (direction != NULL)) { + // Line from other datum line. Nothing to be done + } else if ((p1 != NULL) && (p2 != NULL)) { + // Line from two points + base = new Base::Vector3d(*p1); + direction = new Base::Vector3d(*p2 - *p1); + } else if (line != NULL) { + // Line from gp_lin + base = new Base::Vector3d(line->Location().X(), line->Location().Y(), line->Location().Z()); + direction = new Base::Vector3d(line->Direction().X(), line->Direction().Y(), line->Direction().Z()); + } else if (!s1.IsNull() && !s2.IsNull()) { + GeomAPI_IntSS intersectorSS(s1, s2, Precision::Confusion()); + if (!intersectorSS.IsDone() || (intersectorSS.NbLines() == 0)) + return; + if (intersectorSS.NbLines() > 1) + Base::Console().Warning("More than one intersection line for datum point from surfaces"); + Handle_Geom_Line l = Handle_Geom_Line::DownCast(intersectorSS.Line(1)); + if (l.IsNull()) + return; // non-linear intersection curve + gp_Lin lin = l->Lin(); + base = new Base::Vector3d(lin.Location().X(), lin.Location().Y(), lin.Location().Z()); + direction = new Base::Vector3d(lin.Direction().X(), lin.Direction().Y(), lin.Direction().Z()); + } else { + return; + } + + _Base.setValue(*base); + _Direction.setValue(*direction); + _Base.touch(); // This triggers ViewProvider::updateData() + + // Create a shape, which will be used by the Sketcher. Them main function is to avoid a dependency of + // Sketcher on the PartDesign module + BRepBuilderAPI_MakeEdge builder(gp_Lin(gp_Pnt(base->x, base->y, base->z), gp_Dir(direction->x, direction->y, direction->z))); + if (!builder.IsDone()) + return; + Shape.setValue(builder.Shape()); + + delete base; + delete direction; + if (p1 != NULL) delete p1; + if (p2 != NULL) delete p2; + if (line != NULL) delete line; + } + + Part::Datum::onChanged(prop); +} + + +const std::set Line::getHint() +{ + if (hints.find(refTypes) != hints.end()) + return hints[refTypes]; + else + return std::set(); +} diff --git a/src/Mod/PartDesign/App/DatumLine.h b/src/Mod/PartDesign/App/DatumLine.h new file mode 100644 index 000000000..6ca976106 --- /dev/null +++ b/src/Mod/PartDesign/App/DatumLine.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (c) 2013 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 * + * * + ***************************************************************************/ + + +#ifndef PARTDESIGN_DATUMLINE_H +#define PARTDESIGN_DATUMLINE_H + +#include +#include +#include +#include + +namespace PartDesign +{ + +class PartDesignExport Line : public Part::Datum +{ + PROPERTY_HEADER(PartDesign::Line); + +public: + App::PropertyVector _Base; + App::PropertyVector _Direction; + + Line(); + virtual ~Line(); + + const char* getViewProviderName(void) const { + return "PartDesignGui::ViewProviderDatumLine"; + } + + static void initHints(); + const std::set getHint(); + +protected: + virtual void onChanged(const App::Property* prop); + +private: + // Hints on what further references are required/possible on this feature for a given set of references + static std::map, std::set > hints; +}; + +} //namespace PartDesign + + +#endif // PARTDESIGN_DATUMLINE_H diff --git a/src/Mod/PartDesign/App/DatumPlane.cpp b/src/Mod/PartDesign/App/DatumPlane.cpp new file mode 100644 index 000000000..6c7691388 --- /dev/null +++ b/src/Mod/PartDesign/App/DatumPlane.cpp @@ -0,0 +1,317 @@ +/*************************************************************************** + * Copyright (c) 2013 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 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include +#include "DatumPoint.h" +#include "DatumLine.h" +#include "DatumPlane.h" +#include +#include +#include +#include "Mod/Part/App/PrimitiveFeature.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +using namespace PartDesign; + +// Note: We don't distinguish between e.g. datum lines and edges here +#define PLANE QObject::tr("DPLANE") +#define LINE QObject::tr("DLINE") +#define POINT QObject::tr("DPOINT") +#define ANGLE QObject::tr("Angle") + +std::map, std::set > Plane::hints = std::map, std::set >(); + +void Plane::initHints() +{ + std::set DONE; + DONE.insert(QObject::tr("Done")); + + std::multiset key; + std::set value; + key.insert(PLANE); + hints[key] = DONE; // PLANE -> DONE. Plane from another plane or face + + key.clear(); value.clear(); + key.insert(POINT); + value.insert(POINT); value.insert(LINE); + hints[key] = value; // POINT -> POINT or LINE + + key.clear(); value.clear(); + key.insert(POINT); key.insert(LINE); + hints[key] = DONE; // {POINT, LINE} -> DONE. Plane from point/vertex and line/edge + + key.clear(); value.clear(); + key.insert(POINT); key.insert(POINT); + value.insert(POINT); + hints[key] = value; // {POINT, POINT} -> POINT + + key.clear(); value.clear(); + key.insert(POINT); key.insert(POINT); key.insert(POINT); + hints[key] = DONE; // {POINT, POINT, POINT} -> DONE. Plane from 3 points or vertices + + key.clear(); value.clear(); + key.insert(LINE); + value.insert(POINT); value.insert(PLANE); value.insert(ANGLE); + hints[key] = value; // LINE -> POINT or PLANE or ANGLE + + key.clear(); value.clear(); + key.insert(PLANE); key.insert(LINE); + value.insert(ANGLE); + hints[key] = value; // {PLANE, LINE} -> ANGLE + + key.clear(); value.clear(); + key.insert(PLANE); key.insert(ANGLE); + value.insert(LINE); + hints[key] = value; // {PLANE, ANGLE} -> LINE + + key.clear(); value.clear(); + key.insert(ANGLE); key.insert(LINE); + value.insert(PLANE); + hints[key] = value; // {ANGLE, LINE} -> PLANE + + key.clear(); value.clear(); + key.insert(LINE); key.insert(PLANE); key.insert(ANGLE); + hints[key] = DONE; // {LINE, PLANE, ANGLE} -> DONE. Plane through line with angle to other plane + + + key.clear(); value.clear(); + value.insert(POINT); value.insert(LINE); value.insert(PLANE); value.insert(ANGLE); + hints[key] = value; +} + +// ============================================================================ + +PROPERTY_SOURCE(PartDesign::Plane, Part::Datum) + +Plane::Plane() +{ +ADD_PROPERTY_TYPE(_Base,(Base::Vector3d(0,0,0)),"DatumPlane", + App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Coordinates of the plane base point"); +ADD_PROPERTY_TYPE(_Normal,(Base::Vector3d(1,1,1)),"DatumPlane", + App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Coordinates of the plane normal"); +} + +void Plane::onChanged(const App::Property *prop) +{ + Datum::onChanged(prop); + + if (prop == &Angle) { + // Zero value counts as angle not defined + if (fabs(Angle.getValue()) > Precision::Confusion()) + refTypes.insert(ANGLE); + else + refTypes.erase(ANGLE); + } + + if (prop == &References) { + refTypes.clear(); + std::vector refs = References.getValues(); + std::vector refnames = References.getSubValues(); + + for (int r = 0; r < refs.size(); r++) + refTypes.insert(getRefType(refs[r], refnames[r])); + + if (fabs(Angle.getValue()) > Precision::Confusion()) + refTypes.insert(ANGLE); + + std::set hint = getHint(); + if (!((hint.size() == 1) && (hint.find(QObject::tr("Done")) != hint.end()))) + return; // incomplete references + + // Extract the geometry of the references + Base::Vector3d* p1 = NULL; + Base::Vector3d* p2 = NULL; + Base::Vector3d* p3 = NULL; + Base::Vector3d* normal = NULL; + gp_Lin* line = NULL; + + for (int i = 0; i < refs.size(); i++) { + if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Point::getClassTypeId())) { + PartDesign::Point* p = static_cast(refs[i]); + if (p1 == NULL) + p1 = new Base::Vector3d (p->_Point.getValue()); + else if (p2 == NULL) + p2 = new Base::Vector3d (p->_Point.getValue()); + else + p3 = new Base::Vector3d (p->_Point.getValue()); + } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Line::getClassTypeId())) { + PartDesign::Line* l = static_cast(refs[i]); + Base::Vector3d base = l->_Base.getValue(); + Base::Vector3d dir = l->_Direction.getValue(); + line = new gp_Lin(gp_Pnt(base.x, base.y, base.z), gp_Dir(dir.x, dir.y, dir.z)); + } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Plane::getClassTypeId())) { + PartDesign::Plane* p = static_cast(refs[i]); + p1 = new Base::Vector3d(p->_Base.getValue()); + normal = new Base::Vector3d(p->_Normal.getValue()); + } else if (refs[i]->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { + App::Plane* p = static_cast(refs[i]); + // Note: We only handle the three base planes here + p1 = new Base::Vector3d(0,0,0); + normal = new Base::Vector3d; + if (strcmp(p->getNameInDocument(), "BaseplaneXY") == 0) + *normal = Base::Vector3d(0,0,1); + else if (strcmp(p->getNameInDocument(), "BaseplaneYZ") == 0) + *normal = Base::Vector3d(1,0,0); + else if (strcmp(p->getNameInDocument(), "BaseplaneXZ") == 0) + *normal = Base::Vector3d(0,1,0); + } else if (refs[i]->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { + Part::Feature* feature = static_cast(refs[i]); + const TopoDS_Shape& sh = feature->Shape.getValue(); + if (sh.IsNull()) + return; // "PartDesign::Plane: Reference has NULL shape" + // Get subshape + TopoDS_Shape subshape = feature->Shape.getShape().getSubShape(refnames[i].c_str()); + if (subshape.IsNull()) + return; // "PartDesign::Plane: Reference has NULL subshape"; + + if (subshape.ShapeType() == TopAbs_VERTEX) { + TopoDS_Vertex v = TopoDS::Vertex(subshape); + gp_Pnt p = BRep_Tool::Pnt(v); + if (p1 == NULL) + p1 = new Base::Vector3d(p.X(), p.Y(), p.Z()); + else if (p2 == NULL) + p2 = new Base::Vector3d(p.X(), p.Y(), p.Z()); + else + p3 = new Base::Vector3d(p.X(), p.Y(), p.Z()); + } else if (subshape.ShapeType() == TopAbs_EDGE) { + TopoDS_Edge e = TopoDS::Edge(subshape); + BRepAdaptor_Curve adapt(e); + if (adapt.GetType() != GeomAbs_Line) + return; // Non-linear edge + line = new gp_Lin(adapt.Line()); + } else if (subshape.ShapeType() == TopAbs_FACE) { + TopoDS_Face f = TopoDS::Face(subshape); + BRepAdaptor_Surface adapt(f); + if (adapt.GetType() != GeomAbs_Plane) + return; // Non-planar face + gp_Pnt b = adapt.Plane().Location(); + gp_Dir d = adapt.Plane().Axis().Direction(); + p1 = new Base::Vector3d(b.X(), b.Y(), b.Z()); + normal = new Base::Vector3d(d.X(), d.Y(), d.Z()); + } + } else { + return; //"PartDesign::Plane: Invalid reference type" + } + } + + if ((line != NULL) && (normal != NULL) && (p1 != NULL) && (fabs(Angle.getValue()) > Precision::Confusion())) { + // plane from line, plane, and angle to plane + *normal = normal->Normalize(); + gp_Pnt p = line->Location(); + *p1 = Base::Vector3d(p.X(), p.Y(), p.Z()); + gp_Dir dir = line->Direction(); + Base::Rotation rot(Base::Vector3d(dir.X(), dir.Y(), dir.Z()), Angle.getValue() / 180.0 * M_PI); + rot.multVec(*normal, *normal); + } else if ((p1 != NULL) && (normal != NULL)) { + // plane from other plane. Nothing to be done + } else if ((p1 != NULL) && (p2 != NULL) && (p3 != NULL)) { + // Plane from three points + Base::Vector3d vec1 = *p2 - *p1; + Base::Vector3d vec2 = *p3 - *p1; + normal = new Base::Vector3d(vec1 % vec2); + } else if ((line != NULL) && (p1 != NULL)) { + // Plane from point and line + p2 = new Base::Vector3d(line->Location().X(), line->Location().Y(), line->Location().Z()); + gp_Pnt p(line->Location().X() + line->Direction().X(), line->Location().Y() + line->Direction().Y(), line->Location().Z() + line->Direction().Z()); + p3 = new Base::Vector3d(p.X(), p.Y(), p.Z()); + Base::Vector3d vec1 = *p2 - *p1; + Base::Vector3d vec2 = *p3 - *p1; + normal = new Base::Vector3d(vec1 % vec2); + } else { + return; + } + + *normal = normal->Normalize(); + + if (fabs(Offset.getValue()) > Precision::Confusion()) + *p1 += Offset.getValue() * *normal; + + _Base.setValue(*p1); + _Normal.setValue(*normal); + _Base.touch(); // This triggers ViewProvider::updateData() + + // Create a shape, which will be used by the Sketcher. Them main function is to avoid a dependency of + // Sketcher on the PartDesign module + BRepBuilderAPI_MakeFace builder(gp_Pln(gp_Pnt(p1->x, p1->y, p1->z), gp_Dir(normal->x, normal->y, normal->z))); + if (!builder.IsDone()) + return; + Shape.setValue(builder.Shape()); + + delete p1; + delete normal; + if (p2 != NULL) delete p2; + if (p3 != NULL) delete p3; + if (line != NULL) delete line; + } + + Part::Datum::onChanged(prop); +} + + +const std::set Plane::getHint() +{ + if (hints.find(refTypes) != hints.end()) + return hints[refTypes]; + else + return std::set(); +} diff --git a/src/Mod/PartDesign/App/DatumPlane.h b/src/Mod/PartDesign/App/DatumPlane.h new file mode 100644 index 000000000..03e2026b3 --- /dev/null +++ b/src/Mod/PartDesign/App/DatumPlane.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (c) 2013 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 * + * * + ***************************************************************************/ + + +#ifndef PARTDESIGN_DATUMPLANE_H +#define PARTDESIGN_DATUMPLANE_H + +#include +#include +#include + +namespace PartDesign +{ + +class PartDesignExport Plane : public Part::Datum +{ + PROPERTY_HEADER(PartDesign::Plane); + +public: + App::PropertyVector _Base; + App::PropertyVector _Normal; + + Plane(); + + const char* getViewProviderName(void) const { + return "PartDesignGui::ViewProviderDatumPlane"; + } + + static void initHints(); + const std::set getHint(); + +protected: + virtual void onChanged(const App::Property* prop); + +private: + // Hints on what further references are required/possible on this feature for a given set of references + static std::map, std::set > hints; +}; + +} //namespace PartDesign + + +#endif // PARTDESIGN_DATUMPLANE_H diff --git a/src/Mod/PartDesign/App/DatumPoint.cpp b/src/Mod/PartDesign/App/DatumPoint.cpp new file mode 100644 index 000000000..e9f5d3387 --- /dev/null +++ b/src/Mod/PartDesign/App/DatumPoint.cpp @@ -0,0 +1,331 @@ +/*************************************************************************** + * Copyright (c) 2013 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 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include +#include "DatumPoint.h" +#include "DatumLine.h" +#include "DatumPlane.h" +#include +#include +#include +#include "Mod/Part/App/PrimitiveFeature.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +using namespace PartDesign; + +// Note: We don't distinguish between e.g. datum lines and edges here +#define PLANE QObject::tr("DPLANE") +#define LINE QObject::tr("DLINE") +#define POINT QObject::tr("DPOINT") +#define ANGLE QObject::tr("Angle") + +// ================================ Initialize the hints ===================== + +std::map, std::set > Point::hints = std::map, std::set >(); + +void Point::initHints() +{ + std::set DONE; + DONE.insert(QObject::tr("Done")); + + std::multiset key; + std::set value; + key.insert(POINT); + hints[key] = DONE; // POINT -> DONE. Point from another point or vertex + + key.clear(); value.clear(); + key.insert(LINE); + value.insert(LINE); value.insert(PLANE); + hints[key] = value; // LINE -> LINE or PLANE + + key.clear(); value.clear(); + key.insert(LINE); key.insert(LINE); + hints[key] = DONE; // {LINE, LINE} -> DONE. Point from two lines or edges + + key.clear(); value.clear(); + key.insert(LINE); key.insert(PLANE); + hints[key] = DONE; // {LINE, PLANE} -> DONE. Point from line and plane + + key.clear(); value.clear(); + key.insert(PLANE); + value.insert(PLANE); value.insert(LINE); + hints[key] = value; // PLANE -> PLANE or LINE + + key.clear(); value.clear(); + key.insert(PLANE); key.insert(PLANE); + value.insert(PLANE); + hints[key] = value; // {PLANE, PLANE} -> PLANE + + key.clear(); value.clear(); + key.insert(PLANE); key.insert(PLANE); key.insert(PLANE); + hints[key] = DONE; // {PLANE, PLANE, PLANE} -> DONE. Point from three planes + + key.clear(); value.clear(); + value.insert(POINT); value.insert(LINE); value.insert(PLANE); + hints[key] = value; +} + +// ============================================================================ + +PROPERTY_SOURCE(PartDesign::Point, Part::Datum) + +Point::Point() +{ + ADD_PROPERTY_TYPE(_Point,(Base::Vector3d(0,0,0)),"DatumPoint", + App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Coordinates of the datum point"); +} + +Point::~Point() +{ +} + +void Point::onChanged(const App::Property* prop) +{ + if (prop == &References) { + refTypes.clear(); + std::vector refs = References.getValues(); + std::vector refnames = References.getSubValues(); + + for (int r = 0; r < refs.size(); r++) + refTypes.insert(getRefType(refs[r], refnames[r])); + + std::set hint = getHint(); + if (!((hint.size() == 1) && (hint.find(QObject::tr("Done")) != hint.end()))) + return; // incomplete references + + // Extract the geometry of the references + Base::Vector3d* point = NULL; + Handle_Geom_Curve c1 = NULL; + Handle_Geom_Curve c2 = NULL; + Handle_Geom_Surface s1 = NULL; + Handle_Geom_Surface s2 = NULL; + Handle_Geom_Surface s3 = NULL; + + for (int i = 0; i < refs.size(); i++) { + if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Point::getClassTypeId())) { + PartDesign::Point* p = static_cast(refs[i]); + point = new Base::Vector3d (p->_Point.getValue()); + } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Line::getClassTypeId())) { + PartDesign::Line* l = static_cast(refs[i]); + Base::Vector3d base = l->_Base.getValue(); + Base::Vector3d dir = l->_Direction.getValue(); + if (c1.IsNull()) + c1 = new Geom_Line(gp_Pnt(base.x, base.y, base.z), gp_Dir(dir.x, dir.y, dir.z)); + else + c2 = new Geom_Line(gp_Pnt(base.x, base.y, base.z), gp_Dir(dir.x, dir.y, dir.z)); + } else if (refs[i]->getTypeId().isDerivedFrom(PartDesign::Plane::getClassTypeId())) { + PartDesign::Plane* p = static_cast(refs[i]); + Base::Vector3d base = p->_Base.getValue(); + Base::Vector3d normal = p->_Normal.getValue(); + if (s1.IsNull()) + s1 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); + else if (s2.IsNull()) + s2 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); + else + s3 = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); + } else if (refs[i]->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { + App::Plane* p = static_cast(refs[i]); + // Note: We only handle the three base planes here + gp_Pnt base(0,0,0); + gp_Dir normal; + if (strcmp(p->getNameInDocument(), "BaseplaneXY") == 0) + normal = gp_Dir(0,0,1); + else if (strcmp(p->getNameInDocument(), "BaseplaneYZ") == 0) + normal = gp_Dir(1,0,0); + else if (strcmp(p->getNameInDocument(), "BaseplaneXZ") == 0) + normal = gp_Dir(0,1,0); + + if (s1.IsNull()) + s1 = new Geom_Plane(base, normal); + else if (s2.IsNull()) + s2 = new Geom_Plane(base, normal); + else + s3 = new Geom_Plane(base, normal); + } else if (refs[i]->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { + Part::Feature* feature = static_cast(refs[i]); + const TopoDS_Shape& sh = feature->Shape.getValue(); + if (sh.IsNull()) + return; // "PartDesign::Point: Reference has NULL shape" + // Get subshape + TopoDS_Shape subshape = feature->Shape.getShape().getSubShape(refnames[i].c_str()); + if (subshape.IsNull()) + return; // "PartDesign::Point: Reference has NULL subshape"; + + if (subshape.ShapeType() == TopAbs_VERTEX) { + TopoDS_Vertex v = TopoDS::Vertex(subshape); + gp_Pnt p = BRep_Tool::Pnt(v); + point = new Base::Vector3d(p.X(), p.Y(), p.Z()); + } else if (subshape.ShapeType() == TopAbs_EDGE) { + TopoDS_Edge e = TopoDS::Edge(subshape); + Standard_Real first, last; + if (c1.IsNull()) + c1 = BRep_Tool::Curve(e, first, last); + else + c2 = BRep_Tool::Curve(e, first, last); + } else if (subshape.ShapeType() == TopAbs_FACE) { + TopoDS_Face f = TopoDS::Face(subshape); + if (s1.IsNull()) + s1 = BRep_Tool::Surface(f); + else if (s2.IsNull()) + s2 = BRep_Tool::Surface(f); + else + s3 = BRep_Tool::Surface(f); + } + } else { + return; //"PartDesign::Point: Invalid reference type" + } + } + + if (point != NULL) { + // Point from vertex or other point. Nothing to be done + } else if (!c1.IsNull()) { + if (!c2.IsNull()) { + // Point from intersection of two curves + GeomAPI_ExtremaCurveCurve intersector(c1, c2); + if ((intersector.LowerDistance() > Precision::Confusion()) || (intersector.NbExtrema() == 0)) + return; // No intersection + // Note: We don't check for multiple intersection points + gp_Pnt p, p2; + intersector.Points(1, p, p2); + point = new Base::Vector3d(p.X(), p.Y(), p.Z()); + } else if (!s1.IsNull()) { + GeomAPI_IntCS intersector(c1, s1); + if (!intersector.IsDone() || (intersector.NbPoints() == 0)) + return; + if (intersector.NbPoints() > 1) + Base::Console().Warning("More than one intersection point for datum point from curve and surface"); + + gp_Pnt p = intersector.Point(1); + point = new Base::Vector3d(p.X(), p.Y(), p.Z()); + } else + return; + } else if (!s1.IsNull() && !s2.IsNull() && !s3.IsNull()) { + GeomAPI_IntSS intersectorSS(s1, s2, Precision::Confusion()); + if (!intersectorSS.IsDone() || (intersectorSS.NbLines() == 0)) + return; + if (intersectorSS.NbLines() > 1) + Base::Console().Warning("More than one intersection line for datum point from surfaces"); + Handle_Geom_Curve line = intersectorSS.Line(1); + + GeomAPI_IntCS intersector(line, s3); + if (!intersector.IsDone() || (intersector.NbPoints() == 0)) + return; + if (intersector.NbPoints() > 1) + Base::Console().Warning("More than one intersection point for datum point from surfaces"); + + gp_Pnt p = intersector.Point(1); + point = new Base::Vector3d(p.X(), p.Y(), p.Z()); + } else { + return; + } + + _Point.setValue(*point); + _Point.touch(); // This triggers ViewProvider::updateData() + + // Create a shape, which will be used by the Sketcher. Them main function is to avoid a dependency of + // Sketcher on the PartDesign module + BRepBuilderAPI_MakeVertex builder(gp_Pnt(point->x, point->y, point->z)); + if (!builder.IsDone()) + return; + Shape.setValue(builder.Shape()); + + delete point; + } + + Part::Datum::onChanged(prop); +} + + +const std::set Point::getHint() +{ + if (hints.find(refTypes) != hints.end()) + return hints[refTypes]; + else + return std::set(); +} + +namespace PartDesign { + +const QString getRefType(const App::DocumentObject* obj, const std::string& subname) +{ + Base::Type type = obj->getTypeId(); + + if ((type == App::Plane::getClassTypeId()) || (type == PartDesign::Plane::getClassTypeId())) + return PLANE; + else if (type == PartDesign::Line::getClassTypeId()) + return LINE; + else if (type == PartDesign::Point::getClassTypeId()) + return POINT; + else if (type.isDerivedFrom(Part::Feature::getClassTypeId())) { + // Note: For now, only planar references are possible + if (subname.size() > 4 && subname.substr(0,4) == "Face") + return PLANE; + else if (subname.size() > 4 && subname.substr(0,4) == "Edge") + return LINE; + else if (subname.size() > 6 && subname.substr(0,6) == "Vertex") + return POINT; + } + + throw Base::Exception("Part::Datum::getRefType(): Illegal object type"); +} + +} diff --git a/src/Mod/PartDesign/App/DatumPoint.h b/src/Mod/PartDesign/App/DatumPoint.h new file mode 100644 index 000000000..e36abf1a3 --- /dev/null +++ b/src/Mod/PartDesign/App/DatumPoint.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (c) 2013 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 * + * * + ***************************************************************************/ + + +#ifndef PARTDESIGN_DATUMPOINT_H +#define PARTDESIGN_DATUMPOINT_H + +#include +#include +#include +#include + +namespace PartDesign +{ + +class PartDesignExport Point : public Part::Datum +{ + PROPERTY_HEADER(PartDesign::Point); + +public: + App::PropertyVector _Point; + + Point(); + virtual ~Point(); + + const char* getViewProviderName(void) const { + return "PartDesignGui::ViewProviderDatumPoint"; + } + + static void initHints(); + const std::set getHint(); + + + +protected: + virtual void onChanged(const App::Property* prop); + +private: + // Hints on what further references are required/possible on this feature for a given set of references + static std::map, std::set > hints; +}; + +// This has to be declared somewhere... a good place would be Part::Datum but since the code requires +// access to PartDesign::Point etc. that's not possible +const QString getRefType(const App::DocumentObject* obj, const std::string& subname); + +} //namespace PartDesign + + +#endif // PARTDESIGN_DATUMPOINT_H diff --git a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp index 2b26c6afe..47b6930d3 100644 --- a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp +++ b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp @@ -48,7 +48,9 @@ #include "ViewProviderPolarPattern.h" #include "ViewProviderScaled.h" #include "ViewProviderMultiTransform.h" -#include "ViewProviderDatum.h" +#include "ViewProviderDatumPoint.h" +#include "ViewProviderDatumLine.h" +#include "ViewProviderDatumPlane.h" // use a different name to CreateCommand() void CreatePartDesignCommands(void); diff --git a/src/Mod/PartDesign/Gui/CMakeLists.txt b/src/Mod/PartDesign/Gui/CMakeLists.txt index f3a67b09b..e9f868e8d 100644 --- a/src/Mod/PartDesign/Gui/CMakeLists.txt +++ b/src/Mod/PartDesign/Gui/CMakeLists.txt @@ -106,6 +106,12 @@ SET(PartDesignGuiViewProvider_SRCS ViewProviderMultiTransform.h ViewProviderDatum.cpp ViewProviderDatum.h + ViewProviderDatumPoint.cpp + ViewProviderDatumPoint.h + ViewProviderDatumLine.cpp + ViewProviderDatumLine.h + ViewProviderDatumPlane.cpp + ViewProviderDatumPlane.h ) SOURCE_GROUP("ViewProvider" FILES ${PartDesignGuiViewProvider_SRCS}) diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index 48178f0b4..e5ea1da9f 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -63,13 +63,15 @@ #include #include -#include #include #include #include #include #include #include +#include +#include +#include #include "FeaturePickDialog.h" #include "Workbench.h" diff --git a/src/Mod/PartDesign/Gui/ReferenceSelection.cpp b/src/Mod/PartDesign/Gui/ReferenceSelection.cpp index 6bec9ac39..60b25bf12 100644 --- a/src/Mod/PartDesign/Gui/ReferenceSelection.cpp +++ b/src/Mod/PartDesign/Gui/ReferenceSelection.cpp @@ -34,7 +34,9 @@ #include #include #include -#include +#include +#include +#include #include "ReferenceSelection.h" #include "Workbench.h" @@ -49,8 +51,8 @@ bool ReferenceSelection::allow(App::Document* pDoc, App::DocumentObject* pObj, c // Note: It is assumed that a Part has exactly 3 App::Plane objects at the root of the feature tree return true; - if (pObj->getTypeId().isDerivedFrom(PartDesign::Datum::getClassTypeId())) { - // Allow selecting PartDesign::Datum features + if (pObj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) { + // Allow selecting Part::Datum features if (!ActivePartObject->hasFeature(pObj)) return false; diff --git a/src/Mod/PartDesign/Gui/TaskDatumParameters.cpp b/src/Mod/PartDesign/Gui/TaskDatumParameters.cpp index d608c8544..132427f93 100644 --- a/src/Mod/PartDesign/Gui/TaskDatumParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskDatumParameters.cpp @@ -45,7 +45,7 @@ #include #include #include -#include +#include #include #include "ReferenceSelection.h" #include "Workbench.h" @@ -62,7 +62,7 @@ const QString makeRefString(const App::DocumentObject* obj, const std::string& s return QObject::tr("No reference selected"); if (obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) || - obj->getTypeId().isDerivedFrom(PartDesign::Datum::getClassTypeId())) + obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) // App::Plane or Datum feature return QString::fromAscii(obj->getNameInDocument()); @@ -81,7 +81,7 @@ const QString makeRefString(const App::DocumentObject* obj, const std::string& s } void TaskDatumParameters::makeRefStrings(std::vector& refstrings, std::vector& refnames) { - PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + Part::Datum* pcDatum = static_cast(DatumView->getObject()); std::vector refs = pcDatum->References.getValues(); refnames = pcDatum->References.getSubValues(); @@ -139,7 +139,7 @@ TaskDatumParameters::TaskDatumParameters(ViewProviderDatum *DatumView,QWidget *p ui->lineRef3->blockSignals(true); // Get the feature data - PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + Part::Datum* pcDatum = static_cast(DatumView->getObject()); //std::vector refs = pcDatum->References.getValues(); std::vector refnames = pcDatum->References.getSubValues(); @@ -203,7 +203,7 @@ void TaskDatumParameters::updateUI() ui->spinOffset->setVisible(false); } - PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + Part::Datum* pcDatum = static_cast(DatumView->getObject()); std::vector refs = pcDatum->References.getValues(); completed = false; @@ -308,7 +308,7 @@ void TaskDatumParameters::onSelectionChanged(const Gui::SelectionChanges& msg) return; // Note: The validity checking has already been done in ReferenceSelection.cpp - PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + Part::Datum* pcDatum = static_cast(DatumView->getObject()); std::vector refs = pcDatum->References.getValues(); std::vector refnames = pcDatum->References.getSubValues(); App::DocumentObject* selObj = pcDatum->getDocument()->getObject(msg.pObjectName); @@ -317,7 +317,7 @@ void TaskDatumParameters::onSelectionChanged(const Gui::SelectionChanges& msg) // Remove subname for planes and datum features if (selObj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) || - selObj->getTypeId().isDerivedFrom(PartDesign::Datum::getClassTypeId())) + selObj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) subname = ""; // eliminate duplicate selections @@ -349,7 +349,7 @@ void TaskDatumParameters::onSelectionChanged(const Gui::SelectionChanges& msg) void TaskDatumParameters::onOffsetChanged(double val) { - PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + Part::Datum* pcDatum = static_cast(DatumView->getObject()); pcDatum->Offset.setValue(val); pcDatum->getDocument()->recomputeFeature(pcDatum); updateUI(); @@ -357,7 +357,7 @@ void TaskDatumParameters::onOffsetChanged(double val) void TaskDatumParameters::onAngleChanged(double val) { - PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + Part::Datum* pcDatum = static_cast(DatumView->getObject()); pcDatum->Angle.setValue(val); pcDatum->getDocument()->recomputeFeature(pcDatum); updateUI(); @@ -365,14 +365,14 @@ void TaskDatumParameters::onAngleChanged(double val) void TaskDatumParameters::onCheckFlip(bool on) { - PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + Part::Datum* pcDatum = static_cast(DatumView->getObject()); //pcDatum->Reversed.setValue(on); pcDatum->getDocument()->recomputeFeature(pcDatum); } void TaskDatumParameters::onButtonRef(const bool pressed, const int idx) { - // Note: Even if there is no solid, App::Plane and PartDesign::Datum can still be selected + // Note: Even if there is no solid, App::Plane and Part::Datum can still be selected App::DocumentObject* solid = PartDesignGui::ActivePartObject->getPrevSolidFeature(); if (pressed) { @@ -410,7 +410,7 @@ void TaskDatumParameters::onRefName(const QString& text, const int idx) if (text.length() == 0) { // Reference was removed // Update the reference list - PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + Part::Datum* pcDatum = static_cast(DatumView->getObject()); std::vector refs = pcDatum->References.getValues(); std::vector refnames = pcDatum->References.getSubValues(); std::vector newrefs; @@ -439,7 +439,7 @@ void TaskDatumParameters::onRefName(const QString& text, const int idx) QStringList parts = text.split(QChar::fromAscii(':')); if (parts.length() < 2) parts.push_back(QString::fromAscii("")); - // Check whether this is the name of an App::Plane or PartDesign::Datum feature + // Check whether this is the name of an App::Plane or Part::Datum feature App::DocumentObject* obj = DatumView->getObject()->getDocument()->getObject(parts[0].toAscii()); if (obj == NULL) return; @@ -448,7 +448,7 @@ void TaskDatumParameters::onRefName(const QString& text, const int idx) if (obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { // everything is OK (we assume a Part can only have exactly 3 App::Plane objects located at the base of the feature tree) subElement = ""; - } else if (obj->getTypeId().isDerivedFrom(PartDesign::Datum::getClassTypeId())) { + } else if (obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) { if (!PartDesignGui::ActivePartObject->hasFeature(obj)) return; subElement = ""; @@ -484,7 +484,7 @@ void TaskDatumParameters::onRefName(const QString& text, const int idx) subElement = ss.str(); } - PartDesign::Datum* pcDatum = static_cast(DatumView->getObject()); + Part::Datum* pcDatum = static_cast(DatumView->getObject()); std::vector refs = pcDatum->References.getValues(); std::vector refnames = pcDatum->References.getSubValues(); if (idx < refs.size()) { diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp b/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp index 46773c7cf..badcc5176 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp @@ -52,7 +52,9 @@ #include "ViewProviderDatum.h" #include "TaskDatumParameters.h" #include "Workbench.h" -#include +#include +#include +#include #include #include #include @@ -77,12 +79,12 @@ void ViewProviderDatum::attach(App::DocumentObject *obj) { ViewProviderGeometryObject::attach(obj); - PartDesign::Datum* pcDatum = static_cast(getObject()); - if (pcDatum->getTypeId() == PartDesign::Plane::getClassTypeId()) + App::DocumentObject* o = getObject(); + if (o->getTypeId() == PartDesign::Plane::getClassTypeId()) datumType = QObject::tr("Plane"); - else if (pcDatum->getTypeId() == PartDesign::Line::getClassTypeId()) + else if (o->getTypeId() == PartDesign::Line::getClassTypeId()) datumType = QObject::tr("Line"); - else if (pcDatum->getTypeId() == PartDesign::Point::getClassTypeId()) + else if (o->getTypeId() == PartDesign::Point::getClassTypeId()) datumType = QObject::tr("Point"); SoSeparator* sep = new SoSeparator(); @@ -265,274 +267,3 @@ void ViewProviderDatum::unsetEdit(int ModNum) } } -PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumPoint,PartDesignGui::ViewProviderDatum) - -ViewProviderDatumPoint::ViewProviderDatumPoint() -{ - SoMarkerSet* points = new SoMarkerSet(); - points->markerIndex = SoMarkerSet::DIAMOND_FILLED_9_9; - points->numPoints = 0; - pShapeSep->addChild(points); -} - -ViewProviderDatumPoint::~ViewProviderDatumPoint() -{ - -} - -void ViewProviderDatumPoint::updateData(const App::Property* prop) -{ - // Gets called whenever a property of the attached object changes - PartDesign::Point* pcDatum = static_cast(this->getObject()); - - if (strcmp(prop->getName(),"_Point") == 0) { - Base::Vector3d p = pcDatum->_Point.getValue(); - SoMFVec3f v; - v.setNum(1); - v.set1Value(0, p.x, p.y, p.z); - SoMarkerSet* points = static_cast(pShapeSep->getChild(0)); - - SoVertexProperty* vprop; - if (points->vertexProperty.getValue() == NULL) { - vprop = new SoVertexProperty(); - vprop->vertex = v; - points->vertexProperty = vprop; - } else { - vprop = static_cast(points->vertexProperty.getValue()); - vprop->vertex = v; - } - - points->numPoints = 1; - } - - ViewProviderDatum::updateData(prop); -} - -PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumLine,PartDesignGui::ViewProviderDatum) - -ViewProviderDatumLine::ViewProviderDatumLine() -{ - SoMaterial* material = new SoMaterial(); - material->diffuseColor.setValue(0.9f, 0.9f, 0.13); - material->transparency.setValue(0.2); - pShapeSep->addChild(material); -} - -ViewProviderDatumLine::~ViewProviderDatumLine() -{ - -} - -void ViewProviderDatumLine::updateData(const App::Property* prop) -{ - // Gets called whenever a property of the attached object changes - PartDesign::Line* pcDatum = static_cast(this->getObject()); - - if (strcmp(prop->getName(),"_Base") == 0) { - Base::Vector3d base = pcDatum->_Base.getValue(); - Base::Vector3d dir = pcDatum->_Direction.getValue(); - - // Get limits of the line from bounding box of the body - PartDesign::Body* body = PartDesign::Body::findBodyOf(this->getObject()); - if (body == NULL) - return; - Part::Feature* tipSolid = static_cast(body->getPrevSolidFeature()); - if (tipSolid == NULL) - return; - Base::BoundBox3d bbox = tipSolid->Shape.getShape().getBoundBox(); - bbox.Enlarge(0.1 * bbox.CalcDiagonalLength()); - Base::Vector3d p1, p2; - if (bbox.IsInBox(base)) { - bbox.IntersectionPoint(base, dir, p1, Precision::Confusion()); - bbox.IntersectionPoint(base, -dir, p2, Precision::Confusion()); - } else { - bbox.IntersectWithLine(base, dir, p1, p2); - if ((p1 == Base::Vector3d(0,0,0)) && (p2 == Base::Vector3d(0,0,0))) - bbox.IntersectWithLine(base, -dir, p1, p2); - } - - // Display the line - SoMFVec3f v; - v.setNum(2); - v.set1Value(0, p1.x, p1.y, p1.z); - v.set1Value(1, p2.x, p2.y, p2.z); - SoMFInt32 idx; - idx.setNum(1); - idx.set1Value(0, 2); - - SoLineSet* lineSet; - SoVertexProperty* vprop; - - if (pShapeSep->getNumChildren() == 1) { - lineSet = new SoLineSet(); - vprop = new SoVertexProperty(); - vprop->vertex = v; - lineSet->vertexProperty = vprop; - lineSet->numVertices = idx; - pShapeSep->addChild(lineSet); - } else { - lineSet = static_cast(pShapeSep->getChild(1)); - vprop = static_cast(lineSet->vertexProperty.getValue()); - vprop->vertex = v; - lineSet->numVertices = idx; - } - } - - ViewProviderDatum::updateData(prop); -} - -PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumPlane,PartDesignGui::ViewProviderDatum) - -ViewProviderDatumPlane::ViewProviderDatumPlane() -{ - SoMaterial* material = new SoMaterial(); - material->diffuseColor.setValue(0.9f, 0.9f, 0.13); - material->transparency.setValue(0.2); - pShapeSep->addChild(material); -} - -ViewProviderDatumPlane::~ViewProviderDatumPlane() -{ - -} - -void ViewProviderDatumPlane::updateData(const App::Property* prop) -{ - // Gets called whenever a property of the attached object changes - PartDesign::Plane* pcDatum = static_cast(this->getObject()); - - if (strcmp(prop->getName(),"_Base") == 0) { - Base::Vector3d base = pcDatum->_Base.getValue(); - Base::Vector3d normal = pcDatum->_Normal.getValue(); - - // Get limits of the plane from bounding box of the body - PartDesign::Body* body = PartDesign::Body::findBodyOf(this->getObject()); - if (body == NULL) - return; - Part::Feature* tipSolid = static_cast(body->getPrevSolidFeature()); - if (tipSolid == NULL) - return; - Base::BoundBox3d bbox = tipSolid->Shape.getShape().getBoundBox(); - bbox.Enlarge(0.1 * bbox.CalcDiagonalLength()); - - // Calculate intersection of plane with bounding box edges - // TODO: This can be a lot more efficient if we do the maths ourselves, e.g. - // http://cococubed.asu.edu/code_pages/raybox.shtml - // http://www.fho-emden.de/~hoffmann/cubeplane12112006.pdf - Handle_Geom_Plane plane = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); - std::vector points; - - for (int i = 0; i < 12; i++) { - // Get the edge of the bounding box - Base::Vector3d p1, p2; - bbox.CalcDistance(i, p1, p2); - Base::Vector3d ldir = p2 - p1; - Handle_Geom_Line line = new Geom_Line(gp_Pnt(p1.x, p1.y, p1.z), gp_Dir(ldir.x, ldir.y, ldir.z)); - GeomAPI_IntCS intersector(line, plane); - if (!intersector.IsDone() || (intersector.NbPoints() == 0)) - continue; - gp_Pnt pnt = intersector.Point(1); - Base::Vector3d point(pnt.X(), pnt.Y(), pnt.Z()); - - // Check whether intersection is on the bbox edge (bbox.IsInside() always tests false) - double edgeLength = (p1 - p2).Length(); - double l1 = (p1 - point).Length(); - double l2 = (p2 - point).Length(); - if (fabs(edgeLength - l1 - l2) > 0.001) - continue; - - // Check for duplicates - bool duplicate = false; - for (std::vector::const_iterator p = points.begin(); p != points.end(); p++) { - if ((point - *p).Sqr() < Precision::Confusion()) { - duplicate = true; - break; - } - } - if (!duplicate) - points.push_back(point); - } - - if (points.size() < 3) - return; - - // Sort the points to get a proper polygon, see http://www.fho-emden.de/~hoffmann/cubeplane12112006.pdf p.5 - if (points.size() > 3) { - // Longest component of normal vector - int longest; - if (normal.x > normal.y) - if (normal.x > normal.z) - longest = 0; // x is longest - else - longest = 2; // z is longest - else - if (normal.y > normal.z) - longest = 1; // y is longest - else - longest = 2; // z is longest - - // mean value for intersection points - Base::Vector3d m; - for (std::vector::iterator p = points.begin(); p != points.end(); p++) - m += *p; - m /= points.size(); - - // Sort by angles - double a[points.size()]; - for (int i = 0; i < points.size() - 1; i++) { - if (longest == 0) - a[i] = atan2(points[i].z - m.z, points[i].y - m.y); - else if (longest == 1) - a[i] = atan2(points[i].z - m.z, points[i].x - m.x); - else - a[i] = atan2(points[i].y - m.y, points[i].x - m.x); - - for (int k = i+1; k < points.size(); k++) { - if (longest == 0) - a[k] = atan2(points[k].z - m.z, points[k].y - m.y); - else if (longest == 1) - a[k] = atan2(points[k].z - m.z, points[k].x - m.x); - else - a[k] = atan2(points[k].y - m.y, points[k].x - m.x); - - if (a[k] < a[i]) { - Base::Vector3d temp = points[i]; - points[i] = points[k]; - points[k] = temp; - a[i] = a[k]; - } - } - } - } - - // Display the plane - SoMFVec3f v; - v.setNum(points.size()); - for (int p = 0; p < points.size(); p++) - v.set1Value(p, points[p].x, points[p].y, points[p].z); - SoMFInt32 idx; - idx.setNum(1); - idx.set1Value(0, points.size()); - - SoFaceSet* faceSet; - SoVertexProperty* vprop; - - if (pShapeSep->getNumChildren() == 1) { - faceSet = new SoFaceSet(); - vprop = new SoVertexProperty(); - vprop->vertex = v; - faceSet->vertexProperty = vprop; - faceSet->numVertices = idx; - pShapeSep->addChild(faceSet); - } else { - faceSet = static_cast(pShapeSep->getChild(1)); - vprop = static_cast(faceSet->vertexProperty.getValue()); - vprop->vertex = v; - faceSet->numVertices = idx; - } - } - - ViewProviderDatum::updateData(prop); -} - - diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatum.h b/src/Mod/PartDesign/Gui/ViewProviderDatum.h index b2ef4f5b2..c73c2183a 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderDatum.h +++ b/src/Mod/PartDesign/Gui/ViewProviderDatum.h @@ -69,46 +69,6 @@ protected: }; -class PartDesignGuiExport ViewProviderDatumPoint : public PartDesignGui::ViewProviderDatum -{ - PROPERTY_HEADER(PartDesignGui::ViewProviderDatumPoint); - -public: - /// Constructor - ViewProviderDatumPoint(); - virtual ~ViewProviderDatumPoint(); - - virtual void updateData(const App::Property*); - -}; - -class PartDesignGuiExport ViewProviderDatumLine : public PartDesignGui::ViewProviderDatum -{ - PROPERTY_HEADER(PartDesignGui::ViewProviderDatumLine); - -public: - /// Constructor - ViewProviderDatumLine(); - virtual ~ViewProviderDatumLine(); - - virtual void updateData(const App::Property*); - -}; - -class PartDesignGuiExport ViewProviderDatumPlane : public PartDesignGui::ViewProviderDatum -{ - PROPERTY_HEADER(PartDesignGui::ViewProviderDatumPlane); - -public: - /// Constructor - ViewProviderDatumPlane(); - virtual ~ViewProviderDatumPlane(); - - virtual void updateData(const App::Property*); - -}; - - } // namespace PartDesignGui diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatumLine.cpp b/src/Mod/PartDesign/Gui/ViewProviderDatumLine.cpp new file mode 100644 index 000000000..00630a5e4 --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderDatumLine.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (c) 2013 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 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include "ViewProviderDatumLine.h" +#include "TaskDatumParameters.h" +#include "Workbench.h" +#include +#include +#include +#include +#include + +using namespace PartDesignGui; + +PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumLine,PartDesignGui::ViewProviderDatum) + +ViewProviderDatumLine::ViewProviderDatumLine() +{ + SoMaterial* material = new SoMaterial(); + material->diffuseColor.setValue(0.9f, 0.9f, 0.13); + material->transparency.setValue(0.2); + pShapeSep->addChild(material); +} + +ViewProviderDatumLine::~ViewProviderDatumLine() +{ + +} + +void ViewProviderDatumLine::updateData(const App::Property* prop) +{ + // Gets called whenever a property of the attached object changes + PartDesign::Line* pcDatum = static_cast(this->getObject()); + + if (strcmp(prop->getName(),"_Base") == 0) { + Base::Vector3d base = pcDatum->_Base.getValue(); + Base::Vector3d dir = pcDatum->_Direction.getValue(); + + // Get limits of the line from bounding box of the body + PartDesign::Body* body = PartDesign::Body::findBodyOf(this->getObject()); + if (body == NULL) + return; + Part::Feature* tipSolid = static_cast(body->getPrevSolidFeature()); + if (tipSolid == NULL) + return; + Base::BoundBox3d bbox = tipSolid->Shape.getShape().getBoundBox(); + bbox.Enlarge(0.1 * bbox.CalcDiagonalLength()); + Base::Vector3d p1, p2; + if (bbox.IsInBox(base)) { + bbox.IntersectionPoint(base, dir, p1, Precision::Confusion()); + bbox.IntersectionPoint(base, -dir, p2, Precision::Confusion()); + } else { + bbox.IntersectWithLine(base, dir, p1, p2); + if ((p1 == Base::Vector3d(0,0,0)) && (p2 == Base::Vector3d(0,0,0))) + bbox.IntersectWithLine(base, -dir, p1, p2); + } + + // Display the line + SoMFVec3f v; + v.setNum(2); + v.set1Value(0, p1.x, p1.y, p1.z); + v.set1Value(1, p2.x, p2.y, p2.z); + SoMFInt32 idx; + idx.setNum(1); + idx.set1Value(0, 2); + + SoLineSet* lineSet; + SoVertexProperty* vprop; + + if (pShapeSep->getNumChildren() == 1) { + lineSet = new SoLineSet(); + vprop = new SoVertexProperty(); + vprop->vertex = v; + lineSet->vertexProperty = vprop; + lineSet->numVertices = idx; + pShapeSep->addChild(lineSet); + } else { + lineSet = static_cast(pShapeSep->getChild(1)); + vprop = static_cast(lineSet->vertexProperty.getValue()); + vprop->vertex = v; + lineSet->numVertices = idx; + } + } + + ViewProviderDatum::updateData(prop); +} + diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatumLine.h b/src/Mod/PartDesign/Gui/ViewProviderDatumLine.h new file mode 100644 index 000000000..60e7fb2fe --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderDatumLine.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (c) 2013 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 * + * * + ***************************************************************************/ + + +#ifndef PARTGUI_ViewProviderDatumLine_H +#define PARTGUI_ViewProviderDatumLine_H + +#include "Gui/ViewProviderGeometryObject.h" +#include "ViewProviderDatum.h" + +namespace PartDesignGui { + +class PartDesignGuiExport ViewProviderDatumLine : public PartDesignGui::ViewProviderDatum +{ + PROPERTY_HEADER(PartDesignGui::ViewProviderDatumLine); + +public: + /// Constructor + ViewProviderDatumLine(); + virtual ~ViewProviderDatumLine(); + + virtual void updateData(const App::Property*); + +}; + +} // namespace PartDesignGui + + +#endif // PARTGUI_ViewProviderDatumLine_H diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatumPlane.cpp b/src/Mod/PartDesign/Gui/ViewProviderDatumPlane.cpp new file mode 100644 index 000000000..2265c72db --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderDatumPlane.cpp @@ -0,0 +1,223 @@ +/*************************************************************************** + * Copyright (c) 2013 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 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include "ViewProviderDatumPlane.h" +#include "TaskDatumParameters.h" +#include "Workbench.h" +#include +#include +#include +#include +#include + +using namespace PartDesignGui; + +PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumPlane,PartDesignGui::ViewProviderDatum) + +ViewProviderDatumPlane::ViewProviderDatumPlane() +{ + SoMaterial* material = new SoMaterial(); + material->diffuseColor.setValue(0.9f, 0.9f, 0.13); + material->transparency.setValue(0.2); + pShapeSep->addChild(material); +} + +ViewProviderDatumPlane::~ViewProviderDatumPlane() +{ + +} + +void ViewProviderDatumPlane::updateData(const App::Property* prop) +{ + // Gets called whenever a property of the attached object changes + PartDesign::Plane* pcDatum = static_cast(this->getObject()); + + if (strcmp(prop->getName(),"_Base") == 0) { + Base::Vector3d base = pcDatum->_Base.getValue(); + Base::Vector3d normal = pcDatum->_Normal.getValue(); + + // Get limits of the plane from bounding box of the body + PartDesign::Body* body = PartDesign::Body::findBodyOf(this->getObject()); + if (body == NULL) + return; + Part::Feature* tipSolid = static_cast(body->getPrevSolidFeature()); + if (tipSolid == NULL) + return; + Base::BoundBox3d bbox = tipSolid->Shape.getShape().getBoundBox(); + bbox.Enlarge(0.1 * bbox.CalcDiagonalLength()); + + // Calculate intersection of plane with bounding box edges + // TODO: This can be a lot more efficient if we do the maths ourselves, e.g. + // http://cococubed.asu.edu/code_pages/raybox.shtml + // http://www.fho-emden.de/~hoffmann/cubeplane12112006.pdf + Handle_Geom_Plane plane = new Geom_Plane(gp_Pnt(base.x, base.y, base.z), gp_Dir(normal.x, normal.y, normal.z)); + std::vector points; + + for (int i = 0; i < 12; i++) { + // Get the edge of the bounding box + Base::Vector3d p1, p2; + bbox.CalcEdge(i, p1, p2); + Base::Vector3d ldir = p2 - p1; + Handle_Geom_Line line = new Geom_Line(gp_Pnt(p1.x, p1.y, p1.z), gp_Dir(ldir.x, ldir.y, ldir.z)); + GeomAPI_IntCS intersector(line, plane); + if (!intersector.IsDone() || (intersector.NbPoints() == 0)) + continue; + gp_Pnt pnt = intersector.Point(1); + Base::Vector3d point(pnt.X(), pnt.Y(), pnt.Z()); + + // Check whether intersection is on the bbox edge (bbox.IsInside() always tests false) + double edgeLength = (p1 - p2).Length(); + double l1 = (p1 - point).Length(); + double l2 = (p2 - point).Length(); + if (fabs(edgeLength - l1 - l2) > 0.001) + continue; + + // Check for duplicates + bool duplicate = false; + for (std::vector::const_iterator p = points.begin(); p != points.end(); p++) { + if ((point - *p).Sqr() < Precision::Confusion()) { + duplicate = true; + break; + } + } + if (!duplicate) + points.push_back(point); + } + + if (points.size() < 3) + return; + + // Sort the points to get a proper polygon, see http://www.fho-emden.de/~hoffmann/cubeplane12112006.pdf p.5 + if (points.size() > 3) { + // Longest component of normal vector + int longest; + if (normal.x > normal.y) + if (normal.x > normal.z) + longest = 0; // x is longest + else + longest = 2; // z is longest + else + if (normal.y > normal.z) + longest = 1; // y is longest + else + longest = 2; // z is longest + + // mean value for intersection points + Base::Vector3d m; + for (std::vector::iterator p = points.begin(); p != points.end(); p++) + m += *p; + m /= points.size(); + + // Sort by angles + double a[points.size()]; + for (int i = 0; i < points.size() - 1; i++) { + if (longest == 0) + a[i] = atan2(points[i].z - m.z, points[i].y - m.y); + else if (longest == 1) + a[i] = atan2(points[i].z - m.z, points[i].x - m.x); + else + a[i] = atan2(points[i].y - m.y, points[i].x - m.x); + + for (int k = i+1; k < points.size(); k++) { + if (longest == 0) + a[k] = atan2(points[k].z - m.z, points[k].y - m.y); + else if (longest == 1) + a[k] = atan2(points[k].z - m.z, points[k].x - m.x); + else + a[k] = atan2(points[k].y - m.y, points[k].x - m.x); + + if (a[k] < a[i]) { + Base::Vector3d temp = points[i]; + points[i] = points[k]; + points[k] = temp; + a[i] = a[k]; + } + } + } + } + + // Display the plane + SoMFVec3f v; + v.setNum(points.size()); + for (int p = 0; p < points.size(); p++) + v.set1Value(p, points[p].x, points[p].y, points[p].z); + SoMFInt32 idx; + idx.setNum(1); + idx.set1Value(0, points.size()); + + SoFaceSet* faceSet; + SoLineSet* lineSet; + SoVertexProperty* vprop; + + if (pShapeSep->getNumChildren() == 1) { + faceSet = new SoFaceSet(); + vprop = new SoVertexProperty(); + vprop->vertex = v; + faceSet->vertexProperty = vprop; + faceSet->numVertices = idx; + pShapeSep->addChild(faceSet); + lineSet = new SoLineSet(); + lineSet->vertexProperty = vprop; + lineSet->numVertices = idx; + pShapeSep->addChild(lineSet); + } else { + faceSet = static_cast(pShapeSep->getChild(1)); + vprop = static_cast(faceSet->vertexProperty.getValue()); + vprop->vertex = v; + faceSet->numVertices = idx; + lineSet = static_cast(pShapeSep->getChild(2)); + vprop = static_cast(lineSet->vertexProperty.getValue()); + vprop->vertex = v; + lineSet->numVertices = idx; + } + } + + ViewProviderDatum::updateData(prop); +} + + diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatumPlane.h b/src/Mod/PartDesign/Gui/ViewProviderDatumPlane.h new file mode 100644 index 000000000..53c23ecb5 --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderDatumPlane.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (c) 2013 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 * + * * + ***************************************************************************/ + + +#ifndef PARTGUI_ViewProviderDatumPlane_H +#define PARTGUI_ViewProviderDatumPlane_H + +#include "Gui/ViewProviderGeometryObject.h" +#include "ViewProviderDatum.h" + +namespace PartDesignGui { + +class PartDesignGuiExport ViewProviderDatumPlane : public PartDesignGui::ViewProviderDatum +{ + PROPERTY_HEADER(PartDesignGui::ViewProviderDatumPlane); + +public: + /// Constructor + ViewProviderDatumPlane(); + virtual ~ViewProviderDatumPlane(); + + virtual void updateData(const App::Property*); + +}; + +} // namespace PartDesignGui + + +#endif // PARTGUI_ViewProviderDatumPlane_H diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatumPoint.cpp b/src/Mod/PartDesign/Gui/ViewProviderDatumPoint.cpp new file mode 100644 index 000000000..2ec1fe8ff --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderDatumPoint.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (c) 2013 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 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include "ViewProviderDatumPoint.h" +#include "TaskDatumParameters.h" +#include "Workbench.h" +#include +#include +#include +#include +#include + +using namespace PartDesignGui; + +PROPERTY_SOURCE(PartDesignGui::ViewProviderDatumPoint,PartDesignGui::ViewProviderDatum) + +ViewProviderDatumPoint::ViewProviderDatumPoint() +{ + SoMarkerSet* points = new SoMarkerSet(); + points->markerIndex = SoMarkerSet::DIAMOND_FILLED_9_9; + points->numPoints = 0; + pShapeSep->addChild(points); +} + +ViewProviderDatumPoint::~ViewProviderDatumPoint() +{ + +} + +void ViewProviderDatumPoint::updateData(const App::Property* prop) +{ + // Gets called whenever a property of the attached object changes + PartDesign::Point* pcDatum = static_cast(this->getObject()); + + if (strcmp(prop->getName(),"_Point") == 0) { + Base::Vector3d p = pcDatum->_Point.getValue(); + SoMFVec3f v; + v.setNum(1); + v.set1Value(0, p.x, p.y, p.z); + SoMarkerSet* points = static_cast(pShapeSep->getChild(0)); + + SoVertexProperty* vprop; + if (points->vertexProperty.getValue() == NULL) { + vprop = new SoVertexProperty(); + vprop->vertex = v; + points->vertexProperty = vprop; + } else { + vprop = static_cast(points->vertexProperty.getValue()); + vprop->vertex = v; + } + + points->numPoints = 1; + } + + ViewProviderDatum::updateData(prop); +} diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatumPoint.h b/src/Mod/PartDesign/Gui/ViewProviderDatumPoint.h new file mode 100644 index 000000000..68edadeca --- /dev/null +++ b/src/Mod/PartDesign/Gui/ViewProviderDatumPoint.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (c) 2013 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 * + * * + ***************************************************************************/ + + +#ifndef PARTGUI_ViewProviderDatumPoint_H +#define PARTGUI_ViewProviderDatumPoint_H + +#include "Gui/ViewProviderGeometryObject.h" +#include "ViewProviderDatum.h" + +namespace PartDesignGui { + +class PartDesignGuiExport ViewProviderDatumPoint : public PartDesignGui::ViewProviderDatum +{ + PROPERTY_HEADER(PartDesignGui::ViewProviderDatumPoint); + +public: + /// Constructor + ViewProviderDatumPoint(); + virtual ~ViewProviderDatumPoint(); + + virtual void updateData(const App::Property*); + +}; + +} // namespace PartDesignGui + + +#endif // PARTGUI_ViewProviderDatumPoint_H diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index bdba81f42..02f278c78 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include using namespace PartDesignGui; @@ -262,7 +262,7 @@ void Workbench::setupContextMenu(const char* recipient, Gui::MenuItem* item) con if (strcmp(recipient,"Tree") == 0) { if (Gui::Selection().countObjectsOfType(PartDesign::Feature::getClassTypeId()) + - Gui::Selection().countObjectsOfType(PartDesign::Datum::getClassTypeId()) + + Gui::Selection().countObjectsOfType(Part::Datum::getClassTypeId()) + Gui::Selection().countObjectsOfType(Part::Part2DObject::getClassTypeId()) > 0 ) *item << "PartDesign_MoveTip"; } diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 7e75f2a34..318d2e044 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include # include +# include # include # include # include @@ -34,6 +35,7 @@ # include # include # include +# include # include # include # include @@ -41,6 +43,9 @@ # include # include # include +# include +# include +# include # include # include # include @@ -56,8 +61,10 @@ #include #include +#include + #include -#include +#include #include "SketchObject.h" #include "SketchObjectPy.h" @@ -119,15 +126,7 @@ App::DocumentObjectExecReturn *SketchObject::execute(void) if (support == NULL) throw Base::Exception("Sketch support has been deleted"); - if (support->getTypeId().isDerivedFrom(PartDesign::Plane::getClassTypeId())) { - // We don't want to handle this case in Part::Part2DObject because then Part would depend on PartDesign - PartDesign::Plane* plane = static_cast(support); - Base::Vector3d pos = plane->_Base.getValue(); - Base::Vector3d normal = plane->_Normal.getValue(); - this->Placement.setValue(Base::Placement(pos, Base::Rotation(Base::Vector3d(0,0,1), normal))); - } else { - this->positionBySupport(); - } + this->positionBySupport(); } catch (const Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); @@ -2713,8 +2712,10 @@ int SketchObject::DeleteUnusedInternalGeometry(int GeoId) int SketchObject::addExternal(App::DocumentObject *Obj, const char* SubName) { - // so far only externals to the support of the sketch + // so far only externals to the support of the sketch and datum features if (Support.getValue() != Obj) + if (!Obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) && + !Obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) return -1; // get the actual lists of the externals @@ -2889,6 +2890,56 @@ const Part::Geometry* SketchObject::getGeometry(int GeoId) const return 0; } +// Auxiliary method +Part::Geometry* projectLine(const BRepAdaptor_Curve& curve, const Handle(Geom_Plane)& gPlane, const Base::Placement& invPlm) +{ + double first = curve.FirstParameter(); + bool infinite = false; + if (fabs(first) > 1E99) { + // TODO: What is OCE's definition of Infinite? + // TODO: The clean way to do this is to handle a new sketch geometry Geom::Line + // but its a lot of work to implement... + first = -10000; + //infinite = true; + } + double last = curve.LastParameter(); + if (fabs(last) > 1E99) { + last = +10000; + //infinite = true; + } + + gp_Pnt P1 = curve.Value(first); + gp_Pnt P2 = curve.Value(last); + + GeomAPI_ProjectPointOnSurf proj1(P1,gPlane); + P1 = proj1.NearestPoint(); + GeomAPI_ProjectPointOnSurf proj2(P2,gPlane); + P2 = proj2.NearestPoint(); + + Base::Vector3d p1(P1.X(),P1.Y(),P1.Z()); + Base::Vector3d p2(P2.X(),P2.Y(),P2.Z()); + invPlm.multVec(p1,p1); + invPlm.multVec(p2,p2); + + if (Base::Distance(p1,p2) < Precision::Confusion()) { + Base::Vector3d p = (p1 + p2) / 2; + Part::GeomPoint* point = new Part::GeomPoint(p); + point->Construction = true; + return point; + } + else if (!infinite) { + Part::GeomLineSegment* line = new Part::GeomLineSegment(); + line->setPoints(p1,p2); + line->Construction = true; + return line; + } else { + Part::GeomLine* line = new Part::GeomLine(); + line->setLine(p1, p2 - p1); + line->Construction = true; + return line; + } +} + bool SketchObject::evaluateSupport(void) { // returns false if the shape if broken, null or non-planar @@ -3028,18 +3079,39 @@ void SketchObject::rebuildExternalGeometry(void) ExternalGeo.push_back(VLine); for (int i=0; i < int(Objects.size()); i++) { const App::DocumentObject *Obj=Objects[i]; - const std::string SubElement=SubElements[i]; - - const Part::Feature *refObj=static_cast(Obj); - const Part::TopoShape& refShape=refObj->Shape.getShape(); + const std::string SubElement=SubElements[i]; TopoDS_Shape refSubShape; - try { - refSubShape = refShape.getSubShape(SubElement.c_str()); - } - catch (Standard_Failure) { - Handle_Standard_Failure e = Standard_Failure::Caught(); - throw Base::Exception(e->GetMessageString()); + + if (Obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) { + const Part::Datum* datum = static_cast(Obj); + refSubShape = datum->Shape.getValue(); + } else if (Obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { + try { + const Part::Feature *refObj=static_cast(Obj); + const Part::TopoShape& refShape=refObj->Shape.getShape(); + refSubShape = refShape.getSubShape(SubElement.c_str()); + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + throw Base::Exception(e->GetMessageString()); + } + } else if (Obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { + const App::Plane* pl = static_cast(Obj); + Base::Placement plm = pl->Placement.getValue(); + Base::Vector3d base = plm.getPosition(); + Base::Rotation rot = plm.getRotation(); + Base::Vector3d normal(0,0,1); + rot.multVec(normal, normal); + gp_Pln plane(gp_Pnt(base.x,base.y,base.z), gp_Dir(normal.x, normal.y, normal.z)); + BRepBuilderAPI_MakeFace fBuilder(plane); + if (!fBuilder.IsDone()) + throw Base::Exception("Sketcher: addExternal(): Failed to build face from App::Plane"); + + TopoDS_Face f = TopoDS::Face(fBuilder.Shape()); + refSubShape = f; + } else { + throw Base::Exception("Datum feature type is not yet supported as external geometry for a sketch"); } switch (refSubShape.ShapeType()) @@ -3049,8 +3121,27 @@ void SketchObject::rebuildExternalGeometry(void) const TopoDS_Face& face = TopoDS::Face(refSubShape); BRepAdaptor_Surface surface(face); if (surface.GetType() == GeomAbs_Plane) { + // Check that the plane is perpendicular to the sketch plane + Geom_Plane plane = surface.Plane(); + gp_Dir dnormal = plane.Axis().Direction(); + gp_Dir snormal = sketchPlane.Axis().Direction(); + if (fabs(dnormal.Angle(snormal) - M_PI_2) < Precision::Confusion()) { + // Get vector that is normal to both sketch plane normal and plane normal. This is the line's direction + gp_Dir lnormal = dnormal.Crossed(snormal); + BRepBuilderAPI_MakeEdge builder(gp_Lin(plane.Location(), lnormal)); + builder.Build(); + if (builder.IsDone()) { + const TopoDS_Edge& edge = TopoDS::Edge(builder.Shape()); + BRepAdaptor_Curve curve(edge); + if (curve.GetType() == GeomAbs_Line) { + ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); + } + } + + } + } else { + throw Base::Exception("Non-planar faces are not yet supported for external geometry of sketches"); } - throw Base::Exception("Faces are not yet supported for external geometry of sketches"); } break; case TopAbs_EDGE: @@ -3058,31 +3149,7 @@ void SketchObject::rebuildExternalGeometry(void) const TopoDS_Edge& edge = TopoDS::Edge(refSubShape); BRepAdaptor_Curve curve(edge); if (curve.GetType() == GeomAbs_Line) { - gp_Pnt P1 = curve.Value(curve.FirstParameter()); - gp_Pnt P2 = curve.Value(curve.LastParameter()); - - GeomAPI_ProjectPointOnSurf proj1(P1,gPlane); - P1 = proj1.NearestPoint(); - GeomAPI_ProjectPointOnSurf proj2(P2,gPlane); - P2 = proj2.NearestPoint(); - - Base::Vector3d p1(P1.X(),P1.Y(),P1.Z()); - Base::Vector3d p2(P2.X(),P2.Y(),P2.Z()); - invPlm.multVec(p1,p1); - invPlm.multVec(p2,p2); - - if (Base::Distance(p1,p2) < Precision::Confusion()) { - Base::Vector3d p = (p1 + p2) / 2; - Part::GeomPoint* point = new Part::GeomPoint(p); - point->Construction = true; - ExternalGeo.push_back(point); - } - else { - Part::GeomLineSegment* line = new Part::GeomLineSegment(); - line->setPoints(p1,p2); - line->Construction = true; - ExternalGeo.push_back(line); - } + ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); } else if (curve.GetType() == GeomAbs_Circle) { gp_Dir vec1 = sketchPlane.Axis().Direction(); diff --git a/src/Mod/Sketcher/App/SketchObject.cpp.orig b/src/Mod/Sketcher/App/SketchObject.cpp.orig new file mode 100644 index 000000000..b3180b83a --- /dev/null +++ b/src/Mod/Sketcher/App/SketchObject.cpp.orig @@ -0,0 +1,4180 @@ +/*************************************************************************** +<<<<<<< ef369d31e9c7931d6b64b7c59697c263887085f8 + * Copyright (c) Jürgen Riegel (juergen.riegel@web.de) 2008 * +======= + * Copyright (c) Juergen Riegel (juergen.riegel@web.de) 2008 * +>>>>>>> Moved generic Datum class to Part module to avoid Sketcher dependency on PartDesign + * * + * 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 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // #ifndef _PreComp_ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "SketchObject.h" +#include "SketchObjectPy.h" +#include "Sketch.h" + +using namespace Sketcher; +using namespace Base; + + +PROPERTY_SOURCE(Sketcher::SketchObject, Part::Part2DObject) + + +SketchObject::SketchObject() +{ + ADD_PROPERTY_TYPE(Geometry, (0) ,"Sketch",(App::PropertyType)(App::Prop_None),"Sketch geometry"); + ADD_PROPERTY_TYPE(Constraints, (0) ,"Sketch",(App::PropertyType)(App::Prop_None),"Sketch constraints"); + ADD_PROPERTY_TYPE(ExternalGeometry,(0,0),"Sketch",(App::PropertyType)(App::Prop_None),"Sketch external geometry"); + + for (std::vector::iterator it=ExternalGeo.begin(); it != ExternalGeo.end(); ++it) + if (*it) delete *it; + ExternalGeo.clear(); + Part::GeomLineSegment *HLine = new Part::GeomLineSegment(); + Part::GeomLineSegment *VLine = new Part::GeomLineSegment(); + HLine->setPoints(Base::Vector3d(0,0,0),Base::Vector3d(1,0,0)); + VLine->setPoints(Base::Vector3d(0,0,0),Base::Vector3d(0,1,0)); + HLine->Construction = true; + VLine->Construction = true; + ExternalGeo.push_back(HLine); + ExternalGeo.push_back(VLine); + rebuildVertexIndex(); + + lastDoF=0; + lastHasConflict=false; + lastHasRedundancies=false; + lastSolverStatus=0; + lastSolveTime=0; + + solverNeedsUpdate=false; + + noRecomputes=false; + + ExpressionEngine.setValidator(boost::bind(&Sketcher::SketchObject::validateExpression, this, _1, _2)); + + constraintsRemovedConn = Constraints.signalConstraintsRemoved.connect(boost::bind(&Sketcher::SketchObject::constraintsRemoved, this, _1)); + constraintsRenamedConn = Constraints.signalConstraintsRenamed.connect(boost::bind(&Sketcher::SketchObject::constraintsRenamed, this, _1)); +} + +SketchObject::~SketchObject() +{ + for (std::vector::iterator it=ExternalGeo.begin(); it != ExternalGeo.end(); ++it) + if (*it) delete *it; + ExternalGeo.clear(); +} + +App::DocumentObjectExecReturn *SketchObject::execute(void) +{ + try { + App::DocumentObject* support = Support.getValue(); + if (support == NULL) + throw Base::Exception("Sketch support has been deleted"); + + this->positionBySupport(); + } + catch (const Base::Exception& e) { + return new App::DocumentObjectExecReturn(e.what()); + } + + // setup and diagnose the sketch + try { + rebuildExternalGeometry(); + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\nClear constraints to external geometry\n", e.what()); + // we cannot trust the constraints of external geometries, so remove them + delConstraintsToExternal(); + } + + // We should have an updated Sketcher geometry or this execute should not have happened + // therefore we update our sketch object geometry with the SketchObject one. + // + // set up a sketch (including dofs counting and diagnosing of conflicts) + lastDoF = solvedSketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), + getExternalGeometryCount()); + lastHasConflict = solvedSketch.hasConflicts(); + lastHasRedundancies = solvedSketch.hasRedundancies(); + lastConflicting=solvedSketch.getConflicting(); + lastRedundant=solvedSketch.getRedundant(); + + lastSolveTime=0.0; + lastSolverStatus=GCS::Failed; // Failure is default for notifying the user unless otherwise proven + + solverNeedsUpdate=false; + + if (lastDoF < 0) { // over-constrained sketch + std::string msg="Over-constrained sketch\n"; + appendConflictMsg(lastConflicting, msg); + return new App::DocumentObjectExecReturn(msg.c_str(),this); + } + if (lastHasConflict) { // conflicting constraints + std::string msg="Sketch with conflicting constraints\n"; + appendConflictMsg(lastConflicting, msg); + return new App::DocumentObjectExecReturn(msg.c_str(),this); + } + if (lastHasRedundancies) { // redundant constraints + std::string msg="Sketch with redundant constraints\n"; + appendRedundantMsg(lastRedundant, msg); + return new App::DocumentObjectExecReturn(msg.c_str(),this); + } + // solve the sketch + lastSolverStatus=solvedSketch.solve(); + lastSolveTime=solvedSketch.SolveTime; + + if (lastSolverStatus != 0) + return new App::DocumentObjectExecReturn("Solving the sketch failed",this); + + std::vector geomlist = solvedSketch.extractGeometry(); + Geometry.setValues(geomlist); + for (std::vector::iterator it=geomlist.begin(); it != geomlist.end(); ++it) + if (*it) delete *it; + + // this is not necessary for sketch representation in edit mode, unless we want to trigger an update of + // the objects that depend on this sketch (like pads) + Shape.setValue(solvedSketch.toShape()); + + return App::DocumentObject::StdReturn; +} + +int SketchObject::hasConflicts(void) const +{ + if (lastDoF < 0) // over-constrained sketch + return -2; + if (solvedSketch.hasConflicts()) // conflicting constraints + return -1; + + return 0; +} + +int SketchObject::solve(bool updateGeoAfterSolving/*=true*/) +{ + // if updateGeoAfterSolving=false, the solver information is updated, but the Sketch is nothing + // updated. It is useful to avoid triggering an OnChange when the goeometry did not change but + // the solver needs to be updated. + + // We should have an updated Sketcher geometry or this solver should not have happened + // therefore we update our sketch object geometry with the SketchObject one. + // + // set up a sketch (including dofs counting and diagnosing of conflicts) + lastDoF = solvedSketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), + getExternalGeometryCount()); + + solverNeedsUpdate=false; + + lastHasConflict = solvedSketch.hasConflicts(); + + int err=0; + if (lastDoF < 0) { // over-constrained sketch + err = -3; + // if lastDoF<0, then an over-constrained situation has ensued. + // Geometry is not to be updated, as geometry can not follow the constraints. + // However, solver information must be updated. + this->Constraints.touch(); + } + else if (lastHasConflict) { // conflicting constraints + err = -3; + } + else { + lastSolverStatus=solvedSketch.solve(); + if (lastSolverStatus != 0){ // solving + err = -2; + // if solver failed, geometry was never updated, but invalid constraints were likely added before + // solving (see solve in addConstraint), so solver information is definitely invalid. + this->Constraints.touch(); + + } + + } + + lastHasRedundancies = solvedSketch.hasRedundancies(); + + lastConflicting=solvedSketch.getConflicting(); + lastRedundant=solvedSketch.getRedundant(); + lastSolveTime=solvedSketch.SolveTime; + + if (err == 0 && updateGeoAfterSolving) { + // set the newly solved geometry + std::vector geomlist = solvedSketch.extractGeometry(); + Geometry.setValues(geomlist); + for (std::vector::iterator it = geomlist.begin(); it != geomlist.end(); ++it) + if (*it) delete *it; + } + + return err; +} + +int SketchObject::setDatum(int ConstrId, double Datum) +{ + // set the changed value for the constraint + const std::vector &vals = this->Constraints.getValues(); + if (ConstrId < 0 || ConstrId >= int(vals.size())) + return -1; + ConstraintType type = vals[ConstrId]->Type; + if (type != Distance && + type != DistanceX && + type != DistanceY && + type != Radius && + type != Angle && + type != Tangent && //for tangent, value==0 is autodecide, value==Pi/2 is external and value==-Pi/2 is internal + type != Perpendicular && + type != SnellsLaw) + return -1; + + if ((type == Distance || type == Radius) && Datum <= 0) + return (Datum == 0) ? -5 : -4; + + // copy the list + std::vector newVals(vals); + // clone the changed Constraint + Constraint *constNew = vals[ConstrId]->clone(); + constNew->setValue(Datum); + newVals[ConstrId] = constNew; + this->Constraints.setValues(newVals); + delete constNew; + + int err = solve(); + if (err) + this->Constraints.setValues(vals); + + return err; +} + +int SketchObject::setDriving(int ConstrId, bool isdriving) +{ + const std::vector &vals = this->Constraints.getValues(); + + if (ConstrId < 0 || ConstrId >= int(vals.size())) + return -1; + + ConstraintType type = vals[ConstrId]->Type; + + if (type != Distance && + type != DistanceX && + type != DistanceY && + type != Radius && + type != Angle && + type != SnellsLaw) + return -2; + + if (!(vals[ConstrId]->First>=0 || vals[ConstrId]->Second>=0 || vals[ConstrId]->Third>=0) && isdriving==true) + return -3; // a constraint that does not have at least one element as not-external-geometry can never be driving. + + // copy the list + std::vector newVals(vals); + // clone the changed Constraint + Constraint *constNew = vals[ConstrId]->clone(); + constNew->isDriving = isdriving; + newVals[ConstrId] = constNew; + this->Constraints.setValues(newVals); + if (isdriving) + setExpression(Constraints.createPath(ConstrId), boost::shared_ptr()); + delete constNew; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; +} + +int SketchObject::getDriving(int ConstrId, bool &isdriving) +{ + const std::vector &vals = this->Constraints.getValues(); + + if (ConstrId < 0 || ConstrId >= int(vals.size())) + return -1; + + ConstraintType type = vals[ConstrId]->Type; + + if (type != Distance && + type != DistanceX && + type != DistanceY && + type != Radius && + type != Angle && + type != SnellsLaw) + return -1; + + isdriving=vals[ConstrId]->isDriving; + return 0; +} + +int SketchObject::toggleDriving(int ConstrId) +{ + const std::vector &vals = this->Constraints.getValues(); + + if (ConstrId < 0 || ConstrId >= int(vals.size())) + return -1; + + ConstraintType type = vals[ConstrId]->Type; + + if (type != Distance && + type != DistanceX && + type != DistanceY && + type != Radius && + type != Angle && + type != SnellsLaw) + return -2; + + if (!(vals[ConstrId]->First>=0 || vals[ConstrId]->Second>=0 || vals[ConstrId]->Third>=0) && vals[ConstrId]->isDriving==false) + return -3; // a constraint that does not have at least one element as not-external-geometry can never be driving. + + // copy the list + std::vector newVals(vals); + // clone the changed Constraint + Constraint *constNew = vals[ConstrId]->clone(); + constNew->isDriving = !constNew->isDriving; + newVals[ConstrId] = constNew; + this->Constraints.setValues(newVals); + if (constNew->isDriving) + setExpression(Constraints.createPath(ConstrId), boost::shared_ptr()); + delete constNew; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; +} + +int SketchObject::setUpSketch() +{ + return solvedSketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), + getExternalGeometryCount()); +} + +int SketchObject::movePoint(int GeoId, PointPos PosId, const Base::Vector3d& toPoint, bool relative, bool updateGeoBeforeMoving) +{ + // if we are moving a point at SketchObject level, we need to start from a solved sketch + // if we have conflicts we can forget about moving. However, there is the possibility that we + // need to do programatically moves of new geometry that has not been solved yet and that because + // they were programmetically generated won't generate a conflict. This is the case of Fillet for + // example. This is why exceptionally, it may be required to update the sketch geometry to that of + // of SketchObject upon moving. => use updateGeometry parameter = true then + + + if(updateGeoBeforeMoving || solverNeedsUpdate) { + lastDoF = solvedSketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), + getExternalGeometryCount()); + + lastHasConflict = solvedSketch.hasConflicts(); + lastHasRedundancies = solvedSketch.hasRedundancies(); + lastConflicting=solvedSketch.getConflicting(); + lastRedundant=solvedSketch.getRedundant(); + + solverNeedsUpdate=false; + } + + if (lastDoF < 0) // over-constrained sketch + return -1; + if (lastHasConflict) // conflicting constraints + return -1; + + // move the point and solve + lastSolverStatus = solvedSketch.movePoint(GeoId, PosId, toPoint, relative); + + // moving the point can not result in a conflict that we did not have + // or a redundancy that we did not have before, or a change of DoF + + if (lastSolverStatus == 0) { + std::vector geomlist = solvedSketch.extractGeometry(); + Geometry.setValues(geomlist); + //Constraints.acceptGeometry(getCompleteGeometry()); + for (std::vector::iterator it=geomlist.begin(); it != geomlist.end(); ++it) { + if (*it) delete *it; + } + } + + return lastSolverStatus; +} + +Base::Vector3d SketchObject::getPoint(int GeoId, PointPos PosId) const +{ + if(!(GeoId == H_Axis || GeoId == V_Axis + || (GeoId <= getHighestCurveIndex() && GeoId >= -getExternalGeometryCount()) )) + throw Base::Exception("SketchObject::getPoint. Invalid GeoId was supplied."); + const Part::Geometry *geo = getGeometry(GeoId); + if (geo->getTypeId() == Part::GeomPoint::getClassTypeId()) { + const Part::GeomPoint *p = dynamic_cast(geo); + if (PosId == start || PosId == mid || PosId == end) + return p->getPoint(); + } else if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = dynamic_cast(geo); + if (PosId == start) + return lineSeg->getStartPoint(); + else if (PosId == end) + return lineSeg->getEndPoint(); + } else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = dynamic_cast(geo); + if (PosId == mid) + return circle->getCenter(); + } else if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId()) { + const Part::GeomEllipse *ellipse = dynamic_cast(geo); + if (PosId == mid) + return ellipse->getCenter(); + } else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *aoc = dynamic_cast(geo); + if (PosId == start) + return aoc->getStartPoint(/*emulateCCW=*/true); + else if (PosId == end) + return aoc->getEndPoint(/*emulateCCW=*/true); + else if (PosId == mid) + return aoc->getCenter(); + } else if (geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + const Part::GeomArcOfEllipse *aoc = dynamic_cast(geo); + if (PosId == start) + return aoc->getStartPoint(/*emulateCCW=*/true); + else if (PosId == end) + return aoc->getEndPoint(/*emulateCCW=*/true); + else if (PosId == mid) + return aoc->getCenter(); + } + + return Base::Vector3d(); +} + +int SketchObject::getAxisCount(void) const +{ + const std::vector< Part::Geometry * > &vals = getInternalGeometry(); + + int count=0; + for (std::vector::const_iterator geo=vals.begin(); + geo != vals.end(); geo++) + if ((*geo) && (*geo)->Construction && + (*geo)->getTypeId() == Part::GeomLineSegment::getClassTypeId()) + count++; + + return count; +} + +Base::Axis SketchObject::getAxis(int axId) const +{ + if (axId == H_Axis || axId == V_Axis || axId == N_Axis) + return Part::Part2DObject::getAxis(axId); + + const std::vector< Part::Geometry * > &vals = getInternalGeometry(); + int count=0; + for (std::vector::const_iterator geo=vals.begin(); + geo != vals.end(); geo++) + if ((*geo) && (*geo)->Construction && + (*geo)->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + if (count == axId) { + Part::GeomLineSegment *lineSeg = dynamic_cast(*geo); + Base::Vector3d start = lineSeg->getStartPoint(); + Base::Vector3d end = lineSeg->getEndPoint(); + return Base::Axis(start, end-start); + } + count++; + } + + return Base::Axis(); +} + +void SketchObject::acceptGeometry() +{ + Constraints.acceptGeometry(getCompleteGeometry()); + rebuildVertexIndex(); +} + +bool SketchObject::isSupportedGeometry(const Part::Geometry *geo) const +{ + if (geo->getTypeId() == Part::GeomPoint::getClassTypeId() || + geo->getTypeId() == Part::GeomCircle::getClassTypeId() || + geo->getTypeId() == Part::GeomEllipse::getClassTypeId() || + geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() || + geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() || + geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + return true; + } + if (geo->getTypeId() == Part::GeomTrimmedCurve::getClassTypeId()) { + Handle_Geom_TrimmedCurve trim = Handle_Geom_TrimmedCurve::DownCast(geo->handle()); + Handle_Geom_Circle circle = Handle_Geom_Circle::DownCast(trim->BasisCurve()); + Handle_Geom_Ellipse ellipse = Handle_Geom_Ellipse::DownCast(trim->BasisCurve()); + if (!circle.IsNull() || !ellipse.IsNull()) { + return true; + } + } + return false; +} + +std::vector SketchObject::supportedGeometry(const std::vector &geoList) const +{ + std::vector supportedGeoList; + supportedGeoList.reserve(geoList.size()); + // read-in geometry that the sketcher cannot handle + for (std::vector::const_iterator it = geoList.begin(); it != geoList.end(); ++it) { + if (isSupportedGeometry(*it)) { + supportedGeoList.push_back(*it); + } + } + + return supportedGeoList; +} + +int SketchObject::addGeometry(const std::vector &geoList, bool construction/*=false*/) +{ + const std::vector< Part::Geometry * > &vals = getInternalGeometry(); + + std::vector< Part::Geometry * > newVals(vals); + for (std::vector::const_iterator it = geoList.begin(); it != geoList.end(); ++it) { + if(construction && (*it)->getTypeId() != Part::GeomPoint::getClassTypeId()) + const_cast(*it)->Construction = construction; + + newVals.push_back(*it); + } + Geometry.setValues(newVals); + Constraints.acceptGeometry(getCompleteGeometry()); + rebuildVertexIndex(); + + return Geometry.getSize()-1; +} + +int SketchObject::addGeometry(const Part::Geometry *geo, bool construction/*=false*/) +{ + const std::vector< Part::Geometry * > &vals = getInternalGeometry(); + + std::vector< Part::Geometry * > newVals(vals); + Part::Geometry *geoNew = geo->clone(); + + if(geoNew->getTypeId() != Part::GeomPoint::getClassTypeId()) + geoNew->Construction = construction; + + newVals.push_back(geoNew); + Geometry.setValues(newVals); + Constraints.acceptGeometry(getCompleteGeometry()); + delete geoNew; + rebuildVertexIndex(); + + return Geometry.getSize()-1; +} + +int SketchObject::delGeometry(int GeoId) +{ + const std::vector< Part::Geometry * > &vals = getInternalGeometry(); + if (GeoId < 0 || GeoId >= int(vals.size())) + return -1; + + this->DeleteUnusedInternalGeometry(GeoId); + + std::vector< Part::Geometry * > newVals(vals); + newVals.erase(newVals.begin()+GeoId); + + // Find coincident points to replace the points of the deleted geometry + std::vector GeoIdList; + std::vector PosIdList; + for (PointPos PosId = start; PosId != mid; ) { + getDirectlyCoincidentPoints(GeoId, PosId, GeoIdList, PosIdList); + if (GeoIdList.size() > 1) { + delConstraintOnPoint(GeoId, PosId, true /* only coincidence */); + transferConstraints(GeoIdList[0], PosIdList[0], GeoIdList[1], PosIdList[1]); + } + PosId = (PosId == start) ? end : mid; // loop through [start, end, mid] + } + + const std::vector< Constraint * > &constraints = this->Constraints.getValues(); + std::vector< Constraint * > newConstraints(0); + for (std::vector::const_iterator it = constraints.begin(); + it != constraints.end(); ++it) { + if ((*it)->First != GeoId && (*it)->Second != GeoId && (*it)->Third != GeoId) { + Constraint *copiedConstr = (*it)->clone(); + if (copiedConstr->First > GeoId) + copiedConstr->First -= 1; + if (copiedConstr->Second > GeoId) + copiedConstr->Second -= 1; + if (copiedConstr->Third > GeoId) + copiedConstr->Third -= 1; + newConstraints.push_back(copiedConstr); + } + } + + this->Geometry.setValues(newVals); + this->Constraints.setValues(newConstraints); + this->Constraints.acceptGeometry(getCompleteGeometry()); + rebuildVertexIndex(); + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; +} + +int SketchObject::toggleConstruction(int GeoId) +{ + const std::vector< Part::Geometry * > &vals = getInternalGeometry(); + if (GeoId < 0 || GeoId >= int(vals.size())) + return -1; + + std::vector< Part::Geometry * > newVals(vals); + + Part::Geometry *geoNew = newVals[GeoId]->clone(); + geoNew->Construction = !geoNew->Construction; + newVals[GeoId]=geoNew; + + this->Geometry.setValues(newVals); + //this->Constraints.acceptGeometry(getCompleteGeometry()); <= This is not necessary for a toggle. Reducing redundant solving. Abdullah + solverNeedsUpdate=true; + return 0; +} + +int SketchObject::setConstruction(int GeoId, bool on) +{ + const std::vector< Part::Geometry * > &vals = getInternalGeometry(); + if (GeoId < 0 || GeoId >= int(vals.size())) + return -1; + + std::vector< Part::Geometry * > newVals(vals); + + Part::Geometry *geoNew = newVals[GeoId]->clone(); + geoNew->Construction = on; + newVals[GeoId]=geoNew; + + this->Geometry.setValues(newVals); + //this->Constraints.acceptGeometry(getCompleteGeometry()); <= This is not necessary for a toggle. Reducing redundant solving. Abdullah + solverNeedsUpdate=true; + return 0; +} + +//ConstraintList is used only to make copies. +int SketchObject::addConstraints(const std::vector &ConstraintList) +{ + const std::vector< Constraint * > &vals = this->Constraints.getValues(); + + std::vector< Constraint * > newVals(vals); + newVals.insert(newVals.end(), ConstraintList.begin(), ConstraintList.end()); + + //test if tangent constraints have been added; AutoLockTangency. + std::vector< Constraint * > tbd;//list of temporary copies that need to be deleted + for(std::size_t i = newVals.size()-ConstraintList.size(); iType == Tangent || newVals[i]->Type == Perpendicular ){ + Constraint *constNew = newVals[i]->clone(); + AutoLockTangencyAndPerpty(constNew); + tbd.push_back(constNew); + newVals[i] = constNew; + } + } + + this->Constraints.setValues(newVals); + + //clean up - delete temporary copies of constraints that were made to affect the constraints + for(std::size_t i=0; iConstraints.getSize()-1; +} + +int SketchObject::addConstraint(const Constraint *constraint) +{ + const std::vector< Constraint * > &vals = this->Constraints.getValues(); + + std::vector< Constraint * > newVals(vals); + Constraint *constNew = constraint->clone(); + + if (constNew->Type == Tangent || constNew->Type == Perpendicular) + AutoLockTangencyAndPerpty(constNew); + + newVals.push_back(constNew); + this->Constraints.setValues(newVals); + delete constNew; + return this->Constraints.getSize()-1; +} + +int SketchObject::delConstraint(int ConstrId) +{ + const std::vector< Constraint * > &vals = this->Constraints.getValues(); + if (ConstrId < 0 || ConstrId >= int(vals.size())) + return -1; + + std::vector< Constraint * > newVals(vals); + newVals.erase(newVals.begin()+ConstrId); + this->Constraints.setValues(newVals); + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; +} + +int SketchObject::delConstraintOnPoint(int VertexId, bool onlyCoincident) +{ + int GeoId; + PointPos PosId; + if (VertexId == -1) { // RootPoint + GeoId = -1; + PosId = start; + } else + getGeoVertexIndex(VertexId, GeoId, PosId); + + return delConstraintOnPoint(GeoId, PosId, onlyCoincident); +} + +int SketchObject::delConstraintOnPoint(int GeoId, PointPos PosId, bool onlyCoincident) +{ + const std::vector &vals = this->Constraints.getValues(); + + // check if constraints can be redirected to some other point + int replaceGeoId=Constraint::GeoUndef; + PointPos replacePosId=Sketcher::none; + if (!onlyCoincident) { + for (std::vector::const_iterator it = vals.begin(); it != vals.end(); ++it) { + if ((*it)->Type == Sketcher::Coincident) { + if ((*it)->First == GeoId && (*it)->FirstPos == PosId) { + replaceGeoId = (*it)->Second; + replacePosId = (*it)->SecondPos; + break; + } + else if ((*it)->Second == GeoId && (*it)->SecondPos == PosId) { + replaceGeoId = (*it)->First; + replacePosId = (*it)->FirstPos; + break; + } + } + } + } + + // remove or redirect any constraints associated with the given point + std::vector newVals(0); + for (std::vector::const_iterator it = vals.begin(); it != vals.end(); ++it) { + if ((*it)->Type == Sketcher::Coincident) { + if ((*it)->First == GeoId && (*it)->FirstPos == PosId) { + if (replaceGeoId != Constraint::GeoUndef && + (replaceGeoId != (*it)->Second || replacePosId != (*it)->SecondPos)) { // redirect this constraint + (*it)->First = replaceGeoId; + (*it)->FirstPos = replacePosId; + } + else + continue; // skip this constraint + } + else if ((*it)->Second == GeoId && (*it)->SecondPos == PosId) { + if (replaceGeoId != Constraint::GeoUndef && + (replaceGeoId != (*it)->First || replacePosId != (*it)->FirstPos)) { // redirect this constraint + (*it)->Second = replaceGeoId; + (*it)->SecondPos = replacePosId; + } + else + continue; // skip this constraint + } + } + else if (!onlyCoincident) { + if ((*it)->Type == Sketcher::Distance || + (*it)->Type == Sketcher::DistanceX || (*it)->Type == Sketcher::DistanceY) { + if ((*it)->First == GeoId && (*it)->FirstPos == none && + (PosId == start || PosId == end)) { + // remove the constraint even if it is not directly associated + // with the given point + continue; // skip this constraint + } + else if ((*it)->First == GeoId && (*it)->FirstPos == PosId) { + if (replaceGeoId != Constraint::GeoUndef) { // redirect this constraint + (*it)->First = replaceGeoId; + (*it)->FirstPos = replacePosId; + } + else + continue; // skip this constraint + } + else if ((*it)->Second == GeoId && (*it)->SecondPos == PosId) { + if (replaceGeoId != Constraint::GeoUndef) { // redirect this constraint + (*it)->Second = replaceGeoId; + (*it)->SecondPos = replacePosId; + } + else + continue; // skip this constraint + } + } + else if ((*it)->Type == Sketcher::PointOnObject) { + if ((*it)->First == GeoId && (*it)->FirstPos == PosId) { + if (replaceGeoId != Constraint::GeoUndef) { // redirect this constraint + (*it)->First = replaceGeoId; + (*it)->FirstPos = replacePosId; + } + else + continue; // skip this constraint + } + } + else if ((*it)->Type == Sketcher::Tangent) { + if (((*it)->First == GeoId && (*it)->FirstPos == PosId) || + ((*it)->Second == GeoId && (*it)->SecondPos == PosId)) { + // we could keep the tangency constraint by converting it + // to a simple one but it is not really worth + continue; // skip this constraint + } + } + else if ((*it)->Type == Sketcher::Symmetric) { + if (((*it)->First == GeoId && (*it)->FirstPos == PosId) || + ((*it)->Second == GeoId && (*it)->SecondPos == PosId)) { + continue; // skip this constraint + } + } + } + newVals.push_back(*it); + } + if (newVals.size() < vals.size()) { + this->Constraints.setValues(newVals); + + return 0; + } + + return -1; // no such constraint +} + +int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toGeoId, PointPos toPosId) +{ + const std::vector &vals = this->Constraints.getValues(); + std::vector newVals(vals); + for (int i=0; i < int(newVals.size()); i++) { + if (vals[i]->First == fromGeoId && vals[i]->FirstPos == fromPosId && + !(vals[i]->Second == toGeoId && vals[i]->SecondPos == toPosId)) { + Constraint *constNew = newVals[i]->clone(); + constNew->First = toGeoId; + constNew->FirstPos = toPosId; + newVals[i] = constNew; + } else if (vals[i]->Second == fromGeoId && vals[i]->SecondPos == fromPosId && + !(vals[i]->First == toGeoId && vals[i]->FirstPos == toPosId)) { + Constraint *constNew = newVals[i]->clone(); + constNew->Second = toGeoId; + constNew->SecondPos = toPosId; + newVals[i] = constNew; + } + } + this->Constraints.setValues(newVals); + return 0; +} + +int SketchObject::fillet(int GeoId, PointPos PosId, double radius, bool trim) +{ + if (GeoId < 0 || GeoId > getHighestCurveIndex()) + return -1; + + // Find the other geometry Id associated with the coincident point + std::vector GeoIdList; + std::vector PosIdList; + getDirectlyCoincidentPoints(GeoId, PosId, GeoIdList, PosIdList); + + // only coincident points between two (non-external) edges can be filleted + if (GeoIdList.size() == 2 && GeoIdList[0] >= 0 && GeoIdList[1] >= 0) { + const Part::Geometry *geo1 = getGeometry(GeoIdList[0]); + const Part::Geometry *geo2 = getGeometry(GeoIdList[1]); + if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && + geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) { + const Part::GeomLineSegment *lineSeg1 = dynamic_cast(geo1); + const Part::GeomLineSegment *lineSeg2 = dynamic_cast(geo2); + + Base::Vector3d midPnt1 = (lineSeg1->getStartPoint() + lineSeg1->getEndPoint()) / 2 ; + Base::Vector3d midPnt2 = (lineSeg2->getStartPoint() + lineSeg2->getEndPoint()) / 2 ; + return fillet(GeoIdList[0], GeoIdList[1], midPnt1, midPnt2, radius, trim); + } + } + + return -1; +} + +int SketchObject::fillet(int GeoId1, int GeoId2, + const Base::Vector3d& refPnt1, const Base::Vector3d& refPnt2, + double radius, bool trim) +{ + if (GeoId1 < 0 || GeoId1 > getHighestCurveIndex() || + GeoId2 < 0 || GeoId2 > getHighestCurveIndex()) + return -1; + + const Part::Geometry *geo1 = getGeometry(GeoId1); + const Part::Geometry *geo2 = getGeometry(GeoId2); + if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && + geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) { + const Part::GeomLineSegment *lineSeg1 = dynamic_cast(geo1); + const Part::GeomLineSegment *lineSeg2 = dynamic_cast(geo2); + + Base::Vector3d filletCenter; + if (!Part::findFilletCenter(lineSeg1, lineSeg2, radius, refPnt1, refPnt2, filletCenter)) + return -1; + Base::Vector3d dir1 = lineSeg1->getEndPoint() - lineSeg1->getStartPoint(); + Base::Vector3d dir2 = lineSeg2->getEndPoint() - lineSeg2->getStartPoint(); + + // the intersection point will and two distances will be necessary later for trimming the lines + Base::Vector3d intersection, dist1, dist2; + + // create arc from known parameters and lines + int filletId; + Part::GeomArcOfCircle *arc = Part::createFilletGeometry(lineSeg1, lineSeg2, filletCenter, radius); + if (arc) { + // calculate intersection and distances before we invalidate lineSeg1 and lineSeg2 + if (!find2DLinesIntersection(lineSeg1, lineSeg2, intersection)) { + delete arc; + return -1; + } + dist1.ProjToLine(arc->getStartPoint(/*emulateCCW=*/true)-intersection, dir1); + dist2.ProjToLine(arc->getStartPoint(/*emulateCCW=*/true)-intersection, dir2); + Part::Geometry *newgeo = dynamic_cast(arc); + filletId = addGeometry(newgeo); + if (filletId < 0) { + delete arc; + return -1; + } + } + else + return -1; + + if (trim) { + PointPos PosId1 = (filletCenter-intersection)*dir1 > 0 ? start : end; + PointPos PosId2 = (filletCenter-intersection)*dir2 > 0 ? start : end; + + delConstraintOnPoint(GeoId1, PosId1, false); + delConstraintOnPoint(GeoId2, PosId2, false); + Sketcher::Constraint *tangent1 = new Sketcher::Constraint(); + Sketcher::Constraint *tangent2 = new Sketcher::Constraint(); + + tangent1->Type = Sketcher::Tangent; + tangent1->First = GeoId1; + tangent1->FirstPos = PosId1; + tangent1->Second = filletId; + + tangent2->Type = Sketcher::Tangent; + tangent2->First = GeoId2; + tangent2->FirstPos = PosId2; + tangent2->Second = filletId; + + if (dist1.Length() < dist2.Length()) { + tangent1->SecondPos = start; + tangent2->SecondPos = end; + movePoint(GeoId1, PosId1, arc->getStartPoint(/*emulateCCW=*/true),false,true); + movePoint(GeoId2, PosId2, arc->getEndPoint(/*emulateCCW=*/true),false,true); + } + else { + tangent1->SecondPos = end; + tangent2->SecondPos = start; + movePoint(GeoId1, PosId1, arc->getEndPoint(/*emulateCCW=*/true),false,true); + movePoint(GeoId2, PosId2, arc->getStartPoint(/*emulateCCW=*/true),false,true); + } + + addConstraint(tangent1); + addConstraint(tangent2); + delete tangent1; + delete tangent2; + } + delete arc; + + if(noRecomputes) // if we do not have a recompute after the geometry creation, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } + return -1; +} + +int SketchObject::trim(int GeoId, const Base::Vector3d& point) +{ + if (GeoId < 0 || GeoId > getHighestCurveIndex()) + return -1; + + const std::vector &geomlist = getInternalGeometry(); + const std::vector &constraints = this->Constraints.getValues(); + + int GeoId1=Constraint::GeoUndef, GeoId2=Constraint::GeoUndef; + Base::Vector3d point1, point2; + Part2DObject::seekTrimPoints(geomlist, GeoId, point, GeoId1, point1, GeoId2, point2); + if (GeoId1 < 0 && GeoId2 >= 0) { + std::swap(GeoId1,GeoId2); + std::swap(point1,point2); + } + + Part::Geometry *geo = geomlist[GeoId]; + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = dynamic_cast(geo); + Base::Vector3d startPnt = lineSeg->getStartPoint(); + Base::Vector3d endPnt = lineSeg->getEndPoint(); + Base::Vector3d dir = (endPnt - startPnt).Normalize(); + double length = (endPnt - startPnt)*dir; + double x0 = (point - startPnt)*dir; + if (GeoId1 >= 0 && GeoId2 >= 0) { + double x1 = (point1 - startPnt)*dir; + double x2 = (point2 - startPnt)*dir; + if (x1 > x2) { + std::swap(GeoId1,GeoId2); + std::swap(point1,point2); + std::swap(x1,x2); + } + if (x1 >= 0.001*length && x2 <= 0.999*length) { + if (x1 < x0 && x2 > x0) { + int newGeoId = addGeometry(geo); + // go through all constraints and replace the point (GeoId,end) with (newGeoId,end) + transferConstraints(GeoId, end, newGeoId, end); + + movePoint(GeoId, end, point1,false,true); + movePoint(newGeoId, start, point2,false,true); + + PointPos secondPos1 = Sketcher::none, secondPos2 = Sketcher::none; + ConstraintType constrType1 = Sketcher::PointOnObject, constrType2 = Sketcher::PointOnObject; + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it) { + Constraint *constr = *(it); + if (secondPos1 == Sketcher::none && (constr->First == GeoId1 && constr->Second == GeoId)) { + constrType1= Sketcher::Coincident; + secondPos1 = constr->FirstPos; + } else if (secondPos2 == Sketcher::none && (constr->First == GeoId2 && constr->Second == GeoId)) { + constrType2 = Sketcher::Coincident; + secondPos2 = constr->FirstPos; + } + } + + // constrain the trimming points on the corresponding geometries + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = constrType1; + newConstr->First = GeoId; + newConstr->FirstPos = end; + newConstr->Second = GeoId1; + + if (constrType1 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos1; + delConstraintOnPoint(GeoId1, secondPos1, false); + } + + addConstraint(newConstr); + + // Reset the second pos + newConstr->SecondPos = Sketcher::none; + + newConstr->Type = constrType2; + newConstr->First = newGeoId; + newConstr->FirstPos = start; + newConstr->Second = GeoId2; + + if (constrType2 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos2; + delConstraintOnPoint(GeoId2, secondPos2, false); + } + + addConstraint(newConstr); + + // Reset the second pos + newConstr->SecondPos = Sketcher::none; + + // new line segments colinear + newConstr->Type = Sketcher::Tangent; + newConstr->First = GeoId; + newConstr->FirstPos = none; + newConstr->Second = newGeoId; + addConstraint(newConstr); + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } + } else if (x1 < 0.001*length) { // drop the first intersection point + std::swap(GeoId1,GeoId2); + std::swap(point1,point2); + } else if (x2 > 0.999*length) { // drop the second intersection point + } + else + return -1; + } + + if (GeoId1 >= 0) { + double x1 = (point1 - startPnt)*dir; + if (x1 >= 0.001*length && x1 <= 0.999*length) { + + ConstraintType constrType = Sketcher::PointOnObject; + PointPos secondPos = Sketcher::none; + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it) { + Constraint *constr = *(it); + if ((constr->First == GeoId1 && constr->Second == GeoId)) { + constrType = Sketcher::Coincident; + secondPos = constr->FirstPos; + delConstraintOnPoint(GeoId1, constr->FirstPos, false); + break; + } + } + + if (x1 > x0) { // trim line start + delConstraintOnPoint(GeoId, start, false); + movePoint(GeoId, start, point1,false,true); + + // constrain the trimming point on the corresponding geometry + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = constrType; + newConstr->First = GeoId; + newConstr->FirstPos = start; + newConstr->Second = GeoId1; + + if (constrType == Sketcher::Coincident) + newConstr->SecondPos = secondPos; + + addConstraint(newConstr); + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } + else if (x1 < x0) { // trim line end + delConstraintOnPoint(GeoId, end, false); + movePoint(GeoId, end, point1,false,true); + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = constrType; + newConstr->First = GeoId; + newConstr->FirstPos = end; + newConstr->Second = GeoId1; + + if (constrType == Sketcher::Coincident) + newConstr->SecondPos = secondPos; + + addConstraint(newConstr); + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } + } + } + } else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = dynamic_cast(geo); + Base::Vector3d center = circle->getCenter(); + double theta0 = Base::fmod(atan2(point.y - center.y,point.x - center.x), 2.f*M_PI); + if (GeoId1 >= 0 && GeoId2 >= 0) { + double theta1 = Base::fmod(atan2(point1.y - center.y, point1.x - center.x), 2.f*M_PI); + double theta2 = Base::fmod(atan2(point2.y - center.y, point2.x - center.x), 2.f*M_PI); + if (Base::fmod(theta1 - theta0, 2.f*M_PI) > Base::fmod(theta2 - theta0, 2.f*M_PI)) { + std::swap(GeoId1,GeoId2); + std::swap(point1,point2); + std::swap(theta1,theta2); + } + if (theta1 == theta0 || theta1 == theta2) + return -1; + else if (theta1 > theta2) + theta2 += 2.f*M_PI; + + // Trim Point between intersection points + + // Create a new arc to substitute Circle in geometry list and set parameters + Part::GeomArcOfCircle *geoNew = new Part::GeomArcOfCircle(); + geoNew->setCenter(center); + geoNew->setRadius(circle->getRadius()); + geoNew->setRange(theta1, theta2,/*emulateCCW=*/true); + + std::vector< Part::Geometry * > newVals(geomlist); + newVals[GeoId] = geoNew; + Geometry.setValues(newVals); + Constraints.acceptGeometry(getCompleteGeometry()); + delete geoNew; + rebuildVertexIndex(); + + PointPos secondPos1 = Sketcher::none, secondPos2 = Sketcher::none; + ConstraintType constrType1 = Sketcher::PointOnObject, constrType2 = Sketcher::PointOnObject; + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it) { + Constraint *constr = *(it); + if (secondPos1 == Sketcher::none && (constr->First == GeoId1 && constr->Second == GeoId)) { + constrType1= Sketcher::Coincident; + secondPos1 = constr->FirstPos; + } else if(secondPos2 == Sketcher::none && (constr->First == GeoId2 && constr->Second == GeoId)) { + constrType2 = Sketcher::Coincident; + secondPos2 = constr->FirstPos; + } + } + + // constrain the trimming points on the corresponding geometries + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = constrType1; + newConstr->First = GeoId; + newConstr->FirstPos = start; + newConstr->Second = GeoId1; + + if (constrType1 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos1; + delConstraintOnPoint(GeoId1, secondPos1, false); + } + + addConstraint(newConstr); + + // Reset secondpos in case it was set previously + newConstr->SecondPos = Sketcher::none; + + // Add Second Constraint + newConstr->First = GeoId; + newConstr->FirstPos = end; + newConstr->Second = GeoId2; + + if (constrType2 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos2; + delConstraintOnPoint(GeoId2, secondPos2, false); + } + + addConstraint(newConstr); + + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } + } else if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId()) { + const Part::GeomEllipse *ellipse = dynamic_cast(geo); + Base::Vector3d center = ellipse->getCenter(); + double theta0; + ellipse->closestParameter(point,theta0); + theta0 = Base::fmod(theta0, 2.f*M_PI); + if (GeoId1 >= 0 && GeoId2 >= 0) { + double theta1; + ellipse->closestParameter(point1,theta1); + theta1 = Base::fmod(theta1, 2.f*M_PI); + double theta2; + ellipse->closestParameter(point2,theta2); + theta2 = Base::fmod(theta2, 2.f*M_PI); + if (Base::fmod(theta1 - theta0, 2.f*M_PI) > Base::fmod(theta2 - theta0, 2.f*M_PI)) { + std::swap(GeoId1,GeoId2); + std::swap(point1,point2); + std::swap(theta1,theta2); + } + if (theta1 == theta0 || theta1 == theta2) + return -1; + else if (theta1 > theta2) + theta2 += 2.f*M_PI; + + // Trim Point between intersection points + + // Create a new arc to substitute Circle in geometry list and set parameters + Part::GeomArcOfEllipse *geoNew = new Part::GeomArcOfEllipse(); + geoNew->setCenter(center); + geoNew->setMajorRadius(ellipse->getMajorRadius()); + geoNew->setMinorRadius(ellipse->getMinorRadius()); + geoNew->setMajorAxisDir(ellipse->getMajorAxisDir()); + geoNew->setRange(theta1, theta2, /*emulateCCW=*/true); + + std::vector< Part::Geometry * > newVals(geomlist); + newVals[GeoId] = geoNew; + Geometry.setValues(newVals); + Constraints.acceptGeometry(getCompleteGeometry()); + delete geoNew; + rebuildVertexIndex(); + + PointPos secondPos1 = Sketcher::none, secondPos2 = Sketcher::none; + ConstraintType constrType1 = Sketcher::PointOnObject, constrType2 = Sketcher::PointOnObject; + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it) { + Constraint *constr = *(it); + if (secondPos1 == Sketcher::none && (constr->First == GeoId1 && constr->Second == GeoId)) { + constrType1= Sketcher::Coincident; + secondPos1 = constr->FirstPos; + } else if(secondPos2 == Sketcher::none && (constr->First == GeoId2 && constr->Second == GeoId)) { + constrType2 = Sketcher::Coincident; + secondPos2 = constr->FirstPos; + } + } + + // constrain the trimming points on the corresponding geometries + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = constrType1; + newConstr->First = GeoId; + newConstr->FirstPos = start; + newConstr->Second = GeoId1; + + if (constrType1 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos1; + delConstraintOnPoint(GeoId1, secondPos1, false); + } + + addConstraint(newConstr); + + // Reset secondpos in case it was set previously + newConstr->SecondPos = Sketcher::none; + + // Add Second Constraint + newConstr->First = GeoId; + newConstr->FirstPos = end; + newConstr->Second = GeoId2; + + if (constrType2 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos2; + delConstraintOnPoint(GeoId2, secondPos2, false); + } + + addConstraint(newConstr); + + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } + } else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *aoc = dynamic_cast(geo); + Base::Vector3d center = aoc->getCenter(); + double startAngle, endAngle; + aoc->getRange(startAngle, endAngle, /*emulateCCW=*/true); + double dir = (startAngle < endAngle) ? 1 : -1; // this is always == 1 + double arcLength = (endAngle - startAngle)*dir; + double theta0 = Base::fmod(atan2(point.y - center.y, point.x - center.x) - startAngle, 2.f*M_PI); // x0 + if (GeoId1 >= 0 && GeoId2 >= 0) { + double theta1 = Base::fmod(atan2(point1.y - center.y, point1.x - center.x) - startAngle, 2.f*M_PI) * dir; // x1 + double theta2 = Base::fmod(atan2(point2.y - center.y, point2.x - center.x) - startAngle, 2.f*M_PI) * dir; // x2 + if (theta1 > theta2) { + std::swap(GeoId1,GeoId2); + std::swap(point1,point2); + std::swap(theta1,theta2); + } + if (theta1 >= 0.001*arcLength && theta2 <= 0.999*arcLength) { + // Trim Point between intersection points + if (theta1 < theta0 && theta2 > theta0) { + int newGeoId = addGeometry(geo); + // go through all constraints and replace the point (GeoId,end) with (newGeoId,end) + transferConstraints(GeoId, end, newGeoId, end); + + Part::GeomArcOfCircle *aoc1 = dynamic_cast(geomlist[GeoId]); + Part::GeomArcOfCircle *aoc2 = dynamic_cast(geomlist[newGeoId]); + aoc1->setRange(startAngle, startAngle + theta1, /*emulateCCW=*/true); + aoc2->setRange(startAngle + theta2, endAngle, /*emulateCCW=*/true); + + // constrain the trimming points on the corresponding geometries + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + + // Build Constraints associated with new pair of arcs + newConstr->Type = Sketcher::Equal; + newConstr->First = GeoId; + newConstr->Second = newGeoId; + addConstraint(newConstr); + + PointPos secondPos1 = Sketcher::none, secondPos2 = Sketcher::none; + ConstraintType constrType1 = Sketcher::PointOnObject, constrType2 = Sketcher::PointOnObject; + + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it) { + Constraint *constr = *(it); + if (secondPos1 == Sketcher::none && + (constr->First == GeoId1 && constr->Second == GeoId)) { + constrType1= Sketcher::Coincident; + secondPos1 = constr->FirstPos; + } else if (secondPos2 == Sketcher::none && + (constr->First == GeoId2 && constr->Second == GeoId)) { + constrType2 = Sketcher::Coincident; + secondPos2 = constr->FirstPos; + } + } + + newConstr->Type = constrType1; + newConstr->First = GeoId; + newConstr->FirstPos = end; + newConstr->Second = GeoId1; + + if (constrType1 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos1; + delConstraintOnPoint(GeoId1, secondPos1, false); + } + + addConstraint(newConstr); + + // Reset secondpos in case it was set previously + newConstr->SecondPos = Sketcher::none; + + newConstr->Type = constrType2; + newConstr->First = newGeoId; + newConstr->FirstPos = start; + newConstr->Second = GeoId2; + + if (constrType2 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos2; + delConstraintOnPoint(GeoId2, secondPos2, false); + } + + addConstraint(newConstr); + + newConstr->Type = Sketcher::Coincident; + newConstr->First = GeoId; + newConstr->FirstPos = Sketcher::mid; + newConstr->Second = newGeoId; + newConstr->SecondPos = Sketcher::mid; + addConstraint(newConstr); + + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } else + return -1; + } else if (theta1 < 0.001*arcLength) { // drop the second intersection point + std::swap(GeoId1,GeoId2); + std::swap(point1,point2); + } else if (theta2 > 0.999*arcLength) { + } + else + return -1; + } + + if (GeoId1 >= 0) { + + ConstraintType constrType = Sketcher::PointOnObject; + PointPos secondPos = Sketcher::none; + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it) { + Constraint *constr = *(it); + if ((constr->First == GeoId1 && constr->Second == GeoId)) { + constrType = Sketcher::Coincident; + secondPos = constr->FirstPos; + delConstraintOnPoint(GeoId1, constr->FirstPos, false); + break; + } + } + + double theta1 = Base::fmod(atan2(point1.y - center.y, point1.x - center.x) - startAngle, 2.f*M_PI) * dir; // x1 + if (theta1 >= 0.001*arcLength && theta1 <= 0.999*arcLength) { + if (theta1 > theta0) { // trim arc start + delConstraintOnPoint(GeoId, start, false); + Part::GeomArcOfCircle *aoc1 = dynamic_cast(geomlist[GeoId]); + aoc1->setRange(startAngle + theta1, endAngle, /*emulateCCW=*/true); + // constrain the trimming point on the corresponding geometry + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = constrType; + newConstr->First = GeoId; + newConstr->FirstPos = start; + newConstr->Second = GeoId1; + + if (constrType == Sketcher::Coincident) + newConstr->SecondPos = secondPos; + + addConstraint(newConstr); + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } + else { // trim arc end + delConstraintOnPoint(GeoId, end, false); + Part::GeomArcOfCircle *aoc1 = dynamic_cast(geomlist[GeoId]); + aoc1->setRange(startAngle, startAngle + theta1, /*emulateCCW=*/true); + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = constrType; + newConstr->First = GeoId; + newConstr->FirstPos = end; + newConstr->Second = GeoId1; + + if (constrType == Sketcher::Coincident) + newConstr->SecondPos = secondPos; + + addConstraint(newConstr); + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } + } + } + } else if (geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + const Part::GeomArcOfEllipse *aoe = dynamic_cast(geo); + Base::Vector3d center = aoe->getCenter(); + double startAngle, endAngle; + aoe->getRange(startAngle, endAngle,/*emulateCCW=*/true); + double dir = (startAngle < endAngle) ? 1 : -1; // this is always == 1 + double arcLength = (endAngle - startAngle)*dir; + double theta0 = Base::fmod( + atan2(-aoe->getMajorRadius()*((point.x-center.x)*aoe->getMajorAxisDir().y-(point.y-center.y)*aoe->getMajorAxisDir().x), + aoe->getMinorRadius()*((point.x-center.x)*aoe->getMajorAxisDir().x+(point.y-center.y)*aoe->getMajorAxisDir().y) + )- startAngle, 2.f*M_PI); // x0 + if (GeoId1 >= 0 && GeoId2 >= 0) { + double theta1 = Base::fmod( + atan2(-aoe->getMajorRadius()*((point1.x-center.x)*aoe->getMajorAxisDir().y-(point1.y-center.y)*aoe->getMajorAxisDir().x), + aoe->getMinorRadius()*((point1.x-center.x)*aoe->getMajorAxisDir().x+(point1.y-center.y)*aoe->getMajorAxisDir().y) + )- startAngle, 2.f*M_PI) * dir; // x1 + double theta2 = Base::fmod( + atan2(-aoe->getMajorRadius()*((point2.x-center.x)*aoe->getMajorAxisDir().y-(point2.y-center.y)*aoe->getMajorAxisDir().x), + aoe->getMinorRadius()*((point2.x-center.x)*aoe->getMajorAxisDir().x+(point2.y-center.y)*aoe->getMajorAxisDir().y) + )- startAngle, 2.f*M_PI) * dir; // x2 + + if (theta1 > theta2) { + std::swap(GeoId1,GeoId2); + std::swap(point1,point2); + std::swap(theta1,theta2); + } + if (theta1 >= 0.001*arcLength && theta2 <= 0.999*arcLength) { + // Trim Point between intersection points + if (theta1 < theta0 && theta2 > theta0) { + int newGeoId = addGeometry(geo); + // go through all constraints and replace the point (GeoId,end) with (newGeoId,end) + transferConstraints(GeoId, end, newGeoId, end); + + Part::GeomArcOfEllipse *aoe1 = dynamic_cast(geomlist[GeoId]); + Part::GeomArcOfEllipse *aoe2 = dynamic_cast(geomlist[newGeoId]); + aoe1->setRange(startAngle, startAngle + theta1, /*emulateCCW=*/true); + aoe2->setRange(startAngle + theta2, endAngle, /*emulateCCW=*/true); + + // constrain the trimming points on the corresponding geometries + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + + // Build Constraints associated with new pair of arcs + newConstr->Type = Sketcher::Equal; + newConstr->First = GeoId; + newConstr->Second = newGeoId; + addConstraint(newConstr); + + PointPos secondPos1 = Sketcher::none, secondPos2 = Sketcher::none; + ConstraintType constrType1 = Sketcher::PointOnObject, constrType2 = Sketcher::PointOnObject; + + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it) { + Constraint *constr = *(it); + if (secondPos1 == Sketcher::none && + (constr->First == GeoId1 && constr->Second == GeoId)) { + constrType1= Sketcher::Coincident; + secondPos1 = constr->FirstPos; + } else if (secondPos2 == Sketcher::none && + (constr->First == GeoId2 && constr->Second == GeoId)) { + constrType2 = Sketcher::Coincident; + secondPos2 = constr->FirstPos; + } + } + + newConstr->Type = constrType1; + newConstr->First = GeoId; + newConstr->FirstPos = end; + newConstr->Second = GeoId1; + + if (constrType1 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos1; + delConstraintOnPoint(GeoId1, secondPos1, false); + } + + addConstraint(newConstr); + + // Reset secondpos in case it was set previously + newConstr->SecondPos = Sketcher::none; + + newConstr->Type = constrType2; + newConstr->First = newGeoId; + newConstr->FirstPos = start; + newConstr->Second = GeoId2; + + if (constrType2 == Sketcher::Coincident) { + newConstr->SecondPos = secondPos2; + delConstraintOnPoint(GeoId2, secondPos2, false); + } + + addConstraint(newConstr); + + newConstr->Type = Sketcher::Coincident; + newConstr->First = GeoId; + newConstr->FirstPos = Sketcher::mid; + newConstr->Second = newGeoId; + newConstr->SecondPos = Sketcher::mid; + addConstraint(newConstr); + + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } else + return -1; + } else if (theta1 < 0.001*arcLength) { // drop the second intersection point + std::swap(GeoId1,GeoId2); + std::swap(point1,point2); + } else if (theta2 > 0.999*arcLength) { + } else + return -1; + } + + if (GeoId1 >= 0) { + + ConstraintType constrType = Sketcher::PointOnObject; + PointPos secondPos = Sketcher::none; + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it) { + Constraint *constr = *(it); + if ((constr->First == GeoId1 && constr->Second == GeoId)) { + constrType = Sketcher::Coincident; + secondPos = constr->FirstPos; + delConstraintOnPoint(GeoId1, constr->FirstPos, false); + break; + } + } + + double theta1 = Base::fmod( + atan2(-aoe->getMajorRadius()*((point1.x-center.x)*aoe->getMajorAxisDir().y-(point1.y-center.y)*aoe->getMajorAxisDir().x), + aoe->getMinorRadius()*((point1.x-center.x)*aoe->getMajorAxisDir().x+(point1.y-center.y)*aoe->getMajorAxisDir().y) + )- startAngle, 2.f*M_PI) * dir; // x1 + + if (theta1 >= 0.001*arcLength && theta1 <= 0.999*arcLength) { + if (theta1 > theta0) { // trim arc start + delConstraintOnPoint(GeoId, start, false); + Part::GeomArcOfEllipse *aoe1 = dynamic_cast(geomlist[GeoId]); + aoe1->setRange(startAngle + theta1, endAngle, /*emulateCCW=*/true); + // constrain the trimming point on the corresponding geometry + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = constrType; + newConstr->First = GeoId; + newConstr->FirstPos = start; + newConstr->Second = GeoId1; + + if (constrType == Sketcher::Coincident) + newConstr->SecondPos = secondPos; + + addConstraint(newConstr); + delete newConstr; + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; + } + else { // trim arc end + delConstraintOnPoint(GeoId, end, false); + Part::GeomArcOfEllipse *aoe1 = dynamic_cast(geomlist[GeoId]); + aoe1->setRange(startAngle, startAngle + theta1, /*emulateCCW=*/true); + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + return 0; + } + } + } + } + + return -1; +} + +int SketchObject::addSymmetric(const std::vector &geoIdList, int refGeoId, Sketcher::PointPos refPosId/*=Sketcher::none*/) +{ + const std::vector< Part::Geometry * > &geovals = getInternalGeometry(); + std::vector< Part::Geometry * > newgeoVals(geovals); + + const std::vector< Constraint * > &constrvals = this->Constraints.getValues(); + std::vector< Constraint * > newconstrVals(constrvals); + + int cgeoid = getHighestCurveIndex()+1; + + std::map geoIdMap; + std::map isStartEndInverted; + + // reference is a line + if(refPosId == Sketcher::none) { + const Part::Geometry *georef = getGeometry(refGeoId); + if(georef->getTypeId() != Part::GeomLineSegment::getClassTypeId()) { + Base::Console().Error("Reference for symmetric is neither a point nor a line.\n"); + return -1; + } + + const Part::GeomLineSegment *refGeoLine = static_cast(georef); + //line + Base::Vector3d refstart = refGeoLine->getStartPoint(); + Base::Vector3d vectline = refGeoLine->getEndPoint()-refstart; + + for (std::vector::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) { + const Part::Geometry *geo = getGeometry(*it); + Part::Geometry *geosym = geo->clone(); + + // Handle Geometry + if(geosym->getTypeId() == Part::GeomLineSegment::getClassTypeId()){ + Part::GeomLineSegment *geosymline = static_cast(geosym); + Base::Vector3d sp = geosymline->getStartPoint(); + Base::Vector3d ep = geosymline->getEndPoint(); + + geosymline->setPoints(sp+2.0*(sp.Perpendicular(refGeoLine->getStartPoint(),vectline)-sp), + ep+2.0*(ep.Perpendicular(refGeoLine->getStartPoint(),vectline)-ep)); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else if(geosym->getTypeId() == Part::GeomCircle::getClassTypeId()){ + Part::GeomCircle *geosymcircle = static_cast(geosym); + Base::Vector3d cp = geosymcircle->getCenter(); + + geosymcircle->setCenter(cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp)); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else if(geosym->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){ + Part::GeomArcOfCircle *geoaoc = static_cast(geosym); + Base::Vector3d sp = geoaoc->getStartPoint(true); + Base::Vector3d ep = geoaoc->getEndPoint(true); + Base::Vector3d cp = geoaoc->getCenter(); + + Base::Vector3d ssp = sp+2.0*(sp.Perpendicular(refGeoLine->getStartPoint(),vectline)-sp); + Base::Vector3d sep = ep+2.0*(ep.Perpendicular(refGeoLine->getStartPoint(),vectline)-ep); + Base::Vector3d scp = cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp); + + double theta1 = Base::fmod(atan2(sep.y - scp.y, sep.x - scp.x), 2.f*M_PI); + double theta2 = Base::fmod(atan2(ssp.y - scp.y, ssp.x - scp.x), 2.f*M_PI); + + geoaoc->setCenter(scp); + geoaoc->setRange(theta1,theta2,true); + isStartEndInverted.insert(std::make_pair(*it, true)); + } + else if(geosym->getTypeId() == Part::GeomEllipse::getClassTypeId()){ + Part::GeomEllipse *geosymellipse = static_cast(geosym); + Base::Vector3d cp = geosymellipse->getCenter(); + + Base::Vector3d majdir = geosymellipse->getMajorAxisDir(); + double majord=geosymellipse->getMajorRadius(); + double minord=geosymellipse->getMinorRadius(); + double df= sqrt(majord*majord-minord*minord); + Base::Vector3d f1 = cp + df * majdir; + + Base::Vector3d sf1 = f1+2.0*(f1.Perpendicular(refGeoLine->getStartPoint(),vectline)-f1); + Base::Vector3d scp = cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp); + + geosymellipse->setMajorAxisDir(sf1-scp); + + geosymellipse->setCenter(scp); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else if(geosym->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){ + Part::GeomArcOfEllipse *geosymaoe = static_cast(geosym); + Base::Vector3d cp = geosymaoe->getCenter(); + Base::Vector3d sp = geosymaoe->getStartPoint(true); + Base::Vector3d ep = geosymaoe->getEndPoint(true); + + Base::Vector3d majdir = geosymaoe->getMajorAxisDir(); + double majord=geosymaoe->getMajorRadius(); + double minord=geosymaoe->getMinorRadius(); + double df= sqrt(majord*majord-minord*minord); + Base::Vector3d f1 = cp + df * majdir; + + Base::Vector3d sf1 = f1+2.0*(f1.Perpendicular(refGeoLine->getStartPoint(),vectline)-f1); + Base::Vector3d scp = cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp); + Base::Vector3d ssp = sp+2.0*(sp.Perpendicular(refGeoLine->getStartPoint(),vectline)-sp); + Base::Vector3d sep = ep+2.0*(ep.Perpendicular(refGeoLine->getStartPoint(),vectline)-ep); + + geosymaoe->setMajorAxisDir(sf1-scp); + + geosymaoe->setCenter(scp); + + double theta1,theta2; + geosymaoe->closestParameter(sep,theta1); + geosymaoe->closestParameter(ssp,theta2); + + geosymaoe->setRange(theta1,theta2,true); + isStartEndInverted.insert(std::make_pair(*it, true)); + } + else if(geosym->getTypeId() == Part::GeomPoint::getClassTypeId()){ + Part::GeomPoint *geosympoint = static_cast(geosym); + Base::Vector3d cp = geosympoint->getPoint(); + + geosympoint->setPoint(cp+2.0*(cp.Perpendicular(refGeoLine->getStartPoint(),vectline)-cp)); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else { + Base::Console().Error("Unsupported Geometry!! Just copying it.\n"); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + + newgeoVals.push_back(geosym); + geoIdMap.insert(std::make_pair(*it, cgeoid)); + cgeoid++; + } + } + else { //reference is a point + Vector3d refpoint; + const Part::Geometry *georef = getGeometry(refGeoId); + + if (georef->getTypeId() == Part::GeomPoint::getClassTypeId()) { + refpoint = static_cast(georef)->getPoint(); + } + else if ( refGeoId == -1 && refPosId == Sketcher::start) { + refpoint = Vector3d(0,0,0); + } + else { + switch(refPosId){ + case Sketcher::start: + if(georef->getTypeId() == Part::GeomLineSegment::getClassTypeId()){ + const Part::GeomLineSegment *geosymline = static_cast(georef); + refpoint = geosymline->getStartPoint(); + } + else if(georef->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){ + const Part::GeomArcOfCircle *geoaoc = static_cast(georef); + refpoint = geoaoc->getStartPoint(true); + } + else if(georef->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){ + const Part::GeomArcOfEllipse *geosymaoe = static_cast(georef); + refpoint = geosymaoe->getStartPoint(true); + } + break; + case Sketcher::end: + if(georef->getTypeId() == Part::GeomLineSegment::getClassTypeId()){ + const Part::GeomLineSegment *geosymline = static_cast(georef); + refpoint = geosymline->getEndPoint(); + } + else if(georef->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){ + const Part::GeomArcOfCircle *geoaoc = static_cast(georef); + refpoint = geoaoc->getEndPoint(true); + } + else if(georef->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){ + const Part::GeomArcOfEllipse *geosymaoe = static_cast(georef); + refpoint = geosymaoe->getEndPoint(true); + } + break; + case Sketcher::mid: + if(georef->getTypeId() == Part::GeomCircle::getClassTypeId()){ + const Part::GeomCircle *geosymcircle = static_cast(georef); + refpoint = geosymcircle->getCenter(); + } + else if(georef->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){ + const Part::GeomArcOfCircle *geoaoc = static_cast(georef); + refpoint = geoaoc->getCenter(); + } + else if(georef->getTypeId() == Part::GeomEllipse::getClassTypeId()){ + const Part::GeomEllipse *geosymellipse = static_cast(georef); + refpoint = geosymellipse->getCenter(); + } + else if(georef->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){ + const Part::GeomArcOfEllipse *geosymaoe = static_cast(georef); + refpoint = geosymaoe->getCenter(); + } + break; + default: + Base::Console().Error("Wrong PointPosId.\n"); + return -1; + } + } + + for (std::vector::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) { + const Part::Geometry *geo = getGeometry(*it); + Part::Geometry *geosym = geo->clone(); + + // Handle Geometry + if(geosym->getTypeId() == Part::GeomLineSegment::getClassTypeId()){ + Part::GeomLineSegment *geosymline = static_cast(geosym); + Base::Vector3d sp = geosymline->getStartPoint(); + Base::Vector3d ep = geosymline->getEndPoint(); + Base::Vector3d ssp = sp + 2.0*(refpoint-sp); + Base::Vector3d sep = ep + 2.0*(refpoint-ep); + + geosymline->setPoints(ssp, sep); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else if(geosym->getTypeId() == Part::GeomCircle::getClassTypeId()){ + Part::GeomCircle *geosymcircle = static_cast(geosym); + Base::Vector3d cp = geosymcircle->getCenter(); + + geosymcircle->setCenter(cp + 2.0*(refpoint-cp)); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else if(geosym->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){ + Part::GeomArcOfCircle *geoaoc = static_cast(geosym); + Base::Vector3d sp = geoaoc->getStartPoint(true); + Base::Vector3d ep = geoaoc->getEndPoint(true); + Base::Vector3d cp = geoaoc->getCenter(); + + Base::Vector3d ssp = sp + 2.0*(refpoint-sp); + Base::Vector3d sep = ep + 2.0*(refpoint-ep); + Base::Vector3d scp = cp + 2.0*(refpoint-cp); + + double theta1 = Base::fmod(atan2(ssp.y - scp.y, ssp.x - scp.x), 2.f*M_PI); + double theta2 = Base::fmod(atan2(sep.y - scp.y, sep.x - scp.x), 2.f*M_PI); + + geoaoc->setCenter(scp); + geoaoc->setRange(theta1,theta2,true); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else if(geosym->getTypeId() == Part::GeomEllipse::getClassTypeId()){ + Part::GeomEllipse *geosymellipse = static_cast(geosym); + Base::Vector3d cp = geosymellipse->getCenter(); + + Base::Vector3d majdir = geosymellipse->getMajorAxisDir(); + double majord=geosymellipse->getMajorRadius(); + double minord=geosymellipse->getMinorRadius(); + double df= sqrt(majord*majord-minord*minord); + Base::Vector3d f1 = cp + df * majdir; + + Base::Vector3d sf1 = f1 + 2.0*(refpoint-f1); + Base::Vector3d scp = cp + 2.0*(refpoint-cp); + + geosymellipse->setMajorAxisDir(sf1-scp); + + geosymellipse->setCenter(scp); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else if(geosym->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){ + Part::GeomArcOfEllipse *geosymaoe = static_cast(geosym); + Base::Vector3d cp = geosymaoe->getCenter(); + Base::Vector3d sp = geosymaoe->getStartPoint(true); + Base::Vector3d ep = geosymaoe->getEndPoint(true); + + Base::Vector3d majdir = geosymaoe->getMajorAxisDir(); + double majord=geosymaoe->getMajorRadius(); + double minord=geosymaoe->getMinorRadius(); + double df= sqrt(majord*majord-minord*minord); + Base::Vector3d f1 = cp + df * majdir; + + Base::Vector3d sf1 = f1 + 2.0*(refpoint-f1); + Base::Vector3d scp = cp + 2.0*(refpoint-cp); + Base::Vector3d ssp = sp + 2.0*(refpoint-sp); + Base::Vector3d sep = ep + 2.0*(refpoint-ep); + + geosymaoe->setMajorAxisDir(sf1-scp); + + geosymaoe->setCenter(scp); + + double theta1,theta2; + geosymaoe->closestParameter(ssp,theta1); + geosymaoe->closestParameter(sep,theta2); + + geosymaoe->setRange(theta1,theta2,true); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else if(geosym->getTypeId() == Part::GeomPoint::getClassTypeId()){ + Part::GeomPoint *geosympoint = static_cast(geosym); + Base::Vector3d cp = geosympoint->getPoint(); + + geosympoint->setPoint(cp + 2.0*(refpoint-cp)); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + else { + Base::Console().Error("Unsupported Geometry!! Just copying it.\n"); + isStartEndInverted.insert(std::make_pair(*it, false)); + } + + newgeoVals.push_back(geosym); + geoIdMap.insert(std::make_pair(*it, cgeoid)); + cgeoid++; + } + } + + // add the geometry + Geometry.setValues(newgeoVals); + Constraints.acceptGeometry(getCompleteGeometry()); + rebuildVertexIndex(); + + for (std::vector::const_iterator it = constrvals.begin(); it != constrvals.end(); ++it) { + + std::vector::const_iterator fit=std::find(geoIdList.begin(), geoIdList.end(), (*it)->First); + + if(fit != geoIdList.end()) { // if First of constraint is in geoIdList + + if( (*it)->Second == Constraint::GeoUndef /*&& (*it)->Third == Constraint::GeoUndef*/) { + if( (*it)->Type != Sketcher::DistanceX && + (*it)->Type != Sketcher::DistanceY) { + + Constraint *constNew = (*it)->copy(); + + constNew->First = geoIdMap[(*it)->First]; + newconstrVals.push_back(constNew); + } + } + else { // other geoids intervene in this constraint + + std::vector::const_iterator sit=std::find(geoIdList.begin(), geoIdList.end(), (*it)->Second); + + if(sit != geoIdList.end()) { // Second is also in the list + + if( (*it)->Third == Constraint::GeoUndef ) { + if((*it)->Type == Sketcher::Coincident || + (*it)->Type == Sketcher::Perpendicular || + (*it)->Type == Sketcher::Parallel || + (*it)->Type == Sketcher::Tangent || + (*it)->Type == Sketcher::Distance || + (*it)->Type == Sketcher::Equal || + (*it)->Type == Sketcher::Radius || + (*it)->Type == Sketcher::PointOnObject ){ + Constraint *constNew = (*it)->copy(); + + constNew->First = geoIdMap[(*it)->First]; + constNew->Second = geoIdMap[(*it)->Second]; + if(isStartEndInverted[(*it)->First]){ + if((*it)->FirstPos == Sketcher::start) + constNew->FirstPos = Sketcher::end; + else if((*it)->FirstPos == Sketcher::end) + constNew->FirstPos = Sketcher::start; + } + if(isStartEndInverted[(*it)->Second]){ + if((*it)->SecondPos == Sketcher::start) + constNew->SecondPos = Sketcher::end; + else if((*it)->SecondPos == Sketcher::end) + constNew->SecondPos = Sketcher::start; + } + + if (constNew->Type == Tangent || constNew->Type == Perpendicular) + AutoLockTangencyAndPerpty(constNew,true); + + newconstrVals.push_back(constNew); + } + } + else { + std::vector::const_iterator tit=std::find(geoIdList.begin(), geoIdList.end(), (*it)->Third); + + if(tit != geoIdList.end()) { // Third is also in the list + Constraint *constNew = (*it)->copy(); + constNew->First = geoIdMap[(*it)->First]; + constNew->Second = geoIdMap[(*it)->Second]; + constNew->Third = geoIdMap[(*it)->Third]; + if(isStartEndInverted[(*it)->First]){ + if((*it)->FirstPos == Sketcher::start) + constNew->FirstPos = Sketcher::end; + else if((*it)->FirstPos == Sketcher::end) + constNew->FirstPos = Sketcher::start; + } + if(isStartEndInverted[(*it)->Second]){ + if((*it)->SecondPos == Sketcher::start) + constNew->SecondPos = Sketcher::end; + else if((*it)->SecondPos == Sketcher::end) + constNew->SecondPos = Sketcher::start; + } + if(isStartEndInverted[(*it)->Third]){ + if((*it)->ThirdPos == Sketcher::start) + constNew->ThirdPos = Sketcher::end; + else if((*it)->ThirdPos == Sketcher::end) + constNew->ThirdPos = Sketcher::start; + } + newconstrVals.push_back(constNew); + } + } + } + } + } + } + + if( newconstrVals.size() > constrvals.size() ) + Constraints.setValues(newconstrVals); + + return Geometry.getSize()-1; +} + +int SketchObject::addCopy(const std::vector &geoIdList, const Base::Vector3d& displacement, bool clone /*=false*/, int csize/*=2*/, int rsize/*=1*/, + bool constraindisplacement /*= false*/, double perpscale /*= 1.0*/) +{ + const std::vector< Part::Geometry * > &geovals = getInternalGeometry(); + std::vector< Part::Geometry * > newgeoVals(geovals); + + const std::vector< Constraint * > &constrvals = this->Constraints.getValues(); + std::vector< Constraint * > newconstrVals(constrvals); + + int cgeoid = getHighestCurveIndex()+1; + + int iterfirstgeoid = -1 ; + + Base::Vector3d iterfirstpoint; + + int refgeoid = -1; + + int colrefgeoid = 0, rowrefgeoid = 0; + + int currentrowfirstgeoid= -1, prevrowstartfirstgeoid = -1, prevfirstgeoid = -1; + + Sketcher::PointPos refposId = Sketcher::none; + + std::map geoIdMap; + + Base::Vector3d perpendicularDisplacement = Base::Vector3d(perpscale*displacement.y,perpscale*-displacement.x,0); + + int x,y; + + for (y=0;ygetTypeId() == Part::GeomCircle::getClassTypeId() || + geo->getTypeId() == Part::GeomEllipse::getClassTypeId() ){ + refposId = Sketcher::mid; + } + else + refposId = Sketcher::start; + + continue; // the first element is already in place + } + else { + prevfirstgeoid = iterfirstgeoid; + + iterfirstgeoid = cgeoid; + + if( x == 0 ) { // if first element of second row + prevrowstartfirstgeoid = currentrowfirstgeoid; + currentrowfirstgeoid = cgeoid; + } + } + + for (std::vector::const_iterator it = geoIdList.begin(); it != geoIdList.end(); ++it) { + const Part::Geometry *geo = getGeometry(*it); + Part::Geometry *geocopy = geo->clone(); + + // Handle Geometry + if(geocopy->getTypeId() == Part::GeomLineSegment::getClassTypeId()){ + Part::GeomLineSegment *geosymline = static_cast(geocopy); + Base::Vector3d ep = geosymline->getEndPoint(); + Base::Vector3d ssp = geosymline->getStartPoint()+double(x)*displacement+double(y)*perpendicularDisplacement; + + geosymline->setPoints( ssp, + ep+double(x)*displacement+double(y)*perpendicularDisplacement); + + if(it == geoIdList.begin()) + iterfirstpoint = ssp; + } + else if(geocopy->getTypeId() == Part::GeomCircle::getClassTypeId()){ + Part::GeomCircle *geosymcircle = static_cast(geocopy); + Base::Vector3d cp = geosymcircle->getCenter(); + Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; + + geosymcircle->setCenter(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = scp; + } + else if(geocopy->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){ + Part::GeomArcOfCircle *geoaoc = static_cast(geocopy); + Base::Vector3d cp = geoaoc->getCenter(); + Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; + + geoaoc->setCenter(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = geoaoc->getStartPoint(true); + } + else if(geocopy->getTypeId() == Part::GeomEllipse::getClassTypeId()){ + Part::GeomEllipse *geosymellipse = static_cast(geocopy); + Base::Vector3d cp = geosymellipse->getCenter(); + Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; + + geosymellipse->setCenter(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = scp; + } + else if(geocopy->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()){ + Part::GeomArcOfEllipse *geoaoe = static_cast(geocopy); + Base::Vector3d cp = geoaoe->getCenter(); + Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; + + geoaoe->setCenter(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = geoaoe->getStartPoint(true); + } + else if(geocopy->getTypeId() == Part::GeomPoint::getClassTypeId()){ + Part::GeomPoint *geopoint = static_cast(geocopy); + Base::Vector3d cp = geopoint->getPoint(); + Base::Vector3d scp = cp+double(x)*displacement+double(y)*perpendicularDisplacement; + geopoint->setPoint(scp); + + if(it == geoIdList.begin()) + iterfirstpoint = scp; + } + else { + Base::Console().Error("Unsupported Geometry!! Just skipping it.\n"); + continue; + } + + newgeoVals.push_back(geocopy); + geoIdMap.insert(std::make_pair(*it, cgeoid)); + cgeoid++; + } + + // handle geometry constraints + for (std::vector::const_iterator it = constrvals.begin(); it != constrvals.end(); ++it) { + + std::vector::const_iterator fit=std::find(geoIdList.begin(), geoIdList.end(), (*it)->First); + + if(fit != geoIdList.end()) { // if First of constraint is in geoIdList + + if( (*it)->Second == Constraint::GeoUndef /*&& (*it)->Third == Constraint::GeoUndef*/) { + if( ((*it)->Type != Sketcher::DistanceX && (*it)->Type != Sketcher::DistanceY ) || + (*it)->FirstPos == Sketcher::none ) { // if it is not a point locking DistanceX/Y + if (((*it)->Type == Sketcher::DistanceX || + (*it)->Type == Sketcher::DistanceY || + (*it)->Type == Sketcher::Distance || + (*it)->Type == Sketcher::Radius ) && clone ) { + // Distances on a single Element are mapped to equality constraints in clone mode + Constraint *constNew = (*it)->copy(); + constNew->Type = Sketcher::Equal; + constNew->Second = geoIdMap[(*it)->First]; // first is already (*it->First) + newconstrVals.push_back(constNew); + } + else if ((*it)->Type == Sketcher::Angle && clone){ + // Angles on a single Element are mapped to parallel constraints in clone mode + Constraint *constNew = (*it)->copy(); + constNew->Type = Sketcher::Parallel; + constNew->Second = geoIdMap[(*it)->First]; // first is already (*it->First) + newconstrVals.push_back(constNew); + } + else { + Constraint *constNew = (*it)->copy(); + constNew->First = geoIdMap[(*it)->First]; + newconstrVals.push_back(constNew); + } + } + } + else { // other geoids intervene in this constraint + + std::vector::const_iterator sit=std::find(geoIdList.begin(), geoIdList.end(), (*it)->Second); + + if(sit != geoIdList.end()) { // Second is also in the list + if( (*it)->Third == Constraint::GeoUndef ) { + if (((*it)->Type == Sketcher::DistanceX || + (*it)->Type == Sketcher::DistanceY || + (*it)->Type == Sketcher::Distance) && ((*it)->First == (*it)->Second) && clone ) { + // Distances on a two Elements, which must be points of the same line are mapped to equality constraints in clone mode + Constraint *constNew = (*it)->copy(); + constNew->Type = Sketcher::Equal; + constNew->FirstPos = Sketcher::none; + constNew->Second = geoIdMap[(*it)->First]; // first is already (*it->First) + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + } + else { + Constraint *constNew = (*it)->copy(); + constNew->First = geoIdMap[(*it)->First]; + constNew->Second = geoIdMap[(*it)->Second]; + newconstrVals.push_back(constNew); + } + } + else { + std::vector::const_iterator tit=std::find(geoIdList.begin(), geoIdList.end(), (*it)->Third); + + if(tit != geoIdList.end()) { // Third is also in the list + Constraint *constNew = (*it)->copy(); + constNew->First = geoIdMap[(*it)->First]; + constNew->Second = geoIdMap[(*it)->Second]; + constNew->Third = geoIdMap[(*it)->Third]; + + newconstrVals.push_back(constNew); + } + } + } + } + } + } + + // handle inter-geometry constraints + if(constraindisplacement){ + + // add a construction line + Part::GeomLineSegment *constrline= new Part::GeomLineSegment(); + + Base::Vector3d sp = getPoint(refgeoid,refposId)+ ( ( x == 0 )? + (double(x)*displacement+double(y-1)*perpendicularDisplacement): + (double(x-1)*displacement+double(y)*perpendicularDisplacement)); // position of the reference point + Base::Vector3d ep = iterfirstpoint; // position of the current instance corresponding point + constrline->setPoints(sp,ep); + constrline->Construction=true; + + newgeoVals.push_back(constrline); + + Constraint *constNew; + + if(x == 0) { // first element of a row + + // add coincidents for construction line + constNew = new Constraint(); + constNew->Type = Sketcher::Coincident; + constNew->First = prevrowstartfirstgeoid; + constNew->FirstPos = refposId; + constNew->Second = cgeoid; + constNew->SecondPos = Sketcher::start; + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Coincident; + constNew->First = iterfirstgeoid; + constNew->FirstPos = refposId; + constNew->Second = cgeoid; + constNew->SecondPos = Sketcher::end; + newconstrVals.push_back(constNew); + + if( y == 1 ) { // it is the first added element of this row in the perpendicular to displacementvector direction + rowrefgeoid = cgeoid; + cgeoid++; + + // add length (or equal if perpscale==1) and perpendicular + if(perpscale==1.0) { + constNew = new Constraint(); + constNew->Type = Sketcher::Equal; + constNew->First = rowrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Second = colrefgeoid; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + } else { + constNew = new Constraint(); + constNew->Type = Sketcher::Distance; + constNew->First = rowrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->setValue(perpendicularDisplacement.Length()); + newconstrVals.push_back(constNew); + } + + constNew = new Constraint(); + constNew->Type = Sketcher::Perpendicular; + constNew->First = rowrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Second = colrefgeoid; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + } + else { // it is just one more element in the col direction + cgeoid++; + + // all other first rowers get an equality and perpendicular constraint + constNew = new Constraint(); + constNew->Type = Sketcher::Equal; + constNew->First = rowrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Second = cgeoid-1; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Perpendicular; + constNew->First = cgeoid-1; + constNew->FirstPos = Sketcher::none; + constNew->Second = colrefgeoid; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + } + } + else { // any element not being the first element of a row + + // add coincidents for construction line + constNew = new Constraint(); + constNew->Type = Sketcher::Coincident; + constNew->First = prevfirstgeoid; + constNew->FirstPos = refposId; + constNew->Second = cgeoid; + constNew->SecondPos = Sketcher::start; + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Coincident; + constNew->First = iterfirstgeoid; + constNew->FirstPos = refposId; + constNew->Second = cgeoid; + constNew->SecondPos = Sketcher::end; + newconstrVals.push_back(constNew); + + if(y == 0 && x == 1) { // first element of the first row + colrefgeoid = cgeoid; + cgeoid++; + + // add length and Angle + constNew = new Constraint(); + constNew->Type = Sketcher::Distance; + constNew->First = colrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->setValue(displacement.Length()); + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Angle; + constNew->First = colrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->setValue(atan2(displacement.y,displacement.x)); + newconstrVals.push_back(constNew); + } + else { // any other element + cgeoid++; + + // all other elements get an equality and parallel constraint + constNew = new Constraint(); + constNew->Type = Sketcher::Equal; + constNew->First = colrefgeoid; + constNew->FirstPos = Sketcher::none; + constNew->Second = cgeoid-1; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + + constNew = new Constraint(); + constNew->Type = Sketcher::Parallel; + constNew->First = cgeoid-1; + constNew->FirstPos = Sketcher::none; + constNew->Second = colrefgeoid; + constNew->SecondPos = Sketcher::none; + newconstrVals.push_back(constNew); + } + } + } + + geoIdMap.clear(); // after each creation reset map so that the key-value is univoque + } + } + + Geometry.setValues(newgeoVals); + Constraints.acceptGeometry(getCompleteGeometry()); + rebuildVertexIndex(); + + if( newconstrVals.size() > constrvals.size() ) + Constraints.setValues(newconstrVals); + + return Geometry.getSize()-1; + +} + +int SketchObject::ExposeInternalGeometry(int GeoId) +{ + if (GeoId < 0 || GeoId > getHighestCurveIndex()) + return -1; + + const Part::Geometry *geo = getGeometry(GeoId); + // Only for supported types + if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + // First we search what has to be restored + bool major=false; + bool minor=false; + bool focus1=false; + bool focus2=false; + + const std::vector< Sketcher::Constraint * > &vals = Constraints.getValues(); + + for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin(); + it != vals.end(); ++it) { + if((*it)->Type == Sketcher::InternalAlignment && (*it)->Second == GeoId) + { + switch((*it)->AlignmentType){ + case Sketcher::EllipseMajorDiameter: + major=true; + break; + case Sketcher::EllipseMinorDiameter: + minor=true; + break; + case Sketcher::EllipseFocus1: + focus1=true; + break; + case Sketcher::EllipseFocus2: + focus2=true; + break; + default: + return -1; + } + } + } + + int currentgeoid= getHighestCurveIndex(); + int incrgeo= 0; + + Base::Vector3d center; + double majord; + double minord; + Base::Vector3d majdir; + + std::vector igeo; + std::vector icon; + + if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId()){ + const Part::GeomEllipse *ellipse = static_cast(geo); + + center=ellipse->getCenter(); + majord=ellipse->getMajorRadius(); + minord=ellipse->getMinorRadius(); + majdir=ellipse->getMajorAxisDir(); + } + else { + const Part::GeomArcOfEllipse *aoe = static_cast(geo); + + center=aoe->getCenter(); + majord=aoe->getMajorRadius(); + minord=aoe->getMinorRadius(); + majdir=aoe->getMajorAxisDir(); + } + + Base::Vector3d mindir = Vector3d(-majdir.y, majdir.x); + + Base::Vector3d majorpositiveend = center + majord * majdir; + Base::Vector3d majornegativeend = center - majord * majdir; + Base::Vector3d minorpositiveend = center + minord * mindir; + Base::Vector3d minornegativeend = center - minord * mindir; + + double df= sqrt(majord*majord-minord*minord); + + Base::Vector3d focus1P = center + df * majdir; + Base::Vector3d focus2P = center - df * majdir; + + if(!major) + { + Part::GeomLineSegment *lmajor = new Part::GeomLineSegment(); + lmajor->setPoints(majorpositiveend,majornegativeend); + + igeo.push_back(lmajor); + + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = Sketcher::InternalAlignment; + newConstr->AlignmentType = EllipseMajorDiameter; + newConstr->First = currentgeoid+incrgeo+1; + newConstr->Second = GeoId; + + icon.push_back(newConstr); + incrgeo++; + } + if(!minor) + { + Part::GeomLineSegment *lminor = new Part::GeomLineSegment(); + lminor->setPoints(minorpositiveend,minornegativeend); + + igeo.push_back(lminor); + + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = Sketcher::InternalAlignment; + newConstr->AlignmentType = EllipseMinorDiameter; + newConstr->First = currentgeoid+incrgeo+1; + newConstr->Second = GeoId; + + icon.push_back(newConstr); + incrgeo++; + } + if(!focus1) + { + Part::GeomPoint *pf1 = new Part::GeomPoint(); + pf1->setPoint(focus1P); + + igeo.push_back(pf1); + + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = Sketcher::InternalAlignment; + newConstr->AlignmentType = EllipseFocus1; + newConstr->First = currentgeoid+incrgeo+1; + newConstr->FirstPos = Sketcher::start; + newConstr->Second = GeoId; + + icon.push_back(newConstr); + incrgeo++; + } + if(!focus2) + { + Part::GeomPoint *pf2 = new Part::GeomPoint(); + pf2->setPoint(focus2P); + igeo.push_back(pf2); + + Sketcher::Constraint *newConstr = new Sketcher::Constraint(); + newConstr->Type = Sketcher::InternalAlignment; + newConstr->AlignmentType = EllipseFocus2; + newConstr->First = currentgeoid+incrgeo+1; + newConstr->FirstPos = Sketcher::start; + newConstr->Second = GeoId; + + icon.push_back(newConstr); + } + + this->addGeometry(igeo,true); + this->addConstraints(icon); + + for (std::vector::iterator it=igeo.begin(); it != igeo.end(); ++it) + if (*it) + delete *it; + + for (std::vector::iterator it=icon.begin(); it != icon.end(); ++it) + if (*it) + delete *it; + + icon.clear(); + igeo.clear(); + + return incrgeo; //number of added elements + } + else + return -1; // not supported type +} + +int SketchObject::DeleteUnusedInternalGeometry(int GeoId) +{ + if (GeoId < 0 || GeoId > getHighestCurveIndex()) + return -1; + + const Part::Geometry *geo = getGeometry(GeoId); + // Only for supported types + if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + + int majorelementindex=-1; + int minorelementindex=-1; + int focus1elementindex=-1; + int focus2elementindex=-1; + + const std::vector< Sketcher::Constraint * > &vals = Constraints.getValues(); + + for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin(); + it != vals.end(); ++it) { + if((*it)->Type == Sketcher::InternalAlignment && (*it)->Second == GeoId) + { + switch((*it)->AlignmentType){ + case Sketcher::EllipseMajorDiameter: + majorelementindex=(*it)->First; + break; + case Sketcher::EllipseMinorDiameter: + minorelementindex=(*it)->First; + break; + case Sketcher::EllipseFocus1: + focus1elementindex=(*it)->First; + break; + case Sketcher::EllipseFocus2: + focus2elementindex=(*it)->First; + break; + default: + return -1; + } + } + } + + // Hide unused geometry here + int majorconstraints=0; // number of constraints associated to the geoid of the major axis + int minorconstraints=0; + int focus1constraints=0; + int focus2constraints=0; + + for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin(); + it != vals.end(); ++it) { + + if((*it)->Second == majorelementindex || (*it)->First == majorelementindex || (*it)->Third == majorelementindex) + majorconstraints++; + else if((*it)->Second == minorelementindex || (*it)->First == minorelementindex || (*it)->Third == minorelementindex) + minorconstraints++; + else if((*it)->Second == focus1elementindex || (*it)->First == focus1elementindex || (*it)->Third == focus1elementindex) + focus1constraints++; + else if((*it)->Second == focus2elementindex || (*it)->First == focus2elementindex || (*it)->Third == focus2elementindex) + focus2constraints++; + } + + std::vector delgeometries; + + // those with less than 2 constraints must be removed + if(focus2constraints<2) + delgeometries.push_back(focus2elementindex); + + if(focus1constraints<2) + delgeometries.push_back(focus1elementindex); + + if(minorconstraints<2) + delgeometries.push_back(minorelementindex); + + if(majorconstraints<2) + delgeometries.push_back(majorelementindex); + + std::sort(delgeometries.begin(), delgeometries.end()); // indices over an erased element get automatically updated!! + + if(delgeometries.size()>0) + { + for (std::vector::reverse_iterator it=delgeometries.rbegin(); it!=delgeometries.rend(); ++it) { + delGeometry(*it); + } + } + + return delgeometries.size(); //number of deleted elements + } + else + return -1; // not supported type +} + +int SketchObject::addExternal(App::DocumentObject *Obj, const char* SubName) +{ + // so far only externals to the support of the sketch and datum features + if (Support.getValue() != Obj) + if (!Obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) && + !Obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) + return -1; + + // get the actual lists of the externals + std::vector Objects = ExternalGeometry.getValues(); + std::vector SubElements = ExternalGeometry.getSubValues(); + + const std::vector originalObjects = Objects; + const std::vector originalSubElements = SubElements; + + std::vector::iterator it; + it = std::find(SubElements.begin(), SubElements.end(), SubName); + + // avoid duplicates + if (it != SubElements.end()) + return -1; + + // add the new ones + Objects.push_back(Obj); + SubElements.push_back(std::string(SubName)); + + // set the Link list. + ExternalGeometry.setValues(Objects,SubElements); + try { + rebuildExternalGeometry(); + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + // revert to original values + ExternalGeometry.setValues(originalObjects,originalSubElements); + return -1; + } + + solverNeedsUpdate=true; + Constraints.acceptGeometry(getCompleteGeometry()); + rebuildVertexIndex(); + return ExternalGeometry.getValues().size()-1; +} + +int SketchObject::delExternal(int ExtGeoId) +{ + // get the actual lists of the externals + std::vector Objects = ExternalGeometry.getValues(); + std::vector SubElements = ExternalGeometry.getSubValues(); + + if (ExtGeoId < 0 || ExtGeoId >= int(SubElements.size())) + return -1; + + const std::vector originalObjects = Objects; + const std::vector originalSubElements = SubElements; + + Objects.erase(Objects.begin()+ExtGeoId); + SubElements.erase(SubElements.begin()+ExtGeoId); + + const std::vector< Constraint * > &constraints = Constraints.getValues(); + std::vector< Constraint * > newConstraints(0); + int GeoId = -3 - ExtGeoId; + for (std::vector::const_iterator it = constraints.begin(); + it != constraints.end(); ++it) { + if ((*it)->First != GeoId && (*it)->Second != GeoId && (*it)->Third != GeoId) { + Constraint *copiedConstr = (*it)->clone(); + if (copiedConstr->First < GeoId && + copiedConstr->First != Constraint::GeoUndef) + copiedConstr->First += 1; + if (copiedConstr->Second < GeoId && + copiedConstr->Second != Constraint::GeoUndef) + copiedConstr->Second += 1; + if (copiedConstr->Third < GeoId && + copiedConstr->Third != Constraint::GeoUndef) + copiedConstr->Third += 1; + + newConstraints.push_back(copiedConstr); + } + } + + ExternalGeometry.setValues(Objects,SubElements); + try { + rebuildExternalGeometry(); + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + // revert to original values + ExternalGeometry.setValues(originalObjects,originalSubElements); + return -1; + } + + solverNeedsUpdate=true; + Constraints.setValues(newConstraints); + Constraints.acceptGeometry(getCompleteGeometry()); + rebuildVertexIndex(); + return 0; +} + +int SketchObject::delAllExternal() +{ + // get the actual lists of the externals + std::vector Objects = ExternalGeometry.getValues(); + std::vector SubElements = ExternalGeometry.getSubValues(); + + const std::vector originalObjects = Objects; + const std::vector originalSubElements = SubElements; + + Objects.clear(); + + SubElements.clear(); + + const std::vector< Constraint * > &constraints = Constraints.getValues(); + std::vector< Constraint * > newConstraints(0); + + for (std::vector::const_iterator it = constraints.begin(); it != constraints.end(); ++it) { + if ((*it)->First > -3 && + ((*it)->Second > -3 || (*it)->Second == Constraint::GeoUndef ) && + ((*it)->Third > -3 || (*it)->Third == Constraint::GeoUndef) ) { + Constraint *copiedConstr = (*it)->clone(); + + newConstraints.push_back(copiedConstr); + } + } + + ExternalGeometry.setValues(Objects,SubElements); + try { + rebuildExternalGeometry(); + } + catch (const Base::Exception& e) { + Base::Console().Error("%s\n", e.what()); + // revert to original values + ExternalGeometry.setValues(originalObjects,originalSubElements); + return -1; + } + + solverNeedsUpdate=true; + Constraints.setValues(newConstraints); + Constraints.acceptGeometry(getCompleteGeometry()); + rebuildVertexIndex(); + return 0; +} + +int SketchObject::delConstraintsToExternal() +{ + const std::vector< Constraint * > &constraints = Constraints.getValuesForce(); + std::vector< Constraint * > newConstraints(0); + int GeoId = -3, NullId = -2000; + for (std::vector::const_iterator it = constraints.begin(); + it != constraints.end(); ++it) { + if ( (*it)->First > GeoId + && + ((*it)->Second > GeoId || (*it)->Second == NullId) + && + ((*it)->Third > GeoId || (*it)->Third == NullId)) { + newConstraints.push_back(*it); + } + } + + Constraints.setValues(newConstraints); + Constraints.acceptGeometry(getCompleteGeometry()); + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver + solve(); + + return 0; +} + +const Part::Geometry* SketchObject::getGeometry(int GeoId) const +{ + if (GeoId >= 0) { + const std::vector &geomlist = getInternalGeometry(); + if (GeoId < int(geomlist.size())) + return geomlist[GeoId]; + } + else if (GeoId <= -1 && -GeoId <= int(ExternalGeo.size())) + return ExternalGeo[-GeoId-1]; + + return 0; +} + +// Auxiliary method +Part::Geometry* projectLine(const BRepAdaptor_Curve& curve, const Handle(Geom_Plane)& gPlane, const Base::Placement& invPlm) +{ + double first = curve.FirstParameter(); + bool infinite = false; + if (fabs(first) > 1E99) { + // TODO: What is OCE's definition of Infinite? + // TODO: The clean way to do this is to handle a new sketch geometry Geom::Line + // but its a lot of work to implement... + first = -10000; + //infinite = true; + } + double last = curve.LastParameter(); + if (fabs(last) > 1E99) { + last = +10000; + //infinite = true; + } + + gp_Pnt P1 = curve.Value(first); + gp_Pnt P2 = curve.Value(last); + + GeomAPI_ProjectPointOnSurf proj1(P1,gPlane); + P1 = proj1.NearestPoint(); + GeomAPI_ProjectPointOnSurf proj2(P2,gPlane); + P2 = proj2.NearestPoint(); + + Base::Vector3d p1(P1.X(),P1.Y(),P1.Z()); + Base::Vector3d p2(P2.X(),P2.Y(),P2.Z()); + invPlm.multVec(p1,p1); + invPlm.multVec(p2,p2); + + if (Base::Distance(p1,p2) < Precision::Confusion()) { + Base::Vector3d p = (p1 + p2) / 2; + Part::GeomPoint* point = new Part::GeomPoint(p); + point->Construction = true; + return point; + } + else if (!infinite) { + Part::GeomLineSegment* line = new Part::GeomLineSegment(); + line->setPoints(p1,p2); + line->Construction = true; + return line; + } else { + Part::GeomLine* line = new Part::GeomLine(); + line->setLine(p1, p2 - p1); + line->Construction = true; + return line; + } +} + +bool SketchObject::evaluateSupport(void) +{ + // returns false if the shape if broken, null or non-planar + Part::Feature *part = static_cast(Support.getValue()); + if (!part || !part->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + return false; + + const std::vector &sub = Support.getSubValues(); + assert(sub.size()==1); + // get the selected sub shape (a Face) + const Part::TopoShape &shape = part->Shape.getShape(); + + if (shape._Shape.IsNull()) + return false; + + TopoDS_Shape sh; + try { + sh = shape.getSubShape(sub[0].c_str()); + } + catch (Standard_Failure) { + return false; + } + const TopoDS_Face &face = TopoDS::Face(sh); + if (face.IsNull()) + return false; + + BRepAdaptor_Surface adapt(face); + if (adapt.GetType() != GeomAbs_Plane) + return false; // No planar face + + return true; +} + +void SketchObject::validateExternalLinks(void) +{ + std::vector Objects = ExternalGeometry.getValues(); + std::vector SubElements = ExternalGeometry.getSubValues(); + + bool rebuild = false ; + + for (int i=0; i < int(Objects.size()); i++) { + const App::DocumentObject *Obj=Objects[i]; + const std::string SubElement=SubElements[i]; + + const Part::Feature *refObj=static_cast(Obj); + const Part::TopoShape& refShape=refObj->Shape.getShape(); + + TopoDS_Shape refSubShape; + try { + refSubShape = refShape.getSubShape(SubElement.c_str()); + } + catch (Standard_Failure) { + rebuild = true ; + Objects.erase(Objects.begin()+i); + SubElements.erase(SubElements.begin()+i); + + const std::vector< Constraint * > &constraints = Constraints.getValues(); + std::vector< Constraint * > newConstraints(0); + int GeoId = -3 - i; + for (std::vector::const_iterator it = constraints.begin(); + it != constraints.end(); ++it) { + if ((*it)->First != GeoId && (*it)->Second != GeoId && (*it)->Third != GeoId) { + Constraint *copiedConstr = (*it)->clone(); + if (copiedConstr->First < GeoId && + copiedConstr->First != Constraint::GeoUndef) + copiedConstr->First += 1; + if (copiedConstr->Second < GeoId && + copiedConstr->Second != Constraint::GeoUndef) + copiedConstr->Second += 1; + if (copiedConstr->Third < GeoId && + copiedConstr->Third != Constraint::GeoUndef) + copiedConstr->Third += 1; + + newConstraints.push_back(copiedConstr); + } + } + + Constraints.setValues(newConstraints); + i--; // we deleted an item, so the next one took its place + } + } + + if (rebuild) { + ExternalGeometry.setValues(Objects,SubElements); + rebuildExternalGeometry(); + Constraints.acceptGeometry(getCompleteGeometry()); + rebuildVertexIndex(); + solve(true); // we have to update this sketch and everything depending on it. + } + +} + +void SketchObject::rebuildExternalGeometry(void) +{ + // get the actual lists of the externals + std::vector Objects = ExternalGeometry.getValues(); + std::vector SubElements = ExternalGeometry.getSubValues(); + + Base::Placement Plm = Placement.getValue(); + Base::Vector3d Pos = Plm.getPosition(); + Base::Rotation Rot = Plm.getRotation(); + Base::Vector3d dN(0,0,1); + Rot.multVec(dN,dN); + Base::Vector3d dX(1,0,0); + Rot.multVec(dX,dX); + + Base::Placement invPlm = Plm.inverse(); + Base::Matrix4D invMat = invPlm.toMatrix(); + gp_Trsf mov; + mov.SetValues(invMat[0][0],invMat[0][1],invMat[0][2],invMat[0][3], + invMat[1][0],invMat[1][1],invMat[1][2],invMat[1][3], + invMat[2][0],invMat[2][1],invMat[2][2],invMat[2][3] +#if OCC_VERSION_HEX < 0x060800 + , 0.00001, 0.00001 +#endif + ); //precision was removed in OCCT CR0025194 + + gp_Ax3 sketchAx3(gp_Pnt(Pos.x,Pos.y,Pos.z), + gp_Dir(dN.x,dN.y,dN.z), + gp_Dir(dX.x,dX.y,dX.z)); + gp_Pln sketchPlane(sketchAx3); + + Handle(Geom_Plane) gPlane = new Geom_Plane(sketchPlane); + BRepBuilderAPI_MakeFace mkFace(sketchPlane); + TopoDS_Shape aProjFace = mkFace.Shape(); + + for (std::vector::iterator it=ExternalGeo.begin(); it != ExternalGeo.end(); ++it) + if (*it) delete *it; + ExternalGeo.clear(); + Part::GeomLineSegment *HLine = new Part::GeomLineSegment(); + Part::GeomLineSegment *VLine = new Part::GeomLineSegment(); + HLine->setPoints(Base::Vector3d(0,0,0),Base::Vector3d(1,0,0)); + VLine->setPoints(Base::Vector3d(0,0,0),Base::Vector3d(0,1,0)); + HLine->Construction = true; + VLine->Construction = true; + ExternalGeo.push_back(HLine); + ExternalGeo.push_back(VLine); + for (int i=0; i < int(Objects.size()); i++) { + const App::DocumentObject *Obj=Objects[i]; + const std::string SubElement=SubElements[i]; + + TopoDS_Shape refSubShape; + + if (Obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) { + const Part::Datum* datum = static_cast(Obj); + refSubShape = datum->Shape.getValue(); + } else if (Obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { + try { + const Part::Feature *refObj=static_cast(Obj); + const Part::TopoShape& refShape=refObj->Shape.getShape(); + refSubShape = refShape.getSubShape(SubElement.c_str()); + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + throw Base::Exception(e->GetMessageString()); + } + } else if (Obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { + const App::Plane* pl = static_cast(Obj); + Base::Placement plm = pl->Placement.getValue(); + Base::Vector3d base = plm.getPosition(); + Base::Rotation rot = plm.getRotation(); + Base::Vector3d normal(0,0,1); + rot.multVec(normal, normal); + gp_Pln plane(gp_Pnt(base.x,base.y,base.z), gp_Dir(normal.x, normal.y, normal.z)); + BRepBuilderAPI_MakeFace fBuilder(plane); + if (!fBuilder.IsDone()) + throw Base::Exception("Sketcher: addExternal(): Failed to build face from App::Plane"); + + TopoDS_Face f = TopoDS::Face(fBuilder.Shape()); + refSubShape = f; + } else { + throw Base::Exception("Datum feature type is not yet supported as external geometry for a sketch"); + } + + switch (refSubShape.ShapeType()) + { + case TopAbs_FACE: + { + const TopoDS_Face& face = TopoDS::Face(refSubShape); + BRepAdaptor_Surface surface(face); + if (surface.GetType() == GeomAbs_Plane) { + // Check that the plane is perpendicular to the sketch plane + Geom_Plane plane = surface.Plane(); + gp_Dir dnormal = plane.Axis().Direction(); + gp_Dir snormal = sketchPlane.Axis().Direction(); + if (fabs(dnormal.Angle(snormal) - M_PI_2) < Precision::Confusion()) { + // Get vector that is normal to both sketch plane normal and plane normal. This is the line's direction + gp_Dir lnormal = dnormal.Crossed(snormal); + BRepBuilderAPI_MakeEdge builder(gp_Lin(plane.Location(), lnormal)); + builder.Build(); + if (builder.IsDone()) { + const TopoDS_Edge& edge = TopoDS::Edge(builder.Shape()); + BRepAdaptor_Curve curve(edge); + if (curve.GetType() == GeomAbs_Line) { + ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); + } + } + + } + } else { + throw Base::Exception("Non-planar faces are not yet supported for external geometry of sketches"); + } + } + break; + case TopAbs_EDGE: + { + const TopoDS_Edge& edge = TopoDS::Edge(refSubShape); + BRepAdaptor_Curve curve(edge); + if (curve.GetType() == GeomAbs_Line) { + ExternalGeo.push_back(projectLine(curve, gPlane, invPlm)); + } + else if (curve.GetType() == GeomAbs_Circle) { + gp_Dir vec1 = sketchPlane.Axis().Direction(); + gp_Dir vec2 = curve.Circle().Axis().Direction(); + if (vec1.IsParallel(vec2, Precision::Confusion())) { + gp_Circ circle = curve.Circle(); + gp_Pnt cnt = circle.Location(); + gp_Pnt beg = curve.Value(curve.FirstParameter()); + gp_Pnt end = curve.Value(curve.LastParameter()); + + GeomAPI_ProjectPointOnSurf proj(cnt,gPlane); + cnt = proj.NearestPoint(); + circle.SetLocation(cnt); + cnt.Transform(mov); + circle.Transform(mov); + + if (beg.SquareDistance(end) < Precision::Confusion()) { + Part::GeomCircle* gCircle = new Part::GeomCircle(); + gCircle->setRadius(circle.Radius()); + gCircle->setCenter(Base::Vector3d(cnt.X(),cnt.Y(),cnt.Z())); + + gCircle->Construction = true; + ExternalGeo.push_back(gCircle); + } + else { + Part::GeomArcOfCircle* gArc = new Part::GeomArcOfCircle(); + Handle_Geom_Curve hCircle = new Geom_Circle(circle); + Handle_Geom_TrimmedCurve tCurve = new Geom_TrimmedCurve(hCircle, curve.FirstParameter(), + curve.LastParameter()); + gArc->setHandle(tCurve); + gArc->Construction = true; + ExternalGeo.push_back(gArc); + } + } + else { + // creates an ellipse + throw Base::Exception("Not yet supported geometry for external geometry"); + } + } + else { + try { + BRepOffsetAPI_NormalProjection mkProj(aProjFace); + mkProj.Add(edge); + mkProj.Build(); + const TopoDS_Shape& projShape = mkProj.Projection(); + if (!projShape.IsNull()) { + TopExp_Explorer xp; + for (xp.Init(projShape, TopAbs_EDGE); xp.More(); xp.Next()) { + TopoDS_Edge projEdge = TopoDS::Edge(xp.Current()); + TopLoc_Location loc(mov); + projEdge.Location(loc); + BRepAdaptor_Curve projCurve(projEdge); + if (projCurve.GetType() == GeomAbs_Line) { + gp_Pnt P1 = projCurve.Value(projCurve.FirstParameter()); + gp_Pnt P2 = projCurve.Value(projCurve.LastParameter()); + Base::Vector3d p1(P1.X(),P1.Y(),P1.Z()); + Base::Vector3d p2(P2.X(),P2.Y(),P2.Z()); + + if (Base::Distance(p1,p2) < Precision::Confusion()) { + Base::Vector3d p = (p1 + p2) / 2; + Part::GeomPoint* point = new Part::GeomPoint(p); + point->Construction = true; + ExternalGeo.push_back(point); + } + else { + Part::GeomLineSegment* line = new Part::GeomLineSegment(); + line->setPoints(p1,p2); + line->Construction = true; + ExternalGeo.push_back(line); + } + } + else if (projCurve.GetType() == GeomAbs_Circle) { + gp_Circ c = projCurve.Circle(); + gp_Pnt p = c.Location(); + gp_Pnt P1 = projCurve.Value(projCurve.FirstParameter()); + gp_Pnt P2 = projCurve.Value(projCurve.LastParameter()); + + if (P1.SquareDistance(P2) < Precision::Confusion()) { + Part::GeomCircle* circle = new Part::GeomCircle(); + circle->setRadius(c.Radius()); + circle->setCenter(Base::Vector3d(p.X(),p.Y(),p.Z())); + + circle->Construction = true; + ExternalGeo.push_back(circle); + } + else { + Part::GeomArcOfCircle* arc = new Part::GeomArcOfCircle(); + Handle_Geom_Curve curve = new Geom_Circle(c); + Handle_Geom_TrimmedCurve tCurve = new Geom_TrimmedCurve(curve, projCurve.FirstParameter(), + projCurve.LastParameter()); + arc->setHandle(tCurve); + arc->Construction = true; + ExternalGeo.push_back(arc); + } + } + else if (projCurve.GetType() == GeomAbs_Ellipse) { + gp_Elips e = projCurve.Ellipse(); + gp_Pnt p = e.Location(); + gp_Pnt P1 = projCurve.Value(projCurve.FirstParameter()); + gp_Pnt P2 = projCurve.Value(projCurve.LastParameter()); + + //gp_Dir normal = e.Axis().Direction(); + gp_Dir normal = gp_Dir(0,0,1); + gp_Ax2 xdirref(p, normal); + + if (P1.SquareDistance(P2) < Precision::Confusion()) { + Part::GeomEllipse* ellipse = new Part::GeomEllipse(); + Handle_Geom_Ellipse curve = new Geom_Ellipse(e); + ellipse->setHandle(curve); + ellipse->Construction = true; + ExternalGeo.push_back(ellipse); + } + else { + Part::GeomArcOfEllipse* aoe = new Part::GeomArcOfEllipse(); + Handle_Geom_Curve curve = new Geom_Ellipse(e); + Handle_Geom_TrimmedCurve tCurve = new Geom_TrimmedCurve(curve, projCurve.FirstParameter(), + projCurve.LastParameter()); + aoe->setHandle(tCurve); + aoe->Construction = true; + ExternalGeo.push_back(aoe); + } + } + else { + throw Base::Exception("Not yet supported geometry for external geometry"); + } + } + } + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + throw Base::Exception(e->GetMessageString()); + } + } + } + break; + case TopAbs_VERTEX: + { + gp_Pnt P = BRep_Tool::Pnt(TopoDS::Vertex(refSubShape)); + GeomAPI_ProjectPointOnSurf proj(P,gPlane); + P = proj.NearestPoint(); + Base::Vector3d p(P.X(),P.Y(),P.Z()); + invPlm.multVec(p,p); + + Part::GeomPoint* point = new Part::GeomPoint(p); + point->Construction = true; + ExternalGeo.push_back(point); + } + break; + default: + throw Base::Exception("Unknown type of geometry"); + break; + } + } + + rebuildVertexIndex(); +} + +std::vector SketchObject::getCompleteGeometry(void) const +{ + std::vector vals=getInternalGeometry(); + vals.insert(vals.end(), ExternalGeo.rbegin(), ExternalGeo.rend()); // in reverse order + return vals; +} + +void SketchObject::rebuildVertexIndex(void) +{ + VertexId2GeoId.resize(0); + VertexId2PosId.resize(0); + int imax=getHighestCurveIndex(); + int i=0; + const std::vector< Part::Geometry * > geometry = getCompleteGeometry(); + if (geometry.size() <= 2) + return; + for (std::vector< Part::Geometry * >::const_iterator it = geometry.begin(); + it != geometry.end()-2; ++it, i++) { + if (i > imax) + i = -getExternalGeometryCount(); + if ((*it)->getTypeId() == Part::GeomPoint::getClassTypeId()) { + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(start); + } else if ((*it)->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(start); + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(end); + } else if ((*it)->getTypeId() == Part::GeomCircle::getClassTypeId()) { + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(mid); + } else if ((*it)->getTypeId() == Part::GeomEllipse::getClassTypeId()) { + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(mid); + } else if ((*it)->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(start); + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(end); + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(mid); + } else if ((*it)->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(start); + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(end); + VertexId2GeoId.push_back(i); + VertexId2PosId.push_back(mid); + } + } +} + +const std::vector< std::map > SketchObject::getCoincidenceGroups() +{ + // this function is different from that in getCoincidentPoints in that: + // - getCoincidentPoints only considers direct coincidence (the points that are linked via a single coincidence) + // - this function provides an array of maps of points, each map containing the points that are coincident by virtue + // of any number of interrelated coincidence constraints (if coincidence 1-2 and coincidence 2-3, {1,2,3} are in that set) + + const std::vector< Sketcher::Constraint * > &vals = Constraints.getValues(); + + std::vector< std::map > coincidenttree; + // push the constraints + for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();it != vals.end(); ++it) { + if( (*it)->Type == Sketcher::Coincident ) { + int firstpresentin=-1; + int secondpresentin=-1; + + int i=0; + + for(std::vector< std::map >::const_iterator iti = coincidenttree.begin(); iti != coincidenttree.end(); ++iti,i++) { + // First + std::map::const_iterator filiterator; + filiterator = (*iti).find((*it)->First); + if( filiterator != (*iti).end()) { + if((*it)->FirstPos == (*filiterator).second) + firstpresentin = i; + } + // Second + filiterator = (*iti).find((*it)->Second); + if( filiterator != (*iti).end()) { + if((*it)->SecondPos == (*filiterator).second) + secondpresentin = i; + } + } + + if ( firstpresentin!=-1 && secondpresentin!=-1) { + // we have to merge those sets into one + coincidenttree[firstpresentin].insert(coincidenttree[secondpresentin].begin(), coincidenttree[secondpresentin].end()); + coincidenttree.erase(coincidenttree.begin()+secondpresentin); + } + else if ( firstpresentin==-1 && secondpresentin==-1 ) { + // we do not have any of the values, so create a setCursor + std::map tmp; + tmp.insert(std::pair((*it)->First,(*it)->FirstPos)); + tmp.insert(std::pair((*it)->Second,(*it)->SecondPos)); + coincidenttree.push_back(tmp); + } + else if ( firstpresentin != -1 ) { + // add to existing group + coincidenttree[firstpresentin].insert(std::pair((*it)->Second,(*it)->SecondPos)); + } + else { // secondpresentin != -1 + // add to existing group + coincidenttree[secondpresentin].insert(std::pair((*it)->First,(*it)->FirstPos)); + } + + } + } + + return coincidenttree; +} + +void SketchObject::isCoincidentWithExternalGeometry(int GeoId, bool &start_external, bool &mid_external, bool &end_external) { + + start_external=false; + mid_external=false; + end_external=false; + + const std::vector< std::map > coincidenttree = getCoincidenceGroups(); + + for(std::vector< std::map >::const_iterator it = coincidenttree.begin(); it != coincidenttree.end(); ++it) { + + std::map::const_iterator geoId1iterator; + + geoId1iterator = (*it).find(GeoId); + + if( geoId1iterator != (*it).end()) { + // If First is in this set and the first key in this ordered element key is external + if( (*it).begin()->first < 0 ) { + if( (*geoId1iterator).second == Sketcher::start ) + start_external=true; + else if ( (*geoId1iterator).second == Sketcher::mid ) + mid_external=true; + else if ( (*geoId1iterator).second == Sketcher::end ) + end_external=true; + } + } + } +} + +const std::map SketchObject::getAllCoincidentPoints(int GeoId, PointPos PosId) { + + const std::vector< std::map > coincidenttree = getCoincidenceGroups(); + + for(std::vector< std::map >::const_iterator it = coincidenttree.begin(); it != coincidenttree.end(); ++it) { + + std::map::const_iterator geoId1iterator; + + geoId1iterator = (*it).find(GeoId); + + if( geoId1iterator != (*it).end()) { + // If GeoId is in this set + + if ((*geoId1iterator).second == PosId) // and posId matches + return (*it); + } + } + + std::map empty; + + return empty; +} + + +void SketchObject::getDirectlyCoincidentPoints(int GeoId, PointPos PosId, std::vector &GeoIdList, + std::vector &PosIdList) +{ + const std::vector &constraints = this->Constraints.getValues(); + + GeoIdList.clear(); + PosIdList.clear(); + GeoIdList.push_back(GeoId); + PosIdList.push_back(PosId); + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it) { + if ((*it)->Type == Sketcher::Coincident) { + if ((*it)->First == GeoId && (*it)->FirstPos == PosId) { + GeoIdList.push_back((*it)->Second); + PosIdList.push_back((*it)->SecondPos); + } + else if ((*it)->Second == GeoId && (*it)->SecondPos == PosId) { + GeoIdList.push_back((*it)->First); + PosIdList.push_back((*it)->FirstPos); + } + } + } + if (GeoIdList.size() == 1) { + GeoIdList.clear(); + PosIdList.clear(); + } +} + +void SketchObject::getDirectlyCoincidentPoints(int VertexId, std::vector &GeoIdList, + std::vector &PosIdList) +{ + int GeoId; + PointPos PosId; + getGeoVertexIndex(VertexId, GeoId, PosId); + getDirectlyCoincidentPoints(GeoId, PosId, GeoIdList, PosIdList); +} + +bool SketchObject::arePointsCoincident(int GeoId1, PointPos PosId1, + int GeoId2, PointPos PosId2) +{ + if (GeoId1 == GeoId2 && PosId1 == PosId2) + return true; + + const std::vector< std::map > coincidenttree = getCoincidenceGroups(); + + for(std::vector< std::map >::const_iterator it = coincidenttree.begin(); it != coincidenttree.end(); ++it) { + + std::map::const_iterator geoId1iterator; + + geoId1iterator = (*it).find(GeoId1); + + if( geoId1iterator != (*it).end()) { + // If First is in this set + std::map::const_iterator geoId2iterator; + + geoId2iterator = (*it).find(GeoId2); + + if( geoId2iterator != (*it).end()) { + // If Second is in this set + if ((*geoId1iterator).second == PosId1 && + (*geoId2iterator).second == PosId2) + return true; + } + } + } + + return false; +} + +void SketchObject::appendConflictMsg(const std::vector &conflicting, std::string &msg) +{ + std::stringstream ss; + if (msg.length() > 0) + ss << msg; + if (conflicting.size() > 0) { + if (conflicting.size() == 1) + ss << "Please remove the following constraint:\n"; + else + ss << "Please remove at least one of the following constraints:\n"; + ss << conflicting[0]; + for (unsigned int i=1; i < conflicting.size(); i++) + ss << ", " << conflicting[i]; + ss << "\n"; + } + msg = ss.str(); +} + +void SketchObject::appendRedundantMsg(const std::vector &redundant, std::string &msg) +{ + std::stringstream ss; + if (msg.length() > 0) + ss << msg; + if (redundant.size() > 0) { + if (redundant.size() == 1) + ss << "Please remove the following redundant constraint:\n"; + else + ss << "Please remove the following redundant constraints:\n"; + ss << redundant[0]; + for (unsigned int i=1; i < redundant.size(); i++) + ss << ", " << redundant[i]; + ss << "\n"; + } + msg = ss.str(); +} + +bool SketchObject::evaluateConstraint(const Constraint *constraint) const +{ + //if requireXXX, GeoUndef is treated as an error. If not requireXXX, + //GeoUndef is accepted. Index range checking is done on everything regardless. + bool requireFirst = true; + bool requireSecond = false; + bool requireThird = false; + + switch (constraint->Type) { + case Radius: + requireFirst = true; + break; + case Horizontal: + case Vertical: + requireFirst = true; + break; + case Distance: + case DistanceX: + case DistanceY: + requireFirst = true; + break; + case Coincident: + case Perpendicular: + case Parallel: + case Equal: + case PointOnObject: + case Tangent: + requireFirst = true; + requireSecond = true; + break; + case Symmetric: + requireFirst = true; + requireSecond = true; + requireThird = true; + break; + case Angle: + requireFirst = true; + break; + case SnellsLaw: + requireFirst = true; + requireSecond = true; + requireThird = true; + break; + default: + break; + } + + int intGeoCount = getHighestCurveIndex() + 1; + int extGeoCount = getExternalGeometryCount(); + + //the actual checks + bool ret = true; + int geoId; + geoId = constraint->First; + ret = ret && ((geoId == Constraint::GeoUndef && !requireFirst) + || + (geoId >= -extGeoCount && geoId < intGeoCount) ); + + geoId = constraint->Second; + ret = ret && ((geoId == Constraint::GeoUndef && !requireSecond) + || + (geoId >= -extGeoCount && geoId < intGeoCount) ); + + geoId = constraint->Third; + ret = ret && ((geoId == Constraint::GeoUndef && !requireThird) + || + (geoId >= -extGeoCount && geoId < intGeoCount) ); + + return ret; +} + +bool SketchObject::evaluateConstraints() const +{ + int intGeoCount = getHighestCurveIndex() + 1; + int extGeoCount = getExternalGeometryCount(); + + std::vector geometry = getCompleteGeometry(); + const std::vector& constraints = Constraints.getValuesForce(); + if (static_cast(geometry.size()) != extGeoCount + intGeoCount) + return false; + if (geometry.size() < 2) + return false; + + std::vector::const_iterator it; + for (it = constraints.begin(); it != constraints.end(); ++it) { + if (!evaluateConstraint(*it)) + return false; + } + + if(constraints.size()>0){ + if (!Constraints.scanGeometry(geometry)) return false; + } + + return true; +} + +void SketchObject::validateConstraints() +{ + std::vector geometry = getCompleteGeometry(); + const std::vector& constraints = Constraints.getValues(); + + std::vector newConstraints; + std::vector::const_iterator it; + for (it = constraints.begin(); it != constraints.end(); ++it) { + bool valid = evaluateConstraint(*it); + if (valid) + newConstraints.push_back(*it); + } + + if (newConstraints.size() != constraints.size()) { + Constraints.setValues(newConstraints); + acceptGeometry(); + } +} + +std::string SketchObject::validateExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr) +{ + const App::Property * prop = path.getProperty(); + + assert(expr != 0); + + if (!prop) + return "Property not found"; + + if (prop == &Constraints) { + const Constraint * constraint = Constraints.getConstraint(path); + + if (!constraint->isDriving) + return "Reference constraints cannot be set!"; + } + + std::set deps; + expr->getDeps(deps); + + for (std::set::const_iterator i = deps.begin(); i != deps.end(); ++i) { + const App::Property * prop = (*i).getProperty(); + + if (prop == &Constraints) { + const Constraint * constraint = Constraints.getConstraint(*i); + + if (!constraint->isDriving) + return "Reference constraint from this sketch cannot be used in this expression."; + } + } + return ""; +} + +//This function is necessary for precalculation of an angle when adding +// an angle constraint. It is also used here, in SketchObject, to +// lock down the type of tangency/perpendicularity. +double SketchObject::calculateAngleViaPoint(int GeoId1, int GeoId2, double px, double py) +{ + // Temporary sketch based calculation. Slow, but guaranteed consistency with constraints. + Sketcher::Sketch sk; + + const Part::Geometry *p1=this->getGeometry(GeoId1); + const Part::Geometry *p2=this->getGeometry(GeoId2); + + if(p1!=0 && p2!=0) { + int i1 = sk.addGeometry(this->getGeometry(GeoId1)); + int i2 = sk.addGeometry(this->getGeometry(GeoId2)); + + return sk.calculateAngleViaPoint(i1,i2,px,py); + } + else + throw Base::Exception("Null geometry in calculateAngleViaPoint"); + +/* + // OCC-based calculation. It is faster, but it was removed due to problems + // with reversed geometry (clockwise arcs). More info in "Sketch: how to + // handle reversed external arcs?" forum thread + // http://forum.freecadweb.org/viewtopic.php?f=10&t=9130&sid=1b994fa1236db5ac2371eeb9a53de23f + + const Part::GeomCurve &g1 = *(dynamic_cast(this->getGeometry(GeoId1))); + const Part::GeomCurve &g2 = *(dynamic_cast(this->getGeometry(GeoId2))); + Base::Vector3d p(px, py, 0.0); + + double u1 = 0.0; + double u2 = 0.0; + if (! g1.closestParameterToBasicCurve(p, u1) ) throw Base::Exception("SketchObject::calculateAngleViaPoint: closestParameter(curve1) failed!"); + if (! g2.closestParameterToBasicCurve(p, u2) ) throw Base::Exception("SketchObject::calculateAngleViaPoint: closestParameter(curve2) failed!"); + + gp_Dir tan1, tan2; + if (! g1.tangent(u1,tan1) ) throw Base::Exception("SketchObject::calculateAngleViaPoint: tangent1 failed!"); + if (! g2.tangent(u2,tan2) ) throw Base::Exception("SketchObject::calculateAngleViaPoint: tangent2 failed!"); + + assert(abs(tan1.Z())<0.0001); + assert(abs(tan2.Z())<0.0001); + + double ang = atan2(-tan2.X()*tan1.Y()+tan2.Y()*tan1.X(), tan2.X()*tan1.X() + tan2.Y()*tan1.Y()); + return ang; +*/ +} + +void SketchObject::constraintsRenamed(const std::map &renamed) +{ + ExpressionEngine.renameExpressions(renamed); + + getDocument()->renameObjectIdentifiers(renamed); +} + +void SketchObject::constraintsRemoved(const std::set &removed) +{ + std::set::const_iterator i = removed.begin(); + + while (i != removed.end()) { + ExpressionEngine.setValue(*i, boost::shared_ptr(), 0); + ++i; + } +} + +//Tests if the provided point lies exactly in a curve (satisfies +// point-on-object constraint). It is used to decide whether it is nesessary to +// constrain a point onto curves when 3-element selection tangent-via-point-like +// constraints are applied. +bool SketchObject::isPointOnCurve(int geoIdCurve, double px, double py) +{ + //DeepSOIC: this may be slow, but I wanted to reuse the existing code + Sketcher::Sketch sk; + int icrv = sk.addGeometry(this->getGeometry(geoIdCurve)); + Base::Vector3d pp; + pp.x = px; pp.y = py; + Part::GeomPoint p(pp); + int ipnt = sk.addPoint(p); + int icstr = sk.addPointOnObjectConstraint(ipnt, Sketcher::start, icrv); + double err = sk.calculateConstraintError(icstr); + return err*err < 10.0*sk.getSolverPrecision(); +} + +//This one was done just for fun to see to what precision the constraints are solved. +double SketchObject::calculateConstraintError(int ConstrId) +{ + Sketcher::Sketch sk; + const std::vector &clist = this->Constraints.getValues(); + if (ConstrId < 0 || ConstrId >= int(clist.size())) + return std::numeric_limits::quiet_NaN(); + + Constraint* cstr = clist[ConstrId]->clone(); + double result=0.0; + try{ + std::vector GeoIdList; + int g; + GeoIdList.push_back(cstr->First); + GeoIdList.push_back(cstr->Second); + GeoIdList.push_back(cstr->Third); + + //add only necessary geometry to the sketch + for(std::size_t i=0; igetGeometry(g)); + } + } + + cstr->First = GeoIdList[0]; + cstr->Second = GeoIdList[1]; + cstr->Third = GeoIdList[2]; + int icstr = sk.addConstraint(cstr); + result = sk.calculateConstraintError(icstr); + } catch(...) {//cleanup + delete cstr; + throw; + } + delete cstr; + return result; +} + +PyObject *SketchObject::getPyObject(void) +{ + if (PythonObject.is(Py::_None())) { + // ref counter is set to 1 + PythonObject = Py::Object(new SketchObjectPy(this),true); + } + return Py::new_reference_to(PythonObject); +} + +unsigned int SketchObject::getMemSize(void) const +{ + return 0; +} + +void SketchObject::Save(Writer &writer) const +{ + // save the father classes + Part::Part2DObject::Save(writer); +} + +void SketchObject::Restore(XMLReader &reader) +{ + // read the father classes + Part::Part2DObject::Restore(reader); +} + +void SketchObject::onChanged(const App::Property* prop) +{ + if (isRestoring() && prop == &Geometry) { + std::vector geom = Geometry.getValues(); + std::vector supportedGeom = supportedGeometry(geom); + // To keep upward compatibility ignore unsupported geometry types + if (supportedGeom.size() != geom.size()) { + Geometry.setValues(supportedGeom); + return; + } + } + if (prop == &Geometry || prop == &Constraints) { + Constraints.checkGeometry(getCompleteGeometry()); + } + else if (prop == &ExternalGeometry) { + // make sure not to change anything while restoring this object + if (!isRestoring()) { + // external geometry was cleared + if (ExternalGeometry.getSize() == 0) { + delConstraintsToExternal(); + } + } + } +#if 0 + // For now do not delete anything (#0001791). When changing the support + // face it might be better to check which external geometries can be kept. + else if (prop == &Support) { + // make sure not to change anything while restoring this object + if (!isRestoring()) { + // if support face has changed then clear the external geometry + delConstraintsToExternal(); + for (int i=0; i < getExternalGeometryCount(); i++) { + delExternal(0); + } + } + } +#endif + Part::Part2DObject::onChanged(prop); +} + +void SketchObject::onDocumentRestored() +{ + try { + if(Support.getValue()) { + validateExternalLinks(); + rebuildExternalGeometry(); + } + else { + rebuildVertexIndex(); + } + Constraints.acceptGeometry(getCompleteGeometry()); + } + catch (...) { + } +} + +void SketchObject::getGeoVertexIndex(int VertexId, int &GeoId, PointPos &PosId) const +{ + if (VertexId < 0 || VertexId >= int(VertexId2GeoId.size())) { + GeoId = Constraint::GeoUndef; + PosId = none; + return; + } + GeoId = VertexId2GeoId[VertexId]; + PosId = VertexId2PosId[VertexId]; +} + +int SketchObject::getVertexIndexGeoPos(int GeoId, PointPos PosId) const +{ + for(std::size_t i=0;i &vals = this->Constraints.getValues(); + + std::vector< Constraint * > newVals(vals);//modifiable copy of pointers array + + std::vector< Constraint * > tbd;//list of temporary Constraint copies that need to be deleted later + + for(std::size_t i = 0; iType == Tangent || newVals[i]->Type == Perpendicular ){ + //create a constraint copy, affect it, replace the pointer + cntToBeAffected++; + Constraint *constNew = newVals[i]->clone(); + bool ret = AutoLockTangencyAndPerpty(constNew, /*bForce=*/true, bLock); + if (ret) cntSuccess++; + tbd.push_back(constNew); + newVals[i] = constNew; + Base::Console().Log("Constraint%i will be affected\n", + i+1); + } + } + + this->Constraints.setValues(newVals); + + //clean up - delete temporary copies of constraints that were made to affect the constraints + for(std::size_t i=0; i &vals = this->Constraints.getValues(); + + std::vector< Constraint * > newVals(vals);//modifiable copy of pointers array + + std::vector< Constraint * > tbd;//list of temporary Constraint copies that need to be deleted later + + for(std::size_t ic = 0; icFirst; posId = newVals[ic]->FirstPos; break; + case 2: geoId=newVals[ic]->Second; posId = newVals[ic]->SecondPos; break; + case 3: geoId=newVals[ic]->Third; posId = newVals[ic]->ThirdPos; break; + } + + if ( geoId <= -3 && + (posId==Sketcher::start || posId==Sketcher::end)){ + //we are dealing with a link to an endpoint of external geom + Part::Geometry* g = this->ExternalGeo[-geoId-1]; + if (g->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()){ + const Part::GeomArcOfCircle *segm = dynamic_cast(g); + if(segm->isReversedInXY()){ + //Gotcha! a link to an endpoint of external arc that is reversed. + //create a constraint copy, affect it, replace the pointer + if (!affected) + constNew = newVals[ic]->clone(); + affected=true; + //Do the fix on temp vars + if(posId == Sketcher::start) + posId = Sketcher::end; + else if (posId == Sketcher::end) + posId = Sketcher::start; + } + } + } + if (!affected) continue; + //Propagate the fix made on temp vars to the constraint + switch (ig){ + case 1: constNew->First = geoId; constNew->FirstPos = posId; break; + case 2: constNew->Second = geoId; constNew->SecondPos = posId; break; + case 3: constNew->Third = geoId; constNew->ThirdPos = posId; break; + } + } + if (affected){ + cntToBeAffected++; + tbd.push_back(constNew); + newVals[ic] = constNew; + Base::Console().Log("Constraint%i will be affected\n", + ic+1); + }; + } + + if(!justAnalyze){ + this->Constraints.setValues(newVals); + Base::Console().Log("Swapped start/end of reversed external arcs in %i constraints\n", + cntToBeAffected); + } + + //clean up - delete temporary copies of constraints that were made to affect the constraints + for(std::size_t i=0; iType == Tangent || cstr->Type == Perpendicular); + if(cstr->getValue() != 0.0 && ! bForce) /*tangency type already set. If not bForce - don't touch.*/ + return true; + if(!bLock){ + cstr->setValue(0.0);//reset + } else { + //decide on tangency type. Write the angle value into the datum field of the constraint. + int geoId1, geoId2, geoIdPt; + PointPos posPt; + geoId1 = cstr->First; + geoId2 = cstr->Second; + geoIdPt = cstr->Third; + posPt = cstr->ThirdPos; + if (geoIdPt == Constraint::GeoUndef){//not tangent-via-point, try endpoint-to-endpoint... + geoIdPt = cstr->First; + posPt = cstr->FirstPos; + } + if (posPt == none){//not endpoint-to-curve and not endpoint-to-endpoint tangent (is simple tangency) + //no tangency lockdown is implemented for simple tangency. Do nothing. + return false; + } else { + Base::Vector3d p = getPoint(geoIdPt, posPt); + + //this piece of code is also present in Sketch.cpp, correct for offset + //and to do the autodecision for old sketches. + double angleOffset = 0.0;//the difference between the datum value and the actual angle to apply. (datum=angle+offset) + double angleDesire = 0.0;//the desired angle value (and we are to decide if 180* should be added to it) + if (cstr->Type == Tangent) {angleOffset = -M_PI/2; angleDesire = 0.0;} + if (cstr->Type == Perpendicular) {angleOffset = 0; angleDesire = M_PI/2;} + + double angleErr = calculateAngleViaPoint(geoId1, geoId2, p.x, p.y) - angleDesire; + + //bring angleErr to -pi..pi + if (angleErr > M_PI) angleErr -= M_PI*2; + if (angleErr < -M_PI) angleErr += M_PI*2; + + //the autodetector + if(fabs(angleErr) > M_PI/2 ) + angleDesire += M_PI; + + cstr->setValue(angleDesire + angleOffset); //external tangency. The angle stored is offset by Pi/2 so that a value of 0.0 is invalid and threated as "undecided". + } + } + } catch (Base::Exception& e){ + //failure to determine tangency type is not a big deal, so a warning. + Base::Console().Warning("Error in AutoLockTangency. %s \n", e.what()); + return false; + } + return true; +} + +void SketchObject::setExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr, const char * comment) +{ + DocumentObject::setExpression(path, expr, comment); + + if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver, constraints and UI + solve(); +} + + +// Python Sketcher feature --------------------------------------------------------- + +namespace App { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(Sketcher::SketchObjectPython, Sketcher::SketchObject) +template<> const char* Sketcher::SketchObjectPython::getViewProviderName(void) const { + return "SketcherGui::ViewProviderPython"; +} +template<> PyObject* Sketcher::SketchObjectPython::getPyObject(void) { + if (PythonObject.is(Py::_None())) { + // ref counter is set to 1 + PythonObject = Py::Object(new FeaturePythonPyT(this),true); + } + return Py::new_reference_to(PythonObject); +} +/// @endcond + +// explicit template instantiation +template class SketcherExport FeaturePythonT; +} diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index 328e0fe0b..fa82d573a 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -31,12 +31,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include // inclusion of the generated files (generated out of SketchObjectSFPy.xml) @@ -381,10 +383,20 @@ PyObject* SketchObjectPy::addExternal(PyObject *args) PyErr_SetString(PyExc_ValueError, str.str().c_str()); return 0; } - // check if it belongs to the sketch support - if (this->getSketchObjectPtr()->Support.getValue() != Obj) { + // check if it is a datum feature + // TODO: Allow selection only from Body which this sketch belongs to? + if (Obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) { + // OK + } else if (Obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { + if (this->getSketchObjectPtr()->Support.getValue() != Obj) { + std::stringstream str; + str << ObjectName << "is not supported by this sketch"; + PyErr_SetString(PyExc_ValueError, str.str().c_str()); + return 0; + } + } else if (!Obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId())) { std::stringstream str; - str << ObjectName << "is not supported by this sketch"; + str << ObjectName << "must be a Part feature or a datum feature"; PyErr_SetString(PyExc_ValueError, str.str().c_str()); return 0; } diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index 3fc096e78..4f033ab33 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include #include +#include #include "ViewProviderSketch.h" #include "DrawSketchHandler.h" @@ -4473,7 +4475,12 @@ namespace SketcherGui { { Sketcher::SketchObject *sketch = static_cast(object); App::DocumentObject *support = sketch->Support.getValue(); - // for the moment we allow external constraints only from the support + + // for the moment we allow external constraints only from the support and datum features + if(pObj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) || + pObj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) + return true; + if (pObj != support) return false; if (!sSubName || sSubName[0] == '\0') @@ -4582,8 +4589,13 @@ public: virtual bool onSelectionChanged(const Gui::SelectionChanges& msg) { if (msg.Type == Gui::SelectionChanges::AddSelection) { + App::DocumentObject* obj = sketchgui->getObject()->getDocument()->getObject(msg.pObjectName); + if (obj == NULL) + throw Base::Exception("Sketcher: External geometry: Invalid object in selection"); std::string subName(msg.pSubName); - if ((subName.size() > 4 && subName.substr(0,4) == "Edge") || + if (obj->getTypeId().isDerivedFrom(App::Plane::getClassTypeId()) || + obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId()) || + (subName.size() > 4 && subName.substr(0,4) == "Edge") || (subName.size() > 6 && subName.substr(0,6) == "Vertex")) { try { Gui::Command::openCommand("Add external geometry");