Skip to content
Snippets Groups Projects
  • Brent Huisman's avatar
    Tutorials: ring network (#1313) · 506ff916
    Brent Huisman authored
    * Tutorial ring network added. A few typos fixed, crosslinks fixed.
    * MPI tutorial added.
    * Improved SVG generation, added separate label scaling, increased default label size.
    * Bugfix: didnt generate cable region in gen-labels.py used in `concepts/morphology.rst`.
    * Termified Interconnectivity page.
    * Termified probe nomenclature page. Will be moved in [this PR](https://github.com/arbor-sim/arbor/issues/1353) from python/simulation to concepts/probes_samplers.
    Unverified
    506ff916
make_images.py 14.32 KiB
import copy
import svgwrite
import math
import inputs

tag_colors_colorscheme = ['white', '#ffc2c2', 'gray', '#c2caff', '#81c8aa']
tag_colors_bwscheme = ['lightgray']*len(tag_colors_colorscheme)

#
# ############################################
#

def translate(x, f, xshift):
    return (f*x[0]+xshift, -f*x[1])

def translate_all(points, f, xshift):
    return [translate(x, f, xshift) for x in points]

# Draw one or more morphologies, side by side.
# Each morphology can be drawn as segments or branches.
def morph_image(morphs, methods, filename, **kwargs):
    assert(len(morphs)==len(methods))

    lab_sc = kwargs.get('lab_sc',2)
    sc = kwargs.get('sc',20)
    draw_labels = kwargs.get('draw_labels',True)
    colors = kwargs.get('colors',True)
    tag_colors = tag_colors_colorscheme if colors else tag_colors_bwscheme

    print('generating:', filename)
    dwg = svgwrite.Drawing(filename=filename, debug=True)

    # Width of lines and circle strokes.
    line_width=0.1*sc

    # Padding around image.
    fudge=1.5*sc

    linecolor='black'
    pointcolor='red'
    lines = dwg.add(dwg.g(id='lines',
                          stroke=linecolor,
                          fill='white',
                          stroke_width=line_width))
    points = dwg.add(dwg.g(id='points',
                           stroke=pointcolor,
                           fill=pointcolor,
                           stroke_width=line_width))
    numbers = dwg.add(dwg.g(id='numbers',
                             text_anchor='middle',
                             alignment_baseline='middle'))

    minx = math.inf
    miny = math.inf
    maxx = -math.inf
    maxy = -math.inf

    offset = 0

    bcolor = 'mediumslateblue'
    branchfillcolor = 'lightgray'

    nmorph = len(morphs)

    for l in range(nmorph):
        morph = morphs[l]
        method = methods[l]

        nbranches = len(morph)

        segid = 0
        for i in range(nbranches):
            branch = morph[i]

            lx, ux, ly, uy = branch.minmax()
            minx = min(minx,  sc*lx+offset)
            miny = min(miny,  sc*ly)
            maxx = max(maxx,  sc*ux+offset)
            maxy = max(maxy,  sc*uy)

            if method=='segments':
                for sec in branch.sections:
                    for seg in sec:
                        if seg.length>0.00001: # only draw nonzero length segments
                            line = translate_all(seg.corners(), sc, offset)
                            lines.add(dwg.polygon(points=line, fill=tag_colors[seg.tag]))

                            pos = translate(seg.location(0.5), sc, offset)
                            if draw_labels:
                                points.add(dwg.circle(center=pos,
                                                    stroke='black',
                                                    r=sc*0.55*lab_sc,
                                                    fill='white',
                                                    stroke_width=sc/20*lab_sc))
                                # The svg alignment_baseline attribute:
                                #   - works on Chrome/Chromium
                                #   - doesn't work on Firefox
                                # so for now we just shift the relative position by sc/3
                                label_pos = (pos[0], pos[1]+sc/3)
                                numbers.add(dwg.text(str(segid),
                                                    insert=label_pos,
                                                    stroke='black',
                                                    fill='black',
                                                    font_size=sc*0.55*lab_sc))
                        segid += 1

            elif method=='branches':
                for line in branch.outline():
                    lines.add(dwg.polygon(points=translate_all(line, sc, offset),
                                          fill=branchfillcolor))

                if draw_labels:
                    pos = translate(branch.location(0.5), sc, offset)
                    points.add(dwg.circle(center=pos,
                                        stroke=bcolor,
                                        r=sc*0.55*lab_sc,
                                        fill=bcolor,
                                        stroke_width=sc/20*lab_sc))
                    # The svg alignment_baseline attribute:
                    #   - works on Chrome/Chromium
                    #   - doesn't work on Firefox
                    # so for now we just shift the relative position by sc/3
                    label_pos = (pos[0], pos[1]+sc/3)
                    numbers.add(dwg.text(str(i),
                                        insert=label_pos,
                                        stroke='white',
                                        fill='white',
                                        font_size=sc*0.55*lab_sc))
        offset = maxx - minx + sc


    # Find extent of image.
    minx -= fudge
    miny -= fudge
    maxx += fudge
    maxy += fudge
    width = maxx-minx
    height = maxy-miny
    dwg.viewbox(minx, -maxy, width, height)

    # Write the image to file.
    dwg.save()

# Generate an image that illustrates regions and locsets on a morphology.
#
# Can't handle morpholgies with gaps, where segemnts with a parent-child
# ordering don't have collocated distal-proximal locations respectively.
# Handling this case would make rendering regions more complex, but would
# not bee too hard to support.
def label_image(morphology, labels, filename, **kwargs):

    loc_sc = kwargs.get('loc_sc',2)
    sc = kwargs.get('sc',20)
    drawroot = kwargs.get('drawroot',True)

    morph = morphology
    print('generating:', filename)
    dwg = svgwrite.Drawing(filename=filename, debug=True)

    # Width of lines and circle strokes.
    line_width=0.2*sc

    # Padding around image.
    fudge=1.5*sc

    linecolor='black'
    pointcolor='red'
    lines = dwg.add(dwg.g(id='lines',
                          stroke=linecolor,
                          fill='white',
                          stroke_width=line_width))
    points = dwg.add(dwg.g(id='points',
                           stroke=pointcolor,
                           fill=pointcolor,
                           stroke_width=line_width))

    minx = math.inf
    miny = math.inf
    maxx = -math.inf
    maxy = -math.inf

    offset = 0

    branchfillcolor = 'lightgray'

    nimage = len(labels)
    for l in range(nimage):
        lab = labels[l]

        nbranches = len(morph)

        # Draw the outline of the cell
        for i in range(nbranches):
            branch = morph[i]

            lx, ux, ly, uy = branch.minmax()
            minx = min(minx,  sc*lx+offset)
            miny = min(miny,  sc*ly)
            maxx = max(maxx,  sc*ux+offset)
            maxy = max(maxy,  sc*uy)

            for line in branch.outline():
                lines.add(dwg.polygon(points=translate_all(line, sc, offset),
                                      fill=branchfillcolor,
                                      stroke=branchfillcolor))

        # Draw the root
        root = translate(morph[0].location(0), sc, offset)
        if drawroot:
            points.add(dwg.circle(center=root, stroke='red', r=sc/2.5*loc_sc, fill='white', stroke_width=sc/10*loc_sc))

        if lab['type'] == 'locset':
            for loc in lab['value']:
                bid = loc[0]
                pos = loc[1]

                loc = translate(morph[bid].location(pos), sc, offset)
                points.add(dwg.circle(center=loc, stroke='black', r=sc/3*loc_sc, fill='white', stroke_width=sc/10*loc_sc))

        if lab['type'] == 'region':
            for cab in lab['value']:
                # skip zero length cables
                bid  = cab[0]
                ppos = cab[1]
                dpos = cab[2]

                # Don't draw zero-length cables
                # How should these be drawn: with a line or a circle?
                if ppos==dpos: continue

                for line in morph[bid].outline(ppos, dpos):
                    lines.add(dwg.polygon(points=translate_all(line, sc, offset),
                                          fill='black',
                                          stroke=branchfillcolor))

        offset = maxx - minx + sc

    # Find extent of image.
    minx -= fudge
    miny -= fudge
    maxx += fudge
    maxy += fudge
    width = maxx-minx
    height = maxy-miny
    dwg.viewbox(minx, -maxy, width, height)

    # Write the image to file.
    dwg.save()

def generate(path=''):

    morph_image([inputs.branch_morph2], ['segments'], path+'/term_segments.svg', colors=False, draw_labels=False)
    morph_image([inputs.branch_morph2], ['branches'], path+'/term_branch.svg', colors=False, draw_labels=False)
    label_image(inputs.branch_morph2, [inputs.reg_cable_0_28], path+'/term_cable.svg', drawroot=False)

    morph_image([inputs.label_morph],    ['branches'], path+'/label_branch.svg')

    morph_image([inputs.label_morph],    ['segments'], path+'/label_seg.svg')
    morph_image([inputs.detached_morph], ['segments'], path+'/detached_seg.svg')
    morph_image([inputs.stacked_morph],  ['segments'], path+'/stacked_seg.svg',lab_sc=1.2)
    morph_image([inputs.swc_morph],      ['segments'], path+'/swc_morph.svg',lab_sc=1.5)

    morph_image([inputs.label_morph, inputs.label_morph], ['segments', 'branches'], path+'/label_morph.svg')
    morph_image([inputs.detached_morph, inputs.detached_morph], ['segments', 'branches'], path+'/detached_morph.svg')
    morph_image([inputs.stacked_morph, inputs.stacked_morph], ['segments', 'branches'], path+'/stacked_morph.svg')
    morph_image([inputs.sphere_morph, inputs.sphere_morph], ['segments', 'branches'], path+'/sphere_morph.svg',lab_sc=1.5)
    morph_image([inputs.branch_morph1, inputs.branch_morph1], ['segments', 'branches'], path+'/branch_morph1.svg',lab_sc=1)
    morph_image([inputs.branch_morph2, inputs.branch_morph2], ['segments', 'branches'], path+'/branch_morph2.svg',lab_sc=1)
    morph_image([inputs.branch_morph3, inputs.branch_morph3], ['segments', 'branches'], path+'/branch_morph3.svg',lab_sc=1)
    morph_image([inputs.branch_morph4, inputs.branch_morph4], ['segments', 'branches'], path+'/branch_morph4.svg',lab_sc=1)
    morph_image([inputs.yshaped_morph, inputs.yshaped_morph], ['segments', 'branches'], path+'/yshaped_morph.svg',lab_sc=1.5)
    morph_image([inputs.ysoma_morph1,  inputs.ysoma_morph1],  ['segments', 'branches'], path+'/ysoma_morph1.svg')
    morph_image([inputs.ysoma_morph2,  inputs.ysoma_morph2],  ['segments', 'branches'], path+'/ysoma_morph2.svg')
    morph_image([inputs.ysoma_morph3,  inputs.ysoma_morph3],  ['segments', 'branches'], path+'/ysoma_morph3.svg')

    ####################### locsets

    label_image(inputs.label_morph, [inputs.ls_term, inputs.ls_rand_dend], path+'/locset_label_examples.svg')
    label_image(inputs.label_morph, [inputs.reg_dend, inputs.reg_radlt5], path+'/region_label_examples.svg')
    label_image(inputs.label_morph, [inputs.ls_root], path+'/root_label.svg')
    label_image(inputs.label_morph, [inputs.ls_term], path+'/term_label.svg')
    label_image(inputs.label_morph, [inputs.ls_loc15], path+'/location_label.svg')
    label_image(inputs.label_morph, [inputs.reg_rad36, inputs.ls_distal], path+'/distal_label.svg')
    label_image(inputs.label_morph, [inputs.reg_rad36, inputs.ls_proximal], path+'/proximal_label.svg')
    label_image(inputs.label_morph, [inputs.ls_uniform0, inputs.ls_uniform1], path+'/uniform_label.svg')
    label_image(inputs.label_morph, [inputs.ls_branchmid], path+'/on_branches_label.svg')
    label_image(inputs.label_morph, [inputs.ls_term, inputs.reg_tag3, inputs.ls_restrict], path+'/restrict_label.svg')

    ####################### regions

    label_image(inputs.label_morph, [inputs.reg_empty, inputs.reg_all], path+'/nil_all_label.svg')
    label_image(inputs.label_morph, [inputs.reg_tag1, inputs.reg_tag2, inputs.reg_tag3], path+'/tag_label.svg')
    label_image(inputs.label_morph, [inputs.reg_tag1, inputs.reg_tag3], path+'/tag_label.svg')
    label_image(inputs.label_morph, [inputs.reg_branch0, inputs.reg_branch3], path+'/branch_label.svg')
    label_image(inputs.label_morph, [inputs.reg_segment0, inputs.reg_segment3], path+'/segment_label.svg')
    label_image(inputs.label_morph, [inputs.reg_cable_1_01, inputs.reg_cable_1_31, inputs.reg_cable_1_37], path+'/cable_label.svg')
    label_image(inputs.label_morph, [inputs.ls_proxint_in, inputs.reg_proxint],    path+'/proxint_label.svg')
    label_image(inputs.label_morph, [inputs.ls_proxint_in, inputs.reg_proxintinf], path+'/proxintinf_label.svg')
    label_image(inputs.label_morph, [inputs.ls_distint_in, inputs.reg_distint],    path+'/distint_label.svg')
    label_image(inputs.label_morph, [inputs.ls_distint_in, inputs.reg_distintinf], path+'/distintinf_label.svg')
    label_image(inputs.label_morph, [inputs.reg_lhs, inputs.reg_rhs, inputs.reg_or],  path+'/union_label.svg')
    label_image(inputs.label_morph, [inputs.reg_lhs, inputs.reg_rhs, inputs.reg_and], path+'/intersect_label.svg')
    label_image(inputs.label_morph, [inputs.reg_radlt5],  path+'/radiuslt_label.svg')
    label_image(inputs.label_morph, [inputs.reg_radle5],  path+'/radiusle_label.svg')
    label_image(inputs.label_morph, [inputs.reg_radgt5],  path+'/radiusgt_label.svg')
    label_image(inputs.label_morph, [inputs.reg_radge5],  path+'/radiusge_label.svg')

    ####################### Tutorial examples

    morph_image([inputs.tutorial_morph], ['segments'], path+'/tutorial_morph.svg')
    morph_image([inputs.tutorial_network_ring_morph], ['segments'], path+'/tutorial_network_ring_morph.svg',lab_sc=6)

    ####################### locsets

    label_image(inputs.tutorial_morph, [inputs.tut_ls_root, inputs.tut_ls_terminal], path+'/tutorial_root_term.svg')
    label_image(inputs.tutorial_morph, [inputs.tut_ls_custom_terminal, inputs.tut_ls_axon_terminal], path+'/tutorial_custom_axon_term.svg')
    label_image(inputs.tutorial_network_ring_morph, [inputs.tut_network_ring_ls_synapse_site], path+'/tutorial_network_ring_synapse_site.svg', loc_sc=6)

    ####################### regions
    label_image(inputs.tutorial_morph, [inputs.tut_reg_soma, inputs.tut_reg_axon, inputs.tut_reg_dend, inputs.tut_reg_last], path+'/tutorial_tag.svg')
    label_image(inputs.tutorial_morph, [inputs.tut_reg_all, inputs.tut_reg_rad_gt], path+'/tutorial_all_gt.svg')
    label_image(inputs.tutorial_morph, [inputs.tut_reg_custom], path+'/tutorial_custom.svg')

if __name__ == '__main__':
    generate('.')