X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchAPI%2FSketchAPI_BSpline.cpp;fp=src%2FSketchAPI%2FSketchAPI_BSpline.cpp;h=9023d703bf3ccbd65b8551cc8731226b6bebed3c;hb=a4461306d16531c21f019477c95101e370e551de;hp=0000000000000000000000000000000000000000;hpb=52b602cebc1184e09d86608510b0f14aff5d1d4b;p=modules%2Fshaper.git diff --git a/src/SketchAPI/SketchAPI_BSpline.cpp b/src/SketchAPI/SketchAPI_BSpline.cpp new file mode 100644 index 000000000..9023d703b --- /dev/null +++ b/src/SketchAPI/SketchAPI_BSpline.cpp @@ -0,0 +1,509 @@ +// Copyright (C) 2019-2020 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include "SketchAPI_BSpline.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + +SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr & theFeature) + : SketchAPI_SketchEntity(theFeature) +{ + initialize(); +} + +SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr& theFeature, + const std::list& thePoles, + const std::list& theWeights) + : SketchAPI_SketchEntity(theFeature) +{ + if (initialize()) { + setByDegreePolesAndWeights(ModelHighAPI_Integer(-1), thePoles, theWeights); + } +} + +SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr& theFeature, + const int theDegree, + const std::list& thePoles, + const std::list& theWeights, + const std::list& theKnots, + const std::list& theMults) + : SketchAPI_SketchEntity(theFeature) +{ + if (initialize()) { + if (theKnots.empty() || theMults.empty()) + setByDegreePolesAndWeights(theDegree, thePoles, theWeights); + else + setByParameters(theDegree, thePoles, theWeights, theKnots, theMults); + } +} + +SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr& theFeature, + const ModelHighAPI_Selection& theExternal) + : SketchAPI_SketchEntity(theFeature) +{ + if (initialize()) { + setByExternal(theExternal); + } +} + +SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr& theFeature, + const std::string& theExternalName) + : SketchAPI_SketchEntity(theFeature) +{ + if (initialize()) { + setByExternalName(theExternalName); + } +} + +SketchAPI_BSpline::~SketchAPI_BSpline() +{ +} + +void SketchAPI_BSpline::setByDegreePolesAndWeights(const ModelHighAPI_Integer& theDegree, + const std::list& thePoles, + const std::list& theWeights) +{ + std::list aWeights; + if (theWeights.size() <= 1) { + // prepare array of equal weights + aWeights.assign(thePoles.size(), + theWeights.empty() ? ModelHighAPI_Double(1.0) : theWeights.front()); + } + else + aWeights = theWeights; + + ModelHighAPI_Integer aDegree = theDegree; + std::list aKnots; + std::list aMults; + getDefaultParameters(thePoles, aWeights, aDegree, aKnots, aMults); + + setByParameters(aDegree, thePoles, aWeights, aKnots, aMults); +} + +void SketchAPI_BSpline::setByParameters(const ModelHighAPI_Integer& theDegree, + const std::list& thePoles, + const std::list& theWeights, + const std::list& theKnots, + const std::list& theMults) +{ + fillAttribute(theDegree, degree()); + + fillAttribute(thePoles, poles()); + if (theWeights.size() <= 1) { + // prepare array of equal weights + std::list aWeights(thePoles.size(), + theWeights.empty() ? ModelHighAPI_Double(1.0) : theWeights.front()); + fillAttribute(aWeights, weights()); + } + else + fillAttribute(theWeights, weights()); + + fillAttribute(theKnots, knots()); + fillAttribute(theMults, multiplicities()); + + setStartAndEndPoints(); + execute(); +} + +void SketchAPI_BSpline::setStartAndEndPoints() +{ + fillAttribute(poles()->pnt(0), startPoint()); + fillAttribute(poles()->pnt(poles()->size() - 1), endPoint()); +} + +void SketchAPI_BSpline::setByExternal(const ModelHighAPI_Selection & theExternal) +{ + fillAttribute(theExternal, external()); + execute(); +} + +void SketchAPI_BSpline::setByExternalName(const std::string & theExternalName) +{ + fillAttribute(ModelHighAPI_Selection("EDGE", theExternalName), external()); + execute(); +} + +static CompositeFeaturePtr sketchForFeature(FeaturePtr theFeature) +{ + const std::set& aRefs = theFeature->data()->refsToMe(); + for (std::set::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) + if ((*anIt)->id() == SketchPlugin_Sketch::FEATURES_ID()) + return std::dynamic_pointer_cast((*anIt)->owner()); + return CompositeFeaturePtr(); +} + +static void createInternalConstraint(const CompositeFeaturePtr& theSketch, + const AttributePoint2DPtr& thePoint, + const AttributePoint2DArrayPtr& thePoles, + const int thePoleIndex) +{ + FeaturePtr aConstraint = theSketch->addFeature(SketchPlugin_ConstraintCoincidenceInternal::ID()); + aConstraint->refattr(SketchPlugin_Constraint::ENTITY_A())->setAttr(thePoint); + aConstraint->refattr(SketchPlugin_Constraint::ENTITY_B())->setAttr(thePoles); + aConstraint->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B()) + ->setValue(thePoleIndex); + aConstraint->execute(); +} + +static void createPole(const CompositeFeaturePtr& theSketch, + const FeaturePtr& theBSpline, + const AttributePoint2DArrayPtr& thePoles, + const int thePoleIndex, + const bool theAuxiliary, + std::list& theEntities) +{ + GeomPnt2dPtr aPole = thePoles->pnt(thePoleIndex); + + FeaturePtr aPointFeature = theSketch->addFeature(SketchPlugin_Point::ID()); + AttributePoint2DPtr aCoord = std::dynamic_pointer_cast( + aPointFeature->attribute(SketchPlugin_Point::COORD_ID())); + aCoord->setValue(aPole); + aPointFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline); + aPointFeature->execute(); + + std::ostringstream aName; + aName << theBSpline->name() << "_" << thePoles->id() << "_" << thePoleIndex; + aPointFeature->data()->setName(aName.str()); + aPointFeature->lastResult()->data()->setName(aName.str()); + + aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(theAuxiliary); + + createInternalConstraint(theSketch, aCoord, thePoles, thePoleIndex); + + theEntities.push_back(aPointFeature); +} + +static void createSegment(const CompositeFeaturePtr& theSketch, + const FeaturePtr& theBSpline, + const AttributePoint2DArrayPtr& thePoles, + const int theStartPoleIndex, + const bool theAuxiliary, + std::list& theEntities) +{ + GeomPnt2dPtr aStartPoint = thePoles->pnt(theStartPoleIndex); + GeomPnt2dPtr aEndPoint = thePoles->pnt(theStartPoleIndex + 1); + + FeaturePtr aLineFeature = theSketch->addFeature(SketchPlugin_Line::ID()); + AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast( + aLineFeature->attribute(SketchPlugin_Line::START_ID())); + aLineStart->setValue(aStartPoint); + AttributePoint2DPtr aLineEnd = std::dynamic_pointer_cast( + aLineFeature->attribute(SketchPlugin_Line::END_ID())); + aLineEnd->setValue(aEndPoint); + aLineFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline); + aLineFeature->execute(); + + std::ostringstream aName; + aName << theBSpline->name() << "_segment_" << theStartPoleIndex << "_" << theStartPoleIndex + 1; + aLineFeature->data()->setName(aName.str()); + aLineFeature->lastResult()->data()->setName(aName.str()); + + aLineFeature->boolean(SketchPlugin_Line::AUXILIARY_ID())->setValue(theAuxiliary); + + createInternalConstraint(theSketch, aLineStart, thePoles, theStartPoleIndex); + createInternalConstraint(theSketch, aLineEnd, thePoles, theStartPoleIndex + 1); + + theEntities.push_back(aLineFeature); +} + +static void toMapOfAuxIndices(const std::list& theRegular, + const std::list& theAuxiliary, + std::map& theIndices) +{ + for (auto it = theRegular.begin(); it != theRegular.end(); ++it) + theIndices[*it] = false; + for (auto it = theAuxiliary.begin(); it != theAuxiliary.end(); ++it) + theIndices[*it] = true; +} + +std::list > SketchAPI_BSpline::controlPoles( + const std::list& regular, + const std::list& auxiliary) const +{ + std::map anAux; + toMapOfAuxIndices(regular, auxiliary, anAux); + + std::list anEntities; + + FeaturePtr aBSpline = feature(); + CompositeFeaturePtr aSketch = sketchForFeature(aBSpline); + AttributePoint2DArrayPtr aPoles = poles(); + + for (auto it = anAux.begin(); it != anAux.end(); ++it) + createPole(aSketch, aBSpline, aPoles, it->first, it->second, anEntities); + + return SketchAPI_SketchEntity::wrap(anEntities); +} + +std::list > SketchAPI_BSpline::controlPolygon( + const std::list& regular, + const std::list& auxiliary) const +{ + std::map anAux; + toMapOfAuxIndices(regular, auxiliary, anAux); + + std::list anEntities; + + FeaturePtr aBSpline = feature(); + CompositeFeaturePtr aSketch = sketchForFeature(aBSpline); + AttributePoint2DArrayPtr aPoles = poles(); + + for (auto it = anAux.begin(); it != anAux.end(); ++it) + createSegment(aSketch, aBSpline, aPoles, it->first, it->second, anEntities); + + return SketchAPI_SketchEntity::wrap(anEntities); +} + + +void SketchAPI_BSpline::getDefaultParameters( + const std::list >& thePoles, + const std::list& theWeights, + ModelHighAPI_Integer& theDegree, + std::list& theKnots, + std::list& theMults) const +{ + std::shared_ptr aBSplineCurve; + try { + std::list aWeights; + for (std::list::const_iterator it = theWeights.begin(); + it != theWeights.end(); ++it) + aWeights.push_back(it->value()); + + if (theDegree.intValue() < 0) + aBSplineCurve.reset(new GeomAPI_BSpline2d(thePoles, aWeights)); + else + aBSplineCurve.reset(new GeomAPI_BSpline2d(theDegree.intValue(), thePoles, aWeights)); + } + catch (...) { + // cannot build a B-spline curve + return; + } + + theDegree = aBSplineCurve->degree(); + std::list aKnots = aBSplineCurve->knots(); + std::list aMults = aBSplineCurve->mults(); + theKnots.assign(aKnots.begin(), aKnots.end()); + theMults.assign(aMults.begin(), aMults.end()); +} + +void SketchAPI_BSpline::checkDefaultParameters(bool& isDefaultDegree, + bool& isDefaultWeights, + bool& isDefaultKnotsMults) const +{ + static const double TOLERANCE = 1.e-7; + + AttributePoint2DArrayPtr aPolesAttr = poles(); + AttributeDoubleArrayPtr aWeightsAttr = weights(); + AttributeDoubleArrayPtr aKnotsAttr = knots(); + AttributeIntArrayPtr aMultsAttr = multiplicities(); + + std::list aPoles; + std::list aWeights; + isDefaultWeights = true; + for (int anIndex = 0; anIndex < aPolesAttr->size(); ++anIndex) { + aPoles.push_back(aPolesAttr->pnt(anIndex)); + double aCurWeight = aWeightsAttr->value(anIndex); + isDefaultWeights = isDefaultWeights && fabs(aCurWeight - 1.0) < TOLERANCE; + aWeights.push_back(aCurWeight); + } + + ModelHighAPI_Integer aDegree(-1); + std::list aKnots; + std::list aMults; + getDefaultParameters(aPoles, aWeights, aDegree, aKnots, aMults); + isDefaultDegree = aDegree.intValue() == degree()->value(); + if (!isDefaultDegree) { + // recalculate knots and multiplicities with the actual degree + aDegree = degree()->value(); + getDefaultParameters(aPoles, aWeights, aDegree, aKnots, aMults); + } + + isDefaultKnotsMults = aKnotsAttr->size() == (int)aKnots.size() + && aMultsAttr->size() == (int)aMults.size(); + if (isDefaultKnotsMults) { + std::list::iterator anIt = aKnots.begin(); + for (int anIndex = 0; isDefaultKnotsMults && anIt != aKnots.end(); ++anIt, ++anIndex) + isDefaultKnotsMults = fabs(anIt->value() - aKnotsAttr->value(anIndex)) < TOLERANCE; + } + if (isDefaultKnotsMults) { + std::list::iterator anIt = aMults.begin(); + for (int anIndex = 0; isDefaultKnotsMults && anIt != aMults.end(); ++anIt, ++anIndex) + isDefaultKnotsMults = anIt->intValue() == aMultsAttr->value(anIndex); + } + + isDefaultDegree = isDefaultDegree && isDefaultKnotsMults; + isDefaultWeights = isDefaultWeights && isDefaultKnotsMults; +} + + +static void bsplineAuxiliaryFeature(const AttributeRefAttrPtr& theReference, + FeaturePtr& thePoint, + FeaturePtr& theSegment) +{ + ObjectPtr anAuxObject; + if (theReference->isObject()) + anAuxObject = theReference->object(); + else + anAuxObject = theReference->attr()->owner(); + + FeaturePtr anAuxFeature = ModelAPI_Feature::feature(anAuxObject); + if (anAuxFeature->getKind() == SketchPlugin_Point::ID()) + thePoint = anAuxFeature; + else if (anAuxFeature->getKind() == SketchPlugin_Line::ID() && + theReference->attr()->id() == SketchPlugin_Line::START_ID()) { + // process only coincidence with start point + theSegment = anAuxFeature; + } +} + +static void collectAuxiliaryFeatures(FeaturePtr theBSpline, + std::map& thePoints, + std::map& theSegments) +{ + const std::set& aRefs = theBSpline->data()->refsToMe(); + for (std::set::const_iterator aRefIt = aRefs.begin(); + aRefIt != aRefs.end(); ++aRefIt) { + FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner()); + if (anOwner->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) { + // process internal constraints only + AttributeRefAttrPtr aRefAttrA = anOwner->refattr(SketchPlugin_Constraint::ENTITY_A()); + AttributeRefAttrPtr aRefAttrB = anOwner->refattr(SketchPlugin_Constraint::ENTITY_B()); + AttributePtr anAttrA = aRefAttrA->attr(); + AttributePtr anAttrB = aRefAttrB->attr(); + + AttributeIntegerPtr aPoleIndex; + FeaturePtr aPoint, aLine; + if (anAttrA && anAttrA->attributeType() == GeomDataAPI_Point2DArray::typeId()) { + aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A()); + bsplineAuxiliaryFeature(aRefAttrB, aPoint, aLine); + } + else if (anAttrB && anAttrB->attributeType() == GeomDataAPI_Point2DArray::typeId()) { + aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B()); + bsplineAuxiliaryFeature(aRefAttrA, aPoint, aLine); + } + + if (aPoint) + thePoints[aPoleIndex->value()] = aPoint; + else if (aLine) + theSegments[aPoleIndex->value()] = aLine; + } + } +} + +void SketchAPI_BSpline::dump(ModelHighAPI_Dumper& theDumper) const +{ + if (isCopy()) + return; // no need to dump copied feature + + FeaturePtr aBase = feature(); + const std::string& aSketchName = theDumper.parentName(aBase); + + AttributeSelectionPtr anExternal = aBase->selection(SketchPlugin_SketchEntity::EXTERNAL_ID()); + if (anExternal->context()) { + // B-spline is external + theDumper << aBase << " = " << aSketchName << ".addSpline(" << anExternal << ")" << std::endl; + } else { + // check if some B-spline parameters are default and should not be dumped + bool isDefaultDegree, isDefaultWeights, isDefaultKnotsMults; + checkDefaultParameters(isDefaultDegree, isDefaultWeights, isDefaultKnotsMults); + + theDumper << aBase << " = " << aSketchName << ".addSpline("; + if (!isDefaultDegree) + theDumper << degree() << ", "; + theDumper << poles(); + if (!isDefaultWeights) + theDumper << ", " << weights(); + if (!isDefaultKnotsMults) + theDumper << ", " << knots() << ", " << multiplicities(); + theDumper << ")" << std::endl; + } + // dump "auxiliary" flag if necessary + SketchAPI_SketchEntity::dump(theDumper); + + // dump control polygon + std::map anAuxPoles, anAuxSegments; + collectAuxiliaryFeatures(aBase, anAuxPoles, anAuxSegments); + + if (!anAuxPoles.empty()) + dumpControlPolygon(theDumper, aBase, "controlPoles", anAuxPoles); + if (!anAuxSegments.empty()) + dumpControlPolygon(theDumper, aBase, "controlPolygon", anAuxSegments); +} + +static void dumpList(ModelHighAPI_Dumper& theDumper, + const std::string& theAttrName, + const std::set& theIndices) +{ + theDumper << theAttrName << " = ["; + std::set::const_iterator it = theIndices.begin(); + theDumper << *it; + for (++it; it != theIndices.end(); ++it) + theDumper << ", " << *it; + theDumper << "]"; +} + +void SketchAPI_BSpline::dumpControlPolygon( + ModelHighAPI_Dumper& theDumper, + const FeaturePtr& theBSpline, + const std::string& theMethod, + const std::map& theAuxFeatures) const +{ + theDumper << "["; + bool isFirst = true; + // dump features and split them to auxiliary and regular + std::set aRegular, anAuxiliary; + for (std::map::const_iterator it = theAuxFeatures.begin(); + it != theAuxFeatures.end(); ++it) { + if (!isFirst) + theDumper << ", "; + theDumper << theDumper.name(it->second, false); + theDumper.doNotDumpFeature(it->second); + isFirst = false; + + if (it->second->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) + anAuxiliary.insert(it->first); + else + aRegular.insert(it->first); + } + theDumper << "] = " << theDumper.name(theBSpline) << "." << theMethod << "("; + if (!aRegular.empty()) { + dumpList(theDumper, "regular", aRegular); + if (!anAuxiliary.empty()) + theDumper << ", "; + } + if (!anAuxiliary.empty()) + dumpList(theDumper, "auxiliary", anAuxiliary); + theDumper << ")" << std::endl; +}