import copy
from pykdtree.kdtree import KDTree
import time
from datasci_tools import numpy_dep as np
from datasci_tools import module_utils as modu
from datasci_tools import general_utils as gu
non_optional_features = [
"mesh",
"mesh_face_idx",
"spines",
'spines_obj',
'spines_volume',
'boutons',
'boutons_cdfs',
'boutons_volume',
'head_neck_shaft_idx',
'synapses',
]
only_keep_upstream = [
'web',
'web_cdf',
]
non_optional_features_recalculate = [
"_mesh_volume",
"mesh_center",
]
optional_features = [
"labels",
"endpoints",
"skeleton",
'width',
'width_array',
'width_new'
]
optional_features_recalculate = [
"_endpoints_nodes",
"_skeleton_graph",
]
[docs]def skeleton_adjust(
branch_obj,
skeleton = None,
skeleton_append = None,):
"""
Purpose: To adjust the skeleton
of a branch and then have the
endpoints readjusted
Pseudocode:
1) Adjust skeleton (by stacking or reassingning)
2) recalculate the new endpoints
3) pass back the branch
"""
if skeleton_append is not None:
skeleton = sk.stack_skeletons([branch_obj.skeleton,
skeleton_append])
if skeleton is None:
raise Exception("")
branch_obj.skeleton = skeleton
branch_obj.endpoints = None
branch_obj.calculate_endpoints()
branch_obj._skeleton_graph = None
branch_obj._endpoints_nodes = None
return branch_obj
[docs]def add_jitter_to_endpoint(
branch_obj,
endpoint,
jitter = 2,
verbose = False,
):
"""
Purpose: to add jitter to a
branches coordinate to move it by a certain amount
(and pass back the branch moved)
Pseudocode:
1) Create jitter segment
2) Adjust the branch skeleton
3) Pass back the jitter segment
"""
jitter_sk = sk.jitter_skeleton_from_coordinate(
coordinate =endpoint,
verbose = verbose,
)
bu.skeleton_adjust(
branch_obj,
skeleton_append = jitter_sk,
)
return jitter_sk
[docs]def combine_attr_lists(list_1,list_2,verbose = False):
if list_1 is None and list_2 is None:
if verbose:
print(f"Both None")
return None
if list_1 is None:
if verbose:
print(f"list_1 None")
return copy.deepcopy(list_2)
elif list_2 is None:
if verbose:
print(f"list_2 None")
return copy.deepcopy(list_1)
else:
if type(list_1) == list and type(list_2) == list:
if verbose:
print(f"both lists")
return list_1 + list_2
else:
if verbose:
print(f"both arrays")
return np.concatenate([list_1,list_2])
[docs]def combine_branches(
branch_upstream,
branch_downstream,
add_skeleton = True,
add_labels = False,
verbose = True,
common_endpoint = None,
return_jitter_segment = False,
):
"""
Purpose: To combine two branch objects together
WHERE IT IS ASSUMED THEY SHARE ONE COMMON ENDPOINT
Ex:
from neurd import branch_utils as bu
branch_upstream = copy.deepcopy(neuron_obj[0][upstream_branch])
branch_downstream= copy.deepcopy(neuron_obj[0][downstream_branch])
branch_upstream.labels = ["hellow"]
branch_downstream.labels = ["my","new","labels"]
b_out = bu.combine_branches(
branch_upstream,
branch_downstream,
verbose = True,
add_skeleton = False,
add_labels = False
)
"""
for branch_obj in [branch_upstream,branch_downstream]:
if (len(branch_obj.width_array["no_spine_median_mesh_center"]) !=
len(branch_obj.skeletal_coordinates_upstream_to_downstream) - 1):
raise Exception("")
debug_time = False
st = time.time()
b_up = branch_upstream
b_d = branch_downstream
b_obj = copy.deepcopy(b_up)
if debug_time:
print(f"Copying branch: {time.time() - st}")
st = time.time()
# --working on the non-optional_features
face_offset = len(b_up.mesh.faces)
b_obj.mesh = tu.combine_meshes([b_up.mesh,
b_d.mesh
])
if verbose:
print(f"b_up.mesh.faces = {b_up.mesh.faces.shape}")
print(f"b_d.mesh.faces = {b_d.mesh.faces.shape}")
print(f"b_obj.mesh = {b_obj.mesh.faces.shape}")
if debug_time:
print(f"Combining meshes: {time.time() - st}")
st = time.time()
b_obj.mesh_face_idx = np.concatenate([
b_up.mesh_face_idx,
b_d.mesh_face_idx])
# if verbose:
# print(f"b_up.mesh_face_idx = {b_up.mesh_face_idx}")
# print(f"b_d.mesh_face_idx = {b_d.mesh_face_idx}")
# print(f"b_obj.mesh_face_idx= {b_obj.mesh_face_idx}")
# --------- setting the spine info -----------
spine_objs_to_add = []
if b_d.spines is not None:
if b_d.spines_obj is not None:
spine_objs_to_add = [spu.adjust_obj_with_face_offset(k,
face_offset=face_offset)
for k in b_d.spines_obj]
else:
spine_objs_to_add = []
for sp_attr in ["spines","spines_volume"]:
if getattr(b_obj,sp_attr) is None:
setattr(b_obj,sp_attr,[])
if sp_attr == "spines_obj":
curr_value = spine_objs_to_add
else:
curr_value = getattr(b_d,sp_attr)
setattr(b_obj,sp_attr,getattr(b_obj,sp_attr) + curr_value)
if debug_time:
print(f"Spine adjustment: {time.time() - st}")
st = time.time()
if verbose:
print(f"Total number of spines = {b_obj.n_spines}")
if debug_time:
print(f"computing number of spines : {time.time() - st}")
st = time.time()
# --------- setting the bouton and spines info -----------
for k in ['boutons',
'boutons_cdfs',
'boutons_volume',
"head_neck_shaft_idx",]:
if debug_time:
print(f"combining {k}: {time.time() - st}")
st = time.time()
setattr(b_obj,k,combine_attr_lists(getattr(b_up,k),getattr(b_d,k)))
if debug_time:
print(f"combining lists: {time.time() - st}")
st = time.time()
#------ adjusting the synapses ------------------
synapses_to_add = [syu.adjust_obj_with_face_offset(k,face_offset)
for k in b_d.synapses]
if debug_time:
print(f"synapses offset: {time.time() - st}")
st = time.time()
# ---------------- PARTS TO RECALCULATE
b_obj._mesh_volume = None
b_obj.mesh_volume
if verbose:
print(f"b_obj.mesh_volume = {b_obj.mesh_volume}")
# ---------------- Do skeleton combination ------------------------------
try:
match_idx_1,match_idx_2 = sk.matching_endpoint_singular(
b_obj.endpoints,b_d.endpoints,
return_indices=True,
verbose = False
)
jitter_segment = None
except Exception as e:
if common_endpoint is None:
raise Exception(f"{e}")
match_idx_1 = nu.matching_row_index(
b_obj.endpoints,common_endpoint)
match_idx_2 = nu.matching_row_index(
b_d.endpoints,common_endpoint)
jitter_segment = bu.add_jitter_to_endpoint(
b_d,
b_d.endpoints[1-match_idx_2],
verbose = verbose
)
print(f"b_d.endpoints = {b_d.endpoints}")
upstream_coordinate = b_obj.endpoints[1-match_idx_1]
if add_skeleton:
if verbose:
print(f"Adding skeleton of downstream branch")
sk_len_weights = [b_up.skeletal_length,b_d.skeletal_length]
skeleton_add = b_d.skeleton
width_array_add = b_d.width_array_upstream_to_downstream
new_width_array = {k:np.concatenate([v,width_array_add[k]])
for k,v in b_obj.width_array_upstream_to_downstream.items()}
#calculate a new width array distances to keep track if not computed normally
width_array_skeletal_lengths_d = b_d.width_array_skeletal_lengths_upstream_to_downstream
if width_array_skeletal_lengths_d is None:
width_array_skeletal_lengths_d = bu.skeletal_coordinates_dist_upstream_to_downstream(b_d,cumsum=False)
width_array_skeletal_lengths_b = b_obj.width_array_skeletal_lengths_upstream_to_downstream
if width_array_skeletal_lengths_b is None:
width_array_skeletal_lengths_b = bu.skeletal_coordinates_dist_upstream_to_downstream(b_obj,cumsum=False)
new_width_array_skeletal_lengths = np.concatenate([width_array_skeletal_lengths_b,width_array_skeletal_lengths_d])
b_obj.width = nu.weighted_average([b_up.width,b_d.width],sk_len_weights)
b_obj.width_new = {k:nu.weighted_average([v,b_d.width_new[k]],sk_len_weights)
for k,v in b_obj.width_new.items()}
b_obj.skeleton = sk.stack_skeletons([b_up.skeleton,skeleton_add])
b_obj.calculate_endpoints()
b_obj.order_skeleton_by_smallest_endpoint()
# ------------ Parts to recalculates ----------
b_obj._skeleton_graph = None
b_obj._endpoints_nodes = None
b_obj.skeleton_graph
b_obj.endpoints_nodes
if not np.array_equal(b_obj.endpoints[0],upstream_coordinate):
new_width_array = {k:np.flip(v) for k,v in new_width_array.items()}
new_width_array_skeletal_lengths = np.flip(new_width_array_skeletal_lengths)
b_obj.width_array = new_width_array
b_obj.width_array_skeletal_lengths = new_width_array_skeletal_lengths
if verbose:
print(f"Original lengths = {sk_len_weights}")
print(f"New skeleton length = {b_obj.skeletal_length}")
print(f"New endpoints calculated = {sk.find_skeleton_endpoint_coordinates(b_obj.skeleton)}")
print(f"Adjusted endpoints = {b_obj.endpoints}")
else:
if verbose:
print(f"Not adding skeleton")
# adjust the closest skeleton coordinates of all the synapses to add
for sy in synapses_to_add:
sy.closest_sk_coordinate = sk.closest_skeleton_coordinate(b_obj.skeleton,
sy.closest_face_coordinate)
for sp in spine_objs_to_add:
sp.closest_sk_coordinate = sk.closest_skeleton_coordinate(b_obj.skeleton,
sp.closest_face_coordinate)
if (len(b_obj.width_array["no_spine_median_mesh_center"]) !=
len(b_obj.skeletal_coordinates_upstream_to_downstream) - 1):
raise Exception("")
if debug_time:
print(f"Skeleton adjustment: {time.time() - st}")
st = time.time()
if debug_time:
print(f"Ccalculating skeleton graph and endpoints: {time.time() - st}")
st = time.time()
# ----- adjusting the synapses --------
if verbose:
print(f"--- Adjusting the synapse features based on new skeleton additions ------")
b_obj.synapses = combine_attr_lists(b_obj.synapses,synapses_to_add)
b_obj.spines_obj = combine_attr_lists(b_obj.spines_obj,spine_objs_to_add)
bu.set_endpoints_upstream_downstream_idx_from_upstream_coordinate(b_obj,upstream_coordinate)
up_idx = b_obj.endpoints_upstream_downstream_idx[0]
for syn in b_obj.synapses:
syu.calculate_endpoints_dist(b_obj,syn)
syu.calculate_upstream_downstream_dist_from_up_idx(syn,up_idx=up_idx)
if b_obj.spines_obj is not None:
for sp in b_obj.spines_obj:
spu.calculate_endpoints_dist(b_obj,sp)
spu.calculate_upstream_downstream_dist_from_up_idx(sp,up_idx=up_idx)
if debug_time:
print(f"synapse distances: {time.time() - st}")
st = time.time()
# --- resetting the parameters that need to be reset
b_obj._skeleton_vector_downstream = None
b_obj._width_upstream = None
b_obj._width_downstream = None
b_obj._skeleton_vector_upstream = None
# --------- adding labels -------
if add_labels:
b_obj.labels += b_d.labels
if return_jitter_segment:
return b_obj,jitter_segment
else:
return b_obj
# --------------- setting branch attributes --------------
[docs]def set_branch_attr_on_limb(
limb_obj,
func,
attr_name,
branch_idxs=None,
**kwargs):
"""
Purpose: To set the upstream and downstream order of the
endpoints of a branch in a limb
"""
if branch_idxs is None:
branch_idxs = limb_obj.get_branch_names()
for branch_idx in branch_idxs:
setattr(limb_obj[branch_idx],attr_name,func(limb_obj,branch_idx,**kwargs))
return limb_obj
[docs]def set_branches_endpoints_upstream_downstream_idx_on_limb(
limb_obj,
**kwargs
):
return set_branch_attr_on_limb(
limb_obj,
func = nru.upstream_downstream_endpoint_idx,
attr_name = "endpoints_upstream_downstream_idx",
**kwargs,
)
[docs]def set_branch_attr_on_limb_on_neuron(
neuron_obj,
func,
attr_name,
verbose = False,
**kwargs):
"""
Purpose: To set the upstream and downstream order of the
endpoints of a branch in a limb
"""
for limb_idx in neuron_obj.get_limb_names():
neuron_obj[limb_idx] = bu.set_branch_attr_on_limb(neuron_obj[limb_idx],func,attr_name,**kwargs)
return neuron_obj
[docs]def set_branches_endpoints_upstream_downstream_idx(
neuron_obj,
**kwargs
):
neuron_obj = bu.set_branch_attr_on_limb_on_neuron(
neuron_obj,
func = nru.upstream_downstream_endpoint_idx,
attr_name = "endpoints_upstream_downstream_idx",
**kwargs
)
#print(f"{neuron_obj[0][0].endpoints_upstream_downstream_idx}")
return neuron_obj
[docs]def skeleton_vector_endpoint(
branch_obj,
endpoint_type,
directional_flow = "downstream",
endpoint_coordinate = None,
verbose = False,
plot_restricted_skeleton= False,
offset=500,
comparison_distance=3_000,#2_000,
**kwargs
):
"""
Purpose: To restrict a skeleton
to its upstream or downstream vector ()
The vector is always in the direction of most
upstream skeleton point to downstream skeletal point
Example:
bu.skeleton_vector_endpoint(
branch_obj,
endpoint_type = "downstream",
#endpoint_coordinate = np.array([2504610. , 480431. , 33741.2])
plot_restricted_skeleton = True,
verbose = True,
)
"""
if offset is None:
offset = offset_skeleton_vector_global
if comparison_distance is None:
comparison_distance = comparison_distance_skeleton_vector_global
if endpoint_coordinate is None:
endpoint_coordinate = getattr(branch_obj,f"endpoint_{endpoint_type}")
if verbose:
print(f"{endpoint_type} endpoint: {endpoint_coordinate}")
curr_vec = sk.vector_away_from_endpoint(
skeleton = branch_obj.skeleton,
endpoint = endpoint_coordinate,
verbose = False,
plot_restricted_skeleton = plot_restricted_skeleton,
offset=offset,
comparison_distance=comparison_distance,#2_000,
**kwargs
)
mult_number = 1
if endpoint_type == directional_flow:
mult_number = -1
final_vec= mult_number*curr_vec
if verbose:
print(f"curr_vec = {curr_vec}")
print(f"mult_number = {mult_number}")
print(f"final_vec = {final_vec}")
return final_vec
[docs]def skeleton_vector_upstream(
branch_obj,
directional_flow = "downstream",
endpoint_coordinate = None,
verbose = False,
plot_restricted_skeleton= False,
**kwargs
):
return bu.skeleton_vector_endpoint(
branch_obj,
endpoint_type = "upstream",
directional_flow = directional_flow,
endpoint_coordinate = endpoint_coordinate,
verbose = verbose,
plot_restricted_skeleton= plot_restricted_skeleton,
**kwargs
)
[docs]def skeleton_vector_downstream(
branch_obj,
directional_flow = "downstream",
endpoint_coordinate = None,
verbose = False,
plot_restricted_skeleton= False,
**kwargs
):
return bu.skeleton_vector_endpoint(
branch_obj,
endpoint_type = "downstream",
directional_flow = directional_flow,
endpoint_coordinate = endpoint_coordinate,
verbose = verbose,
plot_restricted_skeleton= plot_restricted_skeleton,
**kwargs
)
[docs]def width_endpoint(
branch_obj,
endpoint, # if None then will select most upstream endpoint of branch
#parameters for the restriction
offset=0,
comparison_distance=2000,
skeleton_segment_size=1000,
verbose = False,
):
"""
Purpose: To compute the width of a branch
around a comparison distance and offset of an endpoint
on it's skeleton
"""
if verbose:
print(f"endpoint = {endpoint}")
(base_final_skeleton,
base_final_widths,
base_final_seg_lengths) = nru.align_and_restrict_branch(branch_obj,
common_endpoint=endpoint,
offset=offset,
comparison_distance=comparison_distance,
skeleton_segment_size=skeleton_segment_size,
verbose=False,
)
branch_width = np.mean(base_final_widths)
overall_ais_width = branch_obj.width_new
if verbose:
print(f"base_final_widths = {base_final_widths}")
print(f"overall_branch_width = {overall_ais_width}")
print(f"branch_width = {branch_width}")
return branch_width
[docs]def width_upstream(
branch_obj,
**kwargs
):
return bu.width_endpoint(
branch_obj,
endpoint = branch_obj.endpoint_upstream,
**kwargs
)
[docs]def width_downstream(
branch_obj,
**kwargs
):
return bu.width_endpoint(
branch_obj,
endpoint = branch_obj.endpoint_downstream,
**kwargs
)
# ---------- synapse dists ------------
[docs]def min_dist_synapse_endpoint(
branch_obj,
synapse_type,
endpoint_type,
verbose = False,
default_value = np.inf,
):
if synapse_type == "synapses":
syns = branch_obj.synapses
else:
if "post" in synapse_type:
syns = branch_obj.synapses_post
elif "pre" in synapse_type:
syns = branch_obj.synapses_pre
else:
raise Exception("")
if len(syns) == 0:
return default_value
dists = [getattr(k,f"{endpoint_type}_dist") for k in syns]
min_dist = np.min(dists)
if verbose:
print(f"For {synapse_type}, {endpoint_type}:min_dist={min_dist} \n dists = {dists}")
return min_dist
[docs]def min_dist_synapses_pre_upstream(
branch_obj,
**kwargs):
return min_dist_synapse_endpoint(
branch_obj,
synapse_type="pre",
endpoint_type="upstream",
**kwargs
)
[docs]def min_dist_synapses_post_upstream(
branch_obj,
**kwargs):
return min_dist_synapse_endpoint(
branch_obj,
synapse_type="post",
endpoint_type="upstream",
**kwargs
)
[docs]def min_dist_synapses_pre_downstream(
branch_obj,
**kwargs):
return min_dist_synapse_endpoint(
branch_obj,
synapse_type="pre",
endpoint_type="downstream",
**kwargs
)
[docs]def min_dist_synapses_post_downstream(
branch_obj,
**kwargs):
return min_dist_synapse_endpoint(
branch_obj,
synapse_type="post",
endpoint_type="downstream",
**kwargs
)
[docs]def closest_mesh_skeleton_dist(
obj,
verbose = False):
"""
Purpose: To find the closest distance between mesh and
the skeleton of a branch
"""
coordinates = np.array(obj.skeleton).reshape(-1,3)
mesh_kd = KDTree(obj.mesh.triangles_center)
dist,face_idx = mesh_kd.query(coordinates)
min_dist = np.min(dist)
if verbose:
print(f"Closest face dist = {min_dist}")
return min_dist
[docs]def mesh_shaft(
obj,
plot = False,
return_mesh = True):
"""
Purpose: To export the shaft mesh of the branch
(aka the mesh without the spine meshes)
"""
from mesh_tools import trimesh_utils as tu
shaft_mesh = tu.subtract_mesh(
obj.mesh,
[k.mesh for k in obj.spines_obj],
return_mesh = return_mesh)
if plot:
if not return_mesh:
plot_mesh = obj.mesh.submesh([shaft_mesh],append=True)
else:
plot_mesh = shaft_mesh
nviz.plot_objects(obj.mesh,
meshes = [plot_mesh],
meshes_colors="red")
return shaft_mesh
[docs]def mesh_shaft_idx(obj,
plot = False,):
return mesh_shaft(obj,plot=plot,return_mesh=False)
[docs]def is_skeleton_upstream_to_downstream(branch_obj,verbose = False):
upstream_endpoint = branch_obj.endpoint_upstream
if verbose:
print(f"endpoints = {branch_obj.endpoints}")
print(f"upstream_endpoint= {upstream_endpoint}")
if np.array_equal(branch_obj.endpoints[0],upstream_endpoint):
up_flag = True
elif np.array_equal(branch_obj.endpoints[1],upstream_endpoint):
up_flag = False
else:
raise Exception("")
if verbose:
print(f"up_flag = {up_flag}")
return up_flag
[docs]def width_array_upstream_to_downstream(branch_obj,verbose = False):
is_upstream = bu.is_skeleton_upstream_to_downstream(branch_obj,verbose)
if is_upstream:
return branch_obj.width_array
else:
if verbose:
print(f"Applying Flip")
return {k:np.flip(v) for k,v in branch_obj.width_array.items()}
[docs]def width_array_skeletal_lengths_upstream_to_downstream(branch_obj,verbose = False):
if branch_obj.width_array_skeletal_lengths is None:
return None
is_upstream = bu.is_skeleton_upstream_to_downstream(branch_obj,verbose)
if is_upstream:
return branch_obj.width_array_skeletal_lengths
else:
return np.flip(branch_obj.width_array_skeletal_lengths)
[docs]def skeletal_coordinates_upstream_to_downstream(
branch_obj,
verbose = False,
skeleton = None,
coordinate_dists = None,
resize=True):
if not resize:
skeleton = branch_obj.skeleton
if skeleton is None:
array = wu.skeleton_resized_ordered(branch_obj.skeleton)
else:
skeleton = sk.order_skeleton(skeleton)
array = skeleton
is_upstream = bu.is_skeleton_upstream_to_downstream(branch_obj,verbose)
if is_upstream:
pass
else:
if verbose:
print(f"Applying Flip")
array= sk.flip_skeleton(array)
if branch_obj.width_array_skeletal_lengths_upstream_to_downstream is not None:
coordinate_dists = np.concatenate([[0],branch_obj.width_array_skeletal_lengths_upstream_to_downstream])
#coordinate_dists = branch_obj.width_array_skeletal_lengths_upstream_to_downstream
if coordinate_dists is not None:
coordinate_dists = np.cumsum(coordinate_dists)
coordinates = sk.coordinates_from_downstream_dist(
array,
coordinate_dists,
verbose = False,
segment_width=0,
plot = False
)
else:
coordinates = sk.skeleton_coordinate_path_from_start(array)
return coordinates
[docs]def skeletal_coordinates_dist_upstream_to_downstream(
branch_obj,
verbose = False,
cumsum = True,
skeleton = None,
**kwargs):
if skeleton is None:
array = bu.skeletal_coordinates_upstream_to_downstream(branch_obj,**kwargs)
else:
array = skeleton
dist_array = np.linalg.norm(array[1:] - array[:-1],axis=1)
if cumsum:
return np.cumsum(dist_array)
else:
return dist_array
[docs]def endpoint_upstream_idx(branch_obj,coordinate = None):
if coordinate is None:
coordinate = branch_obj.endpoint_upstream
return nu.matching_row_index(branch_obj.endpoints,coordinate)
[docs]def endpoint_downstream_idx(branch_obj,coordinate = None):
if coordinate is None:
coordinate = branch_obj.endpoint_downstream
return nu.matching_row_index(branch_obj.endpoints,coordinate)
[docs]def set_endpoints_upstream_downstream_idx_from_upstream_coordinate(
branch_obj,
upstream_coordinate=None,
up_idx = None,
):
"""
Purpose: Set the branch upstream, downstream by a coordinate
"""
if up_idx is None:
up_idx = bu.endpoint_upstream_idx(branch_obj,upstream_coordinate)
branch_obj.endpoints_upstream_downstream_idx = (up_idx,1-up_idx)
[docs]def set_endpoints_upstream_downstream_idx_on_branch(
limb_obj,
branch_idx,):
if limb_obj[branch_idx].endpoints_upstream_downstream_idx is None:
up_idx = nru.upstream_endpoint(limb_obj,branch_idx,return_endpoint_index=True)
bu.set_endpoints_upstream_downstream_idx_from_upstream_coordinate(
limb_obj[branch_idx],
up_idx = up_idx,
)
synapse_dynamics_attrs = [
"upstream_dist",
"head_neck_shaft",
"syn_id",
"volume",
"syn_type",
#"soma_distance"
]
spine_dynamics_attrs = [
"upstream_dist",
"volume",
"area",
"spine_id"
#"soma_distance"
]
[docs]def width_array_upstream_to_dowstream_with_skeletal_points(
branch_obj,
width_name = "no_spine_median_mesh_center",
):
"""
Purpose: Want to get the width at a certain
point on the branch where that certain point
is the closest distcretization to another coordinate
"""
skeleton_coords = branch_obj.skeletal_coordinates_upstream_to_downstream
skeleton_coords_mid = (skeleton_coords[:-1] + skeleton_coords[1:])/2
return branch_obj.width_array_upstream_to_downstream[width_name],skeleton_coords_mid
[docs]def width_array_value_closest_to_coordinate(
branch_obj,
coordinate,
verbose = False,):
"""
Purpose: To find the width closest to certain coordinates
on a branch obj
"""
coordinate = np.array(coordinate)
if coordinate.ndim == 1:
single_flag = True
else:
single_flag = False
coordinate = np.array(coordinate).reshape(-1,3)
widths,sk_coordinates = bu.width_array_upstream_to_dowstream_with_skeletal_points(branch_obj)
closest_widths = widths[nu.closest_idx_for_each_coordinate(
coordinate,
sk_coordinates,
closest_idx_algorithm = "linalg")
]
if verbose:
print(f"closest_widths= {closest_widths}")
if single_flag:
return closest_widths[0]
else:
return closest_widths
[docs]def branch_dynamics_attr_dict_dynamics_from_node(
branch_obj,
width_name = "no_spine_median_mesh_center"
):
"""
Purpose: To save off all of the necessary information
for branch dynamics (of spines,width,synapses) to
"""
sp_atts = []
if branch_obj.spines_obj is not None:
sp_atts = [{att:getattr(spu.Spine(sp),att)
for att in spine_dynamics_attrs} for sp in branch_obj.spines_obj]
syn_atts = []
if branch_obj.synapses is not None:
syn_atts = [{att:getattr(sp,att) if att != "head_neck_shaft"
else spu.spine_str_label(getattr(sp,att))
for att in synapse_dynamics_attrs} for sp in branch_obj.synapses]
skeleton_coords = branch_obj.skeletal_coordinates_upstream_to_downstream
skeleton_coords_dists = branch_obj.skeletal_coordinates_dist_upstream_to_downstream
width_attrs = [dict(
upstream_dist = skeleton_coords_dists[i],
width = k,
) for i,k in enumerate(branch_obj.width_array_upstream_to_downstream[width_name])]
return_dict = dict(
spine_data = sp_atts,
synapse_data = syn_atts,
width_data = width_attrs,
skeleton_data = skeleton_coords,
)
return return_dict
[docs]def refine_width_array_to_match_skeletal_coordinates(
neuron_obj,
verbose = False):
"""
Purpose: To update the widths of those that don't match the
skeletal coordinates
"""
bu.set_branches_endpoints_upstream_downstream_idx(neuron_obj)
def width_array_length(branch_obj,width_name="no_spine_median_mesh_center",**kwargs):
return len(branch_obj.width_array[width_name])
def skeletal_coordinates_upstream_to_downstream_length(branch_obj,**kwargs):
return len(branch_obj.skeletal_coordinates_upstream_to_downstream)
from neurd import neuron_searching as ns
lb = ns.query_neuron(
neuron_obj,
functions_list=[
width_array_length,
skeletal_coordinates_upstream_to_downstream_length],
query = "width_array_length != (skeletal_coordinates_upstream_to_downstream_length-1)",
)
if verbose:
print(f"limb branch to update = {lb}")
wu.neuron_width_calculation_standard(
neuron_obj,
verbose = verbose,
limb_branch_dict=lb,
)
return neuron_obj
[docs]def endpoint_type_with_offset(
branch_obj,
endpoint_type="upstream",
offset =1000,
plot = False,
verbose= False,
):
"""
Purpose: To get the skeleton point
a little offset from the current endpoint
"""
endpoint_coordinate = getattr(branch_obj,f"endpoint_{endpoint_type}")
coordinate = sk.skeleton_coordinate_offset_from_endpoint(
branch_obj.skeleton,
offset_distance = offset,
endpoint_coordinate = endpoint_coordinate,
plot_coordinate = plot,
)
if verbose:
print(f"coordinate = {coordinate} (with enpoint coord = {endpoint_coordinate})")
return coordinate
[docs]def endpoint_upstream_with_offset(
branch_obj,
offset =1000,
plot = False,
verbose= False,
):
"""
Ex:
bu.endpoint_upstream_with_offset(
branch_obj = limb_obj[26],
verbose = True,
offset = 200,
plot = True
)
"""
return bu.endpoint_type_with_offset(
branch_obj=branch_obj,
endpoint_type="upstream",
offset =offset,
plot = plot,
verbose= verbose,
)
[docs]def endpoint_downstream_with_offset(
branch_obj,
offset =1000,
plot = False,
verbose= False,
):
return bu.endpoint_type_with_offset(
branch_obj=branch_obj,
endpoint_type="downstream",
offset =offset,
plot = plot,
verbose= verbose,
)
[docs]def skeleton_angle_from_top(
branch_obj,
top_of_layer_vector = None):
if top_of_layer_vector is None:
top_of_layer_vector = nst.top_of_layer_vector
sk_vector = branch_obj.skeleton_vector_upstream
angle_from_top = np.round(nu.angle_between_vectors(nst.top_of_layer_vector,sk_vector),4)
return angle_from_top
# -------------------------------------------------------
global_parameters_dict_default = dict(
offset_skeleton_vector = 500,
comparison_distance_skeleton_vector = 3000
)
attributes_dict_default = dict()
# ------- microns -----------
global_parameters_dict_microns = {}
attributes_dict_microns = {}
# --------- h01 -------------
global_parameters_dict_h01 = dict()
attributes_dict_h01 = dict()
# data_type = "default"
# algorithms = None
# modules_to_set = [bu]
# def set_global_parameters_and_attributes_by_data_type(dt,
# algorithms_list = None,
# modules = None,
# set_default_first = True,
# verbose=False):
# if modules is None:
# modules = modules_to_set
# modu.set_global_parameters_and_attributes_by_data_type(modules,dt,
# algorithms=algorithms_list,
# set_default_first = set_default_first,
# verbose = verbose)
# set_global_parameters_and_attributes_by_data_type(data_type,
# algorithms)
# def output_global_parameters_and_attributes_from_current_data_type(
# modules = None,
# algorithms = None,
# verbose = True,
# lowercase = True,
# output_types = ("global_parameters"),
# include_default = True,
# algorithms_only = False,
# **kwargs):
# if modules is None:
# modules = modules_to_set
# return modu.output_global_parameters_and_attributes_from_current_data_type(
# modules,
# algorithms = algorithms,
# verbose = verbose,
# lowercase = lowercase,
# output_types = output_types,
# include_default = include_default,
# algorithms_only = algorithms_only,
# **kwargs,
# )
#--- from neurd_packages ---
from . import branch_attr_utils as bau
from . import neuron_utils as nru
from . import neuron_visualizations as nviz
from . import spine_utils as spu
from . import synapse_utils as syu
from . import width_utils as wu
from . import neuron_statistics as nst
#--- from mesh_tools ---
from mesh_tools import skeleton_utils as sk
from mesh_tools import trimesh_utils as tu
#--- from datasci_tools ---
from datasci_tools import general_utils as gu
from datasci_tools import ipyvolume_utils as ipvu
from datasci_tools import module_utils as modu
from datasci_tools import numpy_dep as np
from datasci_tools import numpy_utils as nu
from . import branch_utils as bu