Skip to content

Commit

Permalink
Merge pull request #34 from rchristie/user_markers
Browse files Browse the repository at this point in the history
Add UI for user defined marker points
  • Loading branch information
rchristie authored Mar 25, 2022
2 parents 2cd2944 + 5d5ed3f commit 24ea6e5
Show file tree
Hide file tree
Showing 4 changed files with 442 additions and 127 deletions.
137 changes: 80 additions & 57 deletions mapclientplugins/meshgeneratorstep/model/meshgeneratormodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
import copy
import os
import math
import sys

from opencmiss.maths.vectorops import axis_angle_to_rotation_matrix, euler_to_rotation_matrix, matrix_mult, rotation_matrix_to_euler
from opencmiss.utils.zinc.field import findOrCreateFieldStoredMeshLocation, findOrCreateFieldStoredString, fieldIsManagedCoordinates
from opencmiss.maths.vectorops import axis_angle_to_rotation_matrix, euler_to_rotation_matrix, matrix_mult, \
rotation_matrix_to_euler
from opencmiss.utils.zinc.field import findOrCreateFieldStoredMeshLocation, findOrCreateFieldStoredString, \
fieldIsManagedCoordinates
from opencmiss.utils.zinc.finiteelement import evaluateFieldNodesetRange
from opencmiss.utils.zinc.general import ChangeManager

Expand All @@ -19,7 +22,8 @@
from opencmiss.zinc.result import RESULT_OK, RESULT_WARNING_PART_DONE
from opencmiss.zinc.scene import Scene
from opencmiss.zinc.scenecoordinatesystem import SCENECOORDINATESYSTEM_WORLD
from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, findAnnotationGroupByName
from scaffoldmaker.annotation.annotationgroup import findAnnotationGroupByName, getAnnotationMarkerGroup, \
getAnnotationMarkerLocationField, getAnnotationMarkerNameField
from scaffoldmaker.scaffolds import Scaffolds
from scaffoldmaker.scaffoldpackage import ScaffoldPackage
from scaffoldmaker.utils.exportvtk import ExportVtk
Expand All @@ -40,7 +44,7 @@ def parseListFloat(text: str, delimiter=','):
for s in text.split(delimiter):
try:
values.append(float(s))
except:
except ValueError:
print('Invalid float')
values.append(0.0)
return values
Expand All @@ -57,7 +61,7 @@ def parseListInt(text: str, delimiter=','):
for s in text.split(delimiter):
try:
values.append(int(s))
except:
except ValueError:
print('Invalid integer')
values.append(0)
return values
Expand All @@ -76,7 +80,7 @@ def parseVector3(vectorText: str, delimiter, defaultValue):
for valueText in vectorText.split(delimiter):
try:
vector.append(float(valueText))
except:
except ValueError:
vector.append(defaultValue)
if len(vector) > 3:
vector = vector[:3]
Expand Down Expand Up @@ -276,6 +280,18 @@ def createUserAnnotationGroup(self):
self.redefineCurrentAnnotationGroupFromSelection()
return self._currentAnnotationGroup

def createUserMarkerAnnotationGroup(self):
"""
Create a new marker annotation group with automatic name.
:return: New annotation group.
"""
self._currentAnnotationGroup = self._scaffoldPackages[-1].createUserAnnotationGroup()
try:
self._currentAnnotationGroup.createMarkerNode(self._scaffoldPackages[-1].getNextNodeIdentifier())
except AssertionError:
pass
return self._currentAnnotationGroup

def deleteAnnotationGroup(self, annotationGroup):
"""
Delete the annotation group. If the current annotation group is deleted, set an empty group.
Expand All @@ -291,13 +307,16 @@ def deleteAnnotationGroup(self, annotationGroup):
def redefineCurrentAnnotationGroupFromSelection(self):
if not self._currentAnnotationGroup:
return False
scene = self._region.getScene()
group = self._currentAnnotationGroup.getGroup()
group.clear()
selectionGroup = get_scene_selection_group(scene)
if selectionGroup:
fieldmodule = self._region.getFieldmodule()
with ChangeManager(fieldmodule):
# can only redefine user annotation groups which are not markers
assert self.isUserAnnotationGroup(self._currentAnnotationGroup)
assert not self._currentAnnotationGroup.isMarker()
fieldmodule = self._region.getFieldmodule()
with ChangeManager(fieldmodule):
self._currentAnnotationGroup.clear()
scene = self._region.getScene()
selectionGroup = get_scene_selection_group(scene)
if selectionGroup:
group = self._currentAnnotationGroup.getGroup()
group.setSubelementHandlingMode(FieldGroup.SUBELEMENT_HANDLING_MODE_FULL)
highest_dimension = group_get_highest_dimension(selectionGroup)
group_add_group_elements(group, selectionGroup, highest_dimension)
Expand All @@ -306,15 +325,23 @@ def redefineCurrentAnnotationGroupFromSelection(self):
group_add_group_elements(selectionGroup, group, highest_dimension)
return True

def setCurrentAnnotationGroupName(self, newName):
def setCurrentAnnotationGroupName(self, newName: str):
"""
Rename current annotation group, but ensure it is a user group and name is not already in use.
:return: True on success, otherwise False
"""
if self._currentAnnotationGroup and self.isUserAnnotationGroup(self._currentAnnotationGroup) and \
(not findAnnotationGroupByName(self.getAnnotationGroups(), newName)):
return self._currentAnnotationGroup.setName(newName)
return False
if not self._currentAnnotationGroup:
print("No current annotation group to set name of", file=sys.stderr)
return False
if self._currentAnnotationGroup.getName() == newName:
return True # don't want to be warned about this
if not self.isUserAnnotationGroup(self._currentAnnotationGroup):
print("Can only rename user-defined annotation groups", file=sys.stderr)
return False
if findAnnotationGroupByName(self.getAnnotationGroups(), newName):
print("Name " + newName + " is in use by another annotation group", file=sys.stderr)
return False
return self._currentAnnotationGroup.setName(newName)

def setCurrentAnnotationGroupOntId(self, newOntId):
"""
Expand All @@ -336,10 +363,10 @@ def getCurrentAnnotationGroup(self):
"""
return self._currentAnnotationGroup

def setCurrentAnnotationGroup(self, annotationGroup: AnnotationGroup):
def setCurrentAnnotationGroup(self, annotationGroup):
"""
Set annotationGroup as current and replace the selection with its objects.
:param annotationGroup: Group to select, or None to clear selection.
:param annotationGroup: AnnotationGroup to select, or None to clear selection.
"""
# print('setCurrentAnnotationGroup', annotationGroup.getName() if annotationGroup else None)
self._currentAnnotationGroup = annotationGroup
Expand Down Expand Up @@ -369,7 +396,8 @@ def _setScaffoldType(self, scaffoldType):
self._settings['scaffoldPackage'] = self._scaffoldPackages[0] = ScaffoldPackage(scaffoldType)
else:
# nested ScaffoldPackage
self._scaffoldPackages[-1] = self.getParentScaffoldType().getOptionScaffoldPackage(self._scaffoldPackageOptionNames[-1], scaffoldType)
self._scaffoldPackages[-1] = self.getParentScaffoldType().getOptionScaffoldPackage(
self._scaffoldPackageOptionNames[-1], scaffoldType)
self._customScaffoldPackage = None
self._unsavedNodeEdits = False
self._parameterSetName = self.getEditScaffoldParameterSetNames()[0]
Expand All @@ -385,15 +413,16 @@ def setScaffoldTypeByName(self, name):
scaffoldType = self._getScaffoldTypeByName(name)
if scaffoldType is not None:
parentScaffoldType = self.getParentScaffoldType()
assert (not parentScaffoldType) or (scaffoldType in parentScaffoldType.getOptionValidScaffoldTypes(self._scaffoldPackageOptionNames[-1])), \
'Invalid scaffold type for parent scaffold'
assert (not parentScaffoldType) or (scaffoldType in parentScaffoldType.getOptionValidScaffoldTypes(
self._scaffoldPackageOptionNames[-1])), 'Invalid scaffold type for parent scaffold'
if scaffoldType != self.getEditScaffoldType():
self._setScaffoldType(scaffoldType)

def getAvailableScaffoldTypeNames(self):
scaffoldTypeNames = []
parentScaffoldType = self.getParentScaffoldType()
validScaffoldTypes = parentScaffoldType.getOptionValidScaffoldTypes(self._scaffoldPackageOptionNames[-1]) if parentScaffoldType else None
validScaffoldTypes = parentScaffoldType.getOptionValidScaffoldTypes(
self._scaffoldPackageOptionNames[-1]) if parentScaffoldType else None
for scaffoldType in self._allScaffoldTypes:
if (not parentScaffoldType) or (scaffoldType in validScaffoldTypes):
scaffoldTypeNames.append(scaffoldType.getName())
Expand Down Expand Up @@ -433,7 +462,7 @@ def getEditScaffoldParameterSetNames(self):
if self.editingRootScaffoldPackage():
return self._scaffoldPackages[0].getScaffoldType().getParameterSetNames()
# may need to change if scaffolds nested two deep
return self.getParentScaffoldType().getOptionScaffoldTypeParameterSetNames( \
return self.getParentScaffoldType().getOptionScaffoldTypeParameterSetNames(
self._scaffoldPackageOptionNames[-1], self._scaffoldPackages[-1].getScaffoldType())

def getDefaultScaffoldPackageForParameterSetName(self, parameterSetName):
Expand All @@ -444,7 +473,7 @@ def getDefaultScaffoldPackageForParameterSetName(self, parameterSetName):
scaffoldType = self._scaffoldPackages[0].getScaffoldType()
return ScaffoldPackage(scaffoldType, {'scaffoldSettings': scaffoldType.getDefaultOptions(parameterSetName)})
# may need to change if scaffolds nested two deep
return self.getParentScaffoldType().getOptionScaffoldPackage( \
return self.getParentScaffoldType().getOptionScaffoldPackage(
self._scaffoldPackageOptionNames[-1], self._scaffoldPackages[-1].getScaffoldType(), parameterSetName)

def getEditScaffoldOption(self, key):
Expand Down Expand Up @@ -541,13 +570,14 @@ def performInteractiveFunction(self, functionName, functionOptions):
"""
Perform interactive function of supplied name for current scaffold.
:param functionName: Name of the interactive function.
:param option: User-modified options to pass to the function.
:param functionOptions: User-modified options to pass to the function.
:return: True if scaffold settings changed.
"""
interactiveFunctions = self.getInteractiveFunctions()
for interactiveFunction in interactiveFunctions:
if interactiveFunction[0] == functionName:
settingsChanged, nodesChanged = interactiveFunction[2](self._region, self._scaffoldPackages[-1].getScaffoldSettings(), functionOptions, 'meshEdits')
settingsChanged, nodesChanged = interactiveFunction[2](
self._region, self._scaffoldPackages[-1].getScaffoldSettings(), functionOptions, 'meshEdits')
if nodesChanged:
self._unsavedNodeEdits = True
self._updateScaffoldEdits()
Expand Down Expand Up @@ -582,6 +612,7 @@ def setParameterSetName(self, parameterSetName):

def setScaffoldOption(self, key, value):
"""
:param key: The exact option/parameter name.
:param value: New option value as a string.
:return: True if other dependent options have changed, otherwise False.
On True return client is expected to refresh all option values in UI.
Expand All @@ -590,7 +621,7 @@ def setScaffoldOption(self, key, value):
settings = self.getEditScaffoldSettings()
oldValue = settings[key]
# print('setScaffoldOption: key ', key, ' value ', str(value))
newValue = None
# newValue = None
try:
if type(oldValue) is bool:
newValue = bool(value)
Expand All @@ -610,7 +641,7 @@ def setScaffoldOption(self, key, value):
assert False, 'Unimplemented type in list for scaffold option'
else:
assert False, 'Unimplemented type in scaffold option'
except:
except ValueError:
print('setScaffoldOption: Invalid value')
return
settings[key] = newValue
Expand Down Expand Up @@ -643,9 +674,8 @@ def deleteElementsSelection(self):
"""
Add the elements in the scene selection to the delete element ranges and delete.
"""
fm = self._region.getFieldmodule()
scene = self._region.getScene()
mesh = self._getMesh()
mesh = self.getMesh()
selectionGroup = scene.getSelectionField().castGroup()
meshGroup = selectionGroup.getFieldElementGroup(mesh).getMeshGroup()
if meshGroup.isValid() and (meshGroup.getSize() > 0):
Expand Down Expand Up @@ -788,9 +818,10 @@ def setDisplayNodeDerivatives(self, triState):
"""
self._settings['displayNodeDerivatives'] = triState
for nodeDerivativeLabel in self._nodeDerivativeLabels:
self._setAllGraphicsVisibility('displayNodeDerivatives' + nodeDerivativeLabel,
bool(triState) and self.isDisplayNodeDerivativeLabels(nodeDerivativeLabel),
selectMode=Graphics.SELECT_MODE_DRAW_SELECTED if (triState == 1) else Graphics.SELECT_MODE_ON)
self._setAllGraphicsVisibility(
'displayNodeDerivatives' + nodeDerivativeLabel,
bool(triState) and self.isDisplayNodeDerivativeLabels(nodeDerivativeLabel),
selectMode=Graphics.SELECT_MODE_DRAW_SELECTED if (triState == 1) else Graphics.SELECT_MODE_ON)

def isDisplayNodeDerivativeLabels(self, nodeDerivativeLabel):
"""
Expand All @@ -801,6 +832,7 @@ def isDisplayNodeDerivativeLabels(self, nodeDerivativeLabel):
def setDisplayNodeDerivativeLabels(self, nodeDerivativeLabel, show):
"""
:param nodeDerivativeLabel: Label from self._nodeDerivativeLabels ('D1', 'D2' ...)
:param show: True to show, False to not show.
"""
shown = nodeDerivativeLabel in self._settings['displayNodeDerivativeLabels']
if show:
Expand All @@ -814,7 +846,8 @@ def setDisplayNodeDerivativeLabels(self, nodeDerivativeLabel, show):
else:
if shown:
self._settings['displayNodeDerivativeLabels'].remove(nodeDerivativeLabel)
self._setAllGraphicsVisibility('displayNodeDerivatives' + nodeDerivativeLabel, show and bool(self.getDisplayNodeDerivatives()))
self._setAllGraphicsVisibility('displayNodeDerivatives' + nodeDerivativeLabel,
show and bool(self.getDisplayNodeDerivatives()))

def isDisplayNodeNumbers(self):
return self._getVisibility('displayNodeNumbers')
Expand Down Expand Up @@ -881,8 +914,9 @@ def needPerturbLines(self):
return False
return self.isDisplayLines() and self.isDisplaySurfaces() and not self.isDisplaySurfacesTranslucent()

def _getMesh(self):
def getMesh(self):
fm = self._region.getFieldmodule()
mesh = None
for dimension in range(3, 0, -1):
mesh = fm.findMeshByDimension(dimension)
if mesh.getSize() > 0:
Expand All @@ -892,18 +926,7 @@ def _getMesh(self):
return mesh

def getMeshDimension(self):
return self._getMesh().getDimension()

def getNodeLocation(self, node_id):
fm = self._region.getFieldmodule()
with ChangeManager(fm):
coordinates = fm.findFieldByName('coordinates')
nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
node = nodes.findNodeByIdentifier(node_id)
fc = fm.createFieldcache()
fc.setNode(node)
_, position = coordinates.evaluateReal(fc, 3)
return self._getSceneTransformationFromAdjustedPosition(position)
return self.getMesh().getDimension()

def getSettings(self):
return self._settings
Expand Down Expand Up @@ -948,7 +971,7 @@ def _deleteElementsInRanges(self):
if (len(self._deleteElementRanges) == 0) or (len(self._scaffoldPackages) > 1):
return
fm = self._region.getFieldmodule()
mesh = self._getMesh()
mesh = self.getMesh()
meshDimension = mesh.getDimension()
nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
with ChangeManager(fm):
Expand Down Expand Up @@ -1074,7 +1097,8 @@ def _setGraphicsTransformation(self):
def _createGraphics(self):
fm = self._region.getFieldmodule()
with ChangeManager(fm):
meshDimension = self.getMeshDimension()
mesh = self.getMesh()
meshDimension = mesh.getDimension()
coordinates = self.getModelCoordinatesField()
componentsCount = coordinates.getNumberOfComponents()
nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
Expand Down Expand Up @@ -1112,12 +1136,10 @@ def _createGraphics(self):
elementDerivativeFields.append(fm.createFieldDerivative(coordinates, d + 1))
elementDerivativesField = fm.createFieldConcatenate(elementDerivativeFields)
cmiss_number = fm.findFieldByName('cmiss_number')
markerGroup = fm.findFieldByName('marker').castGroup()
if not markerGroup.isValid():
markerGroup = fm.createFieldConstant([0.0]) # show nothing to avoid warnings
markerName = findOrCreateFieldStoredString(fm, 'marker_name')
radius = fm.findFieldByName('radius')
markerLocation = findOrCreateFieldStoredMeshLocation(fm, self._getMesh(), name='marker_location')
markerGroup = getAnnotationMarkerGroup(fm)
markerLocation = getAnnotationMarkerLocationField(fm, mesh)
markerName = getAnnotationMarkerNameField(fm)
markerHostCoordinates = fm.createFieldEmbedded(coordinates, markerLocation)

# fixed width glyph size is based on average element size in all dimensions
Expand Down Expand Up @@ -1302,7 +1324,8 @@ def done(self):
def writeModel(self, file_name):
self._region.writeFile(file_name)

def getAnnotationsFilename(self, filename_stem):
@staticmethod
def getAnnotationsFilename(filename_stem):
return filename_stem + '_annotations.csv'

def writeAnnotations(self, filename_stem):
Expand Down
Loading

0 comments on commit 24ea6e5

Please sign in to comment.