@@ -291,8 +291,10 @@ def projection(
291291 xlim : tuple [float , float ] | None = None ,
292292 ylim : tuple [float , float ] | None = None ,
293293 draw_skel : bool = True ,
294+ draw_mesh : bool = True ,
294295 draw_edges : bool = True ,
295296 draw_cylinders : bool = False ,
297+ rasterized : bool = True ,
296298 ax : Axes | None = None ,
297299 mesh_cmap : str
298300 | mcolors .Colormap
@@ -311,6 +313,7 @@ def projection(
311313 unit : str | None = None ,
312314 # soma --------------------------------------------------------------- #
313315 draw_soma_mask : bool = True ,
316+ soma_style : str = "dashed" , # "dashed" | "filled"
314317 # colors
315318 color_by : str = "fixed" , # "ntype" or "fixed"
316319) -> tuple [Figure , Axes ]:
@@ -335,8 +338,14 @@ def projection(
335338 xlim, ylim : (min, max) or *None*
336339 Spatial extent **before** plotting. If not given, limits are inferred
337340 from the histogram (when *mesh* is available) or the skeleton.
338- draw_skel, draw_edges, draw_cylinders : bool
341+ draw_skel, draw_mesh, draw_edges, draw_cylinders: bool
339342 Toggles for skeleton glyphs.
343+ soma_style : str
344+ How to plot the soma, currently supported styles are:
345+ - "dashed" : dashed ellipse outline (default)
346+ - "filled" : filled circle with soma colour
347+ rasterized : bool | int
348+ Rasterize skeleton. If int and > 1, not only skeleton will be rasterized.
340349 ax : matplotlib.axes.Axes | None
341350 Existing *Axes* to draw into. When *None*, a new figure is created.
342351 mesh_cmap, vmax_fraction : appearance of the histogram – see original docs.
@@ -385,7 +394,7 @@ def projection(
385394 xy_skel = _project (skel .nodes , ix , iy ) * scl_skel
386395 rr = skel .radii [radius_metric ] * scl_skel
387396
388- if mesh is not None :
397+ if mesh is not None and draw_mesh :
389398 xy_mesh = _project (mesh .vertices , ix , iy ) * scl_mesh
390399 else : # empty placeholder for unified code‑path
391400 xy_mesh = np .empty ((0 , 2 ), dtype = float )
@@ -412,12 +421,12 @@ def _crop_window(xy: np.ndarray) -> np.ndarray:
412421 else :
413422 col_nodes = "red"
414423
415- if mesh is not None and xy_mesh .size :
424+ if mesh is not None and xy_mesh .size and draw_mesh :
416425 keep_mesh = _crop_window (xy_mesh )
417426 xy_mesh = xy_mesh [keep_mesh ]
418427
419428 # ─────────────────────────────── histogram (mesh may be None) ──────────
420- if mesh is not None and xy_mesh .size :
429+ if mesh is not None and xy_mesh .size and draw_mesh :
421430 # ensure bins argument correct
422431 if isinstance (bins , int ):
423432 bins_arg : int | tuple [int , int ] = bins
@@ -448,14 +457,15 @@ def _crop_window(xy: np.ndarray) -> np.ndarray:
448457 fig = ax .figure
449458
450459 # background image – only when we do have a histogram
451- if hist is not None :
460+ if hist is not None and draw_mesh :
452461 ax .imshow (
453462 hist ,
454463 extent = (xedges [0 ], xedges [- 1 ], yedges [0 ], yedges [- 1 ]),
455464 origin = "lower" ,
456465 cmap = _as_cmap (mesh_cmap ),
457466 vmax = hist .max () * vmax_fraction ,
458467 alpha = 1.0 ,
468+ rasterized = rasterized > 1 ,
459469 )
460470
461471 # ──────────────────────── draw skeleton circles (always) ───────────────
@@ -483,6 +493,7 @@ def _crop_window(xy: np.ndarray) -> np.ndarray:
483493 linewidths = 1.0 ,
484494 alpha = circle_alpha ,
485495 zorder = 2 ,
496+ rasterized = rasterized > 0 ,
486497 )
487498
488499 # highlighted nodes – filled circles
@@ -499,50 +510,61 @@ def _crop_window(xy: np.ndarray) -> np.ndarray:
499510 linewidths = 0.9 ,
500511 alpha = highlight_face_alpha ,
501512 zorder = 3.5 ,
513+ rasterized = rasterized > 1 ,
502514 )
503515
504516 # ───────────────────────── soma shell & center (if possible) ───────────
505517 c_xy = _project (skel .nodes [[0 ]] * scl_skel , ix , iy ).ravel ()
506- centre_col = swc_colors [1 ] if color_by == "ntype" else "k"
507- ax .scatter (* c_xy , color = "black" , s = 15 , zorder = 3 )
518+ col_soma = swc_colors [1 ] if color_by == "ntype" else "pink"
519+
520+ if soma_style == 'filled' :
521+ soma_fc = col_soma
522+ soma_ec = 'k'
523+ soma_ls = '-'
524+ soma_mc = 'none'
525+ else :
526+ ax .scatter (* c_xy , color = "black" , s = 15 , zorder = 3 )
527+ soma_fc = 'none'
528+ soma_ec = 'k'
529+ soma_ls = '--'
530+ soma_mc = col_soma
508531
509532 if (
510- draw_soma_mask
511- and mesh is not None
533+ mesh is not None
512534 and skel .soma is not None
513535 and skel .soma .verts is not None
514536 ):
515- xy_soma = _project ( mesh . vertices [ np . asarray ( skel . soma . verts , int )], ix , iy )
516- xy_soma = xy_soma * scl_mesh
517- xy_soma = xy_soma [ _crop_window ( xy_soma )] # respect crop
518-
519- col_soma = swc_colors [ 1 ] if color_by == "ntype" else "pink"
520-
521- ax . scatter (
522- xy_soma [:, 0 ],
523- xy_soma [:, 1 ] ,
524- s = 1 ,
525- c = [ col_soma ] ,
526- alpha = 0.5 ,
527- linewidths = 0 ,
528- label = "soma surface" ,
529- )
537+ if draw_soma_mask :
538+ xy_soma = _project ( mesh . vertices [ np . asarray ( skel . soma . verts , int )], ix , iy )
539+ xy_soma = xy_soma * scl_mesh
540+ xy_soma = xy_soma [ _crop_window ( xy_soma )] # respect crop
541+
542+ ax . scatter (
543+ xy_soma [:, 0 ],
544+ xy_soma [:, 1 ],
545+ s = 1 ,
546+ c = [ soma_mc ] ,
547+ alpha = 0.5 ,
548+ linewidths = 0 ,
549+ label = "soma surface" ,
550+ rasterized = rasterized > 0 ,
551+ )
530552 # dashed ellipse outline
531553 ell = _soma_ellipse2d (skel .soma , plane , scale = scl_skel )
532- ell .set_edgecolor ("k" )
533- ell .set_facecolor ("none" )
534- ell .set_linestyle ("--" )
554+ ell .set_edgecolor (soma_ec )
555+ ell .set_facecolor (soma_fc )
556+ ell .set_linestyle (soma_ls )
535557 ell .set_linewidth (0.8 )
536558 ell .set_alpha (0.9 )
537559 ax .add_patch (ell )
538560 else :
539561 soma_circle = Circle (
540562 c_xy ,
541563 skel .soma .equiv_radius * scl_skel ,
542- facecolor = "none" ,
543- edgecolor = centre_col ,
564+ facecolor = soma_fc ,
565+ edgecolor = soma_ec ,
544566 linewidth = 0.8 ,
545- linestyle = "--" ,
567+ linestyle = soma_ls ,
546568 alpha = 0.9 ,
547569 )
548570 ax .add_patch (soma_circle )
@@ -567,6 +589,7 @@ def _crop_window(xy: np.ndarray) -> np.ndarray:
567589 colors = "black" ,
568590 linewidths = edge_lw ,
569591 alpha = cylinder_alpha ,
592+ rasterized = rasterized > 0 ,
570593 )
571594 ax .add_collection (lc )
572595
@@ -603,11 +626,13 @@ def _crop_window(xy: np.ndarray) -> np.ndarray:
603626 edgecolors = "red" ,
604627 alpha = cylinder_alpha ,
605628 zorder = 10 ,
629+ rasterized = rasterized > 0 ,
606630 )
607631 ax .add_collection (pc )
608632
609633 # ────────────────────────────── final cosmetics ────────────────────────
610- # ax.set_aspect("equal")
634+ if plane in ['xy' , 'yx' ]:
635+ ax .set_aspect ('equal' , adjustable = 'box' )
611636
612637 if unit is None :
613638 unit_str = "" if scl_skel == 1.0 else f"(×{ scl_skel :g} )"
@@ -961,14 +986,17 @@ def _mask_window(xy: np.ndarray) -> np.ndarray:
961986 ax .add_patch (ell )
962987
963988 # ────────────── cosmetics & labels ────────────────────────────────────
964- ax .set_aspect ("equal" )
989+ if plane in ['xy' , 'yx' ]:
990+ ax .set_aspect ("equal" , adjustable = "box" )
991+
965992 if unit is None :
966993 ax .set_xlabel (f"{ plane [0 ]} " )
967994 ax .set_ylabel (f"{ plane [1 ]} " )
968995 else :
969996 ax .set_xlabel (f"{ plane [0 ]} ({ unit } )" )
970997 ax .set_ylabel (f"{ plane [1 ]} ({ unit } )" )
971998
999+
9721000 if xlim is not None :
9731001 ax .set_xlim (xlim )
9741002 if ylim is not None :
@@ -1205,4 +1233,4 @@ def node_details(
12051233 highlight_face_alpha = highlight_alpha ,
12061234 ** kwargs ,
12071235 )
1208- return fig , ax
1236+ return fig , ax
0 commit comments