From 980d9ae313cb6983875a227579ac610eeae22b0f Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Mon, 7 Dec 2020 23:01:11 +1300 Subject: [PATCH 1/5] Smooth derivative directions --- src/scaffoldmaker/utils/derivativemoothing.py | 76 +++++++++++++++---- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/src/scaffoldmaker/utils/derivativemoothing.py b/src/scaffoldmaker/utils/derivativemoothing.py index e98504d9..6ca5d0c1 100644 --- a/src/scaffoldmaker/utils/derivativemoothing.py +++ b/src/scaffoldmaker/utils/derivativemoothing.py @@ -10,7 +10,7 @@ from opencmiss.zinc.field import Field from opencmiss.zinc.node import Node from opencmiss.zinc.result import RESULT_OK as ZINC_OK -from scaffoldmaker.utils.interpolation import DerivativeScalingMode, getCubicHermiteArcLength +from scaffoldmaker.utils.interpolation import DerivativeScalingMode, getCubicHermiteArcLength, interpolateHermiteLagrangeDerivative, interpolateLagrangeHermiteDerivative from scaffoldmaker.utils.vector import setMagnitude @@ -56,6 +56,22 @@ def getLastArcLength(self): def updateLastArcLength(self): self._lastArcLength = self._arcLength + def getDelta(self): + ''' + Caller must have called evaluateArcLength! + ''' + x1 = self._parameters[0] + x2 = self._parameters[2] + componentsCount = len(x1) + return [ (x2[c] - x1[c]) for c in range(componentsCount) ] + + def getExpresson(self, expressionIndex): + ''' + Caller must have called evaluateArcLength! + :param parameterIndex: 0 = start x, 1 = start d, 2 = end x, 3 = end d + ''' + return self._expressions[expressionIndex] + def getParameter(self, parameterIndex): ''' Caller must have called evaluateArcLength! @@ -257,19 +273,19 @@ def addEdgeCurve(self, element, eft, basisLocalNodeFunctions, basisLocalNodes, d for expressionIndex in (1, 3): expression = expressions[expressionIndex] if len(expression) == 1: # handle single term only - nodeIdentifier, nodeValueLabel, nodeVersion, scaleFactor = expression[0] + nodeIdentifier, nodeValueLabel, nodeVersion, totalScaleFactor = expression[0] if self._selectionNodes: if not self._selectionNodes.containsNode(self._nodes.findNodeByIdentifier(nodeIdentifier)): continue derivativeKey = (nodeIdentifier, nodeValueLabel, nodeVersion) - derivativeEdge = (edge, expressionIndex, math.fabs(scaleFactor)) + derivativeEdge = (edge, expressionIndex, totalScaleFactor) derivativeEdges = self._derivativeMap.get(derivativeKey) if derivativeEdges: derivativeEdges.append(derivativeEdge) else: self._derivativeMap[derivativeKey] = [ derivativeEdge ] - def smooth(self, maxIterations=10, arcLengthTolerance=1.0E-6): + def smooth(self, updateDirections=False,maxIterations=10, arcLengthTolerance=1.0E-6): ''' :param maxIterations: Maximum iterations before stopping if not converging. :param arcLengthTolerance: Ratio of difference in arc length from last iteration @@ -300,15 +316,24 @@ def smooth(self, maxIterations=10, arcLengthTolerance=1.0E-6): if edgeCount > 1: nodeIdentifier, nodeValueLabel, nodeVersion = derivativeKey fieldcache.setNode(self._nodes.findNodeByIdentifier(nodeIdentifier)) - result, x = self._field.getNodeParameters(fieldcache, -1, nodeValueLabel, nodeVersion, componentsCount) + if updateDirections: + x = [ 0.0 for _ in range(componentsCount) ] + else: + result, x = self._field.getNodeParameters(fieldcache, -1, nodeValueLabel, nodeVersion, componentsCount) mag = 0.0 for derivativeEdge in derivativeEdges: edge, expressionIndex, totalScaleFactor = derivativeEdge arcLength = edge.getArcLength() + if updateDirections: + delta = edge.getDelta() + if totalScaleFactor < 0.0: + delta = [ -d for d in delta ] + for c in range(componentsCount): + x[c] += delta[c] / arcLength if self._scalingMode == DerivativeScalingMode.ARITHMETIC_MEAN: - mag += arcLength/totalScaleFactor + mag += arcLength/math.fabs(totalScaleFactor) else: # self._scalingMode == DerivativeScalingMode.HARMONIC_MEAN - mag += totalScaleFactor/arcLength + mag += math.fabs(totalScaleFactor)/arcLength if self._scalingMode == DerivativeScalingMode.ARITHMETIC_MEAN: mag /= edgeCount else: # self._scalingMode == DerivativeScalingMode.HARMONIC_MEAN @@ -323,16 +348,39 @@ def smooth(self, maxIterations=10, arcLengthTolerance=1.0E-6): # boundary smoothing over single edge nodeIdentifier, nodeValueLabel, nodeVersion = derivativeKey fieldcache.setNode(self._nodes.findNodeByIdentifier(nodeIdentifier)) - result, x = self._field.getNodeParameters(fieldcache, -1, nodeValueLabel, nodeVersion, componentsCount) edge, expressionIndex, totalScaleFactor = derivativeEdges[0] # re-evaluate arc length so parameters are up-to-date for other end arcLength = edge.evaluateArcLength(self._nodes, self._field, fieldcache) - otherd = edge.getParameter(3 if (expressionIndex == 1) else 1) - othermag = magnitude(otherd) - mag = (2.0*arcLength - othermag)/totalScaleFactor - if (mag <= 0.0): - print('Node', nodeIdentifier, 'label', nodeValueLabel, 'version', nodeVersion, 'has negative mag', mag) - x = setMagnitude(x, mag) + otherExpressionIndex = 3 if (expressionIndex == 1) else 1 + otherd = edge.getParameter(otherExpressionIndex) + if updateDirections: + thisx = edge.getParameter(expressionIndex - 1) + otherx = edge.getParameter(otherExpressionIndex - 1) + bothEndsOnBoundary = False + otherExpression = edge.getExpresson(otherExpressionIndex) + if len(otherExpression) == 1: + otherNodeIdentifier, otherValueLabel, otherNodeVersion, otherTotalScaleFactor = otherExpression[0] + otherDerivativeKey = (otherNodeIdentifier, otherValueLabel, otherNodeVersion) + otherDerivativeEdges = self._derivativeMap.get(otherDerivativeKey) + bothEndsOnBoundary = (otherDerivativeEdges is not None) and (len(otherDerivativeEdges) == 1) + if bothEndsOnBoundary: + if expressionIndex == 1: + x = [ (otherx[c] - thisx[c]) for c in range(componentsCount) ] + else: + x = [ (thisx[c] - otherx[c]) for c in range(componentsCount) ] + else: + if expressionIndex == 1: + x = interpolateLagrangeHermiteDerivative(thisx, otherx, otherd, 0.0) + else: + x = interpolateHermiteLagrangeDerivative(otherx, otherd, thisx, 1.0) + x = [ d/totalScaleFactor for d in x ] + else: + result, x = self._field.getNodeParameters(fieldcache, -1, nodeValueLabel, nodeVersion, componentsCount) + othermag = magnitude(otherd) + mag = (2.0*arcLength - othermag)/totalScaleFactor + if (mag <= 0.0): + print('Node', nodeIdentifier, 'label', nodeValueLabel, 'version', nodeVersion, 'has negative mag', mag) + x = setMagnitude(x, mag) fieldcache.setNode(self._nodes.findNodeByIdentifier(nodeIdentifier)) # need to set again as changed node in edge.evaluateArcLength result = self._field.setNodeParameters(fieldcache, -1, nodeValueLabel, nodeVersion, x) # record modified nodes while ChangeManager is in effect From 04de705e4d942ddbfb168623ba817f24fce7bf30 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Mon, 7 Dec 2020 23:02:02 +1300 Subject: [PATCH 2/5] Add options to interactive functions --- .../meshtypes/meshtype_1d_path1.py | 20 ++++----- src/scaffoldmaker/meshtypes/scaffold_base.py | 43 +++++++++++-------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_1d_path1.py b/src/scaffoldmaker/meshtypes/meshtype_1d_path1.py index 7ab94043..e6ac5354 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_1d_path1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_1d_path1.py @@ -131,14 +131,14 @@ def generateBaseMesh(cls, region, options): return [] @classmethod - def smoothPath(cls, region, options, editGroupName, mode : DerivativeScalingMode): + def smoothPath(cls, region, options, functionOptions, editGroupName, mode : DerivativeScalingMode): x, d1 = extractPathParametersFromRegion(region, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1]) d1 = smoothCubicHermiteDerivativesLine(x, d1, magnitudeScalingMode=mode) setPathParameters(region, [ Node.VALUE_LABEL_D_DS1 ], [ d1 ], editGroupName) return False, True # settings not changed, nodes changed @classmethod - def makeD2Normal(cls, region, options, editGroupName): + def makeD2Normal(cls, region, options, functionOptions, editGroupName): if not options['D2 derivatives']: return d1, d2 = extractPathParametersFromRegion(region, [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2]) @@ -149,7 +149,7 @@ def makeD2Normal(cls, region, options, editGroupName): return False, True # settings not changed, nodes changed @classmethod - def makeD3Normal(cls, region, options, editGroupName): + def makeD3Normal(cls, region, options, functionOptions, editGroupName): if not options['D3 derivatives']: return if options['D2 derivatives']: @@ -166,7 +166,7 @@ def makeD3Normal(cls, region, options, editGroupName): return False, True # settings not changed, nodes changed @classmethod - def smoothCrossDX(cls, region, options, editGroupName, valueLabel): + def smoothCrossDX(cls, region, options, editGroupName, functionOptions, valueLabel): if valueLabel == Node.VALUE_LABEL_D_DS2: if not options['D2 derivatives']: return @@ -189,12 +189,12 @@ def getInteractiveFunctions(cls): Supply client with functions for smoothing path parameters. """ return Scaffold_base.getInteractiveFunctions() + [ - ("Smooth D1 arithmetic", lambda region, options, editGroupName: cls.smoothPath(region, options, editGroupName, DerivativeScalingMode.ARITHMETIC_MEAN)), - ("Smooth D1 harmonic", lambda region, options, editGroupName: cls.smoothPath(region, options, editGroupName, DerivativeScalingMode.HARMONIC_MEAN)), - ("Make D2 normal", lambda region, options, editGroupName: cls.makeD2Normal(region, options, editGroupName)), - ("Make D3 normal", lambda region, options, editGroupName: cls.makeD3Normal(region, options, editGroupName)), - ("Smooth D2", lambda region, options, editGroupName: cls.smoothCrossDX(region, options, editGroupName, Node.VALUE_LABEL_D_DS2)), - ("Smooth D3", lambda region, options, editGroupName: cls.smoothCrossDX(region, options, editGroupName, Node.VALUE_LABEL_D_DS3)) + ("Smooth D1 arithmetic", {}, lambda region, options, functionOptions, editGroupName: cls.smoothPath(region, options, functionOptions, editGroupName, DerivativeScalingMode.ARITHMETIC_MEAN)), + ("Smooth D1 harmonic", {}, lambda region, options, functionOptions, editGroupName: cls.smoothPath(region, options, functionOptions, editGroupName, DerivativeScalingMode.HARMONIC_MEAN)), + ("Make D2 normal", {}, lambda region, options, functionOptions, editGroupName: cls.makeD2Normal(region, options, functionOptions, editGroupName)), + ("Make D3 normal", {}, lambda region, options, functionOptions, editGroupName: cls.makeD3Normal(region, options, functionOptions, editGroupName)), + ("Smooth D2", {}, lambda region, options, functionOptions, editGroupName: cls.smoothCrossDX(region, options, functionOptions, editGroupName, Node.VALUE_LABEL_D_DS2)), + ("Smooth D3", {}, lambda region, options, functionOptions, editGroupName: cls.smoothCrossDX(region, options, functionOptions, editGroupName, Node.VALUE_LABEL_D_DS3)) ] diff --git a/src/scaffoldmaker/meshtypes/scaffold_base.py b/src/scaffoldmaker/meshtypes/scaffold_base.py index 6af81e60..760ad201 100644 --- a/src/scaffoldmaker/meshtypes/scaffold_base.py +++ b/src/scaffoldmaker/meshtypes/scaffold_base.py @@ -155,20 +155,7 @@ def generateMesh(cls, region, options): return annotationGroups @classmethod - def smoothDerivativeMagnitudes(cls, region, options, editGroupName, scalingMode : DerivativeScalingMode): - fieldmodule = region.getFieldmodule() - coordinatesField = fieldmodule.findFieldByName('coordinates').castFiniteElement() - groupName = 'cmiss_selection' - selectionGroup = fieldmodule.findFieldByName(groupName).castGroup() - if not selectionGroup.isValid(): - groupName = False # smooth whole model - smoothing = DerivativeSmoothing(region, coordinatesField, groupName, scalingMode, editGroupName) - smoothing.smooth() - del smoothing - return False, True # settings not changed, nodes changed - - @classmethod - def printNodeFieldParameters(cls, region, options, editGroupName): + def printNodeFieldParameters(cls, region, options, functionOptions, editGroupName): ''' Interactive function for printing node field parameters for pasting into code. ''' @@ -182,9 +169,25 @@ def printNodeFieldParameters(cls, region, options, editGroupName): return False, False coordinates = fieldmodule.findFieldByName('coordinates').castFiniteElement() valueLabels, fieldParameters = extract_node_field_parameters(nodeset, coordinates) - print_node_field_parameters(valueLabels, fieldParameters) #, format_string='{:8.3f}') + numberFormat = '{:' + functionOptions['Number format (e.g. 8.3f)'] + '}' + print_node_field_parameters(valueLabels, fieldParameters, numberFormat) return False, False # no change to settings, nor node parameters + @classmethod + def smoothDerivatives(cls, region, options, functionOptions, editGroupName): + fieldmodule = region.getFieldmodule() + coordinatesField = fieldmodule.findFieldByName('coordinates').castFiniteElement() + groupName = 'cmiss_selection' + selectionGroup = fieldmodule.findFieldByName(groupName).castGroup() + if not selectionGroup.isValid(): + groupName = False # smooth whole model + updateDirections = functionOptions['Update directions'] + scalingMode = DerivativeScalingMode.ARITHMETIC_MEAN if functionOptions['Scaling mode']['Arithmetic mean'] else DerivativeScalingMode.HARMONIC_MEAN + smoothing = DerivativeSmoothing(region, coordinatesField, groupName, scalingMode, editGroupName) + smoothing.smooth(updateDirections) + del smoothing + return False, True # settings not changed, nodes changed + @classmethod def getInteractiveFunctions(cls): """ @@ -200,7 +203,11 @@ def getInteractiveFunctions(cls): :return: list(tuples), (name : str, callable(region, options, editGroupName)). """ return [ - ("Smooth derivative magnitudes arithmetic", lambda region, options, editGroupName: cls.smoothDerivativeMagnitudes(region, options, editGroupName, DerivativeScalingMode.ARITHMETIC_MEAN)), - ("Smooth derivative magnitudes harmonic", lambda region, options, editGroupName: cls.smoothDerivativeMagnitudes(region, options, editGroupName, DerivativeScalingMode.HARMONIC_MEAN)), - ("Print node parameters", lambda region, options, editGroupName: cls.printNodeFieldParameters(region, options, editGroupName)) + ("Print node parameters...", + { 'Number format (e.g. 8.3f)': ' 11e' }, + lambda region, options, functionOptions, editGroupName: cls.printNodeFieldParameters(region, options, functionOptions, editGroupName)), + ("Smooth derivatives...", + { 'Update directions': False, + 'Scaling mode': { 'Arithmetic mean': True, 'Harmonic mean': False } }, + lambda region, options, functionOptions, editGroupName: cls.smoothDerivatives(region, options, functionOptions, editGroupName)) ] From d5e0a5a088385f78d60deb70aa3b5b3f78a0035e Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Wed, 9 Dec 2020 12:10:58 +1300 Subject: [PATCH 3/5] Review fixes --- src/scaffoldmaker/utils/derivativemoothing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scaffoldmaker/utils/derivativemoothing.py b/src/scaffoldmaker/utils/derivativemoothing.py index 6ca5d0c1..16e13a22 100644 --- a/src/scaffoldmaker/utils/derivativemoothing.py +++ b/src/scaffoldmaker/utils/derivativemoothing.py @@ -65,7 +65,7 @@ def getDelta(self): componentsCount = len(x1) return [ (x2[c] - x1[c]) for c in range(componentsCount) ] - def getExpresson(self, expressionIndex): + def getExpression(self, expressionIndex): ''' Caller must have called evaluateArcLength! :param parameterIndex: 0 = start x, 1 = start d, 2 = end x, 3 = end d @@ -285,7 +285,7 @@ def addEdgeCurve(self, element, eft, basisLocalNodeFunctions, basisLocalNodes, d else: self._derivativeMap[derivativeKey] = [ derivativeEdge ] - def smooth(self, updateDirections=False,maxIterations=10, arcLengthTolerance=1.0E-6): + def smooth(self, updateDirections=False, maxIterations=10, arcLengthTolerance=1.0E-6): ''' :param maxIterations: Maximum iterations before stopping if not converging. :param arcLengthTolerance: Ratio of difference in arc length from last iteration @@ -357,7 +357,7 @@ def smooth(self, updateDirections=False,maxIterations=10, arcLengthTolerance=1.0 thisx = edge.getParameter(expressionIndex - 1) otherx = edge.getParameter(otherExpressionIndex - 1) bothEndsOnBoundary = False - otherExpression = edge.getExpresson(otherExpressionIndex) + otherExpression = edge.getExpression(otherExpressionIndex) if len(otherExpression) == 1: otherNodeIdentifier, otherValueLabel, otherNodeVersion, otherTotalScaleFactor = otherExpression[0] otherDerivativeKey = (otherNodeIdentifier, otherValueLabel, otherNodeVersion) From b78806956844a443ec67fe7932b0f05a2f8ba0da Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Wed, 9 Dec 2020 13:02:18 +1300 Subject: [PATCH 4/5] Fix recording of edited nodes --- src/scaffoldmaker/utils/derivativemoothing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scaffoldmaker/utils/derivativemoothing.py b/src/scaffoldmaker/utils/derivativemoothing.py index 16e13a22..ea8e3dee 100644 --- a/src/scaffoldmaker/utils/derivativemoothing.py +++ b/src/scaffoldmaker/utils/derivativemoothing.py @@ -307,10 +307,10 @@ def smooth(self, updateDirections=False, maxIterations=10, arcLengthTolerance=1. converged = False if converged: print('Derivative smoothing: Converged after', iter, 'iterations.') - return + break elif (iter == maxIterations): print('Derivative smoothing: Stopping after', maxIterations, 'iterations without converging.') - return + break for derivativeKey, derivativeEdges in self._derivativeMap.items(): edgeCount = len(derivativeEdges) if edgeCount > 1: From 1e6404ef34450fcc37de6b1ee2f0675566c8161e Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Wed, 9 Dec 2020 13:58:44 +1300 Subject: [PATCH 5/5] Merge 1d path interactive functions using options --- .../meshtypes/meshtype_1d_path1.py | 114 ++++++++++-------- 1 file changed, 63 insertions(+), 51 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_1d_path1.py b/src/scaffoldmaker/meshtypes/meshtype_1d_path1.py index e6ac5354..4ebe49f6 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_1d_path1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_1d_path1.py @@ -131,56 +131,66 @@ def generateBaseMesh(cls, region, options): return [] @classmethod - def smoothPath(cls, region, options, functionOptions, editGroupName, mode : DerivativeScalingMode): - x, d1 = extractPathParametersFromRegion(region, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1]) - d1 = smoothCubicHermiteDerivativesLine(x, d1, magnitudeScalingMode=mode) - setPathParameters(region, [ Node.VALUE_LABEL_D_DS1 ], [ d1 ], editGroupName) - return False, True # settings not changed, nodes changed - - @classmethod - def makeD2Normal(cls, region, options, functionOptions, editGroupName): - if not options['D2 derivatives']: - return - d1, d2 = extractPathParametersFromRegion(region, [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2]) - for c in range(len(d1)): - td2 = vector.vectorRejection(d2[c], d1[c]) - d2[c] = vector.setMagnitude(td2, vector.magnitude(d2[c])) - setPathParameters(region, [Node.VALUE_LABEL_D_DS2], [d2], editGroupName) - return False, True # settings not changed, nodes changed - - @classmethod - def makeD3Normal(cls, region, options, functionOptions, editGroupName): - if not options['D3 derivatives']: - return + def makeSideDerivativesNormal(cls, region, options, functionOptions, editGroupName): + makeD2Normal = options['D2 derivatives'] and functionOptions['Make D2 normal'] + makeD3Normal = options['D3 derivatives'] and functionOptions['Make D3 normal'] + if not (makeD2Normal or makeD3Normal): + return False, False + valueLabels = [ Node.VALUE_LABEL_D_DS1 ] if options['D2 derivatives']: - d1, d2, d3 = extractPathParametersFromRegion(region, [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, - Node.VALUE_LABEL_D_DS3]) - for c in range(len(d1)): - d3[c] = vector.setMagnitude(vector.crossproduct3(d1[c], d2[c]), vector.magnitude(d3[c])) - else: - d1, d3 = extractPathParametersFromRegion(region, [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3]) + valueLabels.append(Node.VALUE_LABEL_D_DS2) + if options['D3 derivatives']: + valueLabels.append(Node.VALUE_LABEL_D_DS3) + parameters = extractPathParametersFromRegion(region, valueLabels) + d1 = parameters[0] + modifyParameters = [] + modifyValueLabels = [] + if makeD2Normal: + d2 = parameters[1] for c in range(len(d1)): - td3 = vector.vectorRejection(d3[c], d1[c]) - d3[c] = vector.setMagnitude(td3, vector.magnitude(d3[c])) - setPathParameters(region, [Node.VALUE_LABEL_D_DS3], [d3], editGroupName) + td2 = vector.vectorRejection(d2[c], d1[c]) + d2[c] = vector.setMagnitude(td2, vector.magnitude(d2[c])) + modifyParameters.append(d2) + modifyValueLabels.append(Node.VALUE_LABEL_D_DS2) + if makeD3Normal: + d3 = parameters[-1] + if options['D2 derivatives']: + for c in range(len(d1)): + d3[c] = vector.setMagnitude(vector.crossproduct3(d1[c], d2[c]), vector.magnitude(d3[c])) + else: + for c in range(len(d1)): + td3 = vector.vectorRejection(d3[c], d1[c]) + d3[c] = vector.setMagnitude(td3, vector.magnitude(d3[c])) + modifyParameters.append(d3) + modifyValueLabels.append(Node.VALUE_LABEL_D_DS3) + setPathParameters(region, modifyValueLabels, modifyParameters, editGroupName) return False, True # settings not changed, nodes changed @classmethod - def smoothCrossDX(cls, region, options, editGroupName, functionOptions, valueLabel): - if valueLabel == Node.VALUE_LABEL_D_DS2: - if not options['D2 derivatives']: - return - crossDerivativeLabel = Node.VALUE_LABEL_D2_DS1DS2 - elif valueLabel == Node.VALUE_LABEL_D_DS3: - if not options['D3 derivatives']: - return - crossDerivativeLabel = Node.VALUE_LABEL_D2_DS1DS3 - else: - assert False, 'Invalid value label' - valueLabels = [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, valueLabel, crossDerivativeLabel] - x, d1, d2, d12 = extractPathParametersFromRegion(region, valueLabels) - d12 = smoothCubicHermiteCrossDerivativesLine(x, d1, d2, d12) - setPathParameters(region, [ crossDerivativeLabel ], [ d12 ], editGroupName) + def smoothSideCrossDerivatives(cls, region, options, functionOptions, editGroupName): + smoothD12 = options['D2 derivatives'] and functionOptions['Smooth D12'] + smoothD13 = options['D3 derivatives'] and functionOptions['Smooth D13'] + if not (smoothD12 or smoothD13): + return False, False + valueLabels = [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1 ] + if smoothD12: + valueLabels += [ Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2 ] + if smoothD13: + valueLabels += [ Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3 ] + parameters = extractPathParametersFromRegion(region, valueLabels) + x = parameters[0] + d1 = parameters[1] + modifyParameters = [] + modifyValueLabels = [] + if smoothD12: + d12 = smoothCubicHermiteCrossDerivativesLine(x, d1, parameters[2], parameters[3]) + modifyParameters.append(d12) + modifyValueLabels.append(Node.VALUE_LABEL_D2_DS1DS2) + if smoothD13: + d13 = smoothCubicHermiteCrossDerivativesLine(x, d1, parameters[-2], parameters[-1]) + modifyParameters.append(d13) + modifyValueLabels.append(Node.VALUE_LABEL_D2_DS1DS3) + setPathParameters(region, modifyValueLabels, modifyParameters, editGroupName) return False, True # settings not changed, nodes changed @classmethod @@ -189,12 +199,14 @@ def getInteractiveFunctions(cls): Supply client with functions for smoothing path parameters. """ return Scaffold_base.getInteractiveFunctions() + [ - ("Smooth D1 arithmetic", {}, lambda region, options, functionOptions, editGroupName: cls.smoothPath(region, options, functionOptions, editGroupName, DerivativeScalingMode.ARITHMETIC_MEAN)), - ("Smooth D1 harmonic", {}, lambda region, options, functionOptions, editGroupName: cls.smoothPath(region, options, functionOptions, editGroupName, DerivativeScalingMode.HARMONIC_MEAN)), - ("Make D2 normal", {}, lambda region, options, functionOptions, editGroupName: cls.makeD2Normal(region, options, functionOptions, editGroupName)), - ("Make D3 normal", {}, lambda region, options, functionOptions, editGroupName: cls.makeD3Normal(region, options, functionOptions, editGroupName)), - ("Smooth D2", {}, lambda region, options, functionOptions, editGroupName: cls.smoothCrossDX(region, options, functionOptions, editGroupName, Node.VALUE_LABEL_D_DS2)), - ("Smooth D3", {}, lambda region, options, functionOptions, editGroupName: cls.smoothCrossDX(region, options, functionOptions, editGroupName, Node.VALUE_LABEL_D_DS3)) + ("Make side derivatives normal...", + { 'Make D2 normal': True, + 'Make D3 normal': True }, + lambda region, options, functionOptions, editGroupName: cls.makeSideDerivativesNormal(region, options, functionOptions, editGroupName)), + ("Smooth side cross derivatives...", + { 'Smooth D12' : True, + 'Smooth D13' : True }, + lambda region, options, functionOptions, editGroupName: cls.smoothSideCrossDerivatives(region, options, functionOptions, editGroupName)) ]