Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New annotated, poseable body scaffold #265

Merged
merged 20 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 35 additions & 15 deletions src/scaffoldmaker/annotation/body_terms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,41 @@

# convention: preferred name, preferred id, followed by any other ids and alternative names
body_terms = [
( "abdomen", "UBERON:0000916", "ILX:0725977" ),
( "thorax", "ILX:0742178" ),
( "neck", "UBERON:0000974", "ILX:0733967" ),
( "head", "UBERON:0000033", "ILX:0104909" ),
( "neck core", "" ),
( "head core", "" ),
( "skin epidermis", "UBERON:0001003", "ILX:0728574" ),
( "diaphragm", "UBERON:0001103", "ILX:0103194" ),
( "spinal cord", "UBERON:0002240", "ILX:0110909" ),
( "body", "UBERON:0000468", "ILX:0101370" ),
( "core", "" ),
( "non core", "" ),
( "core boundary", "" ),
( "arm", "UBERON:0001460"),
( "leg", "UBERON:0000978")
("abdomen", "UBERON:0000916", "ILX:0725977"),
("abdominal cavity", "UBERON:0003684"),
("abdominal cavity boundary", ""),
("abdominopelvic cavity", "UBERON:0035819"),
("arm", "UBERON:0001460"),
("left arm", "FMA:24896"),
("left arm skin epidermis", ""),
("right arm", "FMA:24895"),
("right arm skin epidermis", ""),
("body", "UBERON:0000468", "ILX:0101370"),
("core", ""),
("core boundary", ""),
("dorsal", ""),
("head", "UBERON:0000033", "ILX:0104909"),
("head core", ""),
("diaphragm", "UBERON:0001103", "ILX:0103194"),
("hand", "FMA:9712"),
("left", ""),
("leg", "UBERON:0000978"),
("left leg", "FMA:24981"),
("left leg skin epidermis", ""),
("right leg", "FMA:24980"),
("right leg skin epidermis", ""),
("foot", "FMA:9664"),
("neck", "UBERON:0000974", "ILX:0733967"),
("neck core", ""),
("non core", ""),
("right", ""),
("shell", ""),
("skin epidermis", "UBERON:0001003", "ILX:0728574"),
("spinal cord", "UBERON:0002240", "ILX:0110909"),
("thoracic cavity", "UBERON:0002224"),
("thoracic cavity boundary", ""),
("thorax", "ILX:0742178"),
("ventral", "")
]

def get_body_term(name : str):
Expand Down
77 changes: 68 additions & 9 deletions src/scaffoldmaker/meshtypes/meshtype_1d_network_layout1.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ class MeshType_1d_network_layout1(Scaffold_base):
"Bifurcation": "1-2.1,2.2-3,2.3-4",
"Converging bifurcation": "1-3.1,2-3.2,3.3-4",
"Loop": "1-2-3-4-5-6-7-8-1",
"Snake": "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33",
"Sphere cube": "1.1-2.1,1.2-3.1,1.3-4.1,2.2-5.2,2.3-6.1,3.2-6.2,3.3-7.1,4.2-7.2,4.3-5.1,5.3-8.1,6.3-8.2,7.3-8.3",
"Trifurcation": "1-2.1,2.2-3,2.3-4,2.4-5",
"Trifurcation cross": "1-3.1,2-3.2,3.2-4,3.1-5"
"Trifurcation cross": "1-3.1,2-3.2,3.2-4,3.1-5",
"Vase": "1-2-3-4-5"
}

@classmethod
Expand Down Expand Up @@ -69,7 +71,7 @@ def generateBaseMesh(cls, region, options):
:param options: Dict containing options. See getDefaultOptions().
:return: [] empty list of AnnotationGroup, NetworkMesh
"""
parameterSetName = options['Base parameter set']
parameterSetName = options["Base parameter set"]
structure = options["Structure"]
defineInnerCoordinates = options["Define inner coordinates"]
networkMesh = NetworkMesh(structure)
Expand Down Expand Up @@ -101,6 +103,36 @@ def generateBaseMesh(cls, region, options):
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, d2)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, d3)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, d13)
elif "Snake" in parameterSetName:
snakeRadius = 0.5
tubeRadius = 0.1
nodesCount = nodes.getSize()
elementsCountHalfCircle = 8
elementAngle = math.pi / elementsCountHalfCircle
d1Mag = snakeRadius * elementAngle
xSign = -1.0
xOffset = -snakeRadius
for n in range(nodesCount):
halfCircle = (n % elementsCountHalfCircle) == 0
if halfCircle:
xSign = -xSign
xOffset += 2.0 * snakeRadius
angle = elementAngle * n
cosAngle = math.cos(angle)
sinAngle = math.sin(angle)
node = nodes.findNodeByIdentifier(n + 1)
fieldcache.setNode(node)
x = [xOffset - xSign * snakeRadius * cosAngle, snakeRadius * sinAngle, 0.0]
d1 = [xSign * d1Mag * sinAngle, d1Mag * cosAngle, 0.0]
d2 = [-tubeRadius * cosAngle, xSign * tubeRadius * sinAngle, 0.0]
d12Sign = 0.0 if halfCircle else xSign
d12 = mult(d1, d12Sign * elementAngle * tubeRadius / d1Mag)
d3 = [0.0, 0.0, tubeRadius]
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, x)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, d1)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, d2)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, d12)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, d3)
elif "Sphere cube" in parameterSetName:
# edit node parameters
sphereRadius = 0.5
Expand Down Expand Up @@ -156,21 +188,47 @@ def generateBaseMesh(cls, region, options):
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, v + 1, cd2[n][v])
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, v + 1, cd3[n])
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D2_DS1DS3, v + 1, cd13[n][v])
elif "Vase" in parameterSetName:
midRadius = 1.0
magRadius = 0.5
nodesCount = nodes.getSize()
elementsCountWavelength = 4
elementAngle = 2.0 * math.pi / elementsCountWavelength
for n in range(nodesCount):
angle = elementAngle * n
cosAngle = math.cos(angle)
sinAngle = math.sin(angle)
node = nodes.findNodeByIdentifier(n + 1)
fieldcache.setNode(node)
x = [0.0, 0.0, n]
d1 = [0.0, 0.0, 1.0]
r = midRadius + magRadius * sinAngle
d2 = [0.0, r, 0.0]
d12 = [0.0, 2.0 * magRadius * cosAngle, 0.0]
d3 = [r, 0.0, 0.0]
d13 = [2.0 * magRadius * cosAngle, 0.0, 0.0]
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, x)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, d1)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, d2)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, d12)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, d3)
coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, d13)

if defineInnerCoordinates:
cls._defineInnerCoordinates(region, coordinates, options, networkMesh)
cls.defineInnerCoordinates(region, coordinates, options, networkMesh)

return [], networkMesh

@classmethod
def _defineInnerCoordinates(cls, region, coordinates, options, networkMesh):
def defineInnerCoordinates(cls, region, coordinates, options, networkMesh, innerProportion=0.8):
"""
Copy coordinates to inner coordinates via in-memory model file.
Assign using the interactive function.
:param region: Region to define field in.
:param coordinates: Standard/outer coordinate field.
:param options: Options used to generate scaffold.
:param networkMesh: Network mesh object used to generate scaffold.
:param innerProportion: Proportion of outer coordinates to assign to inner, typically 0.0 < p < 1.0.
"""
assert options["Define inner coordinates"]
coordinates.setName("inner coordinates") # temporarily rename
Expand All @@ -186,8 +244,8 @@ def _defineInnerCoordinates(cls, region, coordinates, options, networkMesh):
"To field": {"coordinates": False, "inner coordinates": True},
"From field": {"coordinates": True, "inner coordinates": False},
"Mode": {"Scale": True, "Offset": False},
"D2 value": 0.8,
"D3 value": 0.8}
"D2 value": innerProportion,
"D3 value": innerProportion}
cls.assignCoordinates(region, options, networkMesh, functionOptions, editGroupName=None)

@classmethod
Expand All @@ -207,13 +265,14 @@ def editStructure(cls, region, options, networkMesh, functionOptions, editGroupN
with ChangeManager(fieldmodule):
clearRegion(region)
structure = options["Structure"] = functionOptions["Structure"]
options["Base parameter set"] = "Default" # to not assign coordinates for one of the special sets
networkMesh.build(structure)
networkMesh.create1DLayoutMesh(region)
coordinates = find_or_create_field_coordinates(fieldmodule).castFiniteElement()
coordinates.setManaged(True) # since cleared by clearRegion
defineInnerCoordinates = options["Define inner coordinates"]
if defineInnerCoordinates:
cls._defineInnerCoordinates(region, coordinates, options, networkMesh)
cls.defineInnerCoordinates(region, coordinates, options, networkMesh)

return True, False # settings changed, nodes not changed (since reset to original coordinates)

Expand Down Expand Up @@ -345,8 +404,8 @@ def makeSideDerivativesNormal(cls, region, options, networkMesh, functionOptions
print("Make side derivatives normal: inner coordinates field not defined")
return False, False
useCoordinates = coordinates if functionOptions["Field"]["coordinates"] else innerCoordinates
makeD2Normal = functionOptions['Make D2 normal']
makeD3Normal = functionOptions['Make D3 normal']
makeD2Normal = functionOptions["Make D2 normal"]
makeD3Normal = functionOptions["Make D3 normal"]
if not (makeD2Normal or makeD3Normal):
return False, False
nodeset = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
Expand Down
14 changes: 14 additions & 0 deletions src/scaffoldmaker/meshtypes/meshtype_2d_tubenetwork1.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ def getDefaultOptions(cls, parameterSetName="Default"):
"Number of elements around": 8,
"Annotation numbers of elements around": [0],
"Target element density along longest segment": 4.0,
"Annotation numbers of elements along": [0],
"Show trim surfaces": False
}
if parameterSetName in ["Loop", "Snake", "Vase"]:
options["Target element density along longest segment"] = 12.0
return options

@staticmethod
Expand All @@ -38,6 +41,7 @@ def getOrderedOptionNames():
"Number of elements around",
"Annotation numbers of elements around",
"Target element density along longest segment",
"Annotation numbers of elements along",
"Show trim surfaces"
]

Expand Down Expand Up @@ -88,6 +92,15 @@ def checkOptions(cls, options):
annotationElementsCountsAround[i] = 4
if options["Target element density along longest segment"] < 1.0:
options["Target element density along longest segment"] = 1.0
annotationAlongCounts = options["Annotation numbers of elements along"]
if len(annotationAlongCounts) == 0:
options["Annotation numbers of elements along"] = [0]
else:
for i in range(len(annotationAlongCounts)):
if annotationAlongCounts[i] <= 0:
annotationAlongCounts[i] = 0
elif annotationAlongCounts[i] < 1:
annotationAlongCounts[i] = 1
return dependentChanges

@classmethod
Expand All @@ -109,6 +122,7 @@ def generateBaseMesh(cls, region, options):
defaultElementsCountAround=options["Number of elements around"],
elementsCountThroughWall=1,
layoutAnnotationGroups=networkLayout.getAnnotationGroups(),
annotationElementsCountsAlong=options["Annotation numbers of elements along"],
annotationElementsCountsAround=options["Annotation numbers of elements around"])
tubeNetworkMeshBuilder.build()
generateData = TubeNetworkMeshGenerateData(
Expand Down
18 changes: 15 additions & 3 deletions src/scaffoldmaker/meshtypes/meshtype_3d_boxnetwork1.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ def getParameterSetNames(cls):
def getDefaultOptions(cls, parameterSetName="Default"):
options = {
"Network layout": ScaffoldPackage(MeshType_1d_network_layout1, defaultParameterSetName=parameterSetName),
"Target element density along longest segment": 4.0
"Target element density along longest segment": 4.0,
"Annotation numbers of elements along": [0]
}
return options

@classmethod
def getOrderedOptionNames(cls):
return [
"Network layout",
"Target element density along longest segment"
"Target element density along longest segment",
"Annotation numbers of elements along"
]

@classmethod
Expand Down Expand Up @@ -70,6 +72,15 @@ def checkOptions(cls, options):
options["Network layout"] = cls.getOptionScaffoldPackage("Network layout", MeshType_1d_network_layout1)
if options["Target element density along longest segment"] < 1.0:
options["Target element density along longest segment"] = 1.0
annotationAlongCounts = options["Annotation numbers of elements along"]
if len(annotationAlongCounts) == 0:
options["Annotation numbers of elements along"] = [0]
else:
for i in range(len(annotationAlongCounts)):
if annotationAlongCounts[i] <= 0:
annotationAlongCounts[i] = 0
elif annotationAlongCounts[i] < 1:
annotationAlongCounts[i] = 1
dependentChanges = False
return dependentChanges

Expand All @@ -83,14 +94,15 @@ def generateBaseMesh(cls, region, options):
"""
networkLayout = options["Network layout"]
targetElementDensityAlongLongestSegment = options["Target element density along longest segment"]
annotationElementsCountsAlong = options["Annotation numbers of elements along"]

layoutRegion = region.createRegion()
networkLayout.generate(layoutRegion) # ask scaffold to generate to get user-edited parameters
layoutAnnotationGroups = networkLayout.getAnnotationGroups()
networkMesh = networkLayout.getConstructionObject()

boxNetworkMeshBuilder = BoxNetworkMeshBuilder(
networkMesh, targetElementDensityAlongLongestSegment, layoutAnnotationGroups)
networkMesh, targetElementDensityAlongLongestSegment, layoutAnnotationGroups, annotationElementsCountsAlong)
boxNetworkMeshBuilder.build()
generateData = BoxNetworkMeshGenerateData(region)
boxNetworkMeshBuilder.generateMesh(generateData)
Expand Down
Loading