Merge branch 'master' into adaptive-work

This commit is contained in:
bryanbendall
2020-12-19 10:50:49 -05:00
committed by GitHub
36 changed files with 2211 additions and 1413 deletions
+2
View File
@@ -1189,6 +1189,8 @@ bool Document::saveAs(void)
escapedstr = Base::Tools::escapeEncodeFilename(escapedstr);
Command::doCommand(Command::Doc,"App.getDocument(\"%s\").saveAs(u\"%s\")"
, DocName, escapedstr.c_str());
// App::Document::saveAs() may modify the passed file name
fi.setFile(QString::fromUtf8(d->_pcDocument->FileName.getValue()));
setModified(false);
getMainWindow()->appendRecentFile(fi.filePath());
}
+5
View File
@@ -1329,6 +1329,11 @@ bool GeomBSplineCurve::isPeriodic() const
return myCurve->IsPeriodic()==Standard_True;
}
bool GeomBSplineCurve::isRational() const
{
return myCurve->IsRational()==Standard_True;
}
bool GeomBSplineCurve::join(const Handle(Geom_BSplineCurve)& spline)
{
GeomConvert_CompCurveToBSplineCurve ccbc(this->myCurve);
+1
View File
@@ -296,6 +296,7 @@ public:
int getMultiplicity(int index) const;
int getDegree() const;
bool isPeriodic() const;
bool isRational() const;
bool join(const Handle(Geom_BSplineCurve)&);
void makeC1Continuous(double, double);
std::list<Geometry*> toBiArcs(double tolerance) const;
+27
View File
@@ -4206,6 +4206,33 @@ bool TopoShape::findPlane(gp_Pln &pln, double tol) const {
}
}
bool TopoShape::isInfinite() const
{
if (_Shape.IsNull())
return false;
try {
// If the shape is empty an exception may be thrown
Bnd_Box bounds;
BRepBndLib::Add(_Shape, bounds);
bounds.SetGap(0.0);
Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
if (Precision::IsInfinite(xMax - xMin))
return true;
if (Precision::IsInfinite(yMax - yMin))
return true;
if (Precision::IsInfinite(zMax - zMin))
return true;
return false;
}
catch (Standard_Failure&) {
return false;
}
}
bool TopoShape::isCoplanar(const TopoShape &other, double tol) const {
if(isNull() || other.isNull())
return false;
+2
View File
@@ -198,6 +198,8 @@ public:
bool isClosed() const;
bool isCoplanar(const TopoShape &other, double tol=-1) const;
bool findPlane(gp_Pln &pln, double tol=-1) const;
/// Returns true if the expansion of the shape is infinite, false otherwise
bool isInfinite() const;
//@}
/** @name Boolean operation*/
+5
View File
@@ -552,6 +552,11 @@ If the shape is an edge it returns True if its vertices are the same.
<UserDocu>isCoplanar(shape,tol=None) -- Checks if this shape is coplanar with the given shape.</UserDocu>
</Documentation>
</Methode>
<Methode Name="isInfinite" Const="true">
<Documentation>
<UserDocu>isInfinite() -- Checks if this shape has an infinite expansion.</UserDocu>
</Documentation>
</Methode>
<Methode Name="findPlane" Const="true">
<Documentation>
<UserDocu>findPlane(tol=None) -- return a plane if the shape is planar</UserDocu>
+11
View File
@@ -1896,6 +1896,17 @@ PyObject* TopoShapePy::isCoplanar(PyObject *args)
}PY_CATCH_OCC
}
PyObject* TopoShapePy::isInfinite(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return nullptr;
PY_TRY {
return Py::new_reference_to(Py::Boolean(getTopoShapePtr()->isInfinite()));
}
PY_CATCH_OCC
}
PyObject* TopoShapePy::findPlane(PyObject *args)
{
double tol = -1;
+16 -15
View File
@@ -547,32 +547,33 @@ Hole::Hole()
readCutDefinitions();
ADD_PROPERTY_TYPE(Threaded, ((long)0), "Hole", App::Prop_None, "Threaded");
ADD_PROPERTY_TYPE(Threaded, (false), "Hole", App::Prop_None, "Threaded");
ADD_PROPERTY_TYPE(ModelActualThread, ((long)0), "Hole", App::Prop_None, "Model actual thread");
ADD_PROPERTY_TYPE(ThreadPitch, ((long)0), "Hole", App::Prop_None, "Thread pitch");
ADD_PROPERTY_TYPE(ThreadAngle, ((long)0), "Hole", App::Prop_None, "Thread angle");
ADD_PROPERTY_TYPE(ThreadCutOffInner, ((long)0), "Hole", App::Prop_None, "Thread CutOff Inner");
ADD_PROPERTY_TYPE(ThreadCutOffOuter, ((long)0), "Hole", App::Prop_None, "Thread CutOff Outer");
ADD_PROPERTY_TYPE(ModelActualThread, (false), "Hole", App::Prop_None, "Model actual thread");
ADD_PROPERTY_TYPE(ThreadPitch, (0.0), "Hole", App::Prop_None, "Thread pitch");
ADD_PROPERTY_TYPE(ThreadAngle, (0.0), "Hole", App::Prop_None, "Thread angle");
ADD_PROPERTY_TYPE(ThreadCutOffInner, (0.0), "Hole", App::Prop_None, "Thread CutOff Inner");
ADD_PROPERTY_TYPE(ThreadCutOffOuter, (0.0), "Hole", App::Prop_None, "Thread CutOff Outer");
ADD_PROPERTY_TYPE(ThreadType, ((long)0), "Hole", App::Prop_None, "Thread type");
ADD_PROPERTY_TYPE(ThreadType, (0L), "Hole", App::Prop_None, "Thread type");
ThreadType.setEnums(ThreadTypeEnums);
ADD_PROPERTY_TYPE(ThreadSize, ((long)0), "Hole", App::Prop_None, "Thread size");
ADD_PROPERTY_TYPE(ThreadSize, (0L), "Hole", App::Prop_None, "Thread size");
ThreadSize.setEnums(ThreadSize_None_Enums);
ADD_PROPERTY_TYPE(ThreadClass, ((long)0), "Hole", App::Prop_None, "Thread class");
ADD_PROPERTY_TYPE(ThreadClass, (0L), "Hole", App::Prop_None, "Thread class");
ThreadClass.setEnums(ThreadClass_None_Enums);
ADD_PROPERTY_TYPE(ThreadFit, ((long)0), "Hole", App::Prop_None, "Thread fit");
ADD_PROPERTY_TYPE(ThreadFit, (0L), "Hole", App::Prop_None, "Thread fit");
ThreadFit.setEnums(ThreadFitEnums);
ADD_PROPERTY_TYPE(Diameter, (6.0), "Hole", App::Prop_None, "Diameter");
ADD_PROPERTY_TYPE(ThreadDirection, ((long)0), "Hole", App::Prop_None, "Thread direction");
ADD_PROPERTY_TYPE(ThreadDirection, (0L), "Hole", App::Prop_None, "Thread direction");
ThreadDirection.setEnums(ThreadDirectionEnums);
ThreadDirection.setReadOnly(true);
ADD_PROPERTY_TYPE(HoleCutType, ((long)0), "Hole", App::Prop_None, "Head cut type");
ADD_PROPERTY_TYPE(HoleCutType, (0L), "Hole", App::Prop_None, "Head cut type");
HoleCutType.setEnums(HoleCutType_None_Enums);
ADD_PROPERTY_TYPE(HoleCutDiameter, (0.0), "Hole", App::Prop_None, "Head cut diameter");
@@ -581,17 +582,17 @@ Hole::Hole()
ADD_PROPERTY_TYPE(HoleCutCountersinkAngle, (90.0), "Hole", App::Prop_None, "Head cut countersink angle");
ADD_PROPERTY_TYPE(DepthType, ((long)0), "Hole", App::Prop_None, "Type");
ADD_PROPERTY_TYPE(DepthType, (0L), "Hole", App::Prop_None, "Type");
DepthType.setEnums(DepthTypeEnums);
ADD_PROPERTY_TYPE(Depth, (25.0), "Hole", App::Prop_None, "Length");
ADD_PROPERTY_TYPE(DrillPoint, ((long)1), "Hole", App::Prop_None, "Drill point type");
ADD_PROPERTY_TYPE(DrillPoint, (1L), "Hole", App::Prop_None, "Drill point type");
DrillPoint.setEnums(DrillPointEnums);
ADD_PROPERTY_TYPE(DrillPointAngle, (118.0), "Hole", App::Prop_None, "Drill point angle");
ADD_PROPERTY_TYPE(Tapered, ((bool)false),"Hole", App::Prop_None, "Tapered");
ADD_PROPERTY_TYPE(Tapered, (false),"Hole", App::Prop_None, "Tapered");
ADD_PROPERTY_TYPE(TaperedAngle, (90.0), "Hole", App::Prop_None, "Tapered angle");
}
File diff suppressed because it is too large Load Diff
@@ -119,6 +119,19 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="settingCoolant">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable to include the default coolant mode in the template.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Coolant Mode</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -192,7 +205,16 @@
<item>
<widget class="QWidget" name="exportButtonBox" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
+22 -19
View File
@@ -35,13 +35,8 @@ __doc__ = "A collection of helper and utility functions for the Path GUI."
def translate(context, text, disambig=None):
return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
LOGLEVEL = False
if LOGLEVEL:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
else:
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())
def updateInputField(obj, prop, widget, onBeforeChange=None):
@@ -53,7 +48,7 @@ def updateInputField(obj, prop, widget, onBeforeChange=None):
If onBeforeChange is specified it is called before a new value is assigned to the property.
Returns True if a new value was assigned, False otherwise (new value is the same as the current).
'''
value = FreeCAD.Units.Quantity(widget.text()).Value
value = widget.property('rawValue')
attr = PathUtil.getProperty(obj, prop)
attrValue = attr.Value if hasattr(attr, 'Value') else attr
@@ -72,10 +67,10 @@ def updateInputField(obj, prop, widget, onBeforeChange=None):
isDiff = True
break
if noExpr:
widget.setReadOnly(False)
widget.setProperty('readonly', False)
widget.setStyleSheet("color: black")
else:
widget.setReadOnly(True)
widget.setProperty('readonly', True)
widget.setStyleSheet("color: gray")
widget.update()
@@ -100,19 +95,26 @@ class QuantitySpinBox:
'''
def __init__(self, widget, obj, prop, onBeforeChange=None):
self.obj = obj
PathLog.track(widget)
self.widget = widget
self.prop = prop
self.onBeforeChange = onBeforeChange
self.attachTo(obj, prop)
attr = PathUtil.getProperty(self.obj, self.prop)
if attr is not None:
if hasattr(attr, 'Value'):
widget.setProperty('unit', attr.getUserPreferred()[2])
widget.setProperty('binding', "%s.%s" % (obj.Name, prop))
self.valid = True
def attachTo(self, obj, prop = None):
'''attachTo(obj, prop=None) ... use an existing editor for the given object and property'''
self.obj = obj
self.prop = prop
if obj and prop:
attr = PathUtil.getProperty(obj, prop)
if attr is not None:
if hasattr(attr, 'Value'):
self.widget.setProperty('unit', attr.getUserPreferred()[2])
self.widget.setProperty('binding', "%s.%s" % (obj.Name, prop))
self.valid = True
else:
PathLog.warning(translate('PathGui', "Cannot find property %s of %s") % (prop, obj.Label))
self.valid = False
else:
PathLog.warning(translate('PathGui', "Cannot find property %s of %s") % (prop, obj.Label))
self.valid = False
def expression(self):
@@ -122,6 +124,7 @@ class QuantitySpinBox:
return ''
def setMinimum(self, quantity):
'''setMinimum(quantity) ... set the minimum'''
if self.valid:
value = quantity.Value if hasattr(quantity, 'Value') else quantity
self.widget.setProperty('setMinimum', value)
+11 -5
View File
@@ -33,12 +33,15 @@ import os
from PySide import QtCore, QtGui
# Qt translation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
#PathLog.trackModule(PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())
class CommandJobCreate:
'''
@@ -87,7 +90,6 @@ class CommandJobTemplateExport:
on Job creation and be available for selection.
'''
def __init__(self):
pass
@@ -111,7 +113,6 @@ class CommandJobTemplateExport:
return job
return None
def IsActive(self):
return self.GetJob() is not None
@@ -163,7 +164,12 @@ class CommandJobTemplateExport:
# setup sheet
setupSheetAttrs = None
if dialog:
setupSheetAttrs = job.Proxy.setupSheet.templateAttributes(dialog.includeSettingToolRapid(), dialog.includeSettingOperationHeights(), dialog.includeSettingOperationDepths(), dialog.includeSettingOpsSettings())
setupSheetAttrs = job.Proxy.setupSheet.templateAttributes(
dialog.includeSettingToolRapid(),
dialog.includeSettingCoolant(),
dialog.includeSettingOperationHeights(),
dialog.includeSettingOperationDepths(),
dialog.includeSettingOpsSettings())
else:
setupSheetAttrs = job.Proxy.setupSheet.templateAttributes(True, True, True)
if setupSheetAttrs:
@@ -174,10 +180,10 @@ class CommandJobTemplateExport:
with open(PathUtil.toUnicode(path), 'w') as fp:
json.dump(encoded, fp, sort_keys=True, indent=2)
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_Job', CommandJobCreate())
FreeCADGui.addCommand('Path_ExportTemplate', CommandJobTemplateExport())
FreeCAD.Console.PrintLog("Loading PathJobCmd... done\n")
+16 -5
View File
@@ -32,12 +32,15 @@ import os
from PySide import QtCore, QtGui
from collections import Counter
# Qt translation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
#PathLog.trackModule(PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())
class _ItemDelegate(QtGui.QStyledItemDelegate):
@@ -148,7 +151,6 @@ class JobCreate:
self.model = QtGui.QStandardItemModel(self.dialog)
self.model.setHorizontalHeaderLabels(['Model', 'Count'])
if self.itemsSolid.hasChildren():
self.model.appendRow(self.itemsSolid)
if expandSolids or not (expand2Ds or expandJobs):
@@ -244,7 +246,7 @@ class JobCreate:
models = []
for i in range(self.itemsSolid.rowCount()):
for j in range(self.itemsSolid.child(i, 1).data(QtCore.Qt.EditRole)): # pylint: disable=unused-variable
for j in range(self.itemsSolid.child(i, 1).data(QtCore.Qt.EditRole)): # pylint: disable=unused-variable
models.append(self.itemsSolid.child(i).data(self.DataObject))
for i in range(self.items2D.rowCount()):
@@ -320,12 +322,14 @@ class JobTemplateExport:
rapidChanged = not job.SetupSheet.Proxy.hasDefaultToolRapids()
depthsChanged = not job.SetupSheet.Proxy.hasDefaultOperationDepths()
heightsChanged = not job.SetupSheet.Proxy.hasDefaultOperationHeights()
coolantChanged = not job.SetupSheet.Proxy.hasDefaultCoolantMode()
opsWithSettings = job.SetupSheet.Proxy.operationsWithSettings()
settingsChanged = rapidChanged or depthsChanged or heightsChanged or 0 != len(opsWithSettings)
settingsChanged = rapidChanged or depthsChanged or heightsChanged or coolantChanged or 0 != len(opsWithSettings)
self.dialog.settingsGroup.setChecked(settingsChanged)
self.dialog.settingToolRapid.setChecked(rapidChanged)
self.dialog.settingOperationDepths.setChecked(depthsChanged)
self.dialog.settingOperationHeights.setChecked(heightsChanged)
self.dialog.settingCoolant.setChecked(coolantChanged)
self.dialog.settingsOpsList.clear()
for op in opsWithSettings:
@@ -358,20 +362,28 @@ class JobTemplateExport:
def includeStock(self):
return self.dialog.stockGroup.isChecked()
def includeStockExtent(self):
return self.dialog.stockExtent.isChecked()
def includeStockPlacement(self):
return self.dialog.stockPlacement.isChecked()
def includeSettings(self):
return self.dialog.settingsGroup.isChecked()
def includeSettingToolRapid(self):
return self.dialog.settingToolRapid.isChecked()
def includeSettingOperationHeights(self):
return self.dialog.settingOperationHeights.isChecked()
def includeSettingOperationDepths(self):
return self.dialog.settingOperationDepths.isChecked()
def includeSettingCoolant(self):
return self.dialog.settingCoolant.isChecked()
def includeSettingOpsSettings(self):
ops = []
for i in range(self.dialog.settingsOpsList.count()):
@@ -382,4 +394,3 @@ class JobTemplateExport:
def exec_(self):
return self.dialog.exec_()
+1 -1
View File
@@ -262,7 +262,7 @@ class ObjectProfile(PathAreaOp.ObjectOp):
return params
def areaOpAreaParamsExpandProfile(self, obj, isHole):
'''areaOpPathParamsExpandProfile(obj, isHole) ... return dictionary with area parameters for expaned profile'''
'''areaOpPathParamsExpandProfile(obj, isHole) ... return dictionary with area parameters for expanded profile'''
params = {}
params['Fill'] = 1
+35 -18
View File
@@ -35,11 +35,13 @@ __doc__ = "A container for all default values and job specific configuration val
_RegisteredOps = {}
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
#PathLog.trackModule(PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())
def translate(context, text, disambig=None):
return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
class Template:
# pylint: disable=no-init
@@ -59,7 +61,7 @@ class Template:
def _traverseTemplateAttributes(attrs, codec):
coded = {}
for key,value in PathUtil.keyValueIter(attrs):
for key, value in PathUtil.keyValueIter(attrs):
if type(value) == dict:
PathLog.debug("%s is a dict" % key)
coded[key] = _traverseTemplateAttributes(value, codec)
@@ -74,6 +76,7 @@ def _traverseTemplateAttributes(attrs, codec):
coded[key] = value
return coded
class SetupSheet:
'''Property container object used by a Job to hold global reference values. '''
@@ -92,20 +95,20 @@ class SetupSheet:
def __init__(self, obj):
self.obj = obj
obj.addProperty('App::PropertySpeed', 'VertRapid', 'ToolController', translate('PathSetupSheet', 'Default speed for horizontal rapid moves.'))
obj.addProperty('App::PropertySpeed', 'VertRapid', 'ToolController', translate('PathSetupSheet', 'Default speed for horizontal rapid moves.'))
obj.addProperty('App::PropertySpeed', 'HorizRapid', 'ToolController', translate('PathSetupSheet', 'Default speed for vertical rapid moves.'))
obj.addProperty('App::PropertyStringList', 'CoolantModes', 'CoolantMode', translate('PathSetupSheet', 'Coolant Modes'))
obj.addProperty('App::PropertyEnumeration', 'CoolantMode', 'CoolantMode', translate('PathSetupSheet', 'Default coolant mode.'))
obj.addProperty('App::PropertyLength', 'SafeHeightOffset', 'OperationHeights', translate('PathSetupSheet', 'The usage of this field depends on SafeHeightExpression - by default its value is added to StartDepth and used for SafeHeight of an operation.'))
obj.addProperty('App::PropertyString', 'SafeHeightExpression', 'OperationHeights', translate('PathSetupSheet', 'Expression set for the SafeHeight of new operations.'))
obj.addProperty('App::PropertyLength', 'ClearanceHeightOffset', 'OperationHeights', translate('PathSetupSheet', 'The usage of this field depends on ClearanceHeightExpression - by default is value is added to StartDepth and used for ClearanceHeight of an operation.'))
obj.addProperty('App::PropertyLength', 'SafeHeightOffset', 'OperationHeights', translate('PathSetupSheet', 'The usage of this field depends on SafeHeightExpression - by default its value is added to StartDepth and used for SafeHeight of an operation.'))
obj.addProperty('App::PropertyString', 'SafeHeightExpression', 'OperationHeights', translate('PathSetupSheet', 'Expression set for the SafeHeight of new operations.'))
obj.addProperty('App::PropertyLength', 'ClearanceHeightOffset', 'OperationHeights', translate('PathSetupSheet', 'The usage of this field depends on ClearanceHeightExpression - by default is value is added to StartDepth and used for ClearanceHeight of an operation.'))
obj.addProperty('App::PropertyString', 'ClearanceHeightExpression', 'OperationHeights', translate('PathSetupSheet', 'Expression set for the ClearanceHeight of new operations.'))
obj.addProperty('App::PropertyString', 'StartDepthExpression', 'OperationDepths', translate('PathSetupSheet', 'Expression used for StartDepth of new operations.'))
obj.addProperty('App::PropertyString', 'FinalDepthExpression', 'OperationDepths', translate('PathSetupSheet', 'Expression used for FinalDepth of new operations.'))
obj.addProperty('App::PropertyString', 'StepDownExpression', 'OperationDepths', translate('PathSetupSheet', 'Expression used for StepDown of new operations.'))
obj.addProperty('App::PropertyString', 'StepDownExpression', 'OperationDepths', translate('PathSetupSheet', 'Expression used for StepDown of new operations.'))
obj.SafeHeightOffset = self.decodeAttributeString(self.DefaultSafeHeightOffset)
obj.ClearanceHeightOffset = self.decodeAttributeString(self.DefaultClearanceHeightOffset)
@@ -154,31 +157,38 @@ class SetupSheet:
return False
return True
def hasDefaultCoolantMode(self):
return self.obj.CoolantMode == "None"
def setFromTemplate(self, attrs):
'''setFromTemplate(attrs) ... sets the default values from the given dictionary.'''
for name in Template.All:
if attrs.get(name) is not None:
setattr(self.obj, name, attrs[name])
for opName,op in PathUtil.keyValueIter(_RegisteredOps):
for opName, op in PathUtil.keyValueIter(_RegisteredOps):
opSetting = attrs.get(opName)
if opSetting is not None:
prototype = op.prototype(opName)
for propName in op.properties():
value = opSetting.get(propName)
if not value is None:
if value is not None:
prop = prototype.getProperty(propName)
propertyName = OpPropertyName(opName, propName)
propertyGroup = OpPropertyGroup(opName)
prop.setupProperty(self.obj, propertyName, propertyGroup, prop.valueFromString(value))
def templateAttributes(self, includeRapids=True, includeCoolantMode=True, includeHeights=True, includeDepths=True, includeOps=None):
def templateAttributes(self,
includeRapids=True,
includeCoolantMode=True,
includeHeights=True,
includeDepths=True,
includeOps=None):
'''templateAttributes(includeRapids, includeHeights, includeDepths) ... answers a dictionary with the default values.'''
attrs = {}
if includeRapids:
attrs[Template.VertRapid] = self.obj.VertRapid.UserString
attrs[Template.VertRapid] = self.obj.VertRapid.UserString
attrs[Template.HorizRapid] = self.obj.HorizRapid.UserString
if includeCoolantMode:
@@ -232,6 +242,7 @@ class SetupSheet:
def encodeAttributeString(self, attr):
'''encodeAttributeString(attr) ... return the encoded string of a template attribute.'''
return PathUtil.toUnicode(attr.replace(self.expressionReference(), self.TemplateReference))
def decodeAttributeString(self, attr):
'''decodeAttributeString(attr) ... return the decoded string of a template attribute.'''
return PathUtil.toUnicode(attr.replace(self.TemplateReference, self.expressionReference()))
@@ -247,7 +258,7 @@ class SetupSheet:
def operationsWithSettings(self):
'''operationsWithSettings() ... returns a list of operations which currently have some settings defined.'''
ops = []
for name,value in PathUtil.keyValueIter(_RegisteredOps):
for name, value in PathUtil.keyValueIter(_RegisteredOps):
for prop in value.registeredPropertyNames(name):
if hasattr(self.obj, prop):
ops.append(name)
@@ -262,9 +273,9 @@ class SetupSheet:
propName = OpPropertyName(opName, prop)
if hasattr(self.obj, propName):
setattr(obj, prop, getattr(self.obj, propName))
except Exception: # pylint: disable=broad-except
except Exception:
PathLog.info("SetupSheet has no support for {}".format(opName))
#traceback.print_exc()
# traceback.print_exc()
def onDocumentRestored(self, obj):
@@ -272,16 +283,17 @@ class SetupSheet:
obj.addProperty('App::PropertyStringList', 'CoolantModes', 'CoolantMode', translate('PathSetupSheet', 'Coolant Modes'))
obj.CoolantModes = self.DefaultCoolantModes
if not hasattr(obj, 'CoolantMode'):
obj.addProperty('App::PropertyEnumeration', 'CoolantMode', 'CoolantMode', translate('PathSetupSheet', 'Default coolant mode.'))
obj.CoolantMode = self.DefaultCoolantModes
def Create(name = 'SetupSheet'):
def Create(name='SetupSheet'):
obj = FreeCAD.ActiveDocument.addObject('App::FeaturePython', name)
obj.Proxy = SetupSheet(obj)
return obj
class _RegisteredOp(object):
def __init__(self, factory, properties):
@@ -296,14 +308,19 @@ class _RegisteredOp(object):
self.factory("OpPrototype.%s" % name, ptt)
return ptt
def RegisterOperation(name, objFactory, setupProperties):
global _RegisteredOps # pylint: disable=global-statement
global _RegisteredOps
_RegisteredOps[name] = _RegisteredOp(objFactory, setupProperties)
def OpNamePrefix(name):
return name.replace('Path', '').replace(' ', '').replace('_', '')
def OpPropertyName(opName, propName):
return "{}{}".format(OpNamePrefix(opName), propName)
def OpPropertyGroup(opName):
return "Op {}".format(opName)
+13 -7
View File
@@ -200,7 +200,9 @@ class ToolBit(object):
return [prop for prop in obj.PropertiesList if obj.getGroupOfProperty(prop) == PropertyGroupAttribute]
def onDocumentRestored(self, obj):
obj.setEditorMode('BitShape', 1)
# when files are shared it is essential to be able to change/set the shape file,
# otherwise the file is hard to use
# obj.setEditorMode('BitShape', 1)
obj.setEditorMode('BitBody', 2)
obj.setEditorMode('File', 1)
obj.setEditorMode('Shape', 2)
@@ -252,7 +254,7 @@ class ToolBit(object):
p = findShape(p)
if not path and p != obj.BitShape:
obj.BitShape = p
doc = FreeCAD.open(p)
doc = FreeCAD.openDocument(p, True)
obj.ShapeName = doc.Name
docOpened = True
return (doc, docOpened)
@@ -286,21 +288,22 @@ class ToolBit(object):
self._removeBitBody(obj)
def _setupBitShape(self, obj, path=None):
PathLog.track(obj.Label)
activeDoc = FreeCAD.ActiveDocument
(doc, docOpened) = self._loadBitBody(obj, path)
obj.Label = doc.RootObjects[0].Label
self._deleteBitSetup(obj)
obj.BitBody = obj.Document.copyObject(doc.RootObjects[0], True)
bitBody = obj.Document.copyObject(doc.RootObjects[0], True)
if docOpened:
FreeCAD.setActiveDocument(activeDoc.Name)
FreeCAD.closeDocument(doc.Name)
if obj.BitBody.ViewObject:
obj.BitBody.ViewObject.Visibility = False
self._copyBitShape(obj)
if bitBody.ViewObject:
bitBody.ViewObject.Visibility = False
for sketch in [o for o in obj.BitBody.Group if o.TypeId == 'Sketcher::SketchObject']:
for sketch in [o for o in bitBody.Group if o.TypeId == 'Sketcher::SketchObject']:
for constraint in [c for c in sketch.Constraints if c.Name != '']:
typ = ParameterTypeConstraint.get(constraint.Type)
PathLog.track(constraint, typ)
@@ -316,6 +319,9 @@ class ToolBit(object):
if constraint.Type == 'Angle':
value = value * 180 / math.pi
PathUtil.setProperty(obj, prop, value)
# has to happen last because it could trigger op.execute evaluations
obj.BitBody = bitBody
self._copyBitShape(obj)
def getBitThumbnail(self, obj):
if obj.BitShape:
+57 -18
View File
@@ -62,24 +62,60 @@ class ToolBitEditor(object):
if self.loadbitbody:
self.tool.Proxy.loadBitBody(self.tool)
# remove example widgets
layout = self.form.bitParams.layout()
for i in range(layout.rowCount() - 1, -1, -1):
layout.removeRow(i)
# used to track property widgets and editors
self.widgets = []
self.setupTool(self.tool)
self.setupAttributes(self.tool)
def setupTool(self, tool):
PathLog.track()
# Can't delete and add fields to the form because of dangling references in case of
# a focus change. see https://forum.freecadweb.org/viewtopic.php?f=10&t=52246#p458583
# Instead we keep widgets once created and use them for new properties, and hide all
# which aren't being needed anymore.
def labelText(name):
return re.sub('([A-Z][a-z]+)', r' \1', re.sub('([A-Z]+)', r' \1', name))
layout = self.form.bitParams.layout()
for i in range(layout.rowCount() - 1, -1, -1):
layout.removeRow(i)
editor = {}
ui = FreeCADGui.UiLoader()
nr = 0
# for all properties either assign them to existing labels and editors
# or create additional ones for them if not enough have already been
# created.
for name in tool.PropertiesList:
if tool.getGroupOfProperty(name) == PathToolBit.PropertyGroupBit:
qsb = ui.createWidget('Gui::QuantitySpinBox')
editor[name] = PathGui.QuantitySpinBox(qsb, tool, name)
label = QtGui.QLabel(re.sub('([A-Z][a-z]+)', r' \1',
re.sub('([A-Z]+)', r' \1', name)))
layout.addRow(label, qsb)
self.bitEditor = editor
if nr < len(self.widgets):
PathLog.debug("re-use row: {} [{}]".format(nr, name))
label, qsb, editor = self.widgets[nr]
label.setText(labelText(name))
editor.attachTo(tool, name)
label.show()
qsb.show()
else:
qsb = ui.createWidget('Gui::QuantitySpinBox')
editor = PathGui.QuantitySpinBox(qsb, tool, name)
label = QtGui.QLabel(labelText(name))
self.widgets.append((label, qsb, editor))
PathLog.debug("create row: {} [{}]".format(nr, name))
if nr >= layout.rowCount():
layout.addRow(label, qsb)
nr = nr + 1
# hide all rows which aren't being used
for i in range(nr, len(self.widgets)):
label, qsb, editor = self.widgets[i]
label.hide()
qsb.hide()
editor.attachTo(None)
PathLog.debug(" hide row: {}".format(i))
img = tool.Proxy.getBitThumbnail(tool)
if img:
self.form.image.setPixmap(QtGui.QPixmap(QtGui.QImage.fromData(img)))
@@ -189,25 +225,28 @@ class ToolBitEditor(object):
self.form.toolName.setText(self.tool.Label)
self.form.shapePath.setText(self.tool.BitShape)
for editor in self.bitEditor:
self.bitEditor[editor].updateSpinBox()
for lbl, qsb, editor in self.widgets:
editor.updateSpinBox()
def updateShape(self):
PathLog.track()
self.tool.BitShape = str(self.form.shapePath.text())
self.setupTool(self.tool)
self.form.toolName.setText(self.tool.Label)
shapePath = str(self.form.shapePath.text())
# Only need to go through this exercise if the shape actually changed.
if self.tool.BitShape != shapePath:
self.tool.BitShape = shapePath
self.setupTool(self.tool)
self.form.toolName.setText(self.tool.Label)
for editor in self.bitEditor:
self.bitEditor[editor].updateSpinBox()
for lbl, qsb, editor in self.widgets:
editor.updateSpinBox()
def updateTool(self):
PathLog.track()
self.tool.Label = str(self.form.toolName.text())
self.tool.BitShape = str(self.form.shapePath.text())
for editor in self.bitEditor:
self.bitEditor[editor].updateProperty()
for lbl, qsb, editor in self.widgets:
editor.updateProperty()
# self.tool.Proxy._updateBitShape(self.tool)
+3 -1
View File
@@ -152,7 +152,9 @@ G0 Z0.500000
table.addTools(t2)
self.assertEqual(len(table.Tools), 2)
self.assertEqual(str(table.Tools), '{1: Tool 12.7mm Drill Bit, 2: Tool my other tool}' )
# gcc7 build needs some special treatment (makes 1L out of a 1) ...
if str(table.Tools) != '{1L: Tool 12.7mm Drill Bit, 2L: Tool my other tool}':
self.assertEqual(str(table.Tools), '{1: Tool 12.7mm Drill Bit, 2: Tool my other tool}')
def test50(self):
"""Test Path.Length calculation"""
+2
View File
@@ -33,6 +33,7 @@
#include "SketchObject.h"
#include "SketchGeometryExtension.h"
#include "ExternalGeometryExtension.h"
#include "SolverGeometryExtension.h"
#include "GeometryFacade.h"
#include "ExternalGeometryFacade.h"
#include "Constraint.h"
@@ -80,6 +81,7 @@ PyMOD_INIT_FUNC(Sketcher)
Sketcher::SketchGeometryExtension ::init();
Sketcher::ExternalGeometryExtension ::init();
Sketcher::SolverGeometryExtension ::init();
Sketcher::GeometryFacade ::init();
Sketcher::ExternalGeometryFacade ::init();
Sketcher::SketchObjectSF ::init();
+2
View File
@@ -58,6 +58,8 @@ SET(Features_SRCS
SketchGeometryExtension.h
ExternalGeometryExtension.cpp
ExternalGeometryExtension.h
SolverGeometryExtension.cpp
SolverGeometryExtension.h
SketchObject.cpp
SketchObject.h
SketchAnalysis.h
+1 -1
View File
@@ -83,7 +83,7 @@ class GeometryFacadePy;
// }
//
//
// Note: The standard GeometryFacade stores Part::Geometry derived clases as a Part::Geometry *, while
// Note: The standard GeometryFacade stores Part::Geometry derived classes as a Part::Geometry *, while
// it has the ability to return a dynamic_cast-ed version to a provided type as follows:
//
// HLine->getGeometry<Part::GeomLineSegment>();
+211 -201
View File
@@ -55,10 +55,13 @@
#include <Mod/Part/App/LineSegmentPy.h>
#include <Mod/Part/App/BSplineCurvePy.h>
#include "Sketch.h"
#include "Constraint.h"
#include "GeometryFacade.h"
#include "SolverGeometryExtension.h"
#include "Sketch.h"
using namespace Sketcher;
using namespace Base;
@@ -105,6 +108,9 @@ void Sketch::clear(void)
if (*it) delete *it;
FixParameters.clear();
param2geoelement.clear();
pDependencyGroups.clear();
// deleting the geometry copied into this sketch
for (std::vector<GeoDef>::iterator it = Geoms.begin(); it != Geoms.end(); ++it)
if (it->geo) delete it->geo;
@@ -159,7 +165,7 @@ int Sketch::setUpSketch(const std::vector<Part::Geometry *> &GeoList,
GCSsys.initSolution(defaultSolverRedundant);
GCSsys.getConflicting(Conflicting);
GCSsys.getRedundant(Redundant);
GCSsys.getDependentParams(pconstraintplistOut);
GCSsys.getDependentParams(pDependentParametersList);
calculateDependentParametersElements();
@@ -179,106 +185,114 @@ void Sketch::clearTemporaryConstraints(void)
void Sketch::calculateDependentParametersElements(void)
{
// initialize solve extensions to a know state
for(auto geo : Geoms) {
std::vector<double *> ownparams;
GCS::Curve * pCurve = nullptr;
switch(geo.type) {
case Point:
{
GCS::Point & point = Points[geo.startPointId];
for(auto param : pconstraintplistOut) {
if (param == point.x || param == point.y) {
point.hasDependentParameters = true;
break;
}
}
}
break;
case Line:
{
GCS::Line & line = Lines[geo.index];
line.PushOwnParams(ownparams);
pCurve = &line;
if(!geo.geo->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId()))
geo.geo->setExtension(std::make_unique<Sketcher::SolverGeometryExtension>());
auto solvext = std::static_pointer_cast<Sketcher::SolverGeometryExtension>(
geo.geo->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock());
if(GCSsys.isEmptyDiagnoseMatrix())
solvext->init(SolverGeometryExtension::Dependent);
else
solvext->init(SolverGeometryExtension::Independent);
}
for(auto param : pDependentParametersList) {
//auto element = param2geoelement.at(param);
auto element = param2geoelement.find(param);
if (element != param2geoelement.end()) {
auto solvext = std::static_pointer_cast<Sketcher::SolverGeometryExtension>(
Geoms[element->second.first].geo->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock());
switch(element->second.second) {
case none:
solvext->setEdge(SolverGeometryExtension::Dependent);
break;
case start:
solvext->setStart(SolverGeometryExtension::Dependent);
break;
case end:
solvext->setEnd(SolverGeometryExtension::Dependent);
break;
case mid:
solvext->setMid(SolverGeometryExtension::Dependent);
break;
}
break;
case Arc:
{
GCS::Arc & arc = Arcs[geo.index];
arc.PushOwnParams(ownparams);
pCurve = &arc;
}
break;
case Circle:
{
GCS::Circle & c = Circles[geo.index];
c.PushOwnParams(ownparams);
pCurve = &c;
}
break;
case Ellipse:
{
GCS::Ellipse & e = Ellipses[geo.index];
e.PushOwnParams(ownparams);
pCurve = &e;
}
break;
case ArcOfEllipse:
{
GCS::ArcOfEllipse & aoe = ArcsOfEllipse[geo.index];
aoe.PushOwnParams(ownparams);
pCurve = &aoe;
}
break;
case ArcOfHyperbola:
{
GCS::ArcOfHyperbola & aoh = ArcsOfHyperbola[geo.index];
aoh.PushOwnParams(ownparams);
pCurve = &aoh;
}
break;
case ArcOfParabola:
{
GCS::ArcOfParabola & aop = ArcsOfParabola[geo.index];
aop.PushOwnParams(ownparams);
pCurve = &aop;
}
break;
case BSpline:
{
GCS::BSpline & bsp = BSplines[geo.index];
bsp.PushOwnParams(ownparams);
pCurve = &bsp;
}
break;
case None:
break;
}
// Points (this is single point elements, not vertices of other elements) are not derived from Curve
if(geo.type != Point && geo.type != None) {
for(auto param : pconstraintplistOut) {
for(auto ownparam : ownparams) {
if (param == ownparam) {
pCurve->hasDependentParameters = true;
break;
}
}
}
std::vector < std::set < double*>> groups;
GCSsys.getDependentParamsGroups(groups);
pDependencyGroups.resize(groups.size());
// translate parameters into elements (Geoid, PointPos)
for(size_t i = 0; i < groups.size(); i++) {
for(size_t j = 0; j < groups[i].size(); j++) {
auto element = param2geoelement.find(*std::next(groups[i].begin(), j));
if (element != param2geoelement.end()) {
pDependencyGroups[i].insert(element->second);
}
}
}
// Points are the only element that defines other elements, so these points (as opposed to those points
// above which are just GeomPoints), have to be handled separately.
for(auto & point : Points) {
for(auto param : pconstraintplistOut) {
if (param == point.x || param == point.y) {
point.hasDependentParameters = true;
break;
// check if groups have a common element, if yes merge the groups
auto havecommonelement = [] ( std::set < std::pair< int, Sketcher::PointPos>>::iterator begin1,
std::set < std::pair< int, Sketcher::PointPos>>::iterator end1,
std::set < std::pair< int, Sketcher::PointPos>>::iterator begin2,
std::set < std::pair< int, Sketcher::PointPos>>::iterator end2) {
while (begin1 != end1 && begin2 != end2) {
if (*begin1 < *begin2)
++begin1;
else if (*begin2 < *begin1)
++begin2;
else
return true;
}
return false;
};
if(pDependencyGroups.size() > 1) { // only if there is more than 1 group
size_t endcount = pDependencyGroups.size()-1;
for(size_t i=0; i < endcount; i++) {
if(havecommonelement(pDependencyGroups[i].begin(), pDependencyGroups[i].end(), pDependencyGroups[i+1].begin(), pDependencyGroups[i+1].end())){
pDependencyGroups[i].insert(pDependencyGroups[i+1].begin(), pDependencyGroups[i+1].end());
pDependencyGroups.erase(pDependencyGroups.begin()+i+1);
endcount--;
}
}
}
}
std::set < std::pair< int, Sketcher::PointPos>> Sketch::getDependencyGroup(int geoId, PointPos pos) const
{
geoId = checkGeoId(geoId);
std::set < std::pair< int, Sketcher::PointPos>> group;
auto key = std::make_pair(geoId, pos);
for( auto & set : pDependencyGroups) {
if (set.find(key) != set.end()) {
group = set;
break;
}
}
return group;
}
int Sketch::resetSolver()
{
clearTemporaryConstraints();
@@ -287,7 +301,7 @@ int Sketch::resetSolver()
GCSsys.initSolution(defaultSolverRedundant);
GCSsys.getConflicting(Conflicting);
GCSsys.getRedundant(Redundant);
GCSsys.getDependentParams(pconstraintplistOut);
GCSsys.getDependentParams(pDependentParametersList);
calculateDependentParametersElements();
@@ -422,6 +436,11 @@ int Sketch::addPoint(const Part::GeomPoint &point, bool fixed)
// store complete set
Geoms.push_back(def);
if(!fixed) {
param2geoelement.emplace( std::piecewise_construct, std::forward_as_tuple(p1.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace( std::piecewise_construct, std::forward_as_tuple(p1.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
}
// return the position of the newly added geometry
return Geoms.size()-1;
}
@@ -476,6 +495,13 @@ int Sketch::addLineSegment(const Part::GeomLineSegment &lineSegment, bool fixed)
// store complete set
Geoms.push_back(def);
if(!fixed) {
param2geoelement.emplace( std::piecewise_construct, std::forward_as_tuple(p1.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace( std::piecewise_construct, std::forward_as_tuple(p1.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace( std::piecewise_construct, std::forward_as_tuple(p2.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace( std::piecewise_construct, std::forward_as_tuple(p2.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
}
// return the position of the newly added geometry
return Geoms.size()-1;
}
@@ -547,6 +573,18 @@ int Sketch::addArc(const Part::GeomArcOfCircle &circleSegment, bool fixed)
if (!fixed)
GCSsys.addConstraintArcRules(a);
if(!fixed) {
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p3.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p3.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(r), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(a1), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(a2), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
}
// return the position of the newly added geometry
return Geoms.size()-1;
}
@@ -635,6 +673,20 @@ int Sketch::addArcOfEllipse(const Part::GeomArcOfEllipse &ellipseSegment, bool f
if (!fixed)
GCSsys.addConstraintArcOfEllipseRules(a);
if(!fixed) {
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p3.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p3.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(f1X), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(f1Y), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(rmin), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(a1), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(a2), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
}
// return the position of the newly added geometry
return Geoms.size()-1;
}
@@ -721,6 +773,21 @@ int Sketch::addArcOfHyperbola(const Part::GeomArcOfHyperbola &hyperbolaSegment,
if (!fixed)
GCSsys.addConstraintArcOfHyperbolaRules(a);
if(!fixed) {
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p3.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p3.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(f1X), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(f1Y), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(rmin), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(a1), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(a2), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
}
// return the position of the newly added geometry
return Geoms.size()-1;
}
@@ -797,6 +864,19 @@ int Sketch::addArcOfParabola(const Part::GeomArcOfParabola &parabolaSegment, boo
if (!fixed)
GCSsys.addConstraintArcOfParabolaRules(a);
if(!fixed) {
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p3.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p3.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p4.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p4.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(a1), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(a2), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
}
// return the position of the newly added geometry
return Geoms.size()-1;
}
@@ -859,13 +939,23 @@ int Sketch::addBSpline(const Part::GeomBSplineCurve &bspline, bool fixed)
p.y = params[params.size()-1];
spoles.push_back(p);
if(!fixed) {
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
}
}
std::vector<double *> sweights;
for(std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it) {
params.push_back(new double( (*it) ));
auto r = new double( (*it) );
params.push_back(r);
sweights.push_back(params[params.size()-1]);
if(!fixed) {
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(r), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
}
}
std::vector<double *> sknots;
@@ -944,6 +1034,14 @@ int Sketch::addBSpline(const Part::GeomBSplineCurve &bspline, bool fixed)
GCSsys.addConstraintP2PCoincident(*(bs.poles.end()-1),bs.end);
}
if(!fixed) {
// Note: Poles and weight parameters are emplaced above
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::start));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p2.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::end));
}
// return the position of the newly added geometry
return Geoms.size()-1;
}
@@ -987,6 +1085,12 @@ int Sketch::addCircle(const Part::GeomCircle &cir, bool fixed)
// store complete set
Geoms.push_back(def);
if(!fixed) {
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(p1.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(r), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
}
// return the position of the newly added geometry
return Geoms.size()-1;
}
@@ -1045,6 +1149,14 @@ int Sketch::addEllipse(const Part::GeomEllipse &elip, bool fixed)
// store complete set
Geoms.push_back(def);
if(!fixed) {
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(c.x), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(c.y), std::forward_as_tuple(Geoms.size()-1, Sketcher::mid));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(f1X), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(f1Y), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
param2geoelement.emplace(std::piecewise_construct, std::forward_as_tuple(rmin), std::forward_as_tuple(Geoms.size()-1, Sketcher::none));
}
// return the position of the newly added geometry
return Geoms.size()-1;
}
@@ -1063,6 +1175,14 @@ std::vector<Part::Geometry *> Sketch::extractGeometry(bool withConstructionEleme
return temp;
}
void Sketch::updateExtension(int geoId, std::unique_ptr<Part::GeometryExtension> && ext)
{
geoId = checkGeoId(geoId);
Geoms[geoId].geo->setExtension(std::move(ext));
}
Py::Tuple Sketch::getPyGeometry(void) const
{
Py::Tuple tuple(Geoms.size());
@@ -3692,116 +3812,6 @@ Base::Vector3d Sketch::getPoint(int geoId, PointPos pos) const
return Base::Vector3d();
}
bool Sketch::hasDependentParameters(int geoId, PointPos pos) const
{
try {
geoId = checkGeoId(geoId);
}
catch (Base::Exception&) {
return false;
}
if(Geoms[geoId].external)
return true;
switch(Geoms[geoId].type) {
case Point:
{
switch(pos) { // NOTE: points are added to all the cases, see addition.
case none: return Points[Geoms[geoId].index].hasDependentParameters;break;
case start: return Points[Geoms[geoId].startPointId].hasDependentParameters;break;
case end: return Points[Geoms[geoId].endPointId].hasDependentParameters;break;
case mid: return Points[Geoms[geoId].midPointId].hasDependentParameters;break;
}
}
break;
case Line:
{
switch(pos) {
case none: return Lines[Geoms[geoId].index].hasDependentParameters;break;
case start: return Points[Geoms[geoId].startPointId].hasDependentParameters;break;
case end: return Points[Geoms[geoId].endPointId].hasDependentParameters;break;
case mid: return false;break;
}
}
break;
case Arc:
{
switch(pos) {
case none: return Arcs[Geoms[geoId].index].hasDependentParameters;break;
case start: return Points[Geoms[geoId].startPointId].hasDependentParameters;break;
case end: return Points[Geoms[geoId].endPointId].hasDependentParameters;break;
case mid: return Points[Geoms[geoId].midPointId].hasDependentParameters;break;
}
}
break;
case Circle:
{
switch(pos) { // NOTE: points are added to all the cases, see addition.
case none: return Circles[Geoms[geoId].index].hasDependentParameters;break;
case start: return false;break;
case end: return false;break;
case mid: return Points[Geoms[geoId].midPointId].hasDependentParameters;break;
}
}
break;
case Ellipse:
{
switch(pos) { // NOTE: points are added to all the cases, see addition.
case none: return Ellipses[Geoms[geoId].index].hasDependentParameters;break;
case start: return false;break;
case end: return false;break;
case mid: return Points[Geoms[geoId].midPointId].hasDependentParameters;break;
}
}
break;
case ArcOfEllipse:
{
switch(pos) {
case none: return ArcsOfEllipse[Geoms[geoId].index].hasDependentParameters;break;
case start: return Points[Geoms[geoId].startPointId].hasDependentParameters;break;
case end: return Points[Geoms[geoId].endPointId].hasDependentParameters;break;
case mid: return Points[Geoms[geoId].midPointId].hasDependentParameters;break;
}
}
break;
case ArcOfHyperbola:
{
switch(pos) {
case none: return ArcsOfHyperbola[Geoms[geoId].index].hasDependentParameters;break;
case start: return Points[Geoms[geoId].startPointId].hasDependentParameters;break;
case end: return Points[Geoms[geoId].endPointId].hasDependentParameters;break;
case mid: return Points[Geoms[geoId].midPointId].hasDependentParameters;break;
}
}
break;
case ArcOfParabola:
{
switch(pos) {
case none: return ArcsOfParabola[Geoms[geoId].index].hasDependentParameters;break;
case start: return Points[Geoms[geoId].startPointId].hasDependentParameters;break;
case end: return Points[Geoms[geoId].endPointId].hasDependentParameters;break;
case mid: return Points[Geoms[geoId].midPointId].hasDependentParameters;break;
}
}
break;
case BSpline:
{
switch(pos) {
case none: return BSplines[Geoms[geoId].index].hasDependentParameters;break;
case start: return Points[Geoms[geoId].startPointId].hasDependentParameters;break;
case end: return Points[Geoms[geoId].endPointId].hasDependentParameters;break;
case mid: return false;break;
}
}
break;
case None:
return false; break;
}
return false;
}
TopoShape Sketch::toShape(void) const
{
TopoShape result;
+13 -4
View File
@@ -88,6 +88,8 @@ public:
/// returns the actual geometry
std::vector<Part::Geometry *> extractGeometry(bool withConstructionElements=true,
bool withExternalElements=false) const;
void updateExtension(int geoId, std::unique_ptr<Part::GeometryExtension> && ext);
/// get the geometry as python objects
Py::Tuple getPyGeometry(void) const;
@@ -96,9 +98,6 @@ public:
/// retrieves a point
Base::Vector3d getPoint(int geoId, PointPos pos) const;
/// retrieves whether a geometry has dependent parameters or not
bool hasDependentParameters(int geoId, PointPos pos) const;
// Inline methods
inline bool hasConflicts(void) const { return !Conflicting.empty(); }
inline const std::vector<int> &getConflicting(void) const { return Conflicting; }
@@ -106,6 +105,11 @@ public:
inline const std::vector<int> &getRedundant(void) const { return Redundant; }
inline bool hasMalformedConstraints(void) const { return malformedConstraints; }
public:
std::set < std::pair< int, Sketcher::PointPos>> getDependencyGroup(int geoId, PointPos pos) const;
public:
/** set the datum of a distance or angle constraint to a certain value and solve
* This can cause the solving to fail!
@@ -409,7 +413,12 @@ protected:
std::vector<int> Conflicting;
std::vector<int> Redundant;
std::vector<double *> pconstraintplistOut;
std::vector<double *> pDependentParametersList;
std::vector < std::set < std::pair< int, Sketcher::PointPos>>> pDependencyGroups;
// this map is intended to convert a parameter (double *) into a GeoId/PointPos pair
std::map<double *, std::pair<int,Sketcher::PointPos>> param2geoelement;
// solving parameters
std::vector<double*> Parameters; // with memory allocation
+42 -65
View File
@@ -79,10 +79,12 @@
#include <Mod/Part/App/BodyBase.h>
#include <Mod/Part/App/GeometryMigrationExtension.h>
#include "SketchObject.h"
#include "Sketch.h"
#include <Mod/Sketcher/App/Sketch.h>
#include <Mod/Sketcher/App/SketchObjectPy.h>
#include <Mod/Sketcher/App/SketchGeometryExtensionPy.h>
#include <Mod/Sketcher/App/SolverGeometryExtension.h>
#include "SketchObject.h"
#undef DEBUG
@@ -146,7 +148,6 @@ SketchObject::SketchObject()
internaltransaction=false;
managedoperation=false;
deletinginternalgeometry=false;
}
SketchObject::~SketchObject()
@@ -957,10 +958,6 @@ int SketchObject::delGeometry(int GeoId, bool deleteinternalgeo)
this->deleteUnusedInternalGeometry(GeoId, true);
Geometry.touch();
if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver
solve();
return 0;
}
}
@@ -1002,13 +999,12 @@ int SketchObject::delGeometry(int GeoId, bool deleteinternalgeo)
this->Geometry.setValues(newVals);
this->Constraints.setValues(std::move(newConstraints));
}
// Update geometry indices and rebuild vertexindex now via onChanged, so that ViewProvider::UpdateData is triggered.
if(!deletinginternalgeometry) {
Geometry.touch();
if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver
solve();
}
// Update geometry indices and rebuild vertexindex now via onChanged, so that ViewProvider::UpdateData is triggered.
Geometry.touch();
if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver
solve();
return 0;
}
@@ -4979,8 +4975,6 @@ int SketchObject::exposeInternalGeometry(int GeoId)
int SketchObject::deleteUnusedInternalGeometry(int GeoId, bool delgeoid)
{
Base::StateLocker lock(deletinginternalgeometry, true);
if (GeoId < 0 || GeoId > getHighestCurveIndex())
return -1;
@@ -5264,16 +5258,7 @@ int SketchObject::deleteUnusedInternalGeometry(int GeoId, bool delgeoid)
if(delgeoid)
delgeometries.push_back(GeoId);
std::sort(delgeometries.begin(), delgeometries.end()); // indices over an erased element get automatically updated!!
if (delgeometries.size()>0) {
for (std::vector<int>::reverse_iterator it=delgeometries.rbegin(); it!=delgeometries.rend(); ++it) {
delGeometry(*it,false);
}
}
int ndeleted = delgeometries.size();
delgeometries.clear();
int ndeleted = delGeometries(delgeometries);
return ndeleted; //number of deleted elements
}
@@ -7025,55 +7010,47 @@ void SketchObject::getGeometryWithDependentParameters(std::vector<std::pair<int,
{
auto geos = getInternalGeometry();
GCS::QRAlgorithm curQRAlg = getSolvedSketch().getQRAlgorithm();
if(curQRAlg == GCS::EigenSparseQR) {
getSolvedSketch().setQRAlgorithm(GCS::EigenDenseQR);
solve(false);
}
auto addelement = [this,&geometrymap](int geoId, PointPos pos){
if(getSolvedSketch().hasDependentParameters(geoId, pos))
geometrymap.emplace_back(geoId,pos);
};
int geoid = 0;
for(auto geo : geos) {
if(geo->getTypeId() == Part::GeomPoint::getClassTypeId()) {
addelement(geoid, Sketcher::start);
}
else if(geo->getTypeId() == Part::GeomLineSegment::getClassTypeId() ||
geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) {
if(geo) {
if(geo->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) {
addelement(geoid, Sketcher::start);
addelement(geoid, Sketcher::end);
addelement(geoid, Sketcher::none);
}
else if(geo->getTypeId() == Part::GeomCircle::getClassTypeId() ||
geo->getTypeId() == Part::GeomEllipse::getClassTypeId() ) {
auto solvext = std::static_pointer_cast<const Sketcher::SolverGeometryExtension>(
geo->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock());
addelement(geoid, Sketcher::mid);
addelement(geoid, Sketcher::none);
}
else if(geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ) {
addelement(geoid, Sketcher::start);
addelement(geoid, Sketcher::end);
addelement(geoid, Sketcher::mid);
addelement(geoid, Sketcher::none);
if (solvext->getGeometry() == Sketcher::SolverGeometryExtension::NotFullyConstraint) {
// The solver differentiates whether the parameters that are dependent are not those
// of start, end, mid, and assigns them to the edge (edge params = curve params - parms of start, end, mid).
// The user looking at the UI expects that the edge of a NotFullyConstraint geometry
// will always move, even if the edge parameters are independent, for example if mid is
// the only dependent parameter. In other words, the user could reasonably restrict the edge
// to reach a fully constrained element. Under this understanding, the edge parameter would
// always be dependent, unless the element is fully constrained.
//
// While this is ok from a user visual expectation point of view, it leads to a loss of information
// of whether restricting the point start, end, mid that is dependent may suffice, or even if such
// points are restricted, the edge would still need to be restricted.
//
// Because Python gets the information in this function, it would lead to Python users having access
// to a lower amount of detail.
//
// For this reason, this function returns edge as dependent parameter if and only if constraining the
// parameters of the points would not suffice to constraint the element.
if (solvext->getEdge() == SolverGeometryExtension::Dependent)
geometrymap.emplace_back(geoid,Sketcher::none);
if (solvext->getStart() == SolverGeometryExtension::Dependent)
geometrymap.emplace_back(geoid,Sketcher::start);
if (solvext->getEnd() == SolverGeometryExtension::Dependent)
geometrymap.emplace_back(geoid,Sketcher::start);
if (solvext->getMid() == SolverGeometryExtension::Dependent)
geometrymap.emplace_back(geoid,Sketcher::start);
}
}
}
geoid++;
}
if(curQRAlg == GCS::EigenSparseQR) {
getSolvedSketch().setQRAlgorithm(GCS::EigenSparseQR);
}
}
bool SketchObject::evaluateConstraint(const Constraint *constraint) const
-2
View File
@@ -511,8 +511,6 @@ private:
bool internaltransaction;
bool managedoperation; // indicates whether changes to properties are the deed of SketchObject or not (for input validation)
bool deletinginternalgeometry; // sets a lock to deletinginternalgeometryoperation so that no individual triggers are perform on each element.
};
typedef App::FeaturePythonT<SketchObject> SketchObjectPython;
@@ -0,0 +1,66 @@
/***************************************************************************
* Copyright (c) 2019 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
* *
* 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"
#include <Base/Exception.h>
#include "SolverGeometryExtension.h"
using namespace Sketcher;
//---------- Geometry Extension
TYPESYSTEM_SOURCE(Sketcher::SolverGeometryExtension,Part::GeometryExtension)
SolverGeometryExtension::SolverGeometryExtension():
Edge(SolverGeometryExtension::Dependent),
Start(SolverGeometryExtension::Dependent),
Mid(SolverGeometryExtension::Dependent),
End(SolverGeometryExtension::Dependent)
{
}
std::unique_ptr<Part::GeometryExtension> SolverGeometryExtension::copy(void) const
{
auto cpy = std::make_unique<SolverGeometryExtension>();
cpy->Edge = this->Edge;
cpy->Start = this->Start;
cpy->End = this->End;
cpy->Mid = this->Mid;
cpy->setName(this->getName()); // Base Class
#if defined (__GNUC__) && (__GNUC__ <=4)
return std::move(cpy);
#else
return cpy;
#endif
}
PyObject * SolverGeometryExtension::getPyObject(void)
{
THROWM(Base::NotImplementedError, "SolverGeometryExtension does not have a Python counterpart");
}
@@ -0,0 +1,92 @@
/***************************************************************************
* Copyright (c) 2019 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
* *
* 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 *
* *
***************************************************************************/
#ifndef SKETCHER_SOLVERGEOMETRYEXTENSION_H
#define SKETCHER_SOLVERGEOMETRYEXTENSION_H
#include <Mod/Part/App/GeometryExtension.h>
namespace Sketcher
{
class SketcherExport SolverGeometryExtension : public Part::GeometryExtension
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
enum SolverStatus {
FullyConstraint = 0,
NotFullyConstraint = 1,
NumSolverStatus
};
enum ParameterStatus {
Dependent = 0,
Independent = 1,
NumParameterStatus
};
SolverGeometryExtension();
SolverGeometryExtension(long cid);
virtual ~SolverGeometryExtension() override = default;
virtual std::unique_ptr<Part::GeometryExtension> copy(void) const override;
virtual PyObject *getPyObject(void) override;
SolverStatus getGeometry() const {return ( Edge == Independent &&
Start == Independent &&
End == Independent &&
Mid == Independent) ? FullyConstraint : NotFullyConstraint;}
ParameterStatus getEdge() const {return Edge;}
void setEdge(ParameterStatus status) {Edge = status;}
ParameterStatus getStart() const {return Start;}
void setStart(ParameterStatus status) {Start = status;}
ParameterStatus getMid() const {return Mid;}
void setMid(ParameterStatus status) {Mid = status;}
ParameterStatus getEnd() const {return End;}
void setEnd(ParameterStatus status) {End = status;}
void init(ParameterStatus status) {
Edge = status;
Start = status;
Mid = status;
End = status;
}
private:
SolverGeometryExtension(const SolverGeometryExtension&) = default;
private:
ParameterStatus Edge;
ParameterStatus Start;
ParameterStatus Mid;
ParameterStatus End;
};
} //namespace Sketcher
#endif // SKETCHER_SOLVERGEOMETRYEXTENSION_H
File diff suppressed because it is too large Load Diff
+75 -3
View File
@@ -27,6 +27,17 @@
#include <boost/concept_check.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <Eigen/QR>
#define EIGEN_VERSION (EIGEN_WORLD_VERSION * 10000 \
+ EIGEN_MAJOR_VERSION * 100 \
+ EIGEN_MINOR_VERSION)
#if EIGEN_VERSION >= 30202
#define EIGEN_SPARSEQR_COMPATIBLE
#include <Eigen/Sparse>
#endif
namespace GCS
{
///////////////////////////////////////
@@ -91,7 +102,11 @@ namespace GCS
VEC_pD pdrivenlist; // list of parameters of driven constraints
MAP_pD_I pIndex;
VEC_pD pdependentparameters; // list of dependent parameters by the system
VEC_pD pDependentParameters; // list of dependent parameters by the system
// This is a map of primary and secondary identifiers that are found dependent by the solver
// GCS ignores from a type point
std::vector< std::set<double *> > pDependentParametersGroups;
std::vector<Constraint *> clist;
std::map<Constraint *,VEC_pD > c2p; // constraint to parameter adjacency list
@@ -116,12 +131,66 @@ namespace GCS
bool hasDiagnosis; // if dofs, conflictingTags, redundantTags are up to date
bool isInit; // if plists, clists, reductionmaps are up to date
bool emptyDiagnoseMatrix; // false only if there is at least one driving constraint.
int solve_BFGS(SubSystem *subsys, bool isFine=true, bool isRedundantsolving=false);
int solve_LM(SubSystem *subsys, bool isRedundantsolving=false);
int solve_DL(SubSystem *subsys, bool isRedundantsolving=false);
void makeReducedJacobian(Eigen::MatrixXd &J, std::map<int,int> &jacobianconstraintmap, GCS::VEC_pD &pdiagnoselist, std::map< int , int> &tagmultiplicity);
void makeDenseQRDecomposition( const Eigen::MatrixXd &J,
const std::map<int,int> &jacobianconstraintmap,
Eigen::FullPivHouseholderQR<Eigen::MatrixXd>& qrJT,
int &rank, Eigen::MatrixXd &R, bool transposeJ = true, bool silent = false);
#ifdef EIGEN_SPARSEQR_COMPATIBLE
void makeSparseQRDecomposition( const Eigen::MatrixXd &J,
const std::map<int,int> &jacobianconstraintmap,
Eigen::SparseQR<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int> > &SqrJT,
int &rank, Eigen::MatrixXd &R, bool transposeJ = true, bool silent = false);
#endif
// This function name is long for a reason:
// - Only for DenseQR
// - Only for Transposed Jacobian QR decomposition
void identifyDependentGeometryParametersInTransposedJacobianDenseQRDecomposition(
const Eigen::FullPivHouseholderQR<Eigen::MatrixXd>& qrJT,
const GCS::VEC_pD &pdiagnoselist,
int paramsNum, int rank
);
template <typename T>
void identifyConflictingRedundantConstraints( Algorithm alg,
const T & qrJT,
const std::map<int,int> &jacobianconstraintmap,
const std::map< int , int> &tagmultiplicity,
GCS::VEC_pD &pdiagnoselist,
Eigen::MatrixXd &R,
int constrNum, int rank,
int &nonredundantconstrNum
);
void eliminateNonZerosOverPivotInUpperTriangularMatrix(Eigen::MatrixXd &R, int rank);
#ifdef EIGEN_SPARSEQR_COMPATIBLE
void identifyDependentParametersSparseQR( const Eigen::MatrixXd &J,
const std::map<int,int> &jacobianconstraintmap,
const GCS::VEC_pD &pdiagnoselist,
bool silent=true);
#endif
void identifyDependentParametersDenseQR( const Eigen::MatrixXd &J,
const std::map<int,int> &jacobianconstraintmap,
const GCS::VEC_pD &pdiagnoselist,
bool silent=true);
template <typename T>
void identifyDependentParameters( T & qrJ,
Eigen::MatrixXd &Rparams,
int rank,
const GCS::VEC_pD &pdiagnoselist,
bool silent=true);
#ifdef _GCS_EXTRACT_SOLVER_SUBSYSTEM_
void extractSubsystem(SubSystem *subsys, bool isRedundantsolving);
#endif
@@ -292,8 +361,11 @@ namespace GCS
{ conflictingOut = hasDiagnosis ? conflictingTags : VEC_I(0); }
void getRedundant(VEC_I &redundantOut) const
{ redundantOut = hasDiagnosis ? redundantTags : VEC_I(0); }
void getDependentParams(VEC_pD &pconstraintplistOut) const
{ pconstraintplistOut = pdependentparameters;}
void getDependentParams(VEC_pD &pdependentparameterlist) const
{ pdependentparameterlist = pDependentParameters;}
void getDependentParamsGroups(std::vector<std::set<double *>> &pdependentparametergroups) const
{ pdependentparametergroups = pDependentParametersGroups;}
bool isEmptyDiagnoseMatrix() const {return emptyDiagnoseMatrix;}
};
+14 -21
View File
@@ -28,14 +28,7 @@
namespace GCS
{
class DependentParameters
{
public:
DependentParameters():hasDependentParameters(false) {}
bool hasDependentParameters;
};
class Point : public DependentParameters
class Point
{
public:
Point(){x = 0; y = 0;}
@@ -45,7 +38,7 @@ namespace GCS
};
typedef std::vector<Point> VEC_P;
///Class DeriVector2 holds a vector value and its derivative on the
///parameter that the derivatives are being calculated for now. x,y is the
///actual vector (v). dx,dy is a derivative of the vector by a parameter
@@ -96,7 +89,7 @@ namespace GCS
// Geometries
///////////////////////////////////////
class Curve: public DependentParameters //a base class for all curve-based objects (line, circle/arc, ellipse/arc)
class Curve //a base class for all curve-based objects (line, circle/arc, ellipse/arc)
{
public:
virtual ~Curve(){}
@@ -169,7 +162,7 @@ namespace GCS
virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
virtual Arc* Copy();
};
class MajorRadiusConic: public Curve
{
public:
@@ -179,13 +172,13 @@ namespace GCS
virtual double getRadMaj() = 0;
DeriVector2 CalculateNormal(Point &p, double* derivparam = 0) = 0;
};
class Ellipse: public MajorRadiusConic
{
public:
Ellipse(){ radmin = 0;}
virtual ~Ellipse(){}
Point center;
Point center;
Point focus1;
double *radmin;
virtual double getRadMaj(const DeriVector2 &center, const DeriVector2 &f1, double b, double db, double &ret_dRadMaj);
@@ -197,7 +190,7 @@ namespace GCS
virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
virtual Ellipse* Copy();
};
class ArcOfEllipse: public Ellipse
{
public:
@@ -215,13 +208,13 @@ namespace GCS
virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
virtual ArcOfEllipse* Copy();
};
class Hyperbola: public MajorRadiusConic
{
public:
Hyperbola(){ radmin = 0;}
virtual ~Hyperbola(){}
Point center;
Point center;
Point focus1;
double *radmin;
virtual double getRadMaj(const DeriVector2 &center, const DeriVector2 &f1, double b, double db, double &ret_dRadMaj);
@@ -232,7 +225,7 @@ namespace GCS
virtual int PushOwnParams(VEC_pD &pvec);
virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
virtual Hyperbola* Copy();
};
};
class ArcOfHyperbola: public Hyperbola
{
@@ -249,20 +242,20 @@ namespace GCS
virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
virtual ArcOfHyperbola* Copy();
};
class Parabola: public Curve
{
public:
Parabola(){ }
virtual ~Parabola(){}
Point vertex;
Point vertex;
Point focus1;
DeriVector2 CalculateNormal(Point &p, double* derivparam = 0);
virtual DeriVector2 Value(double u, double du, double* derivparam = 0);
virtual int PushOwnParams(VEC_pD &pvec);
virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
virtual Parabola* Copy();
};
};
class ArcOfParabola: public Parabola
{
@@ -306,7 +299,7 @@ namespace GCS
virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
virtual BSpline* Copy();
};
} //namespace GCS
#endif // PLANEGCS_GEO_H
+5 -1
View File
@@ -260,11 +260,15 @@ bool SketcherGui::isBsplinePole(const Part::Geometry * geo)
{
auto gf = GeometryFacade::getFacade(geo);
return gf->getInternalType() == InternalType::BSplineControlPoint;
if(gf)
return gf->getInternalType() == InternalType::BSplineControlPoint;
THROWM(Base::ValueError, "Null geometry in isBsplinePole - please report")
}
bool SketcherGui::isBsplinePole(const Sketcher::SketchObject* Obj, int GeoId)
{
auto geom = Obj->getGeometry(GeoId);
return isBsplinePole(geom);
+28 -45
View File
@@ -48,6 +48,7 @@
#include <Mod/Part/App/Geometry.h>
#include <Mod/Sketcher/App/SketchObject.h>
#include <Mod/Sketcher/App/SolverGeometryExtension.h>
#include "ViewProviderSketch.h"
#include "SketchRectangularArrayDialog.h"
@@ -739,69 +740,51 @@ void CmdSketcherSelectElementsWithDoFs::activated(int iMsg)
auto geos = Obj->getInternalGeometry();
// Solver parameter detection algorithm only works for Dense QR with full pivoting. If we are using Sparse QR, we
// have to re-solve using Dense QR.
GCS::QRAlgorithm curQRAlg = Obj->getSolvedSketch().getQRAlgorithm();
if (curQRAlg == GCS::EigenSparseQR) {
Obj->getSolvedSketch().setQRAlgorithm(GCS::EigenDenseQR);
Obj->solve(false);
}
auto testselectvertex = [&Obj, &ss, &doc_name, &obj_name](int geoId, PointPos pos) {
ss.str(std::string());
if (Obj->getSolvedSketch().hasDependentParameters(geoId, pos)) {
int vertex = Obj->getVertexIndexGeoPos(geoId, pos);
if (vertex > -1) {
ss << "Vertex" << vertex + 1;
int vertex = Obj->getVertexIndexGeoPos(geoId, pos);
if (vertex > -1) {
ss << "Vertex" << vertex + 1;
Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
}
Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
}
};
auto testselectedge = [&Obj, &ss, &doc_name, &obj_name](int geoId) {
auto testselectedge = [&ss, &doc_name, &obj_name](int geoId) {
ss.str(std::string());
if(Obj->getSolvedSketch().hasDependentParameters(geoId, Sketcher::none)) {
ss << "Edge" << geoId + 1;
Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
}
ss << "Edge" << geoId + 1;
Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
};
int geoid = 0;
for (auto geo : geos) {
if (geo->getTypeId() == Part::GeomPoint::getClassTypeId()) {
testselectvertex(geoid, Sketcher::start);
}
else if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId() ||
geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) {
testselectvertex(geoid, Sketcher::start);
testselectvertex(geoid, Sketcher::end);
testselectedge(geoid);
}
else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId() ||
geo->getTypeId() == Part::GeomEllipse::getClassTypeId()) {
testselectvertex(geoid, Sketcher::mid);
testselectedge(geoid);
}
else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) {
testselectvertex(geoid, Sketcher::start);
testselectvertex(geoid, Sketcher::end);
testselectvertex(geoid, Sketcher::mid);
testselectedge(geoid);
if(geo) {
if(geo->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) {
auto solvext = std::static_pointer_cast<const Sketcher::SolverGeometryExtension>(
geo->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock());
if (solvext->getGeometry() == Sketcher::SolverGeometryExtension::NotFullyConstraint) {
// Coded for consistency with getGeometryWithDependentParameters, read the comments
// on that function
if (solvext->getEdge() == SolverGeometryExtension::Dependent)
testselectedge(geoid);
if (solvext->getStart() == SolverGeometryExtension::Dependent)
testselectvertex(geoid, Sketcher::start);
if (solvext->getEnd() == SolverGeometryExtension::Dependent)
testselectvertex(geoid, Sketcher::end);
if (solvext->getMid() == SolverGeometryExtension::Dependent)
testselectvertex(geoid, Sketcher::mid);
}
}
}
geoid++;
}
if (curQRAlg == GCS::EigenSparseQR) {
Obj->getSolvedSketch().setQRAlgorithm(GCS::EigenSparseQR);
}
}
bool CmdSketcherSelectElementsWithDoFs::isActive(void)
@@ -261,6 +261,10 @@ void SketcherSettingsColors::saveSettings()
ui->ExternalColor->onSave();
ui->FullyConstrainedColor->onSave();
ui->InternalAlignedGeoColor->onSave();
ui->FullyConstraintElementColor->onSave();
ui->FullyConstraintConstructionElementColor->onSave();
ui->FullyConstraintInternalAlignmentColor->onSave();
ui->FullyConstraintConstructionPointColor->onSave();
ui->ConstrainedColor->onSave();
ui->NonDrivingConstraintColor->onSave();
@@ -288,6 +292,10 @@ void SketcherSettingsColors::loadSettings()
ui->ExternalColor->onRestore();
ui->FullyConstrainedColor->onRestore();
ui->InternalAlignedGeoColor->onRestore();
ui->FullyConstraintElementColor->onRestore();
ui->FullyConstraintConstructionElementColor->onRestore();
ui->FullyConstraintInternalAlignmentColor->onRestore();
ui->FullyConstraintConstructionPointColor->onRestore();
ui->ConstrainedColor->onRestore();
ui->NonDrivingConstraintColor->onRestore();
+151 -19
View File
@@ -320,6 +320,138 @@
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_41">
<property name="minimumSize">
<size>
<width>182</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Fully constrained edit edge color</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="Gui::PrefColorButton" name="FullyConstraintElementColor">
<property name="toolTip">
<string>Color of fully constrained edge color in edit mode</string>
</property>
<property name="color">
<color>
<red>128</red>
<green>208</green>
<blue>160</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>FullyConstraintElementColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>View</cstring>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_42">
<property name="minimumSize">
<size>
<width>182</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Fully constrained edit construction edge color</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="Gui::PrefColorButton" name="FullyConstraintConstructionElementColor">
<property name="toolTip">
<string>Color of fully constrained construction edge color in edit mode</string>
</property>
<property name="color">
<color>
<red>143</red>
<green>169</green>
<blue>253</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>FullyConstraintConstructionElementColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>View</cstring>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="label_43">
<property name="minimumSize">
<size>
<width>182</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Fully constrained edit internal alignment edge color</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="Gui::PrefColorButton" name="FullyConstraintInternalAlignmentColor">
<property name="toolTip">
<string>Color of fully constrained internal alignment edge color in edit mode</string>
</property>
<property name="color">
<color>
<red>222</red>
<green>222</green>
<blue>200</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>FullyConstraintInternalAlignmentColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>View</cstring>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_44">
<property name="minimumSize">
<size>
<width>182</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Fully constrained edit vertex color</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="Gui::PrefColorButton" name="FullyConstraintConstructionPointColor">
<property name="toolTip">
<string>Color of fully constrained vertex color in edit mode</string>
</property>
<property name="color">
<color>
<red>255</red>
<green>149</green>
<blue>128</blue>
</color>
</property>
<property name="prefEntry" stdset="0">
<cstring>FullyConstraintConstructionPointColor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>View</cstring>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_14">
<property name="minimumSize">
<size>
@@ -332,7 +464,7 @@
</property>
</widget>
</item>
<item row="9" column="1">
<item row="13" column="1">
<widget class="Gui::PrefColorButton" name="ConstrainedColor">
<property name="toolTip">
<string>Color of driving constraints in edit mode</string>
@@ -352,14 +484,14 @@
</property>
</widget>
</item>
<item row="10" column="0">
<item row="14" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Reference constraint color</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="14" column="1">
<widget class="Gui::PrefColorButton" name="NonDrivingConstraintColor">
<property name="toolTip">
<string>Color of reference constraints in edit mode</string>
@@ -379,14 +511,14 @@
</property>
</widget>
</item>
<item row="11" column="0">
<item row="15" column="0">
<widget class="QLabel" name="labelexpr">
<property name="text">
<string>Expression dependent constraint color</string>
</property>
</widget>
</item>
<item row="11" column="1">
<item row="15" column="1">
<widget class="Gui::PrefColorButton" name="ExprBasedConstrDimColor">
<property name="toolTip">
<string>Color of expression dependent constraints in edit mode</string>
@@ -406,14 +538,14 @@
</property>
</widget>
</item>
<item row="12" column="0">
<item row="16" column="0">
<widget class="QLabel" name="labeldeact">
<property name="text">
<string>Deactivated constraint color</string>
</property>
</widget>
</item>
<item row="12" column="1">
<item row="16" column="1">
<widget class="Gui::PrefColorButton" name="DeactivatedConstrDimColor">
<property name="toolTip">
<string>Color of deactivated constraints in edit mode</string>
@@ -433,7 +565,7 @@
</property>
</widget>
</item>
<item row="13" column="0">
<item row="17" column="0">
<widget class="QLabel" name="label_15">
<property name="minimumSize">
<size>
@@ -446,7 +578,7 @@
</property>
</widget>
</item>
<item row="13" column="1">
<item row="17" column="1">
<widget class="Gui::PrefColorButton" name="DatumColor">
<property name="toolTip">
<string>Color of the datum portion of a driving constraint</string>
@@ -466,7 +598,7 @@
</property>
</widget>
</item>
<item row="14" column="0">
<item row="18" column="0">
<widget class="QLabel" name="label_16">
<property name="minimumSize">
<size>
@@ -479,7 +611,7 @@
</property>
</widget>
</item>
<item row="14" column="1">
<item row="18" column="1">
<widget class="Gui::PrefSpinBox" name="SketcherDatumWidth">
<property name="toolTip">
<string>The default line thickness for new shapes</string>
@@ -501,7 +633,7 @@
</property>
</widget>
</item>
<item row="15" column="0">
<item row="19" column="0">
<widget class="QLabel" name="label_12">
<property name="minimumSize">
<size>
@@ -514,7 +646,7 @@
</property>
</widget>
</item>
<item row="15" column="1">
<item row="19" column="1">
<widget class="Gui::PrefSpinBox" name="DefaultSketcherVertexWidth">
<property name="toolTip">
<string>The default line thickness for new shapes</string>
@@ -536,7 +668,7 @@
</property>
</widget>
</item>
<item row="16" column="0">
<item row="20" column="0">
<widget class="QLabel" name="label_13">
<property name="minimumSize">
<size>
@@ -549,7 +681,7 @@
</property>
</widget>
</item>
<item row="16" column="1">
<item row="20" column="1">
<widget class="Gui::PrefSpinBox" name="DefaultSketcherLineWidth">
<property name="toolTip">
<string>The default line thickness for new shapes</string>
@@ -571,7 +703,7 @@
</property>
</widget>
</item>
<item row="17" column="0">
<item row="21" column="0">
<widget class="QLabel" name="label_5">
<property name="minimumSize">
<size>
@@ -584,7 +716,7 @@
</property>
</widget>
</item>
<item row="17" column="1">
<item row="21" column="1">
<widget class="Gui::PrefColorButton" name="CursorTextColor">
<property name="toolTip">
<string>Text color of the coordinates</string>
@@ -604,7 +736,7 @@
</property>
</widget>
</item>
<item row="18" column="0">
<item row="22" column="0">
<widget class="QLabel" name="label_19">
<property name="minimumSize">
<size>
@@ -617,7 +749,7 @@
</property>
</widget>
</item>
<item row="18" column="1">
<item row="22" column="1">
<widget class="Gui::PrefColorButton" name="CursorCrosshairColor">
<property name="toolTip">
<string>Color of crosshair cursor.
+171 -50
View File
@@ -121,6 +121,7 @@
#include "TaskSketcherValidation.h"
#include "CommandConstraints.h"
#include "ViewProviderSketchGeometryExtension.h"
#include <Mod/Sketcher/App/SolverGeometryExtension.h>
FC_LOG_LEVEL_INIT("Sketch",true,true)
@@ -143,24 +144,28 @@ using namespace SketcherGui;
using namespace Sketcher;
namespace bp = boost::placeholders;
SbColor ViewProviderSketch::VertexColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0)
SbColor ViewProviderSketch::CurveColor (1.0f,1.0f,1.0f); // #FFFFFF -> (255,255,255)
SbColor ViewProviderSketch::CurveDraftColor (0.0f,0.0f,0.86f); // #0000DC -> ( 0, 0,220)
SbColor ViewProviderSketch::CurveExternalColor (0.8f,0.2f,0.6f); // #CC3399 -> (204, 51,153)
SbColor ViewProviderSketch::CrossColorH (0.8f,0.4f,0.4f); // #CC6666 -> (204,102,102)
SbColor ViewProviderSketch::CrossColorV (0.4f,0.8f,0.4f); // #66CC66 -> (102,204,102)
SbColor ViewProviderSketch::FullyConstrainedColor (0.0f,1.0f,0.0f); // #00FF00 -> ( 0,255, 0)
SbColor ViewProviderSketch::ConstrDimColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0)
SbColor ViewProviderSketch::ConstrIcoColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0)
SbColor ViewProviderSketch::NonDrivingConstrDimColor (0.0f,0.149f,1.0f); // #0026FF -> ( 0, 38,255)
SbColor ViewProviderSketch::ExprBasedConstrDimColor (1.0f,0.5f,0.149f); // #FF7F26 -> (255, 127, 38)
SbColor ViewProviderSketch::InformationColor (0.0f,1.0f,0.0f); // #00FF00 -> ( 0,255, 0)
SbColor ViewProviderSketch::PreselectColor (0.88f,0.88f,0.0f); // #E1E100 -> (225,225, 0)
SbColor ViewProviderSketch::SelectColor (0.11f,0.68f,0.11f); // #1CAD1C -> ( 28,173, 28)
SbColor ViewProviderSketch::PreselectSelectedColor (0.36f,0.48f,0.11f); // #5D7B1C -> ( 93,123, 28)
SbColor ViewProviderSketch::CreateCurveColor (0.8f,0.8f,0.8f); // #CCCCCC -> (204,204,204)
SbColor ViewProviderSketch::DeactivatedConstrDimColor (0.8f,0.8f,0.8f); // #CCCCCC -> (204,204,204)
SbColor ViewProviderSketch::InternalAlignedGeoColor (0.7f,0.7f,0.5f); // #B2B27F -> (178,178,127)
SbColor ViewProviderSketch::VertexColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0)
SbColor ViewProviderSketch::CurveColor (1.0f,1.0f,1.0f); // #FFFFFF -> (255,255,255)
SbColor ViewProviderSketch::CurveDraftColor (0.0f,0.0f,0.86f); // #0000DC -> ( 0, 0,220)
SbColor ViewProviderSketch::CurveExternalColor (0.8f,0.2f,0.6f); // #CC3399 -> (204, 51,153)
SbColor ViewProviderSketch::CrossColorH (0.8f,0.4f,0.4f); // #CC6666 -> (204,102,102)
SbColor ViewProviderSketch::CrossColorV (0.4f,0.8f,0.4f); // #66CC66 -> (102,204,102)
SbColor ViewProviderSketch::FullyConstrainedColor (0.0f,1.0f,0.0f); // #00FF00 -> ( 0,255, 0)
SbColor ViewProviderSketch::ConstrDimColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0)
SbColor ViewProviderSketch::ConstrIcoColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0)
SbColor ViewProviderSketch::NonDrivingConstrDimColor (0.0f,0.149f,1.0f); // #0026FF -> ( 0, 38,255)
SbColor ViewProviderSketch::ExprBasedConstrDimColor (1.0f,0.5f,0.149f); // #FF7F26 -> (255, 127, 38)
SbColor ViewProviderSketch::InformationColor (0.0f,1.0f,0.0f); // #00FF00 -> ( 0,255, 0)
SbColor ViewProviderSketch::PreselectColor (0.88f,0.88f,0.0f); // #E1E100 -> (225,225, 0)
SbColor ViewProviderSketch::SelectColor (0.11f,0.68f,0.11f); // #1CAD1C -> ( 28,173, 28)
SbColor ViewProviderSketch::PreselectSelectedColor (0.36f,0.48f,0.11f); // #5D7B1C -> ( 93,123, 28)
SbColor ViewProviderSketch::CreateCurveColor (0.8f,0.8f,0.8f); // #CCCCCC -> (204,204,204)
SbColor ViewProviderSketch::DeactivatedConstrDimColor (0.8f,0.8f,0.8f); // #CCCCCC -> (204,204,204)
SbColor ViewProviderSketch::InternalAlignedGeoColor (0.7f,0.7f,0.5f); // #B2B27F -> (178,178,127)
SbColor ViewProviderSketch::FullyConstraintElementColor (0.50f,0.81f,0.62f); // #80D0A0 -> (128,208,160)
SbColor ViewProviderSketch::FullyConstraintConstructionElementColor (0.56f,0.66f,0.99f); // #8FA9FD -> (143,169,253)
SbColor ViewProviderSketch::FullyConstraintInternalAlignmentColor (0.87f,0.87f,0.78f); // #DEDEC8 -> (222,222,200)
SbColor ViewProviderSketch::FullyConstraintConstructionPointColor (1.0f,0.58f,0.50f); // #FF9580 -> (255,149,128)
// Variables for holding previous click
SbTime ViewProviderSketch::prvClickTime;
SbVec2s ViewProviderSketch::prvClickPos;
@@ -1166,24 +1171,64 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor
// This is because dragging gives unwanted cosmetic results due to the scale ratio.
// This is an heuristic as it does not check all indirect routes.
if(GeometryFacade::isInternalType(geo, InternalType::BSplineControlPoint)) {
bool weight = false;
bool weight_driven = false;
bool equal = false;
bool block = false;
if(geo->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) {
auto solvext = std::static_pointer_cast<const Sketcher::SolverGeometryExtension>(
geo->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock());
// Edge parameters are Independent, so weight won't move
if(solvext->getEdge()==Sketcher::SolverGeometryExtension::Independent) {
Mode = STATUS_NONE;
return false;
}
// The B-Spline is constrained to be non-rational (equal weights), moving produces a bad effect
// because OCCT will normalize the values of the weights.
auto grp = getSketchObject()->getSolvedSketch().getDependencyGroup(edit->DragCurve, Sketcher::none);
int bsplinegeoid = -1;
std::vector<int> polegeoids;
for( auto c : getSketchObject()->Constraints.getValues()) {
if( c->Type == Sketcher::InternalAlignment &&
c->AlignmentType == BSplineControlPoint &&
c->First == edit->DragCurve ) {
bsplinegeoid = c->Second;
break;
}
}
if(bsplinegeoid == -1) {
Mode = STATUS_NONE;
return false;
}
for( auto c : getSketchObject()->Constraints.getValues()) {
if( c->Type == Sketcher::InternalAlignment &&
c->AlignmentType == BSplineControlPoint &&
c->Second == bsplinegeoid ) {
polegeoids.push_back(c->First);
}
}
bool allingroup = true;
for( auto polegeoid : polegeoids ) {
std::pair< int, Sketcher::PointPos > thispole = std::make_pair(polegeoid,Sketcher::none);
if(grp.find(thispole) == grp.end()) // not found
allingroup = false;
}
if(allingroup) { // it is constrained to be non-rational
Mode = STATUS_NONE;
return false;
}
for(auto c : getSketchObject()->Constraints.getValues()) {
weight = weight || (c->Type == Sketcher::Weight && c->First == edit->DragCurve);
weight_driven = weight_driven || (c->Type == Sketcher::Weight && !c->isDriving && c->First == edit->DragCurve);
equal = equal || (c->Type == Sketcher::Equal && (c->First == edit->DragCurve || c->Second == edit->DragCurve));
block = block || (c->Type == Sketcher::Block && c->First == edit->DragCurve);
}
if( (weight && !weight_driven) ||
(equal && !weight_driven) ||
block) {
Mode = STATUS_NONE;
return false;
}
}
getSketchObject()->getSolvedSketch().initMove(edit->DragCurve, Sketcher::none, false);
@@ -2756,6 +2801,22 @@ void ViewProviderSketch::updateColor(void)
return false;
};
auto isFullyConstraintElement = [](Sketcher::SketchObject* obj, int GeoId) -> bool {
const Part::Geometry* geom = obj->getGeometry(GeoId);
if(geom) {
if(geom->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) {
auto solvext = std::static_pointer_cast<const Sketcher::SolverGeometryExtension>(
geom->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock());
return (solvext->getGeometry() == Sketcher::SolverGeometryExtension::FullyConstraint);
}
}
return false;
};
// colors of the point set
if (edit->FullyConstrained) {
for (int i=0; i < PtNum; i++)
@@ -2765,10 +2826,20 @@ void ViewProviderSketch::updateColor(void)
for (int i=0; i < PtNum; i++) {
int GeoId = edit->PointIdToGeoId[i];
if(isInternalAlignedGeom(getSketchObject(), GeoId))
pcolor[i] = InternalAlignedGeoColor;
else
pcolor[i] = VertexColor;
bool constrainedElement = isFullyConstraintElement(getSketchObject(), GeoId);
if(isInternalAlignedGeom(getSketchObject(), GeoId)) {
if(constrainedElement)
pcolor[i] = FullyConstraintInternalAlignmentColor;
else
pcolor[i] = InternalAlignedGeoColor;
}
else {
if(constrainedElement)
pcolor[i] = FullyConstraintConstructionPointColor;
else
pcolor[i] = VertexColor;
}
}
}
@@ -2820,6 +2891,8 @@ void ViewProviderSketch::updateColor(void)
bool selected = (edit->SelCurvSet.find(GeoId) != edit->SelCurvSet.end());
bool preselected = (edit->PreselectCurve == GeoId);
bool constrainedElement = isFullyConstraintElement(getSketchObject(), GeoId);
if (selected && preselected) {
color[i] = PreselectSelectedColor;
for (int k=j; j<k+indexes; j++) {
@@ -2849,10 +2922,18 @@ void ViewProviderSketch::updateColor(void)
}
}
else if (isConstructionGeom(getSketchObject(), GeoId)) {
if(isInternalAlignedGeom(getSketchObject(), GeoId))
color[i] = InternalAlignedGeoColor;
else
color[i] = CurveDraftColor;
if(isInternalAlignedGeom(getSketchObject(), GeoId)) {
if(constrainedElement)
color[i] = FullyConstraintInternalAlignmentColor;
else
color[i] = InternalAlignedGeoColor;
}
else {
if(constrainedElement)
color[i] = FullyConstraintConstructionElementColor;
else
color[i] = CurveDraftColor;
}
for (int k=j; j<k+indexes; j++) {
verts[j].getValue(x,y,z);
@@ -2866,6 +2947,13 @@ void ViewProviderSketch::updateColor(void)
verts[j] = SbVec3f(x,y,zNormLine);
}
}
else if (isFullyConstraintElement(getSketchObject(), GeoId)) {
color[i] = FullyConstraintElementColor;
for (int k=j; j<k+indexes; j++) {
verts[j].getValue(x,y,z);
verts[j] = SbVec3f(x,y,zNormLine);
}
}
else {
color[i] = CurveColor;
for (int k=j; j<k+indexes; j++) {
@@ -3769,6 +3857,16 @@ void ViewProviderSketch::draw(bool temp /*=false*/, bool rebuildinformationlayer
Coords.emplace_back(x, y, 0);
// save scale factor for any prospective dragging operation
// 1. Solver must be updated, in case a dragging operation starts
// 2. if temp geometry is being used (with memory allocation), then the copy we have here must be updated. If
// no temp geometry is being used, then the normal geometry must be updated.
{// make solver be ready for a dragging operation
auto vpext = std::make_unique<SketcherGui::ViewProviderSketchGeometryExtension>();
vpext->setRepresentationFactor(scalefactor);
getSketchObject()->getSolvedSketch().updateExtension(GeoId, std::move(vpext));
}
if(!circle->hasExtension(SketcherGui::ViewProviderSketchGeometryExtension::getClassTypeId()))
{
// It is ok to add this kind of extension to a const geometry because:
@@ -6040,10 +6138,30 @@ bool ViewProviderSketch::setEdit(int ModNum)
color = (unsigned long)(CurveDraftColor.getPackedValue());
color = hGrp->GetUnsigned("ConstructionColor", color);
CurveDraftColor.setPackedValue((uint32_t)color, transparency);
// set the construction curve color
// set the internal alignment geometry color
color = (unsigned long)(InternalAlignedGeoColor.getPackedValue());
color = hGrp->GetUnsigned("InternalAlignedGeoColor", color);
InternalAlignedGeoColor.setPackedValue((uint32_t)color, transparency);
// set the color for a fully constrained element
color = (unsigned long)(FullyConstraintElementColor.getPackedValue());
color = hGrp->GetUnsigned("FullyConstraintElementColor", color);
FullyConstraintElementColor.setPackedValue((uint32_t)color, transparency);
// set the color for fully constrained construction element
color = (unsigned long)(FullyConstraintConstructionElementColor.getPackedValue());
color = hGrp->GetUnsigned("FullyConstraintConstructionElementColor", color);
FullyConstraintConstructionElementColor.setPackedValue((uint32_t)color, transparency);
// set the color for fully constrained internal alignment element
color = (unsigned long)(FullyConstraintInternalAlignmentColor.getPackedValue());
color = hGrp->GetUnsigned("FullyConstraintInternalAlignmentColor", color);
FullyConstraintInternalAlignmentColor.setPackedValue((uint32_t)color, transparency);
// set the color for fully constrained construction points
color = (unsigned long)(FullyConstraintConstructionPointColor.getPackedValue());
color = hGrp->GetUnsigned("FullyConstraintConstructionPointColor", color);
FullyConstraintConstructionPointColor.setPackedValue((uint32_t)color, transparency);
// set fullyconstraint element color
color = (unsigned long)(FullyConstraintElementColor.getPackedValue());
color = hGrp->GetUnsigned("FullyConstraintElementColor", color);
FullyConstraintElementColor.setPackedValue((uint32_t)color, transparency);
// set the cross lines color
//CrossColorV.setPackedValue((uint32_t)color, transparency);
//CrossColorH.setPackedValue((uint32_t)color, transparency);
@@ -6101,14 +6219,17 @@ bool ViewProviderSketch::setEdit(int ModNum)
getSketchObject()->validateExternalLinks();
}
// First drawing with non-temporal geometry, then updating solver information
// This ensures that any ViewProvider geometry extension is set before the geometry
// is loaded into the solver, which ensures that any prospective draw using temporal
// geometry (draw with first parameter true) has the right ViewProvider geometry extensions
// set - This fixes Weight constraint dragging on a just opened sketch.
getSketchObject()->solve(false);
UpdateSolverInformation();
draw(false,true);
// There are geometry extensions introduced by the solver and geometry extensions introduced by the viewprovider.
// 1. It is important that the solver has geometry with updated extensions.
// 2. It is important that the viewprovider has up-to-date solver information
//
// The decision is to maintain the "first solve then draw" order, which is consistent with the rest of the Sketcher
// for example in geometry creation. Then, the ViewProvider is responsible for updating the solver geometry when
// appropriate, as it is the ViewProvider that is introducing its geometry extensions.
//
// In order to have updated solver information, solve must take "true", this cause the Geometry property to be updated
// with the solver information, including solver extensions, and triggers a draw(true) via ViewProvider::UpdateData.
getSketchObject()->solve(true);
connectUndoDocument = getDocument()
->signalUndoDocument.connect(boost::bind(&ViewProviderSketch::slotUndoDocument, this, bp::_1));
@@ -401,6 +401,10 @@ protected:
static SbColor InformationColor;
static SbColor DeactivatedConstrDimColor;
static SbColor InternalAlignedGeoColor;
static SbColor FullyConstraintElementColor;
static SbColor FullyConstraintConstructionElementColor;
static SbColor FullyConstraintInternalAlignmentColor;
static SbColor FullyConstraintConstructionPointColor;
static SbTime prvClickTime;
static SbVec2s prvClickPos; //used by double-click-detector