forked from NWoolridge/NeuronBuild
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNeuronBuild_1.5.py
More file actions
485 lines (390 loc) · 19.2 KB
/
NeuronBuild_1.5.py
File metadata and controls
485 lines (390 loc) · 19.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
"""
NeuronBuild | Nick Woolridge | 2013 | n.woolridge@utoronto.ca
version date: March 11 2013
A script to import swc files downloaded from neuromorpho.org, and create accurate
spline-based models of neuronal structure. The original swc file format is detailed here:
Cannon, R.C, Turner, D.A, Pyapali, G.K, Wheal, H.V. An on-line archive of reconstructed
hippocampal neurons. Journal of Neuroscience Methods. 84 1–2. pp 49-54. 1998
The reconstruction units are μm (micrometers).
Note: soma (cell body) definitions vary from file to file; this script assumes a three point spline
(which is very common). The soma object is disabled by default, since they rarely produce acceptable geometry.
Note: use of neuromorpho files may come with an obligation to cite the original publication.
How to use:
- add to your C4D scripts folder (on Mac OS X: Applications/MAXON/CINEMA 4D R14/library/scripts or in the user prefs folder)
- Browse and download a .swc or .swc.txt file from http://neuromorpho.org/
- Open the script manager in C4D, the script should be in the pop-up menu at the top of the window.
- In the Script manager in C4D load the NeuronBuild script and click "Execute".
- An import options dialog should appear; choose options for imported geometry, and click "Import File".
- In the open file dialog, choose the swc file and click "OK".
- A neuron should appear in your viewport.
- If all the geometry options are chosen, the geometry consists of a HyperNURBs object, which contains a Connect object, which
contains a null object, which contains the sweep objects that define the axons and dendrites.
Since the Soma (cell body) definition in the swc files is so rudimentary, you may want to
delete or hide it, and let the soma be defined by the merging dendrite roots. Within the sweep
objects are n-sided splines (named "Profile") set to 6-sides; you could search for these
objects and change the number of sides to 4 to simplify the geometry. Also in the SweepNURBs objects
are the splines that define the dendrite paths, and rail splines that define their radius.
New in 1.4:
Modifications made by Graham Johnson on March 11, 2013
– support for Cinema 4D r12 and r13 (Oconnect and AddMultiLineEditText compatibility
– convert right handed .swc data to Cinema 4D left-handed with coordSystem test
– added safety test if user hits cancel button while in the system browser. Reports as 'Cancelled in Browser.'
This software is open-source under the MIT License.
Copyright (c) 2013 Nicholas Woolridge
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
import c4d, os
from c4d import gui
#Welcome to the world of Python
#Create IDs for the GUI elements in the settings dialog
DLG_GROUP_1 = 1000
DLG_GROUP_2 = 1001
TEXTBOX = 1002
CANCELBUTTON = 1003
IMPORTBUTTON = 1004
HNCHECK = 1005
CONNECTCHECK = 1006
RAILCHECK = 1007
SWEEPCHECK = 1008
PROFILETEXT = 1009
PROFILESIDES = 1010
coordsystem="left"
versionNumber=c4d.GetC4DVersion()
def somaMake(somaLines, neuroFile, fileName):
"""Create splines to make the cell body."""
#reference global variables that set model parameters
global DoHN, DoConnect, DoRail, DoSweep, NSides
#create spline
Spline = c4d.BaseObject(c4d.Ospline)
#add name to spline
Spline[c4d.ID_BASELIST_NAME] = "Soma"
#set type to linear
Spline[c4d.SPLINEOBJECT_TYPE] = 0
#set number of points for spline
Spline.ResizeObject(len(somaLines))
for n in range(0, len(somaLines)):
currLine = somaLines[n]
#create the variables for positioning the points
sx = float(currLine[2])
sy = float(currLine[3])
sz = float(currLine[4])
if coordsystem=="left": #Convert to left-hand for C4D added by GJ March 11, 2013
sz = -sz
sRad = float(currLine[5])
pos = c4d.Vector(sx, sy, sz)
Spline.SetPoint(n, pos)
#create the soma spline
doc.InsertObject(Spline)
#create sweep object
if DoSweep == True:
Sweep = c4d.BaseObject(c4d.Osweep)
Sweep[c4d.ID_BASELIST_NAME] = "Soma Sweep"
Sweep[c4d.SWEEPOBJECT_CONSTANT] = False
Sweep[c4d.SWEEPOBJECT_RAILDIRECTION] = False
Sweep[c4d.CAP_TYPE] = 1
Sweep.SetPhong(True, True, 80)
Sweep.SetDeformMode(False)
doc.InsertObject(Sweep)
#create the profile for the sweep
Profile = c4d.BaseObject(c4d.Osplinenside)
Profile[c4d.ID_BASELIST_NAME] = "Profile"
Profile[c4d.PRIM_NSIDE_RADIUS] = sRad
Profile[c4d.PRIM_NSIDE_SIDES] = NSides
doc.InsertObject(Profile)
Spline.InsertUnder(Sweep)
Profile.InsertUnder(Sweep)
#add undo for spline creation
doc.AddUndo(c4d.UNDOTYPE_NEW, Spline)
#insert the spline under the null object
if DoSweep == True:
parent = doc.SearchObject(fileName)
Sweep.InsertUnder(parent)
c4d.EventAdd()
def splineMake(splineLines, neuroFile, fileName):
"""Create splines to define the dendrites and axons."""
#reference global variables that set model parameters
global DoHN, DoConnect, DoRail, DoSweep, NSides
#run through the data file, identifying contiguous spline segments. this is possible because
#the last value in the data refers to the "root" point of that branch segment
splineSep = []
for n in range(0, len(splineLines)):
currLine = splineLines[n]
sIndex = int(currLine[0])
sRoot = int(currLine[6])
if sIndex > sRoot + 1:
splineSep.append(sIndex)
#now build the spline segments as separate splines
for n in range(0, len(splineSep)):
if n < (len(splineSep) - 1):
#determine the offset between segemnts; this is the number of vertices in each spline segment
offset = splineSep[n+1] - splineSep[n]
#create an empty spline
Spline = c4d.BaseObject(c4d.Ospline)
#allocate points for the spline
Spline.ResizeObject(offset)
#get the data line that starts the segment
splineStart = neuroFile[splineSep[n]]
splineType = int(splineStart[1])
#determine what type of spline it is and name it
if splineType == 2:
name = "Axon " + str(n)
elif splineType == 3:
name = "Basal Dendrite " + str(n)
elif splineType == 4:
name = "Apical Dendrite " + str(n)
Spline[c4d.ID_BASELIST_NAME] = name
Spline[c4d.SPLINEOBJECT_TYPE] = 0
#find the root poiont for this segment by going back to
#the line in neuroFile that contains it
#and then we have to step back one line in the source file
rootLine = neuroFile[((int(splineStart[6])) - 1)]
rootRoot = neuroFile[int(rootLine[6]) - 1]
x, y, z = float(rootRoot[2]), float(rootRoot[3]), float(rootRoot[4])
rootPos = c4d.Vector(x, y, z)
if coordsystem=="left": #Convert to left-hand for C4D added by GJ March 11, 2013
rootPos = c4d.Vector(x, y, -z)
Spline.SetPoint(0, rootPos)
for m in range(1, offset):
l = int(splineSep[n]) + (m - 1)
currLine = neuroFile[l]
#create the variables for positioning the points
sx = float(currLine[2])
sy = float(currLine[3])
sz = float(currLine[4])
if coordsystem=="left": #Convert to left-hand for C4D added by GJ March 11, 2013
sz = -sz
#radius, not used yet
sRad = float(currLine[5])
pos = c4d.Vector(sx, sy, sz)
Spline.SetPoint(m, pos)
#create the spline
doc.InsertObject(Spline)
c4d.EventAdd()
#in order to scale the sweepNURBs object, we create a copy of the spline
#to act as a rail spline, and then add the radius value to one of the coords
if DoRail == True:
railSpline = Spline.GetClone()
railSpline[c4d.ID_BASELIST_NAME] = name + " Rail"
rVector = c4d.Vector((x + (float(rootRoot[5]))), y, z)
if coordsystem == "left": #Convert to left-hand for C4D added by GJ March 11, 2013
rVector = c4d.Vector((x + (float(rootRoot[5]))), y, -z)
railSpline.SetPoint(0, rVector)
for m in range(1, offset):
l = int(splineSep[n]) + (m - 1)
currLine = neuroFile[l]
#create the variables for positioning the points
sRad = float(currLine[5])
#add the radius to the x coord this time
sx = (float(currLine[2]) + sRad)
sy = float(currLine[3])
sz = float(currLine[4])
if coordsystem=="left": #Convert to left-hand for C4D added by GJ March 11, 2013
sz = -sz
#radius, not used yet
pos = c4d.Vector(sx, sy, sz)
railSpline.SetPoint(m, pos)
doc.InsertObject(railSpline)
c4d.EventAdd()
#create the sweep object
if DoSweep == True:
Sweep = c4d.BaseObject(c4d.Osweep)
Sweep[c4d.ID_BASELIST_NAME] = name
#the seep object needs to have two default settings disabled
Sweep[c4d.SWEEPOBJECT_CONSTANT] = False
Sweep[c4d.SWEEPOBJECT_RAILDIRECTION] = False
Sweep[c4d.CAP_TYPE] = 1
Sweep.SetPhong(True, True, 80)
#create the profile for the sweep
Profile = c4d.BaseObject(c4d.Osplinenside)
Profile[c4d.ID_BASELIST_NAME] = "Profile"
Profile[c4d.PRIM_NSIDE_RADIUS] = sRad
Profile[c4d.PRIM_NSIDE_SIDES] = NSides
doc.InsertObject(Profile)
#insert the splines as children of the sweepNURBs object, in the correct order
if DoRail == True:
if DoSweep == True:
railSpline.InsertUnder(Sweep)
if DoSweep == True:
Spline.InsertUnder(Sweep)
Profile.InsertUnder(Sweep)
#add undo for spline creation
doc.AddUndo(c4d.UNDOTYPE_NEW, Spline)
#insert the spline under the null object
if DoSweep == True:
parent = doc.SearchObject(fileName)
Sweep.InsertUnder(parent)
c4d.EventAdd()
def readFile(path):
"""Access the neuromorpho swc file, strip comments, and load it into the neuroFile nested list. """
#reference global variables that set model parameters
global DoHN, DoConnect, DoRail, DoSweep
neuroFile = []
somaLines = []
splineLines = []
#Get the name of the file and split off the last file extension
filePath = os.path.basename(path)
fileName = os.path.splitext(filePath)[0]
#here we read the file, and create two sub lists: one for the soma, and one for the axons and dendrites
for line in open(path):
# ignore lines that are comments
if line.startswith('#'):
pass
else:
# create nested list of data
x = [value for value in line.split()]
neuroFile.append(x)
#create
if x[1] == "1":
somaLines.append(x)
else:
splineLines.append(x)
#the numlines variable stores the length of the data files (number of points)
numLines = len(neuroFile)
#create null to contain splines
Null = c4d.BaseObject(c4d.Onull)
Null[c4d.ID_BASELIST_NAME] = fileName
#insert the null in the scene
doc.InsertObject(Null)
#add undo for null creation
doc.AddUndo(c4d.UNDOTYPE_NEW, Null)
c4d.EventAdd()
#call the functions that build the soma and other splines
somaMake(somaLines, neuroFile, fileName)
splineMake(splineLines, neuroFile, fileName)
#create connect object
if DoConnect == True:
if versionNumber >= 14000:
Connect = c4d.BaseObject(c4d.Oconnector)
Connect[c4d.ID_BASELIST_NAME] = fileName
#insert the connector in the scene
doc.InsertObject(Connect)
else:
c4d.CallCommand(1011010)
Connect = doc.SearchObject("Connect")
if Connect:
#add undo for Connect creation
doc.AddUndo(c4d.UNDOTYPE_NEW, Connect)
c4d.EventAdd()
Null.InsertUnder(Connect)
#create HN object
if DoHN == True:
HN = c4d.BaseObject(c4d.Osds)
HN[c4d.ID_BASELIST_NAME] = fileName
#insert the HyperNURBs in the scene
doc.InsertObject(HN)
#add undo for HN creation
doc.AddUndo(c4d.UNDOTYPE_NEW, HN)
c4d.EventAdd()
if DoConnect == True:
Connect.InsertUnder(HN)
else:
Null.InsertUnder(HN)
#close out the undo stack
doc.EndUndo()
class SettingsDlg(gui.GeDialog):
def __init__(self, title=None, input_text=None):
self.title = title or "User Input"
self.input_text = input_text or ""
self.result = None
def CreateLayout(self):
self.SetTitle("NeuronBuild")
#creat the layout of the dialog
self.GroupBegin(DLG_GROUP_1, flags=c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT, cols=1, rows=7, title="NeuronBuild", groupflags=5)
self.GroupBorderSpace(10, 10, 10, 10)
#add multiline edit box for script description
if versionNumber >= 13000:
self.AddMultiLineEditText(TEXTBOX, flags=c4d.BFH_CENTER|c4d.BFV_TOP, inith=100, initw=300, style=c4d.DR_MULTILINE_READONLY|c4d.DR_MULTILINE_WORDWRAP)
else:
self.AddMultiLineEditText(TEXTBOX, flags=c4d.BFH_CENTER|c4d.BFV_TOP, inith=100, initw=300)
# self.AddStaticText(TEXTBOX, flags=c4d.BFH_CENTER|c4d.BFV_TOP, inith=100, initw=300, borderstyle=1)
#add check boxes for various construction options
self.AddCheckbox(HNCHECK, flags=c4d.BFH_LEFT, initw=300, inith=0, name="Add HyperNURBs object")
self.SetBool(HNCHECK, True)
self.AddCheckbox(CONNECTCHECK, flags=c4d.BFH_LEFT, initw=300, inith=0, name="Add Connect object")
self.SetBool(CONNECTCHECK, True)
self.AddCheckbox(RAILCHECK, flags=c4d.BFH_LEFT, initw=300, inith=0, name="Add rail splines (controls thickness)")
self.SetBool(RAILCHECK, True)
self.AddCheckbox(SWEEPCHECK, flags=c4d.BFH_LEFT, initw=300, inith=0, name="Add SweepNURBs object")
self.SetBool(SWEEPCHECK, True)
self.AddStaticText(PROFILETEXT, flags=c4d.BFH_LEFT, initw=300, inith=0, name="Sweep profile sides:")
self.AddEditSlider(PROFILESIDES, flags=c4d.BFH_FIT, initw=30, inith=0)
self.SetLong(PROFILESIDES, 6, min=3, max=8, step=1)
self.GroupEnd()
self.GroupBegin(DLG_GROUP_2, flags=c4d.BFH_RIGHT|c4d.BFV_BOTTOM, cols=2, rows=1, title="", groupflags=5)
self.GroupBorderSpace(10, 10, 10, 10)
self.AddButton(CANCELBUTTON, flags=c4d.BFH_RIGHT, name="Cancel")
self.AddButton(IMPORTBUTTON, flags=c4d.BFH_RIGHT, name="Import File")
self.GroupEnd()
return True
def InitValues(self):
#initiate the gadgets with values
if versionNumber >= 13000:
self.SetString(TEXTBOX, "The NeuronBuild script imports accurate neuromorpho SWC files "\
"and creates spline-based geometry (scale assumes µm as world unit). Choose modeling options below; " \
"if all boxes are unchecked, only the base splines will be added to the scene.")
else:
self.SetString(TEXTBOX, "The NeuronBuild script imports\naccurate neuromorpho SWC files\n"\
"and creates spline-based geometry\n(scale assumes µm as world unit).\nChoose modeling options below;\n" \
"if all boxes are unchecked, only\nthe base splines will be added to\nthe scene.")
self.result = True
return True
def Command(self, id, msg):
#reference global variables that set model parameters
global DoHN, DoConnect, DoRail, DoSweep, NSides
#handle user input
if id==IMPORTBUTTON:
close = True
DoHN = self.GetBool(HNCHECK)
DoConnect = self.GetBool(CONNECTCHECK)
DoRail = self.GetBool(RAILCHECK)
DoSweep = self.GetBool(SWEEPCHECK)
NSides = self.GetLong(PROFILESIDES)
self.result = True
elif id==CANCELBUTTON:
close = True
self.result = None
else:
close = False
DoHN = self.GetBool(HNCHECK)
DoConnect = self.GetBool(CONNECTCHECK)
DoRail = self.GetBool(RAILCHECK)
DoSweep = self.GetBool(SWEEPCHECK)
NSides = self.GetLong(PROFILESIDES)
if close:
self.Close()
return True
def open_settings_dialog(default=None, title=None, width=240, height=290):
dialog = SettingsDlg(title, default)
dialog.Open(c4d.DLG_TYPE_MODAL, defaultw=width, defaulth=height)
return dialog.result
def main():
"""Call the readfile function. Start undo here."""
#reference global variables that set model parameters
global DoHN, DoConnect, DoRail, DoSweep, NSides
value = open_settings_dialog("test", "test")
#test to see wehther the dialog "Cancel" button is pressed
if value is None:
print "Cancelled."
else:
doc.StartUndo()
# open the C4D file browser to allow the user to choose a text file to read
neuromorphoFile = c4d.storage.LoadDialog()
# run the read file procedure
if neuromorphoFile:
readFile(neuromorphoFile)
else:
print "Cancelled in Browser."
if __name__=='__main__':
main()