/*************************************************************************** * Copyright (c) 2022 Abdullah Tahiri * * * * 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 #endif // #ifndef _PreComp_ #include #include #include #include #include "PythonConverter.h" using namespace Sketcher; std::string PythonConverter::convert(const Part::Geometry* geo, Mode mode) { // "addGeometry(Part.LineSegment(App.Vector(%f,%f,0),App.Vector(%f,%f,0)),%s)" std::string command; auto sg = process(geo); command = boost::str(boost::format("addGeometry(%s,%s)\n") % sg.creation % (sg.construction ? "True" : "False")); if ((geo->getTypeId() != Part::GeomEllipse::getClassTypeId() || geo->getTypeId() != Part::GeomArcOfEllipse::getClassTypeId() || geo->getTypeId() != Part::GeomArcOfHyperbola::getClassTypeId() || geo->getTypeId() != Part::GeomArcOfParabola::getClassTypeId() || geo->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) && mode == Mode::CreateInternalGeometry) { command += boost::str(boost::format("exposeInternalGeometry(len(ActiveSketch.Geometry))\n")); } return command; } std::string PythonConverter::convert(const Sketcher::Constraint* constraint, GeoIdMode geoIdMode) { // addConstraint(Sketcher.Constraint('Distance',%d,%f)) std::string command; auto cg = process(constraint, geoIdMode); command = boost::str(boost::format("addConstraint(%s)\n") % cg); return command; } std::string PythonConverter::convert(const std::string& doc, const std::vector& geos, Mode mode) { if (geos.empty()) { return std::string(); } // Generates a list for consecutive geometries of construction type, or of normal type auto printGeoList = [&doc](const std::string& geolist, int ngeos, bool construction) { std::string command; if (ngeos > 0) { if (construction) { command = boost::str( boost::format("constrGeoList = []\n%s%s.addGeometry(constrGeoList,%s)\n" "del constrGeoList\n") % geolist % doc % "True"); } else { command = boost::str( boost::format("geoList = []\n%s%s.addGeometry(geoList,%s)\ndel geoList\n") % geolist % doc % "False"); } } return command; }; std::string command = boost::str(boost::format("lastGeoId = len(ActiveSketch.Geometry)\n")); // Adds a list of consecutive geometries of a same construction type to the generating command auto addToCommands = [&command, &printGeoList](const std::string& geolist, int ngeos, bool construction) { auto newcommand = printGeoList(geolist, ngeos, construction); if (command.empty()) { command = std::move(newcommand); } else { command += "\n"; command += newcommand; } }; std::string geolist; int ngeos = 0; bool currentconstruction = Sketcher::GeometryFacade::getConstruction(geos[0]); for (auto geo : geos) { auto sg = process(geo); if (sg.construction != currentconstruction) { // if it switches from construction to normal or vice versa, flush elements so far in // order to keep order of creation addToCommands(geolist, ngeos, currentconstruction); geolist.clear(); ngeos = 0; currentconstruction = sg.construction; } if (sg.construction) { geolist = boost::str(boost::format("%sconstrGeoList.append(%s)\n") % geolist % sg.creation); } else { geolist = boost::str(boost::format("%sgeoList.append(%s)\n") % geolist % sg.creation); } ngeos++; } addToCommands(geolist, ngeos, currentconstruction); int index = 0; if (mode == Mode::CreateInternalGeometry) { for (auto geo : geos) { index++; if (geo->getTypeId() != Part::GeomEllipse::getClassTypeId() || geo->getTypeId() != Part::GeomArcOfEllipse::getClassTypeId() || geo->getTypeId() != Part::GeomArcOfHyperbola::getClassTypeId() || geo->getTypeId() != Part::GeomArcOfParabola::getClassTypeId() || geo->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) { std::string newcommand = boost::str(boost::format("exposeInternalGeometry(lastGeoId + %d)\n") % (index)); command += newcommand; } } } return command; } std::string PythonConverter::convert(const std::string& doc, const std::vector& constraints, GeoIdMode geoIdMode) { if (constraints.size() == 1) { auto cg = convert(constraints[0], geoIdMode); return boost::str(boost::format("%s.%s\n") % doc % cg); } std::string constraintlist = "constraintList = []"; for (auto constraint : constraints) { auto cg = process(constraint, geoIdMode); constraintlist = boost::str(boost::format("%s\nconstraintList.append(%s)") % constraintlist % cg); } if (!constraints.empty()) { constraintlist = boost::str(boost::format("%s\n%s.addConstraint(constraintList)\ndel constraintList\n") % constraintlist % doc); } return constraintlist; } PythonConverter::SingleGeometry PythonConverter::process(const Part::Geometry* geo) { static std::map> converterMap = { {Part::GeomLineSegment::getClassTypeId(), [](const Part::Geometry* geo) { auto sgeo = static_cast(geo); SingleGeometry sg; sg.creation = boost::str( boost::format( "Part.LineSegment(App.Vector(%f, %f, %f),App.Vector(%f, %f, %f))") % sgeo->getStartPoint().x % sgeo->getStartPoint().y % sgeo->getStartPoint().z % sgeo->getEndPoint().x % sgeo->getEndPoint().y % sgeo->getEndPoint().z); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, {Part::GeomArcOfCircle::getClassTypeId(), [](const Part::Geometry* geo) { auto arc = static_cast(geo); SingleGeometry sg; sg.creation = boost::str(boost::format("Part.ArcOfCircle(Part.Circle(App.Vector(%f, %f, " "%f), App.Vector(%f, %f, %f), %f), %f, %f)") % arc->getCenter().x % arc->getCenter().y % arc->getCenter().z % arc->getAxisDirection().x % arc->getAxisDirection().y % arc->getAxisDirection().z % arc->getRadius() % arc->getFirstParameter() % arc->getLastParameter()); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, {Part::GeomPoint::getClassTypeId(), [](const Part::Geometry* geo) { auto sgeo = static_cast(geo); SingleGeometry sg; sg.creation = boost::str(boost::format("Part.Point(App.Vector(%f, %f, %f))") % sgeo->getPoint().x % sgeo->getPoint().y % sgeo->getPoint().z); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, {Part::GeomEllipse::getClassTypeId(), [](const Part::Geometry* geo) { auto ellipse = static_cast(geo); SingleGeometry sg; auto center = ellipse->getCenter(); auto periapsis = center + ellipse->getMajorAxisDir() * ellipse->getMajorRadius(); auto positiveB = center + ellipse->getMinorAxisDir() * ellipse->getMinorRadius(); sg.creation = boost::str(boost::format("Part.Ellipse(App.Vector(%f, %f, %f), App.Vector(%f, " "%f, %f), App.Vector(%f, %f, %f))") % periapsis.x % periapsis.y % periapsis.z % positiveB.x % positiveB.y % positiveB.z % center.x % center.y % center.z); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, {Part::GeomArcOfEllipse::getClassTypeId(), [](const Part::Geometry* geo) { auto aoe = static_cast(geo); SingleGeometry sg; auto center = aoe->getCenter(); auto periapsis = center + aoe->getMajorAxisDir() * aoe->getMajorRadius(); auto positiveB = center + aoe->getMinorAxisDir() * aoe->getMinorRadius(); sg.creation = boost::str( boost::format( "Part.ArcOfEllipse(Part.Ellipse(App.Vector(%f, %f, %f), App.Vector(%f, " "%f, %f), App.Vector(%f, %f, %f)), %f, %f)") % periapsis.x % periapsis.y % periapsis.z % positiveB.x % positiveB.y % positiveB.z % center.x % center.y % center.z % aoe->getFirstParameter() % aoe->getLastParameter()); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, {Part::GeomArcOfHyperbola::getClassTypeId(), [](const Part::Geometry* geo) { auto aoh = static_cast(geo); SingleGeometry sg; auto center = aoh->getCenter(); auto majAxisPoint = center + aoh->getMajorAxisDir() * aoh->getMajorRadius(); auto minAxisPoint = center + aoh->getMinorAxisDir() * aoh->getMinorRadius(); sg.creation = boost::str( boost::format("Part.ArcOfHyperbola(Part.Hyperbola(App.Vector(%f, %f, %f), " "App.Vector(%f, %f, %f), App.Vector(%f, %f, %f)), %f, %f)") % majAxisPoint.x % majAxisPoint.y % majAxisPoint.z % minAxisPoint.x % minAxisPoint.y % minAxisPoint.z % center.x % center.y % center.z % aoh->getFirstParameter() % aoh->getLastParameter()); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, {Part::GeomArcOfParabola::getClassTypeId(), [](const Part::Geometry* geo) { auto aop = static_cast(geo); SingleGeometry sg; auto focus = aop->getFocus(); auto axisPoint = aop->getCenter(); sg.creation = boost::str( boost::format("Part.ArcOfParabola(Part.Parabola(App.Vector(%f, %f, %f), " "App.Vector(%f, %f, %f), App.Vector(0, 0, 1)), %f, %f)") % focus.x % focus.y % focus.z % axisPoint.x % axisPoint.y % axisPoint.z % aop->getFirstParameter() % aop->getLastParameter()); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, {Part::GeomBSplineCurve::getClassTypeId(), [](const Part::Geometry* geo) { auto bSpline = static_cast(geo); std::stringstream stream; std::vector poles = bSpline->getPoles(); for (auto& pole : poles) { stream << "App.Vector(" << pole.x << "," << pole.y << "),"; } std::string controlpoints = stream.str(); // remove last comma and add brackets int index = controlpoints.rfind(','); controlpoints.resize(index); controlpoints.insert(0, 1, '['); controlpoints.append(1, ']'); SingleGeometry sg; sg.creation = boost::str( boost::format("Part.BSplineCurve (%s, None, None, %s, %d, None, False)") % controlpoints.c_str() % (bSpline->isPeriodic() ? "True" : "False") % bSpline->getDegree()); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, {Part::GeomCircle::getClassTypeId(), [](const Part::Geometry* geo) { auto circle = static_cast(geo); SingleGeometry sg; sg.creation = boost::str( boost::format( "Part.Circle(App.Vector(%f, %f, %f), App.Vector(%f, %f, %f), %f)") % circle->getCenter().x % circle->getCenter().y % circle->getCenter().z % circle->getAxisDirection().x % circle->getAxisDirection().y % circle->getAxisDirection().z % circle->getRadius()); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, }; auto result = converterMap.find(geo->getTypeId()); if (result == converterMap.end()) { THROWM(Base::ValueError, "PythonConverter: Geometry Type not supported") } auto creator = result->second; return creator(geo); } std::string PythonConverter::process(const Sketcher::Constraint* constraint, GeoIdMode geoIdMode) { bool addLastIdVar = geoIdMode == GeoIdMode::AddLastGeoIdToGeoIds; bool addLastIdVar1 = constraint->First >= 0 && addLastIdVar; bool addLastIdVar2 = constraint->Second >= 0 && addLastIdVar; bool addLastIdVar3 = constraint->Third >= 0 && addLastIdVar; std::string geoId1 = (addLastIdVar1 ? "lastGeoId + " : "") + std::to_string(constraint->First); std::string geoId2 = (addLastIdVar2 ? "lastGeoId + " : "") + std::to_string(constraint->Second); std::string geoId3 = (addLastIdVar3 ? "lastGeoId + " : "") + std::to_string(constraint->Third); static std::map< const Sketcher::ConstraintType, std::function< std::string(const Sketcher::Constraint*, std::string&, std::string&, std::string&)>> converterMap = { {Sketcher::Coincident, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { return boost::str( boost::format("Sketcher.Constraint('Coincident', %s, %i, %s, %i)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); }}, {Sketcher::Horizontal, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { return boost::str(boost::format("Sketcher.Constraint('Horizontal', %s)") % geoId1); } else { return boost::str( boost::format("Sketcher.Constraint('Horizontal', %s, %i, %s, %i)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); } }}, {Sketcher::Vertical, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { return boost::str(boost::format("Sketcher.Constraint('Vertical', %s)") % geoId1); } else { return boost::str( boost::format("Sketcher.Constraint('Vertical', %s, %i, %s, %i)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); } }}, {Sketcher::Block, []([[maybe_unused]] const Sketcher::Constraint* constr, std::string& geoId1, [[maybe_unused]] std::string& geoId2, [[maybe_unused]] std::string& geoId3) { return boost::str(boost::format("Sketcher.Constraint('Block', %s)") % geoId1); }}, {Sketcher::Tangent, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { if (constr->FirstPos == Sketcher::PointPos::none) { return boost::str(boost::format("Sketcher.Constraint('Tangent', %s, %s)") % geoId1 % geoId2); } else if (constr->SecondPos == Sketcher::PointPos::none) { return boost::str(boost::format("Sketcher.Constraint('Tangent', %s, %i, %s)") % geoId1 % static_cast(constr->FirstPos) % geoId2); } else { return boost::str( boost::format("Sketcher.Constraint('Tangent', %s, %i, %s, %i)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); } }}, {Sketcher::Parallel, []([[maybe_unused]] const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { return boost::str(boost::format("Sketcher.Constraint('Parallel', %s, %s)") % geoId1 % geoId2); }}, {Sketcher::Perpendicular, []([[maybe_unused]] const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { if (constr->FirstPos == Sketcher::PointPos::none) { return boost::str(boost::format("Sketcher.Constraint('Perpendicular', %s, %s)") % geoId1 % geoId2); } else if (constr->SecondPos == Sketcher::PointPos::none) { return boost::str( boost::format("Sketcher.Constraint('Perpendicular', %s, %i, %s)") % geoId1 % static_cast(constr->FirstPos) % geoId2); } else { return boost::str( boost::format("Sketcher.Constraint('Perpendicular', %s, %i, %s, %i)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); } }}, {Sketcher::Equal, []([[maybe_unused]] const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { return boost::str(boost::format("Sketcher.Constraint('Equal', %s, %s)") % geoId1 % geoId2); }}, {Sketcher::InternalAlignment, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { if (constr->AlignmentType == EllipseMajorDiameter || constr->AlignmentType == EllipseMinorDiameter || constr->AlignmentType == HyperbolaMajor || constr->AlignmentType == HyperbolaMinor || constr->AlignmentType == ParabolaFocalAxis) { return boost::str( boost::format("Sketcher.Constraint('InternalAlignment:%s', %s, %s)") % constr->internalAlignmentTypeToString() % geoId1 % geoId2); } else if (constr->AlignmentType == EllipseFocus1 || constr->AlignmentType == EllipseFocus2 || constr->AlignmentType == HyperbolaFocus || constr->AlignmentType == ParabolaFocus) { return boost::str( boost::format("Sketcher.Constraint('InternalAlignment:%s', %s, %i, %s)") % constr->internalAlignmentTypeToString() % geoId1 % static_cast(constr->FirstPos) % geoId2); } else if (constr->AlignmentType == BSplineControlPoint) { return boost::str( boost::format( "Sketcher.Constraint('InternalAlignment:%s', %s, %i, %s, %i)") % constr->internalAlignmentTypeToString() % geoId1 % static_cast(constr->FirstPos) % geoId2 % constr->InternalAlignmentIndex); } else if (constr->AlignmentType == BSplineKnotPoint) { return boost::str( boost::format("Sketcher.Constraint('InternalAlignment:%s', %s, 1, %s, %i)") % constr->internalAlignmentTypeToString() % geoId1 % geoId2 % constr->InternalAlignmentIndex); } THROWM(Base::ValueError, "PythonConverter: Constraint Alignment Type not supported") }}, {Sketcher::Distance, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { return boost::str(boost::format("Sketcher.Constraint('Distance', %s, %f)") % geoId1 % constr->getValue()); } else if (constr->FirstPos == Sketcher::PointPos::none) { return boost::str(boost::format("Sketcher.Constraint('Distance', %s, %s, %f)") % geoId1 % geoId2 % constr->getValue()); } else if (constr->SecondPos == Sketcher::PointPos::none) { return boost::str( boost::format("Sketcher.Constraint('Distance', %s, %i, %s, %f)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % constr->getValue()); } else { return boost::str( boost::format("Sketcher.Constraint('Distance', %s, %i, %s, %i, %f)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % constr->getValue()); } }}, {Sketcher::Angle, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { return boost::str(boost::format("Sketcher.Constraint('Angle', %s, %f)") % geoId1 % constr->getValue()); } else if (constr->Third == GeoEnum::GeoUndef) { if (constr->SecondPos == Sketcher::PointPos::none) { return boost::str(boost::format("Sketcher.Constraint('Angle', %s, %s, %f)") % geoId1 % geoId2 % constr->getValue()); } else { return boost::str( boost::format("Sketcher.Constraint('Angle', %s, %i, %s, %i, %f)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % constr->getValue()); } } else { return boost::str( boost::format("Sketcher.Constraint('AngleViaPoint', %s, %s, %s, %i, %f)") % geoId1 % geoId2 % geoId3 % static_cast(constr->ThirdPos) % constr->getValue()); } }}, {Sketcher::DistanceX, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { return boost::str(boost::format("Sketcher.Constraint('DistanceX', %s, %f)") % geoId1 % constr->getValue()); } else if (constr->SecondPos == Sketcher::PointPos::none) { return boost::str(boost::format("Sketcher.Constraint('DistanceX', %s, %i, %f)") % geoId1 % static_cast(constr->FirstPos) % constr->getValue()); } else { return boost::str( boost::format("Sketcher.Constraint('DistanceX', %s, %i, %s, %i, %f)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % constr->getValue()); } }}, {Sketcher::DistanceY, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { return boost::str(boost::format("Sketcher.Constraint('DistanceY', %s, %f)") % geoId1 % constr->getValue()); } else if (constr->SecondPos == Sketcher::PointPos::none) { return boost::str(boost::format("Sketcher.Constraint('DistanceY', %s, %i, %f)") % geoId1 % static_cast(constr->FirstPos) % constr->getValue()); } else { return boost::str( boost::format("Sketcher.Constraint('DistanceY', %s, %i, %s, %i, %f)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % constr->getValue()); } }}, {Sketcher::Radius, [](const Sketcher::Constraint* constr, std::string& geoId1, [[maybe_unused]] std::string& geoId2, [[maybe_unused]] std::string& geoId3) { return boost::str(boost::format("Sketcher.Constraint('Radius', %s, %f)") % geoId1 % constr->getValue()); }}, {Sketcher::Diameter, [](const Sketcher::Constraint* constr, std::string& geoId1, [[maybe_unused]] std::string& geoId2, [[maybe_unused]] std::string& geoId3) { return boost::str(boost::format("Sketcher.Constraint('Diameter', %s, %f)") % geoId1 % constr->getValue()); }}, {Sketcher::Weight, [](const Sketcher::Constraint* constr, std::string& geoId1, [[maybe_unused]] std::string& geoId2, [[maybe_unused]] std::string& geoId3) { return boost::str(boost::format("Sketcher.Constraint('Weight', %s, %f)") % geoId1 % constr->getValue()); }}, {Sketcher::PointOnObject, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, [[maybe_unused]] std::string& geoId3) { return boost::str(boost::format("Sketcher.Constraint('PointOnObject', %s, %i, %s)") % geoId1 % static_cast(constr->FirstPos) % geoId2); }}, {Sketcher::Symmetric, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, std::string& geoId3) { if (constr->ThirdPos == Sketcher::PointPos::none) { return boost::str( boost::format("Sketcher.Constraint('Symmetric', %s, %i, %s, %i, %s)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % geoId3); } else { return boost::str( boost::format("Sketcher.Constraint('Symmetric', %s, %i, %s, %i, %s, %i)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % geoId3 % static_cast(constr->ThirdPos)); } }}, {Sketcher::SnellsLaw, [](const Sketcher::Constraint* constr, std::string& geoId1, std::string& geoId2, std::string& geoId3) { return boost::str( boost::format("Sketcher.Constraint('SnellsLaw', %s, %i, %s, %i, %s, %f)") % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % geoId3 % constr->getValue()); }}, }; auto result = converterMap.find(constraint->Type); if (result == converterMap.end()) { THROWM(Base::ValueError, "PythonConverter: Constraint Type not supported") } auto creator = result->second; return creator(constraint, geoId1, geoId2, geoId3); } std::vector PythonConverter::multiLine(std::string&& singlestring) { std::vector tokens; split_regex(tokens, singlestring, boost::regex("(\n)+")); return tokens; }