Skip to content

Commit

Permalink
Add canAutoAlign API. Make detection on auto-alignment better.
Browse files Browse the repository at this point in the history
  • Loading branch information
hsorby committed Dec 12, 2024
1 parent 270e100 commit a25c47b
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 46 deletions.
128 changes: 82 additions & 46 deletions src/scaffoldfitter/fitterstepalign.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,17 +144,83 @@ def setAlignManually(self, alignManually):
return True
return False

def canAlignGroups(self):
def _alignable_group_count(self):
count = 0
fieldmodule = self._fitter.getFieldmodule()
groups = get_group_list(fieldmodule)
return len(groups) > 0
with ChangeManager(fieldmodule):
for group in groups:
dataGroup = self._fitter.getGroupDataProjectionNodesetGroup(group)
if not dataGroup:
continue
meshGroup = self._fitter.getGroupDataProjectionMeshGroup(group)
if not meshGroup:
continue
count += 1

return count

def _match_markers(self):
writeDiagnostics = self.getDiagnosticLevel() > 0
matches = {}

markerGroup = self._fitter.getMarkerGroup()
if markerGroup is None:
if writeDiagnostics:
print("Align: No marker group to align with.")
return matches

def canAlignMarkers(self):
markerNodeGroup, markerLocation, markerCoordinates, markerName = self._fitter.getMarkerModelFields()
nodes_ok = markerNodeGroup is not None and markerCoordinates is not None and markerName is not None
if markerNodeGroup is None or markerCoordinates is None or markerName is None:
if writeDiagnostics:
print("Align: No marker group, coordinates or name fields.")

return matches

markerDataGroup, markerDataCoordinates, markerDataName = self._fitter.getMarkerDataFields()
data_ok = markerDataGroup is not None and markerDataCoordinates is not None and markerDataName is not None
return nodes_ok and data_ok
if markerDataGroup is None or markerDataCoordinates is None or markerDataName is None:
if writeDiagnostics:
print("Align: No marker data group, coordinates or name fields.")

return matches

modelMarkers = getNodeNameCentres(markerNodeGroup, markerCoordinates, markerName)
dataMarkers = getNodeNameCentres(markerDataGroup, markerDataCoordinates, markerDataName)

# match model and data markers, warn of unmatched markers
for modelName in modelMarkers:
# name match allows case and whitespace differences
matchName = modelName.strip().casefold()
for dataName in dataMarkers:
if dataName.strip().casefold() == matchName:
entry_name = f"{modelName}_marker"
matches[entry_name] = (modelMarkers[modelName], dataMarkers[dataName])
if writeDiagnostics:
print("Align: Model marker '" + modelName + "' found in data" +
(" as '" + dataName + "'" if (dataName != modelName) else ""))
dataMarkers.pop(dataName)
break
else:
if writeDiagnostics:
print("Align: Model marker '" + modelName + "' not found in data")
if writeDiagnostics:
for dataName in dataMarkers:
print("Align: Data marker '" + dataName + "' not found in model")

return matches

def canAutoAlign(self):
group_count = self._alignable_group_count()
matches = self._match_markers()
total = group_count + len(matches)
return total > 2

def canAlignGroups(self):
return self._alignable_group_count() > 2

def canAlignMarkers(self):
matches = self._match_markers()
return len(matches) > 2

def getRotation(self):
return self._rotation
Expand Down Expand Up @@ -264,7 +330,7 @@ def _doAutoAlign(self):
meshGroup = self._fitter.getGroupDataProjectionMeshGroup(group)
if not meshGroup:
continue
groupName = group.getName()
groupName = f"{group.getName()}_group"
# use centre of bounding box as middle of data; previous use of mean was affected by uneven density
minDataCoordinates, maxDataCoordinates = evaluate_field_nodeset_range(dataCoordinates, dataGroup)
middleDataCoordinates = mult(add(minDataCoordinates, maxDataCoordinates), 0.5)
Expand All @@ -275,46 +341,16 @@ def _doAutoAlign(self):
del one

if self._alignMarkers:
markerGroup = self._fitter.getMarkerGroup()
assert markerGroup, "Align: No marker group to align with"

markerNodeGroup, markerLocation, markerCoordinates, markerName = self._fitter.getMarkerModelFields()
assert markerNodeGroup and markerCoordinates and markerName, \
"Align: No marker group, coordinates or name fields"
modelMarkers = getNodeNameCentres(markerNodeGroup, markerCoordinates, markerName)

markerDataGroup, markerDataCoordinates, markerDataName = self._fitter.getMarkerDataFields()
assert markerDataGroup and markerDataCoordinates and markerDataName, \
"Align: No marker data group, coordinates or name fields"
dataMarkers = getNodeNameCentres(markerDataGroup, markerDataCoordinates, markerDataName)

# match model and data markers, warn of unmatched markers
writeDiagnostics = self.getDiagnosticLevel() > 0
for modelName in modelMarkers:
# name match allows case and whitespace differences
matchName = modelName.strip().casefold()
for dataName in dataMarkers:
if dataName.strip().casefold() == matchName:
pointMap[modelName] = (modelMarkers[modelName], dataMarkers[dataName])
if writeDiagnostics:
print("Align: Model marker '" + modelName + "' found in data" +
(" as '" + dataName + "'" if (dataName != modelName) else ""))
dataMarkers.pop(dataName)
break
else:
if writeDiagnostics:
print("Align: Model marker '" + modelName + "' not found in data")
if writeDiagnostics:
for dataName in dataMarkers:
print("Align: Data marker '" + dataName + "' not found in model")
matches = self._match_markers()
pointMap.update(matches)

self._optimiseAlignment(pointMap)

def getTransformationMatrix(self):
'''
"""
:return: 4x4 row-major transformation matrix with first index down rows, second across columns,
suitable for multiplication p' = Mp where p = [ x, y, z, h ].
'''
"""
# apply transformation in order: scale then rotation then translation
if not all((v == 0.0) for v in self._rotation):
rotationMatrix = euler_to_rotation_matrix(self._rotation)
Expand Down Expand Up @@ -342,13 +378,13 @@ def _optimiseAlignment(self, pointMap):
region = self._fitter.getContext().createRegion()
fieldmodule = region.getFieldmodule()

# get centre of mass CM and span of model coordinates and data
# Get centre of mass CM and span of model coordinates and data.
modelsum = [0.0, 0.0, 0.0]
datasum = [0.0, 0.0, 0.0]
modelMin = copy.deepcopy(list(pointMap.values())[0][0])
modelMax = copy.deepcopy(list(pointMap.values())[0][0])
dataMin = copy.deepcopy(list(pointMap.values())[0][1])
dataMax = copy.deepcopy(list(pointMap.values())[0][1])
modelMin = [math.inf] * 3 # copy.deepcopy(list(pointMap.values())[0][0])
modelMax = [-math.inf] * 3 # copy.deepcopy(list(pointMap.values())[0][0])
dataMin = [math.inf] * 3 # copy.deepcopy(list(pointMap.values())[0][1])
dataMax = [-math.inf] * 3 # copy.deepcopy(list(pointMap.values())[0][1])
for name, positions in pointMap.items():
modelx = positions[0]
datax = positions[1]
Expand Down
4 changes: 4 additions & 0 deletions tests/test_fitcube.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ def test_alignMarkersFitRegularData(self):
self.assertEqual(2, len(fitter.getFitterSteps()))
self.assertTrue(align.setAlignMarkers(True))
self.assertTrue(align.isAlignMarkers())
self.assertTrue(align.canAlignMarkers())
self.assertTrue(align.canAlignGroups())
self.assertTrue(align.canAutoAlign())

align.run()
# fitter.getRegion().writeFile(os.path.join(here, "resources", "km_fitgeometry2.exf"))
rotation = align.getRotation()
Expand Down

0 comments on commit a25c47b

Please sign in to comment.