diff --git a/share/lib/hoc/import3d/import3d_gui.hoc b/share/lib/hoc/import3d/import3d_gui.hoc index 205d24a1a8..7aee664298 100755 --- a/share/lib/hoc/import3d/import3d_gui.hoc +++ b/share/lib/hoc/import3d/import3d_gui.hoc @@ -1041,7 +1041,7 @@ func set_nameindex() {local i, min localobj sec * @param $2 (optional) If set to 1, will keep a public reference to a list of strings that were executed by this method (commands). */ -proc instantiate() {local i, j, min, haspy, ispycontext, keepCommands localobj sec, xx, yy, zz, dd, pyobj, allpyobjs +proc instantiate() {local i, j, min, haspy, ispycontext, keepCommands localobj sec, psec, xx, yy, zz, dd, pyobj, allpyobjs commands = new List() chk_valid() haspy = nrnpython("import neuron") @@ -1077,6 +1077,7 @@ proc instantiate() {local i, j, min, haspy, ispycontext, keepCommands localobj // connect for i = 0, swc.sections.count-1 { sec = swc.sections.object(i) + psec = sec.parentsec if (sec.is_subsidiary) { continue } name(sec, tstr) if (i == 0 && !ispycontext) { @@ -1086,25 +1087,33 @@ proc instantiate() {local i, j, min, haspy, ispycontext, keepCommands localobj execute(tstr1, $o1) } } - if (sec.parentsec != nil) { - name(sec.parentsec, tstr1) + if (psec != nil) { + name(psec, tstr1) if (!ispycontext) { sprint(tstr1, "%s connect %s(0), %g\n", tstr1, tstr, sec.parentx) commands.append(new String(tstr1)) execute(tstr1, $o1) - } else { - pyobj.neuron._connect_sections_in_obj($o1, tstr, 0, tstr1, sec.parentx) - } + } else { + pyobj.neuron._connect_sections_in_obj($o1, tstr, 0, tstr1, sec.parentx) + } } // 3-d point info if (sec.first == 1) { + // if psec is contour, update logical connection + if (psec != nil) { + if (psec.iscontour_) { + for j = 0, 2 { + sec.raw.x[j][0] = psec.processed_contour_center.x[j] + } + } + } if (ispycontext) { pyobj.neuron._pt3dstyle_in_obj($o1, tstr, sec.raw.x[0][0], sec.raw.x[1][0], sec.raw.x[2][0]) } else { sprint(tstr1, "%s { pt3dstyle(1, %.8g, %.8g, %.8g) }\n", tstr, sec.raw.x[0][0], sec.raw.x[1][0], sec.raw.x[2][0]) commands.append(new String(tstr1)) execute(tstr1, $o1) - } + } } j = sec.first xx = sec.raw.getrow(0).c(j) @@ -1199,12 +1208,13 @@ proc mksubset() { execute(tstr1, $o1) } -proc contour2centroid() {local i, j, imax, imin, ok localobj mean, pts, d, max, min, tobj, rad, rad2, side2, pt, major, m, minor +proc contour2centroid() {local i, j, imax, imin, ok localobj sec, mean, pts, d, max, min, tobj, rad, rad2, side2, pt, major, m, minor if (object_id($o5.contour_list)) { contourstack2centroid($o1, $o2, $o3, $o4, $o5) return } - mean = swc.sections.object(0).contourcenter($o1, $o2, $o3) + sec = swc.sections.object(0) + mean = sec.contourcenter($o1, $o2, $o3) if (g != nil) { g.beginline(6,1) for i=0, $o1.size-1 { @@ -1307,6 +1317,14 @@ if (g != nil) g.line(tobj.x[0], tobj.x[1]) g.flush for i=1,3 { $oi = pts.getrow(i-1) } // print d d.printf print rad rad.printf // print side2 side2.printf print rad2 rad2.printf + + // store the center of the processed contour for later + // updating of child logical connection to center of section + j = $o1.size-1 + for i = 1, 3 { + mean.x[i-1] = ($oi.x[0] + $oi.x[j])/2 + } + sec.processed_contour_center = mean.c() } proc contourstack2centroid() {local i, j, area, d localobj c diff --git a/share/lib/hoc/import3d/import3d_sec.hoc b/share/lib/hoc/import3d/import3d_sec.hoc index fd47330f1e..09396234f9 100755 --- a/share/lib/hoc/import3d/import3d_sec.hoc +++ b/share/lib/hoc/import3d/import3d_sec.hoc @@ -16,6 +16,11 @@ public stk_triang_vec, stk_triang_area, is_subsidiary public volatile2, contourcenter, ztrans, approximate_contour_by_circle public pl_point, insrt, set_pt, stk_center, accurate_triangle_area objref raw, xyz, d, g, parentsec, contour_list, this, stk_triang_vec +// processed_contour_center vector introduced because it may differ +// from the raw contour center +public processed_contour_center +objref processed_contour_center + proc init() { is_subsidiary = 0 ztrans = 0 @@ -184,7 +189,6 @@ proc rotate() { } } - // a utility function obfunc contourcenter() {local i localobj mean, pts, perim, d // convert contour defined by $o1, $o2, $o3 vectors to diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c6efbaa74c..6a9af16c48 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -309,7 +309,8 @@ if(NRN_ENABLE_PYTHON) GROUP hoctests NAME ${name}_${ext} ${${ext}_preload} # PRELOAD_SANITIZER for Python COMMAND ${${ext}_exe} "${hoc_script}" - SCRIPT_PATTERNS "${hoc_script}" "tests/${name}.json" ${${ext}test_utils}) + SCRIPT_PATTERNS "${hoc_script}" "tests/${name}.json" "tests/${name}.asc" + ${${ext}test_utils}) endforeach() endforeach() diff --git a/test/hoctests/tests/test_pt3dstyle.asc b/test/hoctests/tests/test_pt3dstyle.asc new file mode 100644 index 0000000000..441e8ac4bc --- /dev/null +++ b/test/hoctests/tests/test_pt3dstyle.asc @@ -0,0 +1,24 @@ +("CellBody" + (Color Red) + (CellBody) + (3.80077 3.41923 0 0) ; 1, 1 + (3.81077 1.71923 0 0) ; 1, 2 + (6.30077 -1.15077 0 0) ; 1, 3 + (5.79077 -3.76077 0 0) ; 1, 4 + (2.48077 -6.86077 0 0) ; 1, 5 + (-0.849229 -8.24077 0 0) ; 1, 6 + (-4.37923 -7.22077 0 0) ; 1, 7 + (-5.62923 -4.94077 0 0) ; 1, 8 + (-5.19923 -0.95077 0 0) ; 1, 9 + (-3.06923 4.74923 0 0) ; 1, 10 + (-1.70923 6.51923 0 0) ; 1, 11 + (-2.10923 7.95923 0 0) ; 1, 12 + (0.760771 8.75923 0 0) ; 1, 13 +); + +( (Color Blue) + (Axon) + (-1.10012 -6.51191 1.75185 1.33) + (-1.39509 -8.25791 2.22157 1.33) + (-2.03149 -16.6016 2.41259 0.66) +); diff --git a/test/hoctests/tests/test_pt3dstyle.py b/test/hoctests/tests/test_pt3dstyle.py new file mode 100644 index 0000000000..dc208e6151 --- /dev/null +++ b/test/hoctests/tests/test_pt3dstyle.py @@ -0,0 +1,55 @@ +from neuron import h +from math import isclose +import os + + +class Cell: + def __init__(self, id): + self.id = id + + def __str__(self): + return "Cell_%d" % self.id + + +cell = Cell(0) +h.load_file("stdrun.hoc") +h.load_file("import3d.hoc") +nl = h.Import3d_Neurolucida3() +nl.quiet = 1 +dir_path = os.path.dirname(os.path.realpath(__file__)) +fname = os.path.join(dir_path, "test_pt3dstyle.asc") +nl.input(fname) +import_neuron = h.Import3d_GUI(nl, 0) +import_neuron.instantiate(cell) + + +def logcon(s): + lcf = tuple([h.ref(0.0) for _ in range(3)]) + lc = h.pt3dstyle(1, *lcf, sec=s) + return lc, [ref[0] for ref in lcf] + + +def vec3d(s): + vecs = [[] for _ in range(4)] # x, y ,z, d + for i, f in enumerate([h.x3d, h.y3d, h.z3d, h.diam3d]): + for j in range(s.n3d()): + vecs[i].append(f(j)) + return vecs + + +def allpts(): + return [vec3d(s) for s in h.allsec()] + + +stdpts = allpts() +stdlogcon = logcon(cell.axon[0]) + +h.define_shape() + +assert stdpts == allpts() +# The logical connection is only accurate to float32 resolution +print(stdlogcon) +print(logcon(cell.axon[0])) +# assert stdlogcon == logcon(cell.axon[0]) +for i, val in enumerate(logcon(cell.axon[0])[1]): + assert isclose(stdlogcon[1][i], val, rel_tol=1e-7)