diff --git a/src/App/DocumentObjectPyImp.cpp b/src/App/DocumentObjectPyImp.cpp index 185c94a40..bbdf351e0 100644 --- a/src/App/DocumentObjectPyImp.cpp +++ b/src/App/DocumentObjectPyImp.cpp @@ -170,7 +170,14 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj) throw Py::AttributeError(s.str()); } - prop->setPyObject(obj); + try { + prop->setPyObject(obj); + } + catch (const Base::TypeError& e) { + std::stringstream s; + s << "Property '" << prop->getName() << "': " << e.what(); + throw Py::TypeError(s.str()); + } return 1; } diff --git a/src/Mod/Drawing/App/CMakeLists.txt b/src/Mod/Drawing/App/CMakeLists.txt index 9c78a98cc..0f5c202f1 100644 --- a/src/Mod/Drawing/App/CMakeLists.txt +++ b/src/Mod/Drawing/App/CMakeLists.txt @@ -57,8 +57,24 @@ SOURCE_GROUP("Features" FILES ${Features_SRCS}) SOURCE_GROUP("Algorithms" FILES ${DrawingAlgos_SRCS}) SET(Drawing_Templates + Templates/A0_Landscape_ISO7200.svg + Templates/A0_Landscape_plain.svg + Templates/A0_Portrait_plain.svg + Templates/A1_Landscape_ISO7200.svg + Templates/A1_Landscape_plain.svg + Templates/A1_Portrait_plain.svg + Templates/A2_Landscape_ISO7200.svg + Templates/A2_Landscape_plain.svg + Templates/A2_Portrait_plain.svg Templates/A3_Landscape.svg + Templates/A3_Landscape_ISO7200.svg + Templates/A3_Landscape_plain.svg + Templates/A3_Portrait_plain.svg Templates/A4_Landscape.svg + Templates/A4_Landscape_ISO7200.svg + Templates/A4_Landscape_plain.svg + Templates/A4_Portrait_ISO7200.svg + Templates/A4_Portrait_plain.svg ) if(MSVC) diff --git a/src/Mod/Drawing/App/DrawingExport.cpp b/src/Mod/Drawing/App/DrawingExport.cpp index 145923fa4..d765c0f87 100644 --- a/src/Mod/Drawing/App/DrawingExport.cpp +++ b/src/Mod/Drawing/App/DrawingExport.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include +# include # include # include # include @@ -33,6 +34,7 @@ #include #include +#include #include #include #include @@ -71,6 +73,7 @@ #include #include #include +#include #include "DrawingExport.h" #include @@ -78,6 +81,87 @@ using namespace Drawing; +TopoDS_Edge DrawingOutput::asCircle(const BRepAdaptor_Curve& c) const +{ + double curv=0; + gp_Pnt pnt, center; + + // approximate the circle center from three positions + BRepLProp_CLProps prop(c,c.FirstParameter(),2,Precision::Confusion()); + curv += prop.Curvature(); + prop.CentreOfCurvature(pnt); + center.ChangeCoord().Add(pnt.Coord()); + + prop.SetParameter(0.5*(c.FirstParameter()+c.LastParameter())); + curv += prop.Curvature(); + prop.CentreOfCurvature(pnt); + center.ChangeCoord().Add(pnt.Coord()); + + prop.SetParameter(c.LastParameter()); + curv += prop.Curvature(); + prop.CentreOfCurvature(pnt); + center.ChangeCoord().Add(pnt.Coord()); + + center.ChangeCoord().Divide(3); + curv /= 3; + + // get circle from curvature information + double radius = 1 / curv; + + TopLoc_Location location; + Handle(Poly_Polygon3D) polygon = BRep_Tool::Polygon3D(c.Edge(), location); + if (!polygon.IsNull()) { + const TColgp_Array1OfPnt& nodes = polygon->Nodes(); + for (int i = nodes.Lower(); i <= nodes.Upper(); i++) { + gp_Pnt p = nodes(i); + double dist = p.Distance(center); + if (std::abs(dist - radius) > 0.001) + return TopoDS_Edge(); + } + + gp_Circ circ; + circ.SetLocation(center); + circ.SetRadius(radius); + gp_Pnt p1 = nodes(nodes.Lower()); + gp_Pnt p2 = nodes(nodes.Upper()); + double dist = p1.Distance(p2); + + if (dist < Precision::Confusion()) { + BRepBuilderAPI_MakeEdge mkEdge(circ); + return mkEdge.Edge(); + } + else { + gp_Vec dir1(center, p1); + dir1.Normalize(); + gp_Vec dir2(center, p2); + dir2.Normalize(); + p1 = gp_Pnt(center.XYZ() + radius * dir1.XYZ()); + p2 = gp_Pnt(center.XYZ() + radius * dir2.XYZ()); + BRepBuilderAPI_MakeEdge mkEdge(circ, p1, p2); + return mkEdge.Edge(); + } + } + + return TopoDS_Edge(); +} + +TopoDS_Edge DrawingOutput::asBSpline(const BRepAdaptor_Curve& c, int maxDegree) const +{ + Standard_Real tol3D = 0.001; + Standard_Integer maxSegment = 10; + Handle_BRepAdaptor_HCurve hCurve = new BRepAdaptor_HCurve(c); + // approximate the curve using a tolerance + Approx_Curve3d approx(hCurve,tol3D,GeomAbs_C0,maxSegment,maxDegree); + if (approx.IsDone() && approx.HasResult()) { + // have the result + Handle_Geom_BSplineCurve spline = approx.Curve(); + BRepBuilderAPI_MakeEdge mkEdge(spline, spline->FirstParameter(), spline->LastParameter()); + return mkEdge.Edge(); + } + + return TopoDS_Edge(); +} + SVGOutput::SVGOutput() { } @@ -97,7 +181,17 @@ std::string SVGOutput::exportEdges(const TopoDS_Shape& input) printEllipse(adapt, i, result); } else if (adapt.GetType() == GeomAbs_BSplineCurve) { - printBSpline(adapt, i, result); + TopoDS_Edge circle = asCircle(adapt); + if (circle.IsNull()) { + printBSpline(adapt, i, result); + } + else { + BRepAdaptor_Curve adapt_circle(circle); + printCircle(adapt_circle, result); + } + } + else if (adapt.GetType() == GeomAbs_BezierCurve) { + printBezier(adapt, i, result); } // fallback else { @@ -181,6 +275,70 @@ void SVGOutput::printEllipse(const BRepAdaptor_Curve& c, int id, std::ostream& o } } +void SVGOutput::printBezier(const BRepAdaptor_Curve& c, int id, std::ostream& out) +{ + try { + std::stringstream str; + str << "NbPoles(); + + // if it's a bezier with degree higher than 3 convert it into a B-spline + if (bezier->Degree() > 3 || bezier->IsRational()) { + TopoDS_Edge edge = asBSpline(c, 3); + if (!edge.IsNull()) { + BRepAdaptor_Curve spline(edge); + printBSpline(spline, id, out); + } + else { + Standard_Failure::Raise("do it the generic way"); + } + + return; + } + + + gp_Pnt p1 = bezier->Pole(1); + str << p1.X() << "," << p1.Y(); + if (bezier->Degree() == 3) { + if (poles != 4) + Standard_Failure::Raise("do it the generic way"); + gp_Pnt p2 = bezier->Pole(2); + gp_Pnt p3 = bezier->Pole(3); + gp_Pnt p4 = bezier->Pole(4); + str << " C" + << p2.X() << "," << p2.Y() << " " + << p3.X() << "," << p3.Y() << " " + << p4.X() << "," << p4.Y() << " "; + } + else if (bezier->Degree() == 2) { + if (poles != 3) + Standard_Failure::Raise("do it the generic way"); + gp_Pnt p2 = bezier->Pole(2); + gp_Pnt p3 = bezier->Pole(3); + str << " Q" + << p2.X() << "," << p2.Y() << " " + << p3.X() << "," << p3.Y() << " "; + } + else if (bezier->Degree() == 1) { + if (poles != 2) + Standard_Failure::Raise("do it the generic way"); + gp_Pnt p2 = bezier->Pole(2); + str << " L" << p2.X() << "," << p2.Y() << " "; + } + else { + Standard_Failure::Raise("do it the generic way"); + } + + str << "\" />"; + out << str.str(); + } + catch (Standard_Failure) { + printGeneric(c, id, out); + } +} + void SVGOutput::printBSpline(const BRepAdaptor_Curve& c, int id, std::ostream& out) { try { diff --git a/src/Mod/Drawing/App/DrawingExport.h b/src/Mod/Drawing/App/DrawingExport.h index c525e0f51..e2e295159 100644 --- a/src/Mod/Drawing/App/DrawingExport.h +++ b/src/Mod/Drawing/App/DrawingExport.h @@ -25,6 +25,7 @@ #define DRAWING_EXPORT_H #include +#include class TopoDS_Shape; class BRepAdaptor_Curve; @@ -32,7 +33,16 @@ class BRepAdaptor_Curve; namespace Drawing { -class DrawingExport SVGOutput +class DrawingExport DrawingOutput +{ +public: + // If the curve is approximately a circle it will be returned, + // otherwise a null edge is returned. + TopoDS_Edge asCircle(const BRepAdaptor_Curve&) const; + TopoDS_Edge asBSpline(const BRepAdaptor_Curve&, int maxDegree) const; +}; + +class DrawingExport SVGOutput : public DrawingOutput { public: SVGOutput(); @@ -42,11 +52,12 @@ private: void printCircle(const BRepAdaptor_Curve&, std::ostream&); void printEllipse(const BRepAdaptor_Curve&, int id, std::ostream&); void printBSpline(const BRepAdaptor_Curve&, int id, std::ostream&); + void printBezier(const BRepAdaptor_Curve&, int id, std::ostream&); void printGeneric(const BRepAdaptor_Curve&, int id, std::ostream&); }; /* dxf output section - Dan Falck 2011/09/25 */ -class DrawingExport DXFOutput +class DrawingExport DXFOutput : public DrawingOutput { public: DXFOutput(); diff --git a/src/Mod/Drawing/Gui/Command.cpp b/src/Mod/Drawing/Gui/Command.cpp index ab3492aaf..8695d9822 100644 --- a/src/Mod/Drawing/Gui/Command.cpp +++ b/src/Mod/Drawing/Gui/Command.cpp @@ -126,15 +126,39 @@ Gui::Action * CmdDrawingNewPage::createAction(void) QAction* defaultAction = 0; int defaultId = 0; + QString lastPaper; + int lastId = -1; + std::string path = App::Application::getResourceDir(); path += "Mod/Drawing/Templates/"; QDir dir(QString::fromUtf8(path.c_str()), QString::fromAscii("*.svg")); for (unsigned int i=0; i -1) { QString paper = rx.cap(1); int id = rx.cap(2).toInt(); QString orientation = rx.cap(3); + QString info = rx.cap(4).mid(1); + info.chop(1); + if (!info.isEmpty()) { + info[0] = info[0].toUpper(); + } + + // group by paper size + if (!lastPaper.isEmpty()) { + if (lastPaper != paper) { + QAction* sep = pcAction->addAction(QString()); + sep->setSeparator(true); + } + else if (lastId != id) { + QAction* sep = pcAction->addAction(QString()); + sep->setSeparator(true); + } + } + + lastPaper = paper; + lastId = id; + QFile file(QString::fromAscii(":/icons/actions/drawing-landscape-A0.svg")); QAction* a = pcAction->addAction(QString()); if (file.open(QFile::ReadOnly)) { @@ -147,6 +171,7 @@ Gui::Action * CmdDrawingNewPage::createAction(void) a->setProperty("TemplatePaper", paper); a->setProperty("TemplateOrientation", orientation); a->setProperty("TemplateId", id); + a->setProperty("TemplateInfo", info); a->setProperty("Template", dir.absoluteFilePath(dir[i])); if (id == 3) { @@ -157,6 +182,7 @@ Gui::Action * CmdDrawingNewPage::createAction(void) } _pcAction = pcAction; + languageChange(); if (defaultAction) { pcAction->setIcon(defaultAction->icon()); @@ -179,6 +205,8 @@ void CmdDrawingNewPage::languageChange() Gui::ActionGroup* pcAction = qobject_cast(_pcAction); QList a = pcAction->actions(); for (QList::iterator it = a.begin(); it != a.end(); ++it) { + if ((*it)->isSeparator()) + continue; QString paper = (*it)->property("TemplatePaper").toString(); int id = (*it)->property("TemplateId").toInt(); QString orientation = (*it)->property("TemplateOrientation").toString(); @@ -186,19 +214,38 @@ void CmdDrawingNewPage::languageChange() orientation = QCoreApplication::translate("Drawing_NewPage", "Landscape", 0, QCoreApplication::CodecForTr); else if (orientation.compare(QLatin1String("portrait"), Qt::CaseInsensitive) == 0) orientation = QCoreApplication::translate("Drawing_NewPage", "Portrait", 0, QCoreApplication::CodecForTr); + QString info = (*it)->property("TemplateInfo").toString(); - (*it)->setText(QCoreApplication::translate( - "Drawing_NewPage", "%1%2 %3", 0, - QCoreApplication::CodecForTr) - .arg(paper) - .arg(id) - .arg(orientation)); - (*it)->setToolTip(QCoreApplication::translate( - "Drawing_NewPage", "Insert new %1%2 %3 drawing", 0, - QCoreApplication::CodecForTr) - .arg(paper) - .arg(id) - .arg(orientation)); + if (info.isEmpty()) { + (*it)->setText(QCoreApplication::translate( + "Drawing_NewPage", "%1%2 %3", 0, + QCoreApplication::CodecForTr) + .arg(paper) + .arg(id) + .arg(orientation)); + (*it)->setToolTip(QCoreApplication::translate( + "Drawing_NewPage", "Insert new %1%2 %3 drawing", 0, + QCoreApplication::CodecForTr) + .arg(paper) + .arg(id) + .arg(orientation)); + } + else { + (*it)->setText(QCoreApplication::translate( + "Drawing_NewPage", "%1%2 %3 (%4)", 0, + QCoreApplication::CodecForTr) + .arg(paper) + .arg(id) + .arg(orientation) + .arg(info)); + (*it)->setToolTip(QCoreApplication::translate( + "Drawing_NewPage", "Insert new %1%2 %3 (%4) drawing", 0, + QCoreApplication::CodecForTr) + .arg(paper) + .arg(id) + .arg(orientation) + .arg(info)); + } } } diff --git a/src/Mod/Drawing/Gui/DrawingView.cpp b/src/Mod/Drawing/Gui/DrawingView.cpp index e1269c985..dc10e6c9a 100644 --- a/src/Mod/Drawing/Gui/DrawingView.cpp +++ b/src/Mod/Drawing/Gui/DrawingView.cpp @@ -332,7 +332,9 @@ void DrawingView::setDocumentObject(const std::string& name) void DrawingView::closeEvent(QCloseEvent* ev) { - ev->accept(); + MDIView::closeEvent(ev); + if (!ev->isAccepted()) + return; // when closing the view from GUI notify the view provider to mark it invisible if (_pcDocument && !m_objectName.empty()) { diff --git a/src/Mod/Drawing/Templates/A0_Landscape_ISO7200.svg b/src/Mod/Drawing/Templates/A0_Landscape_ISO7200.svg new file mode 100644 index 000000000..a77eb59bd --- /dev/null +++ b/src/Mod/Drawing/Templates/A0_Landscape_ISO7200.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + Created by: + Title: + Supplementary information: + Size: + Sheet: + Scale: + Part number: + Drawing number: + Date: + Revision: + + + AUTHOR NAME + DRAWING TITLE + + FreeCAD DRAWING + + + A0 + X / Y + SCALE + PART NUMBER + DRAWING NUMBER + YYYY-MM-DD + A + + + + + diff --git a/src/Mod/Drawing/Templates/A1_Landscape_ISO7200.svg b/src/Mod/Drawing/Templates/A1_Landscape_ISO7200.svg new file mode 100644 index 000000000..7cbfbc000 --- /dev/null +++ b/src/Mod/Drawing/Templates/A1_Landscape_ISO7200.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + Created by: + Title: + Supplementary information: + Size: + Sheet: + Scale: + Part number: + Drawing number: + Date: + Revision: + + + AUTHOR NAME + DRAWING TITLE + + FreeCAD DRAWING + + + A1 + X / Y + SCALE + PART NUMBER + DRAWING NUMBER + YYYY-MM-DD + A + + + + + diff --git a/src/Mod/Drawing/Templates/A2_Landscape_ISO7200.svg b/src/Mod/Drawing/Templates/A2_Landscape_ISO7200.svg new file mode 100644 index 000000000..aa00324e6 --- /dev/null +++ b/src/Mod/Drawing/Templates/A2_Landscape_ISO7200.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + Created by: + Title: + Supplementary information: + Size: + Sheet: + Scale: + Part number: + Drawing number: + Date: + Revision: + + + AUTHOR NAME + DRAWING TITLE + + FreeCAD DRAWING + + + A2 + X / Y + SCALE + PART NUMBER + DRAWING NUMBER + YYYY-MM-DD + A + + + + + diff --git a/src/Mod/Import/App/AppImportPy.cpp b/src/Mod/Import/App/AppImportPy.cpp index cac2e5261..edd87a66f 100644 --- a/src/Mod/Import/App/AppImportPy.cpp +++ b/src/Mod/Import/App/AppImportPy.cpp @@ -121,7 +121,6 @@ static PyObject * importer(PyObject *self, PyObject *args) try { IGESControl_Controller::Init(); - Interface_Static::SetIVal("read.surfacecurve.mode",3); IGESCAFControl_Reader aReader; // http://www.opencascade.org/org/forum/thread_20603/?forum=3 aReader.SetReadVisible(hGrp->GetBool("SkipBlankEntities", true) diff --git a/src/Mod/Import/Gui/AppImportGuiPy.cpp b/src/Mod/Import/Gui/AppImportGuiPy.cpp index 2a62ed7b8..fcf515de1 100644 --- a/src/Mod/Import/Gui/AppImportGuiPy.cpp +++ b/src/Mod/Import/Gui/AppImportGuiPy.cpp @@ -162,7 +162,6 @@ static PyObject * importer(PyObject *self, PyObject *args) try { IGESControl_Controller::Init(); - Interface_Static::SetIVal("read.surfacecurve.mode",3); IGESCAFControl_Reader aReader; // http://www.opencascade.org/org/forum/thread_20603/?forum=3 aReader.SetReadVisible(hGrp->GetBool("SkipBlankEntities", true) @@ -518,7 +517,6 @@ static PyObject * ocaf(PyObject *self, PyObject *args) Base::Reference hGrp = App::GetApplication().GetUserParameter() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part")->GetGroup("IGES"); IGESControl_Controller::Init(); - Interface_Static::SetIVal("read.surfacecurve.mode",3); IGESCAFControl_Reader aReader; // http://www.opencascade.org/org/forum/thread_20603/?forum=3 aReader.SetReadVisible(hGrp->GetBool("SkipBlankEntities", true) diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index afefb26e1..684f73a64 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -297,6 +297,32 @@ void PartExport initPart() Base::Reference hGrp = App::GetApplication().GetUserParameter() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part"); + // General + Base::Reference hGenGrp = hGrp->GetGroup("General"); + // http://www.opencascade.org/org/forum/thread_20801/ + // read.surfacecurve.mode: + // A preference for the computation of curves in an entity which has both 2D and 3D representation. + // Each TopoDS_Edge in TopoDS_Face must have a 3D and 2D curve that references the surface. + // If both 2D and 3D representation of the entity are present, the computation of these curves depends on + // the following values of parameter: + // 0: "Default" - no preference, both curves are taken + // 3: "3DUse_Preferred" - 3D curves are used to rebuild 2D ones + // Additional modes for IGES + // 2: "2DUse_Preferred" - the 2D is used to rebuild the 3D in case of their inconsistency + // -2: "2DUse_Forced" - the 2D is always used to rebuild the 3D (even if 2D is present in the file) + // -3: "3DUse_Forced" - the 3D is always used to rebuild the 2D (even if 2D is present in the file) + int readsurfacecurve = hGenGrp->GetInt("ReadSurfaceCurveMode", 0); + Interface_Static::SetIVal("read.surfacecurve.mode", readsurfacecurve); + + // write.surfacecurve.mode (STEP-only): + // This parameter indicates whether parametric curves (curves in parametric space of surface) should be + // written into the STEP file. This parameter can be set to Off in order to minimize the size of the resulting + // STEP file. + // Off (0) : writes STEP files without pcurves. This mode decreases the size of the resulting file. + // On (1) : (default) writes pcurves to STEP file + int writesurfacecurve = hGenGrp->GetInt("WriteSurfaceCurveMode", 1); + Interface_Static::SetIVal("write.surfacecurve.mode", writesurfacecurve); + //IGES handling Base::Reference hIgesGrp = hGrp->GetGroup("IGES"); int value = Interface_Static::IVal("write.iges.brep.mode"); diff --git a/src/Mod/Part/App/ImportIges.cpp b/src/Mod/Part/App/ImportIges.cpp index a5734616e..1ef0bc619 100644 --- a/src/Mod/Part/App/ImportIges.cpp +++ b/src/Mod/Part/App/ImportIges.cpp @@ -69,9 +69,7 @@ int Part::ImportIgesParts(App::Document *pcDoc, const char* FileName) try { Base::FileInfo fi(FileName); // read iges file - // http://www.opencascade.org/org/forum/thread_20801/ IGESControl_Controller::Init(); - Interface_Static::SetIVal("read.surfacecurve.mode",3); // load data exchange message files Message_MsgFile::LoadFromEnv("CSF_XSMessage","IGES"); diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 00f12d431..9b07ce655 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -569,9 +569,7 @@ void TopoShape::importIges(const char *FileName) { try { // read iges file - // http://www.opencascade.org/org/forum/thread_20801/ IGESControl_Controller::Init(); - Interface_Static::SetIVal("read.surfacecurve.mode",3); IGESControl_Reader aReader; // Ignore construction elements // http://www.opencascade.org/org/forum/thread_20603/?forum=3 diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index c19994abd..6a3e0c475 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -1376,18 +1376,29 @@ int Sketch::addAngleConstraint(int geoId, double value) { geoId = checkGeoId(geoId); - if (Geoms[geoId].type != Line) - return -1; + if (Geoms[geoId].type == Line) { + GCS::Line &l = Lines[Geoms[geoId].index]; - GCS::Line &l = Lines[Geoms[geoId].index]; + // add the parameter for the angle + FixParameters.push_back(new double(value)); + double *angle = FixParameters[FixParameters.size()-1]; - // add the parameter for the angle - FixParameters.push_back(new double(value)); - double *angle = FixParameters[FixParameters.size()-1]; + int tag = ++ConstraintsCounter; + GCSsys.addConstraintP2PAngle(l.p1, l.p2, angle, tag); + return ConstraintsCounter; + } + else if (Geoms[geoId].type == Arc) { + GCS::Arc &a = Arcs[Geoms[geoId].index]; - int tag = ++ConstraintsCounter; - GCSsys.addConstraintP2PAngle(l.p1, l.p2, angle, tag); - return ConstraintsCounter; + // add the parameter for the angle + FixParameters.push_back(new double(value)); + double *angle = FixParameters[FixParameters.size()-1]; + + int tag = ++ConstraintsCounter; + GCSsys.addConstraintL2LAngle(a.center, a.start, a.center, a.end, angle, tag); + return ConstraintsCounter; + } + return -1; } // line to line angle constraint diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 851ce1824..da7319a73 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -2070,7 +2070,7 @@ void CmdSketcherConstrainAngle::activated(int iMsg) if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) { double ActAngle = 0.0; - openCommand("add angle constraint"); + openCommand("Add angle constraint"); //add missing point-on-object constraints if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){ @@ -2163,7 +2163,7 @@ void CmdSketcherConstrainAngle::activated(int iMsg) std::swap(PosId1,PosId2); } - openCommand("add angle constraint"); + openCommand("Add angle constraint"); Gui::Command::doCommand( Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Angle',%d,%d,%d,%d,%f)) ", selection[0].getFeatName(),GeoId1,PosId1,GeoId2,PosId2,ActAngle); @@ -2187,12 +2187,28 @@ void CmdSketcherConstrainAngle::activated(int iMsg) Base::Vector3d dir = lineSeg->getEndPoint()-lineSeg->getStartPoint(); double ActAngle = atan2(dir.y,dir.x); - openCommand("add angle constraint"); + openCommand("Add angle constraint"); Gui::Command::doCommand( Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Angle',%d,%f)) ", selection[0].getFeatName(),GeoId1,ActAngle); commitCommand(); + finishDistanceConstraint(this, Obj); + return; + } + else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc; + arc = dynamic_cast(geom); + double startangle, endangle; + arc->getRange(startangle, endangle); + double angle = endangle - startangle; + + openCommand("Add angle constraint"); + Gui::Command::doCommand( + Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Angle',%d,%f)) ", + selection[0].getFeatName(),GeoId1,angle); + commitCommand(); + finishDistanceConstraint(this, Obj); return; } diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 2eb00338c..9358db36e 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -1265,12 +1265,19 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2D &toPo dir2.RotateZ(-M_PI/2); } - } else if (Constr->First != Constraint::GeoUndef) { // line angle + } else if (Constr->First != Constraint::GeoUndef) { // line/arc angle const Part::Geometry *geo = GeoById(geomlist, Constr->First); - if (geo->getTypeId() != Part::GeomLineSegment::getClassTypeId()) + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = dynamic_cast(geo); + p0 = (lineSeg->getEndPoint()+lineSeg->getStartPoint())/2; + } + else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = dynamic_cast(geo); + p0 = arc->getCenter(); + } + else { return; - const Part::GeomLineSegment *lineSeg = dynamic_cast(geo); - p0 = (lineSeg->getEndPoint()+lineSeg->getStartPoint())/2; + } } else return; @@ -3721,16 +3728,26 @@ Restart: } else if (Constr->First != Constraint::GeoUndef) { const Part::Geometry *geo = GeoById(*geomlist, Constr->First); - if (geo->getTypeId() != Part::GeomLineSegment::getClassTypeId()) + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = dynamic_cast(geo); + p0 = Base::convertTo((lineSeg->getEndPoint()+lineSeg->getStartPoint())/2); + + Base::Vector3d dir = lineSeg->getEndPoint()-lineSeg->getStartPoint(); + startangle = 0.; + range = atan2(dir.y,dir.x); + endangle = startangle + range; + } + else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = dynamic_cast(geo); + p0 = Base::convertTo(arc->getCenter()); + + Base::Vector3d dir = arc->getEndPoint()-arc->getStartPoint(); + arc->getRange(startangle, endangle); + range = endangle - startangle; + } + else { break; - const Part::GeomLineSegment *lineSeg = dynamic_cast(geo); - - p0 = Base::convertTo((lineSeg->getEndPoint()+lineSeg->getStartPoint())/2); - - Base::Vector3d dir = lineSeg->getEndPoint()-lineSeg->getStartPoint(); - startangle = 0.; - range = atan2(dir.y,dir.x); - endangle = startangle + range; + } } else break;