mascaf.morphology_graph

class mascaf.morphology_graph.MorphologyGraph(*args, **kwargs)[source]

Bases: Graph3D

Graph representation of neuronal morphology with radii.

This class subclasses networkx.Graph and can contain cycles. Nodes should be keyed directly by their junction id and store at least the attributes xyz (3-vector) and radius (float). Use to_swc_file() to export to SWC format, which breaks cycles by duplicating nodes.

__init__(*args, **kwargs)[source]

Initialize a graph with edges, name, or graph attributes.

Parameters:
  • incoming_graph_data (input graph (optional, default: None)) – Data to initialize graph. If None (default) an empty graph is created. The data can be an edge list, or any NetworkX graph object. If the corresponding optional Python packages are installed the data can also be a 2D NumPy array, a SciPy sparse array, or a PyGraphviz graph.

  • attr (keyword arguments, optional (default= no attributes)) – Attributes to add to graph as key=value pairs.

Return type:

None

See also

convert

Examples

>>> G = nx.Graph()  # or DiGraph, MultiGraph, MultiDiGraph, etc
>>> G = nx.Graph(name="my graph")
>>> e = [(1, 2), (2, 3), (3, 4)]  # list of edges
>>> G = nx.Graph(e)

Arbitrary graph attribute pairs (key=value) may be assigned

>>> G = nx.Graph(e, day="Friday")
>>> G.graph
{'day': 'Friday'}
position_attr = 'xyz'
classmethod from_swc_file(path)[source]

Load a MorphologyGraph from an SWC file, restoring cycles.

Reads an SWC file and creates a MorphologyGraph. If the file contains CYCLE_BREAK annotations in the header (generated by to_swc_file), this method will restore the original cycles by reconnecting duplicate nodes to their originals.

Parameters:

path (str) – Path to the SWC file to load.

Returns:

Graph with nodes containing ‘xyz’ and ‘radius’ attributes. If CYCLE_BREAK annotations are present, cycles are restored.

Return type:

MorphologyGraph

Examples

>>> graph = MorphologyGraph.from_swc_file("output.swc")
>>> graph.print_attributes()
add_junction(j)[source]

Add a Junction as a graph node.

The node key is j.id; stored attributes are xyz and radius.

Parameters:

j (Junction) – The junction to add.

Return type:

None

copy()[source]

Return a deep copy of the graph with all node arrays copied.

Returns:

A new graph with the same topology and independent node data.

Return type:

MorphologyGraph

to_swc_model()[source]

Convert MorphologyGraph to a SWCModel instance.

Creates a SWCModel by adding all nodes with their attributes (x, y, z, r, t) and edges from this graph. Note that SWCModel is also a NetworkX Graph, so this is a conversion between graph types.

Returns:

A SWCModel instance with the same topology and attributes.

Return type:

SWCModel

Examples

>>> graph = MorphologyGraph()
>>> # ... add nodes and edges ...
>>> swc_model = graph.to_swc_model()
>>> frusta = FrustaSet.from_swc_model(swc_model)
compute_volume(account_for_overlaps=False)[source]

Compute total volume of the morphology as sum of frustum segments.

Each edge represents a truncated cone (frustum) connecting two nodes. The volume of a frustum is: V = (π*h/3) * (r1² + r1*r2 + r2²) where h is the length and r1, r2 are the radii at the endpoints.

For nodes with degree > 2 (branch points), overlap correction is applied by subtracting half a ball volume per edge beyond 2.

Parameters:

account_for_overlaps (bool) – If True, subtract branch-point overlap corrections from the naive frustum sum (half a ball volume per edge beyond two at each junction).

Returns:

Total volume of all segments in the morphology.

Return type:

float

Examples

>>> graph = MorphologyGraph()
>>> # ... add nodes and edges ...
>>> volume = graph.compute_volume()
compute_surface_area(account_for_overlaps=False)[source]

Compute total lateral surface area of the morphology.

Each edge represents a truncated cone (frustum) connecting two nodes. The lateral surface area is: A = π * (r1 + r2) * s where s = sqrt(h² + (r1 - r2)²) is the slant height.

End caps are added for terminal nodes (degree 1). For nodes with degree > 2 (branch points), overlap correction is applied by subtracting quarter of a ball surface area per edge beyond 2.

Parameters:

account_for_overlaps (bool) – If True, subtract branch-point overlap corrections from the naive sum (quarter of a ball surface area per edge beyond two at each junction).

Returns:

Total lateral surface area of all segments in the morphology.

Return type:

float

Examples

>>> graph = MorphologyGraph()
>>> # ... add nodes and edges ...
>>> area = graph.compute_surface_area()
scale_radii_to_match_mesh(mesh, metric='surface_area', account_for_overlaps=False)[source]

Scale all radii to match the mesh’s surface area or volume.

This method finds a uniform scaling factor k for all radii such that the cable model’s surface area or volume (as defined by compute_surface_area() / compute_volume()) equals that of the input mesh. Because lateral frustum area and volume do not scale as pure powers of k when edge lengths are fixed, k is computed with a one-dimensional root solve (not sqrt / cbrt of a single ratio).

Parameters:
  • mesh (trimesh.Trimesh or MeshManager) – The mesh to match. Can be either a trimesh.Trimesh object or a MeshManager instance.

  • metric (str) – Which metric to match. Options are: - “surface_area”: Match total surface area - “volume”: Match total volume

  • account_for_overlaps (bool) – If True, subtract branch-point overlap corrections when computing the morphology’s surface area or volume (same as for compute_surface_area() / compute_volume()).

Returns:

The scaling factor applied to all radii.

Return type:

float

Raises:

ValueError – If metric is not “surface_area” or “volume”, or if the mesh has zero area/volume, or if the morphology has zero area/volume.

Examples

>>> from mascaf import MeshManager, MorphologyGraph
>>> mesh_mgr = MeshManager(mesh_path="neuron.obj")
>>> graph = MorphologyGraph.from_swc_file("output.swc")
>>> scale_factor = graph.scale_radii_to_match_mesh(mesh_mgr)
>>> print(f"Radii scaled by factor: {scale_factor:.3f}")
print_attributes(*, node_info=False, edge_info=False)[source]

Print graph attributes and optional node/edge details.

Parameters:
  • node_info (bool) – If True, print per-node attributes (xyz, radius, and any other attributes).

  • edge_info (bool) – If True, print all edges (u – v) with edge attributes if any.

Return type:

None

to_swc_file(path=None, *, tag=3, annotate_breaks=True)[source]

Export the skeleton to SWC format, breaking cycles by duplicating nodes.

The SWC format is a line-based format with columns n T x y z R parent, where n is the node id, T is the SWC type index, x,y,z are coordinates, R is radius, and parent is the parent’s id (or -1 for the root).

Constructs a spanning forest over the undirected graph; for every non-tree edge that would introduce a cycle it duplicates one endpoint and attaches the duplicate as a child of the other endpoint.

Parameters:
  • path (str | None) – If provided, write the SWC text to this file. If None, return the SWC text as a string.

  • tag (int) – Integer placed in the SWC T (type) column for all nodes.

  • annotate_breaks (bool) – If True, include header comment lines indicating how to reconnect duplicates to restore each broken cycle.

Returns:

The SWC text (whether or not it was also written to a file).

Return type:

str

Raises:

KeyError – If any node is missing the xyz or radius attribute.

class mascaf.morphology_graph.Junction(id, xyz, radius)[source]

Bases: object

Container for a traced skeleton node.

Fields mirror what the tracing pipeline in trace.py constructs for each sample along a polyline. The essential geometry is in xyz (XYZ) and radius; other fields are retained for diagnostics/bookkeeping.

Parameters:
id: int
xyz: ndarray
radius: float
__init__(id, xyz, radius)
Parameters:
Return type:

None