-// Copyright (C) 2020 CEA/DEN, EDF R&D
+// Copyright (C) 2020-2022 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
#include <ModelAPI_AttributeInteger.h>
#include <ModelAPI_AttributeIntArray.h>
#include <ModelAPI_AttributeRefList.h>
+#include <ModelAPI_AttributeString.h>
#include <ModelAPI_Events.h>
#include <ModelAPI_ResultConstruction.h>
#include <ModelAPI_Tools.h>
#include <ModelAPI_Validator.h>
#include <GeomAlgoAPI_MakeShapeList.h>
+#include <GeomAlgoAPI_MapShapesAndAncestors.h>
#include <GeomAlgoAPI_Offset.h>
#include <GeomAlgoAPI_ShapeTools.h>
#include <GeomAlgoAPI_WireBuilder.h>
+#include <GeomAlgoAPI_Fillet1D.h>
+#include <GeomAlgoAPI_Tools.h>
#include <GeomAPI_BSpline.h>
#include <GeomAPI_Circ.h>
#include <GeomDataAPI_Point2D.h>
#include <GeomDataAPI_Point2DArray.h>
+#include <math.h>
+
static const double tolerance = 1.e-7;
SketchPlugin_Offset::SketchPlugin_Offset()
data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
+ // Always initialize approximation to false by default for backward compatibility
+ AttributeBooleanPtr approxAttr = std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(
+ data()->addAttribute(APPROX_ID(), ModelAPI_AttributeBoolean::typeId()));
+ approxAttr->setValue(false);
+
// store original entities
data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefList::typeId());
// store offset entities
registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
ModelAPI_Session::get()->validators()->
registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
+
+ AttributeStringPtr aJointAttr = std::dynamic_pointer_cast<ModelAPI_AttributeString>
+ (data()->addAttribute(JOINT_ID(), ModelAPI_AttributeString::typeId()));
+ if (!aJointAttr->isInitialized())
+ aJointAttr->setValue(JOINT_KEEP_DISTANCE());
}
void SketchPlugin_Offset::execute()
SketchPlugin_Sketch* aSketch = sketch();
if (!aSketch) return;
+ // 0. Joint type
+ AttributeStringPtr aJointAttr = string(JOINT_ID());
+ std::string aType = JOINT_KEEP_DISTANCE();
+ if (aJointAttr->isInitialized())
+ aType = aJointAttr->value();
+
+ GeomAlgoAPI_OffsetJoint aJoint;
+ if (aType == JOINT_ARCS())
+ aJoint = GeomAlgoAPI_OffsetJoint::Arcs;
+ else if (aType == JOINT_LINES())
+ aJoint = GeomAlgoAPI_OffsetJoint::Lines;
+ else // Default mode
+ aJoint = GeomAlgoAPI_OffsetJoint::KeepDistance;
+
// 1. Sketch plane
std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
if (isUpdateFlushed)
Events_Loop::loop()->setFlushed(anUpdateEvent, false);
+ // Save the current feature of the document, because new features may appear while executing.
+ // In this case, they will become current. But if the number of copies is updated from outside
+ // of sketch (e.g. by parameter change), the history line should not hold in sketch.
+ keepCurrentFeature();
+
// 5. Gather wires and make offset for each wire
ListOfMakeShape anOffsetAlgos;
std::set<FeaturePtr> aProcessedEdgesSet;
}
// 5.d. Make offset for the wire
- std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape(
- new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign));
+ AttributeBooleanPtr anApproxAttr = boolean(APPROX_ID());
+ if (!anApproxAttr->isInitialized())
+ {
+ // It must be initialized at least by SketchPlugin_Offset::initAttributes()
+ return;
+ }
+
+ std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape
+ (new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign, aJoint, anApproxAttr->value()));
- std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList(new GeomAlgoAPI_MakeShapeList);
- aMakeList->appendAlgo(aWireBuilder);
- aMakeList->appendAlgo(anOffsetShape);
- anOffsetAlgos.push_back(aMakeList);
+ if (anOffsetShape->isDone()) {
+ if (aJoint == GeomAlgoAPI_OffsetJoint::Arcs) {
+ // For Arcs joint make fillet at all straight edges intersections
+ // of the wire, resulting from GeomAlgoAPI_Offset algorithm
+ makeFillet(fabs(aValue), aWireBuilder, anOffsetShape, anOffsetAlgos);
+ }
+ else {
+ std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList (new GeomAlgoAPI_MakeShapeList);
+ aMakeList->appendAlgo(aWireBuilder);
+ aMakeList->appendAlgo(anOffsetShape);
+ anOffsetAlgos.push_back(aMakeList);
+ }
+ }
+ else {
+ setError("Offset algorithm failed");
+ }
}
}
// created features in CREATED_ID() to remove them on next execute()
addToSketch(anOffsetAlgos);
+ restoreCurrentFeature();
+
// send events to update the sub-features by the solver
if (isUpdateFlushed)
Events_Loop::loop()->setFlushed(anUpdateEvent, true);
}
else { // non-rational B-spline
aWeightsAttr->setSize((int)aWeights.size());
- std::list<double>::iterator anIt = aWeights.begin();
- for (int anIndex = 0; anIt != aWeights.end(); ++anIt, ++anIndex)
- aWeightsAttr->setValue(anIndex, *anIt);
+ std::list<double>::iterator aWIt = aWeights.begin();
+ for (int anIndex = 0; aWIt != aWeights.end(); ++aWIt, ++anIndex)
+ aWeightsAttr->setValue(anIndex, *aWIt);
}
AttributeDoubleArrayPtr aKnotsAttr =
}
}
}
- // TODO: hilight selection in the viewer
data()->blockSendAttributeUpdated(aWasBlocked);
return true;
thePrevious);
return anAIS;
}
+
+
+void SketchPlugin_Offset::makeFillet
+ (const double theValue,
+ const std::shared_ptr<GeomAlgoAPI_WireBuilder>& theWireBuilder,
+ const std::shared_ptr<GeomAlgoAPI_Offset>& theOffsetShape,
+ ListOfMakeShape& theOffsetAlgos)
+{
+ std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList (new GeomAlgoAPI_MakeShapeList);
+ aMakeList->appendAlgo(theWireBuilder);
+ aMakeList->appendAlgo(theOffsetShape);
+
+ bool isOK = true;
+
+ GeomShapePtr aResWire = theOffsetShape->shape();
+ GeomAlgoAPI_MapShapesAndAncestors aMapVE
+ (aResWire, GeomAPI_Shape::VERTEX, GeomAPI_Shape::EDGE);
+ const MapShapeToShapes& aSubshapes = aMapVE.map();
+
+ // find vertices for fillet
+ std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aFilletVertices;
+ for (MapShapeToShapes::const_iterator anIt = aSubshapes.begin();
+ anIt != aSubshapes.end(); ++anIt) {
+ // vertex should have 2 adjacent edges
+ if (anIt->second.size() != 2)
+ continue;
+
+ // both edges should be linear
+ ListOfShape anEdges;
+ anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end());
+ GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front()));
+ GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back()));
+ if (!anEdge1->isLine() || !anEdge2->isLine())
+ continue;
+
+ // skip vertices, which smoothly connect adjacent edges
+ GeomVertexPtr aSharedVertex(new GeomAPI_Vertex(anIt->first));
+ if (GeomAlgoAPI_ShapeTools::isTangent(anEdge1, anEdge2, aSharedVertex))
+ continue;
+
+ aFilletVertices.insert(anIt->first);
+ }
+
+ if (!aFilletVertices.empty()) {
+ isOK = false; // the wire needs correction
+ ListOfShape aVerticesList (aFilletVertices.begin(), aFilletVertices.end());
+
+ // Fillet1D on all linear edges intersections
+ std::shared_ptr<GeomAlgoAPI_Fillet1D> aFilletBuilder
+ (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList, theValue));
+
+ std::string anError;
+ if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed
+ (aFilletBuilder, getKind(), anError)) {
+ aMakeList->appendAlgo(aFilletBuilder);
+ isOK = true;
+ }
+ else {
+ ListOfShape aFailedVertices = aFilletBuilder->failedVertices();
+ if (aFailedVertices.size() != 0) {
+ // Exclude failed vertices and also vertices, joined
+ // with failed by one edge, and run algorithm once again
+ ListOfShape::iterator itVertices = aFailedVertices.begin();
+ for (; itVertices != aFailedVertices.end(); itVertices++) {
+ GeomShapePtr aFailedVertex = *itVertices;
+ aFilletVertices.erase(aFailedVertex);
+ // remove also neighbour vertices
+ MapShapeToShapes::const_iterator anIt = aSubshapes.find(aFailedVertex);
+ if (anIt != aSubshapes.end()) { // should be always true
+ ListOfShape anEdges;
+ anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end());
+ GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front()));
+ GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back()));
+ GeomVertexPtr V1, V2;
+ anEdge1->vertices(V1, V2);
+ if (V1->isEqual(aFailedVertex)) V1 = V2;
+ aFilletVertices.erase(V1);
+ anEdge2->vertices(V1, V2);
+ if (V1->isEqual(aFailedVertex)) V1 = V2;
+ aFilletVertices.erase(V1);
+ }
+ }
+ if (aFilletVertices.size() == 0) {
+ // there are no suitable vertices for fillet
+ isOK = true;
+ }
+ else {
+ // Fillet1D one more try
+ ListOfShape aVerticesList1 (aFilletVertices.begin(), aFilletVertices.end());
+
+ std::shared_ptr<GeomAlgoAPI_Fillet1D> aFilletBuilder1
+ (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList1, theValue));
+
+ if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed
+ (aFilletBuilder1, getKind(), anError)) {
+ aMakeList->appendAlgo(aFilletBuilder1);
+ isOK = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (isOK)
+ theOffsetAlgos.push_back(aMakeList);
+ else
+ setError("Offset algorithm failed");
+}