]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Fillet creation by point
authordbv <dbv@opencascade.com>
Mon, 2 Nov 2015 12:43:52 +0000 (15:43 +0300)
committerdbv <dbv@opencascade.com>
Mon, 2 Nov 2015 12:44:08 +0000 (15:44 +0300)
src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp
src/SketchPlugin/SketchPlugin_Plugin.cpp
src/SketchPlugin/SketchPlugin_Sketch.cpp
src/SketchPlugin/SketchPlugin_Tools.cpp
src/SketchPlugin/SketchPlugin_Tools.h
src/SketchPlugin/SketchPlugin_Validators.cpp
src/SketchPlugin/SketchPlugin_Validators.h
src/SketchPlugin/plugin-Sketch.xml

index a9f00c9f48b58520803e6a7b11f23c71869d9a60..25d2daf2af090750355b50942a450f4bafe543ed 100644 (file)
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintTangent.h>
 #include <SketchPlugin_ConstraintRadius.h>
+#include <SketchPlugin_Tools.h>
 
 #include <SketcherPrs_Factory.h>
+#include <SketcherPrs_Tools.h>
 
 #include <Config_PropManager.h>
 #include <Events_Loop.h>
@@ -46,7 +48,6 @@ static void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB
                                   std::shared_ptr<GeomAPI_XY>& theTangentA,
                                   std::shared_ptr<GeomAPI_XY>& theTangentB);
 
-
 SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet()
 {
 }
@@ -55,11 +56,10 @@ void SketchPlugin_ConstraintFillet::initAttributes()
 {
   data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
   data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
-  data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
-  data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefList::typeId());
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId());
   data()->addAttribute(PREVIOUS_VALUE, ModelAPI_AttributeDouble::typeId());
   // initialize attribute not applicable for user
-  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
   data()->attribute(PREVIOUS_VALUE)->setInitialized();
   std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(PREVIOUS_VALUE))->setValue(0.0);
 }
@@ -79,32 +79,83 @@ void SketchPlugin_ConstraintFillet::execute()
       aData->attribute(SketchPlugin_Constraint::VALUE()))->value();
   AttributeRefAttrPtr aBaseA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
       aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
-  AttributeRefAttrPtr aBaseB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-      aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
-  if (!aBaseA->isInitialized() || !aBaseB->isInitialized() ||
-      !aBaseA->isObject() || !aBaseB->isObject())
+  if (!aBaseA->isInitialized() || aBaseA->isObject()) {
+    setError("Bad vertex selected");
     return;
+  }
+
   // Check the fillet shapes is not initialized yet
   AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
-      aData->attribute(SketchPlugin_Constraint::ENTITY_C()));
+      aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
   bool needNewObjects = aRefListOfFillet->size() == 0;
 
-  // Obtain features for the base objects
-  FeaturePtr aFeatureA, aFeatureB;
-  aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aBaseA->object());
-  if (aRC) aFeatureA = aRC->document()->feature(aRC);
-  aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aBaseB->object());
-  if (aRC) aFeatureB = aRC->document()->feature(aRC);
-  if (!aFeatureA || !aFeatureB)
+  // Obtain base features
+  AttributePtr anAttrBase = aBaseA->attr();
+  const std::set<AttributePtr>& aRefsList = anAttrBase->owner()->data()->refsToMe();
+  std::set<AttributePtr>::const_iterator aIt;
+  FeaturePtr aCoincident;
+  for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
+    std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
+    FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
+    if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
+      AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+        aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
+      AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+        aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
+      if(anAttrRefA.get() && !anAttrRefA->isObject()) {
+        AttributePtr anAttrA = anAttrRefA->attr();
+        if(anAttrBase == anAttrA) {
+          aCoincident = aConstrFeature;
+          break;
+        }
+      }
+      if(anAttrRefA.get() && !anAttrRefB->isObject()) {
+        AttributePtr anAttrB = anAttrRefB->attr();
+        if(anAttrBase == anAttrB) {
+          aCoincident = aConstrFeature;
+          break;
+        }
+      }
+    }
+  }
+
+  if(!aCoincident.get()) {
+    setError("No coincident edges at selected vertex");
+    return;
+  }
+
+  std::set<FeaturePtr> aCoinsideLines;
+  SketchPlugin_Tools::findCoincidences(aCoincident,
+                                       SketchPlugin_ConstraintCoincidence::ENTITY_A(),
+                                       aCoinsideLines);
+  SketchPlugin_Tools::findCoincidences(aCoincident,
+                                       SketchPlugin_ConstraintCoincidence::ENTITY_B(),
+                                       aCoinsideLines);
+  if(aCoinsideLines.size() != 2) {
+    setError("At selected vertex should be two coincident lines");
+    return;
+  }
+
+  std::set<FeaturePtr>::iterator aLinesIt = aCoinsideLines.begin();
+  FeaturePtr anOldFeatureA = *aLinesIt;
+  if(!anOldFeatureA) {
+    setError("One of the edges is empty");
+    return;
+  }
+  aLinesIt++;
+  FeaturePtr anOldFeatureB = *aLinesIt;
+  if(!anOldFeatureB) {
+    setError("One of the edges is empty");
     return;
+  }
 
   FeaturePtr aNewFeatureA, aNewFeatureB, aNewArc;
   if (needNewObjects) {
     // Create list of objects composing a fillet
     // copy aFeatureA
-    aNewFeatureA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aFeatureA, sketch());
+    aNewFeatureA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureB, sketch());
     // copy aFeatureB
-    aNewFeatureB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aFeatureB, sketch());
+    aNewFeatureB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureB, sketch());
     // create filleting arc (it will be attached to the list later)
     aNewArc = sketch()->addFeature(SketchPlugin_Arc::ID());
   } else {
@@ -118,7 +169,7 @@ void SketchPlugin_ConstraintFillet::execute()
 
   // Calculate arc attributes
   static const int aNbFeatures = 2;
-  FeaturePtr aFeature[aNbFeatures] = {aFeatureA, aFeatureB};
+  FeaturePtr aFeature[aNbFeatures] = {anOldFeatureA, anOldFeatureB};
   FeaturePtr aNewFeature[aNbFeatures] = {aNewFeatureA, aNewFeatureB};
   std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point
   bool isStart[aNbFeatures]; // indicates which point the features share
@@ -194,7 +245,7 @@ void SketchPlugin_ConstraintFillet::execute()
 
   // Calculate fillet arc parameters
   std::shared_ptr<GeomAPI_XY> aCenter, aTangentPntA, aTangentPntB;
-  calculateFilletCenter(aFeatureA, aFeatureB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB);
+  calculateFilletCenter(anOldFeatureA, anOldFeatureB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB);
   // update features
   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
       aNewFeatureA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->setValue(
@@ -260,18 +311,18 @@ void SketchPlugin_ConstraintFillet::execute()
     myProducedFeatures.push_back(aConstraint);
     ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
     // 2. Fillet arc radius
-    aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
-    aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-        aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
-    aRefAttr->setObject(aNewArc->lastResult());
-    std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
-        aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius);
-    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-        aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue(
-        isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]);
-    aConstraint->execute();
-    myProducedFeatures.push_back(aConstraint);
-    ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
+    //aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
+    //aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+    //    aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
+    //aRefAttr->setObject(aNewArc->lastResult());
+    //std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
+    //    aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius);
+    //std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+    //    aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue(
+    //    isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]);
+    //aConstraint->execute();
+    //myProducedFeatures.push_back(aConstraint);
+    //ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
     // 3. Tangency of fillet arc and features
     for (int i = 0; i < aNbFeatures; i++) {
       aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
@@ -311,14 +362,14 @@ void SketchPlugin_ConstraintFillet::execute()
       myProducedFeatures.push_back(aConstraint);
     }
     // make base features auxiliary
-    aFeatureA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
-    aFeatureB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
+    anOldFeatureA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
+    anOldFeatureB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
     myBaseObjects.clear();
-    myBaseObjects.push_back(aFeatureA);
-    myBaseObjects.push_back(aFeatureB);
+    myBaseObjects.push_back(anOldFeatureA);
+    myBaseObjects.push_back(anOldFeatureB);
     // exchange the naming IDs of newly created and old line that become auxiliary
-    sketch()->exchangeIDs(aFeatureA, aNewFeatureA);
-    sketch()->exchangeIDs(aFeatureB, aNewFeatureB);
+    sketch()->exchangeIDs(anOldFeatureA, aNewFeatureA);
+    sketch()->exchangeIDs(anOldFeatureB, aNewFeatureB);
   } else {
     // Update radius value
     int aNbSubs = sketch()->numberOfSubs();
@@ -354,11 +405,10 @@ void SketchPlugin_ConstraintFillet::execute()
 
 void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
 {
-  if (theID == SketchPlugin_Constraint::ENTITY_A() ||
-      theID == SketchPlugin_Constraint::ENTITY_B()) {
+  if (theID == SketchPlugin_Constraint::ENTITY_A()) {
     // clear the list of fillet entities
     AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
-        data()->attribute(SketchPlugin_Constraint::ENTITY_C()));
+        data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
     aRefListOfFillet->clear();
 
     // remove all produced objects and constraints
index cfa5e8a095e35f92ed3c07b14d8d450579e931b6..a40e7772252eb6b964b55ac651ba5626de49487f 100644 (file)
@@ -70,6 +70,8 @@ SketchPlugin_Plugin::SketchPlugin_Plugin()
                               new SketchPlugin_CopyValidator);
   aFactory->registerValidator("SketchPlugin_SolverErrorValidator",
                               new SketchPlugin_SolverErrorValidator);
+  aFactory->registerValidator("SketchPlugin_FilletVertexValidator",
+                              new SketchPlugin_FilletVertexValidator);
 
   // register this plugin
   ModelAPI_Session::get()->registerPlugin(this);
index 21247d13a37b631f049f8327f5cf77e5452d0456..3f6c91b59e77071cb62f56cafe583d2d9e7fa763 100644 (file)
@@ -301,7 +301,7 @@ FeaturePtr SketchPlugin_Sketch::addUniqueNamedCopiedFeature(FeaturePtr theFeatur
   // as a name for the feature, the generated unique name is set
   aNewFeature->data()->setName(aUniqueFeatureName);
   // text expressions could block setValue of some attributes
-  clearExpressions(aNewFeature);
+  SketchPlugin_Tools::clearExpressions(aNewFeature);
 
   return aNewFeature;
 }
index 134e00ddf131da4fd890714b09ff1284caf121c1..297649e623deb03cdd26d518dad8e5788fe2fc3c 100644 (file)
@@ -9,6 +9,10 @@
 #include <GeomDataAPI_Point.h>
 #include <GeomDataAPI_Point2D.h>
 #include <ModelAPI_AttributeDouble.h>
+#include <SketchPlugin_ConstraintCoincidence.h>
+#include <SketcherPrs_Tools.h>
+
+namespace SketchPlugin_Tools {
 
 void clearExpressions(AttributeDoublePtr theAttribute)
 {
@@ -55,3 +59,42 @@ void clearExpressions(FeaturePtr theFeature)
     clearExpressions(*anAttributeIt);
   }
 }
+
+std::shared_ptr<GeomAPI_Pnt2d> getCoincidencePoint(FeaturePtr theStartCoin)
+{
+  std::shared_ptr<GeomAPI_Pnt2d> aPnt = SketcherPrs_Tools::getPoint(theStartCoin.get(), 
+                                                                    SketchPlugin_Constraint::ENTITY_A());
+  if (aPnt.get() == NULL)
+    aPnt = SketcherPrs_Tools::getPoint(theStartCoin.get(), SketchPlugin_Constraint::ENTITY_B());
+  return aPnt;
+}
+
+void findCoincidences(FeaturePtr theStartCoin,
+                      std::string theAttr,
+                      std::set<FeaturePtr>& theList)
+{
+  AttributeRefAttrPtr aPnt = theStartCoin->refattr(theAttr);
+  if (!aPnt) return;
+  FeaturePtr aObj = ModelAPI_Feature::feature(aPnt->object());
+  if (theList.find(aObj) == theList.end()) {
+    std::shared_ptr<GeomAPI_Pnt2d> aOrig = getCoincidencePoint(theStartCoin);
+    if (aOrig.get() == NULL)
+      return;
+    theList.insert(aObj);
+    const std::set<AttributePtr>& aRefsList = aObj->data()->refsToMe();
+    std::set<AttributePtr>::const_iterator aIt;
+    for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
+      std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
+      FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
+      if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { 
+        std::shared_ptr<GeomAPI_Pnt2d> aPnt = getCoincidencePoint(aConstrFeature);
+        if (aPnt.get() && aOrig->isEqual(aPnt)) {
+          findCoincidences(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A(), theList);
+          findCoincidences(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_B(), theList);
+        }
+      }
+    }
+  }
+}
+
+} // namespace SketchPlugin_Tools
index a574db78a03f84cfd693cec81dbc3f49736edeb4..9123cab7a29d450b0cd8550fd411839574f4ef24 100644 (file)
@@ -7,9 +7,27 @@
 #ifndef SKETCHPLUGIN_TOOLS_H_
 #define SKETCHPLUGIN_TOOLS_H_
 
+#include <GeomAPI_Pnt2d.h>
+
 #include <ModelAPI_Feature.h>
 
+namespace SketchPlugin_Tools {
+
 /// Clears text expressions for all attributes of the feature 
 void clearExpressions(FeaturePtr theFeature);
 
+/// \return coincidence point
+/// \param[in] theStartCoin coincidence feature
+std::shared_ptr<GeomAPI_Pnt2d> getCoincidencePoint(FeaturePtr theStartCoin);
+
+/// Finds lines coincident at point
+/// \param[in] theStartCoin coincidence feature
+/// \param[in] theAttr attribute name
+/// \param[out] theList list of lines
+void findCoincidences(FeaturePtr theStartCoin,
+                      std::string theAttr,
+                      std::set<FeaturePtr>& theList);
+
+}; // namespace SketchPlugin_Tools
+
 #endif // SKETCHPLUGIN_TOOLS_H_
\ No newline at end of file
index be53bdcf704862eda784f95a7dc7c9538713fbf0..9c8dd5809a2bc112ab80c535cc17ba3365fec1eb 100755 (executable)
@@ -14,6 +14,7 @@
 #include "SketchPlugin_Line.h"
 #include "SketchPlugin_Point.h"
 #include "SketchPlugin_Sketch.h"
+#include "SketchPlugin_Tools.h"
 
 #include "SketcherPrs_Tools.h"
 
@@ -398,3 +399,62 @@ bool SketchPlugin_SolverErrorValidator::isNotObligatory(std::string theFeature,
   return true;
 }
 
+bool SketchPlugin_FilletVertexValidator::isValid(const AttributePtr& theAttribute,
+                                                 const std::list<std::string>& theArguments,
+                                                 std::string& theError) const
+{
+  if(!theAttribute.get()) {
+    return false;
+  }
+
+  AttributeRefAttrPtr aBase = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
+  if (aBase->isObject()) {
+    return false;
+  }
+
+  AttributePtr anAttrBase = aBase->attr();
+  const std::set<AttributePtr>& aRefsList = anAttrBase->owner()->data()->refsToMe();
+  std::set<AttributePtr>::const_iterator aIt;
+  FeaturePtr aCoincident;
+  for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
+    std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
+    FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
+    if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
+      AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+        aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
+      AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+        aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
+      if(anAttrRefA.get() && !anAttrRefA->isObject()) {
+        AttributePtr anAttrA = anAttrRefA->attr();
+        if(anAttrBase == anAttrA) {
+          aCoincident = aConstrFeature;
+          break;
+        }
+      }
+      if(anAttrRefA.get() && !anAttrRefB->isObject()) {
+        AttributePtr anAttrB = anAttrRefB->attr();
+        if(anAttrBase == anAttrB) {
+          aCoincident = aConstrFeature;
+          break;
+        }
+      }
+    }
+  }
+
+  if(!aCoincident.get()) {
+    return false;
+  }
+
+  std::set<FeaturePtr> aCoinsideLines;
+  SketchPlugin_Tools::findCoincidences(aCoincident,
+                                       SketchPlugin_ConstraintCoincidence::ENTITY_A(),
+                                       aCoinsideLines);
+  SketchPlugin_Tools::findCoincidences(aCoincident,
+                                       SketchPlugin_ConstraintCoincidence::ENTITY_B(),
+                                       aCoinsideLines);
+  if(aCoinsideLines.size() != 2) {
+    return false;
+  }
+
+  return true;
+}
index da003f8b0368dab2b514bc398ee5bbf3a1ee405b..966f33be1bd1533f746c112f85bcec4abd1c5054 100644 (file)
@@ -162,4 +162,22 @@ class SketchPlugin_SolverErrorValidator : public ModelAPI_FeatureValidator
   virtual bool isNotObligatory(std::string theFeature, std::string theAttribute);
 };
 
+/**\class SketchPlugin_FilletVertexValidator
+ * \ingroup Validators
+ * \brief Validator for the point for fillet creation.
+ *
+ * Checks that selected point have exactly two coincident lines.
+ */
+class SketchPlugin_FilletVertexValidator : public ModelAPI_AttributeValidator
+{
+ public:
+  //! returns true if attribute is valid
+  //! \param theAttribute the checked attribute
+  //! \param theArguments arguments of the attribute (not used)
+  //! \param theError error message
+  virtual bool isValid(const AttributePtr& theAttribute,
+                       const std::list<std::string>& theArguments,
+                       std::string& theError) const;
+};
+
 #endif
index 546a445ccc84fdda8a3f14c807a926f15ad11e8f..3891315ef09cb141d9000fe39864c018da3bee43 100644 (file)
         <boolvalue id="Auxiliary" label="Auxiliary" default="false" tooltip="Construction element" obligatory="0"/>
         <validator id="GeomValidators_Different" parameters="ArcCenter,ArcStartPoint,ArcEndPoint"/>
       </feature>
+      <!--  SketchConstraintFillet  -->
+      <feature id="SketchConstraintFillet" title="Fillet" tooltip="Create constraint defining fillet between two objects" icon=":icons/fillet.png">
+        <sketch_shape_selector id="ConstraintEntityA"
+            label="Point" tooltip="Select point for fillet (should be shared by two entities only)" shape_types="vertex">
+          <validator id="SketchPlugin_FilletVertexValidator"/>
+        </sketch_shape_selector>
+        <doublevalue label="Radius" tooltip="Fillet arc radius" id="ConstraintValue" min="0" default="1" use_reset="false">
+          <validator id="GeomValidators_Positive"/>
+        </doublevalue>
+      </feature>
     </group>
       
     <group id="Constraints">
     </group>
     
     <group id="Edit">
-      <!--  SketchConstraintFillet  -->
-      <feature id="SketchConstraintFillet" title="Fillet" tooltip="Create constraint defining fillet between two objects" icon=":icons/fillet.png">
-        <sketch_shape_selector id="ConstraintEntityA" 
-            label="First object" tooltip="Select line or arc" shape_types="edge">
-          <validator id="PartSet_DifferentObjects"/>
-          <validator id="PartSet_CoincidentAttr" parameters="ConstraintEntityB"/>
-        </sketch_shape_selector>
-
-        <sketch_shape_selector id="ConstraintEntityB"
-            label="Second object" tooltip="Select line or arc" shape_types="edge">
-          <validator id="PartSet_DifferentObjects"/>
-          <validator id="PartSet_CoincidentAttr" parameters="ConstraintEntityA"/>
-        </sketch_shape_selector>
-
-        <doublevalue label="Value" tooltip="Fillet radius" id="ConstraintValue" min="0" default="1" use_reset="false">
-          <validator id="GeomValidators_Positive"/>
-        </doublevalue>
-        <validator id="PartSet_FilletSelection"/>
-      </feature>
-      
       <!--  SketchConstraintMirror  -->
       <feature
         id="SketchConstraintMirror"