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
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
|
# Copyright 2008 Nanorex, Inc. See LICENSE file for details.
"""
MultipleDnaSegmentResize_EditCommand provides a way to edit (resize) one or more
DnaSegments. To resize segments, select the segments you want to resize,
highlight the axis of any one (in the default command), right click and select
'Resize DnaSegments' from the context menu to enter this command. Then you can
interactively modify the segment list using the Property Manager
and use the resize handles to resize all segments at once.
@author: Ninad
@copyright: 2008 Nanorex, Inc. See LICENSE file for details.
@version:$Id$
History:
2008-05-09 - 2008-05-14 Created / modified.
"""
from Numeric import dot
from geometry.VQT import V, norm, vlen
from utilities.constants import applegreen
from dna.commands.DnaSegment.DnaSegment_EditCommand import DnaSegment_EditCommand
from dna.commands.MultipleDnaSegmentResize.MultipleDnaSegmentResize_GraphicsMode import MultipleDnaSegmentResize_GraphicsMode
from dna.model.Dna_Constants import getDuplexLength
from dna.model.Dna_Constants import getNumberOfBasePairsFromDuplexLength
from dna.generators.B_Dna_PAM3_Generator import B_Dna_PAM3_Generator
from exprs.attr_decl_macros import Instance, State
from exprs.__Symbols__ import _self
from exprs.Exprs import call_Expr
from exprs.Exprs import norm_Expr
from exprs.ExprsConstants import Width, Point
from widgets.prefs_widgets import ObjAttr_StateRef
from dna.commands.DnaSegment.DnaSegment_ResizeHandle import DnaSegment_ResizeHandle
from utilities.debug import print_compact_stack
from dna.command_support.DnaSegmentList import DnaSegmentList
from dna.commands.MultipleDnaSegmentResize.MultipleDnaSegmentResize_PropertyManager import MultipleDnaSegmentResize_PropertyManager
CYLINDER_WIDTH_DEFAULT_VALUE = 0.0
HANDLE_RADIUS_DEFAULT_VALUE = 4.0
ORIGIN = V(0, 0, 0)
_superclass = DnaSegment_EditCommand
class MultipleDnaSegmentResize_EditCommand(DnaSegment_EditCommand):
#Graphics Mode
GraphicsMode_class = MultipleDnaSegmentResize_GraphicsMode
#Property Manager
PM_class = MultipleDnaSegmentResize_PropertyManager
cmdname = 'MULTIPLE_DNA_SEGMENT_RESIZE' # REVIEW: needed? correct?
commandName = 'MULTIPLE_DNA_SEGMENT_RESIZE'
featurename = "Edit Multiple Dna Segments"
from utilities.constants import CL_SUBCOMMAND
command_level = CL_SUBCOMMAND
command_parent = 'BUILD_DNA'
#This command operates on (resizes) multiple DnaSegments at once.
#It does that by looping through the DnaSegments and resizing them
#individually. self.currentStruct is set to the 'current' DnaSegment
#in that loop and then it is used in the resizing code.
#@see: DnaSegmentList class
#@see: self.modifyStructure()
currentStruct = None
handlePoint1 = State( Point, ORIGIN)
handlePoint2 = State( Point, ORIGIN)
#The minimum 'stopper'length used for resize handles
#@see: self._update_resizeHandle_stopper_length for details.
_resizeHandle_stopper_length = State(Width, -100000)
rotationHandleBasePoint1 = State( Point, ORIGIN)
rotationHandleBasePoint2 = State( Point, ORIGIN)
#See self._update_resizeHandle_radius where this gets changed.
#also see DnaSegment_ResizeHandle to see how its implemented.
handleSphereRadius1 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)
handleSphereRadius2 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)
cylinderWidth = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
cylinderWidth2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
#@TODO: modify the 'State params for rotation_distance
rotation_distance1 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
rotation_distance2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
leftHandle = Instance(
DnaSegment_ResizeHandle(
command = _self,
height_ref = call_Expr( ObjAttr_StateRef, _self, 'cylinderWidth'),
origin = handlePoint1,
fixedEndOfStructure = handlePoint2,
direction = norm_Expr(handlePoint1 - handlePoint2),
sphereRadius = handleSphereRadius1,
range = (_resizeHandle_stopper_length, 10000)
))
rightHandle = Instance(
DnaSegment_ResizeHandle(
command = _self,
height_ref = call_Expr( ObjAttr_StateRef, _self, 'cylinderWidth2'),
origin = handlePoint2,
fixedEndOfStructure = handlePoint1,
direction = norm_Expr(handlePoint2 - handlePoint1),
sphereRadius = handleSphereRadius2,
range = (_resizeHandle_stopper_length, 10000)
))
def _update_previousParams_in_model_changed(self):
"""
Overrides superclass method. Does nothing in this class.
@see: self.model_changed() which calls this method.
"""
pass
def isAddSegmentsToolActive(self):
"""
Returns True if the Add segments tool in the PM, that allows adding
dna segments from the resize segment list, is active.
@see: MultipleDnaSegmentResize_GraphicsMode.chunkLeftUp()
@see: MultipleDnaSegmentResize_GraphicsMode.end_selection_from_GLPane()
@see: self.isRemoveSegmentsToolActive()
@see: self.addSegmentToResizeSegmentList()
"""
if self.propMgr is None:
return False
return self.propMgr.isAddSegmentsToolActive()
def isRemoveSegmentsToolActive(self):
"""
Returns True if the Remove Segments tool in the PM, that allows removing
dna segments from the resize segment list, is active.
@see: MultipleDnaSegmentResize_GraphicsMode.chunkLeftUp()
@see: MultipleDnaSegmentResize_GraphicsMode.end_selection_from_GLPane()
@see: self.isAddSegmentsToolActive()
@see: self.removeSegmentFromResizeSegmentList()
"""
if self.propMgr is None:
return False
return self.propMgr.isRemoveSegmentsToolActive()
def addSegmentToResizeSegmentList(self, segment):
"""
Adds the given segment to the resize segment list
@param segment: The DnaSegment to be added to the resize segment list.
Also does other things such as updating resize handles etc.
@type sement: B{DnaSegment}
@see: self.isAddSegmentsToolActive()
"""
assert isinstance(segment, self.win.assy.DnaSegment)
self.struct.addToStructList(segment)
def removeSegmentFromResizeSegmentList(self, segment):
"""
Removes the given segment from the resize segment list
@param segment: The DnaSegment to be removed from the resize segment
list. Also does other things such as updating resize handles etc.
@type sement: B{DnaSegment}
@see: self.isRemoveSegmentsToolActive()
"""
assert isinstance(segment, self.win.assy.DnaSegment)
self.struct.removeFromStructList(segment)
def setResizeStructList(self, structList):
"""
Replaces the list of segments to be resized with the
given segmentList. Calls the related method of self.struct.
@param structList: New list of segments to be resized.
@type structList: list
@see: DnaSegmentList.setResizeStructList()
"""
if self.hasValidStructure():
self.struct.setStructList(structList)
self.updateHandlePositions()
def getResizeSegmentList(self):
"""
Returns a list of segments to be resized at once.
@see: DnaSegmentList.getStructList()
"""
if self.hasValidStructure():
return self.struct.getStructList()
return ()
def updateResizeSegmentList(self):
"""
Update the list of resize segments (maintained by self.struct)
"""
if self.hasValidStructure():
self.struct.updateStructList()
def editStructure(self, struct = None):
"""
Overrides superclass method. It first creates a B{DnaSegmentList} object
using the provided list (struct argument) The DnaSegmentList object
acts as self.struct.
@param struct: The caller of this method should provide a list of
containing all the structurs that need to be edited (resized) at
once. Caller also needs to make sure that this list contains
objects of class DnaSegment.
@type struct: list
@see: B{DnaSegmentList}
"""
if isinstance(struct, list):
dnaSegmentListObject = DnaSegmentList(self,
structList = struct)
_superclass.editStructure(self, struct = dnaSegmentListObject)
def _updatePropMgrParams(self):
"""
Subclasses may override this method.
Update some property manager parameters with the parameters of
self.struct (which is being edited)
@see: self.editStructure()
@TODO: For multiple Dna segment resizing, this method does nothing as
of 2008-05-14. Need to be revised.
"""
#Commented out code as of 2008-05-14 ===========
#endPoint1, endPoint2 = self.struct.getAxisEndPoints()
#params_for_propMgr = (None,
#endPoint1,
#endPoint2)
##TODO 2008-03-25: better to get all parameters from self.struct and
##set it in propMgr? This will mostly work except that reverse is
##not true. i.e. we can not specify same set of params for
##self.struct.setProps ...because endPoint1 and endPoint2 are derived.
##by the structure when needed. Commenting out following line of code
##UPDATE 2008-05-06 Fixes a bug due to which the parameters in propMGr
##of DnaSegment_EditCommand are not same as the original structure
##(e.g. bases per turn and duplexrise)
###self.propMgr.setParameters(params_for_propMgr)
pass
def _update_resizeHandle_radius(self):
"""
Finds out the sphere radius to use for the resize handles, based on
atom /chunk or glpane display (whichever decides the display of the end
atoms. The default value is 1.2.
@see: self.updateHandlePositions()
@see: B{Atom.drawing_radius()}
@TODO: Refactor this. It does essentially same thing as the
superclass method. The problem is due to the use of the global constant
HANDLE_RADIUS_DEFAULT_VALUE which needs to be different (bigger) in this
class. So better to make it a class constant (need similar changes
in several commands. To be done in a refactoring project.
"""
atm1 , atm2 = self.struct.getAxisEndAtoms()
if atm1 is not None:
self.handleSphereRadius1 = max(1.005*atm1.drawing_radius(),
1.005*HANDLE_RADIUS_DEFAULT_VALUE)
if atm2 is not None:
self.handleSphereRadius2 = max(1.005*atm2.drawing_radius(),
1.005*HANDLE_RADIUS_DEFAULT_VALUE)
def _modifyStructure(self, params):
"""
Modify the structure based on the parameters specified.
Overrides EditCommand._modifystructure. This method removes the old
structure and creates a new one using self._createStructure. This
was needed for the structures like this (Dna, Nanotube etc) . .
See more comments in the method.
"""
if self.currentStruct is None:
print_compact_stack("bug: self.currentStruct doesn't exist"\
"exiting self._modifyStructure")
return
assert isinstance(self.currentStruct , self.win.assy.DnaSegment)
self.dna = B_Dna_PAM3_Generator()
duplexRise = self.currentStruct.getDuplexRise()
basesPerTurn = self.currentStruct.getBasesPerTurn()
numberOfBasePairsToAddOrRemove = self._determine_numberOfBasePairs_to_change()
ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end()
if ladderEndAxisAtom is None:
print_compact_stack("bug: unable to resize the DnaSegment"\
"%r, end axis ato not found"%self.currentStruct)
return
if numberOfBasePairsToAddOrRemove != 0:
resizeEnd_final_position = self._get_resizeEnd_final_position(
ladderEndAxisAtom,
abs(numberOfBasePairsToAddOrRemove),
duplexRise )
self.dna.modify(self.currentStruct,
ladderEndAxisAtom,
numberOfBasePairsToAddOrRemove,
basesPerTurn,
duplexRise,
ladderEndAxisAtom.posn(),
resizeEnd_final_position)
self.previousParams = params
return
def modifyStructure(self):
"""
Called when a resize handle is dragged to change the length of one
or more DnaSegments (to be resized together) Called upon leftUp .
To acheive this resizing, it loops through the DnaSegments to be resized
and calls self._modifyStructure() for individual DnaSegments.
Note that Client should call this public method and should never call
the private method self._modifyStructure.
@see: B{DnaSegment_ResizeHandle.on_release} (the caller)
@see: B{SelectChunks_GraphicsMode.leftUp} (which calls the
the relevent method in DragHandler API. )
@see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior}
@see: B{self._modifyStructure}
@see: B{DnaSegmentList.updateAverageEndPoints()}
"""
if self.grabbedHandle is None:
return
for segment in self.getResizeSegmentList():
#TODO 2008-05-14: DnaSegmentList class relies on self.currentStruct.
#See if there is a better way. Ok for now
self.currentStruct = segment
#self._modifyStructure needs 'param' argument. As of 2008-05-14
#it is not supported/needed for multiple dna segment resizing.
#So just pass an empty tuple.
params = ()
self._modifyStructure(params)
#Reset the self.currentStruct after use
self.currentStruct = None
#Update the average end points of the self.struct so that resize handles
#are placed at proper positions.
self.struct.updateAverageEndPoints()
self.win.assy.changed()
self.win.win_update()
def _get_resizeEnd_final_position(self,
ladderEndAxisAtom,
numberOfBases,
duplexRise):
"""
Returns the final position of the resize end.
Note that we can not use the grabbedHandle's currentPosition as the
final position because resize handle is placed at an 'average' position.
So we must compute the correct vector using the 'self.currentStruct'
"""
final_position = None
other_axisEndAtom = self.struct.getOtherAxisEndAtom(ladderEndAxisAtom)
if other_axisEndAtom is None:
return None
axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn()
segment_length_to_add = getDuplexLength('B-DNA',
numberOfBases,
duplexRise = duplexRise)
signFactor = + 1
##if self.grabbedHandle is not None:
##vec = self.grabbedHandle.currentPosition - \
## self.grabbedHandle.fixedEndOfStructure
##if dot(vec, axis_vector) < 0:
##signFactor = -1
##else:
##signFactor = +1
axis_vector = axis_vector * signFactor
final_position = ladderEndAxisAtom.posn() + \
norm(axis_vector)*segment_length_to_add
return final_position
def hasResizableStructure(self):
"""
"""
if len(self.struct.getStructList()) == 0:
why_not = 'Segment list is empty'
isResizable = False
return isResizable, why_not
return _superclass.hasResizableStructure(self)
def _getStructureType(self):
"""
Returns the type of the structure this command supports.
This is used in isinstance test.
@see: EditCommand._getStructureType()
"""
return DnaSegmentList
def getDnaRibbonParams(self):
"""
Overrides superclass method.
@TODO:[2008-05-14] This could SLOW things up! Its called multiple times
from the graphics mode (for all the DnaSegments to be resized at once).
There is no apparent solution to this, but we could try optimizing the
following code. Also needs some refactoring.
@see: MultipleDnaSegmentResize_GraphicsMode._drawHandles()
@see: self._determine_numberOfBasePairs_to_change()
"""
#Optimization: First test a few basic things to see if the dna ribbon
#should be drawn, before doing more computations -- Ninad 2008-05-14
params_when_adding_bases = None
params_when_removing_bases = None
if self.grabbedHandle is None:
return None, None
if self.grabbedHandle.origin is None:
return None, None
ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end()
if ladderEndAxisAtom is None:
return None, None
numberOfBasePairsToAddOrRemove = self._determine_numberOfBasePairs_to_change()
if numberOfBasePairsToAddOrRemove == 0:
return None, None
direction_of_drag = norm(self.grabbedHandle.currentPosition - \
self.grabbedHandle.origin)
#@TODO: BUG: DO NOT use self._get_resizeEnd_final_position to compute
#the resizeEnd_final_position. It computes the segment length to add
#using the number of base pairs to add ... and it uses
#getDuplexLength method which in turn rounds off the length as an integer
#multiple of duplexRise. Although this is what we want in self._modify()
#the drawDnaRibbon draws the base pair only if step size is > 3.18!
#so no basepair is drawn at exact val of 3.18. This is BUG, harder to
#fix. for drawing dna ribbon, lets justcompute the resize end final
#position using the following code. -- Ninad 2008-05-14
other_axisEndAtom = self.struct.getOtherAxisEndAtom(ladderEndAxisAtom)
if other_axisEndAtom is None:
return None, None
axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn()
currentPosition = self.grabbedHandle.currentPosition
changedLength = vlen(currentPosition - self.grabbedHandle.origin)
#NOTE: (TODO) we call self._determine_numberOfBasePairs_to_change() at the
#beginning of this method which checks various things such as
#total distance moved by the handle etc to determine whether to draw
#the ribbon. So those checks are not done here. If that call is removed
#then we need to do those checks.
if dot(self.grabbedHandle.direction, direction_of_drag) < 0:
signFactor = -1.0
else:
signFactor = 1.0
resizeEnd_final_position = ladderEndAxisAtom.posn() + \
norm(axis_vector)*changedLength*signFactor
#If the segment is being shortened (determined by checking the
#direction of drag) , no need to draw the rubberband line.
if dot(self.grabbedHandle.direction, direction_of_drag) < 0:
params_when_adding_bases = None
params_when_removing_bases = (resizeEnd_final_position)
return params_when_adding_bases, \
params_when_removing_bases
basesPerTurn = self.struct.getBasesPerTurn()
duplexRise = self.struct.getDuplexRise()
ladder = ladderEndAxisAtom.molecule.ladder
endBaseAtomList = ladder.get_endBaseAtoms_containing_atom(
ladderEndAxisAtom)
ribbon1_start_point = None
ribbon2_start_point = None
ribbon1_direction = None
ribbon2_direction = None
ribbon1Color = applegreen
ribbon2Color = applegreen
if endBaseAtomList and len(endBaseAtomList) > 2:
strand_atom1 = endBaseAtomList[0]
strand_atom2 = endBaseAtomList[2]
if strand_atom1:
ribbon1_start_point = strand_atom1.posn()
for bond_direction, neighbor in strand_atom1.bond_directions_to_neighbors():
if neighbor and neighbor.is_singlet():
ribbon1_direction = bond_direction
break
ribbon1Color = strand_atom1.molecule.color
if not ribbon1Color:
ribbon1Color = strand_atom1.element.color
if strand_atom2:
ribbon2_start_point = strand_atom2.posn()
for bond_direction, neighbor in strand_atom2.bond_directions_to_neighbors():
if neighbor and neighbor.is_singlet():
ribbon2_direction = bond_direction
break
ribbon2Color = strand_atom2.molecule.color
if not ribbon2Color:
ribbon2Color = strand_atom2.element.color
params_when_adding_bases = ( ladderEndAxisAtom.posn(),
resizeEnd_final_position,
basesPerTurn,
duplexRise,
ribbon1_start_point,
ribbon2_start_point,
ribbon1_direction,
ribbon2_direction,
ribbon1Color,
ribbon2Color )
params_when_removing_bases = None
return params_when_adding_bases, params_when_removing_bases
def _determine_numberOfBasePairs_to_change(self):
"""
Overrides superclass method
@TODO: This is significantly different (and perhaps better)
than the superclass method. See how to incorporate changes in
this method in superclass.
@see: self.getDnaRibbonParams()
"""
currentPosition = self.grabbedHandle.currentPosition
fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure
duplexRise = self.struct.getDuplexRise()
changedLength = vlen(currentPosition - self.grabbedHandle.origin)
direction_of_drag = norm(self.grabbedHandle.currentPosition - \
self.grabbedHandle.origin)
#Even when the direction of drag is negative (i.e. the basepairs being
#removed), make sure not to remove base pairs for very small movement
#of the grabbed handle
if changedLength < 0.2*duplexRise:
return 0
#This check quickly determines if the grabbed handle moved by a distance
#more than the duplexRise and avoids further computations
#This condition is applicable only when the direction of drag is
#positive..i.e. bases bing added to the segment.
if changedLength < duplexRise and \
dot(self.grabbedHandle.direction, direction_of_drag) > 0:
return 0
#If the segment is being shortened (determined by checking the
#direction of drag)
numberOfBasesToAddOrRemove = \
getNumberOfBasePairsFromDuplexLength(
'B-DNA',
changedLength,
duplexRise = duplexRise)
if dot(self.grabbedHandle.direction, direction_of_drag) < 0:
numberOfBasesToAddOrRemove = - numberOfBasesToAddOrRemove
if numberOfBasesToAddOrRemove > 0:
#dna.modify will remove the first base pair it creates
#(that basepair will only be used for proper alignment of the
#duplex with the existing structure) So we need to compensate for
#this basepair by adding 1 to the new number of base pairs.
#UPDATE 2008-05-14: The following commented out code
#i.e. "##numberOfBasesToAddOrRemove += 1" is not required in this
#class , because the way we compute the number of base pairs to
#be added is different than than how its done at the moment in the
#superclass. In this method, we compute bases to be added from
#the resize end and that computation INCLUDES the resize end.
#so the number that it returns is already one more than the actual
#bases to be added. so commenting out the following line
# -- Ninad 2008-05-14
##numberOfBasesToAddOrRemove += 1
##print "*** numberOfBasesToAddOrRemove = ", numberOfBasesToAddOrRemove
##print "**** changedLength =", changedLength
pass
###UPDATE 2008-06-26: for some reason, when the number of base pairs
###to be added (i.e. value of numberOfBasesToAddOrRemove) is 1 more
###than the actual number of base pairs to be added. So subtract 1
###from this number. Cause not debugged. -- Ninad
##if numberOfBasesToAddOrRemove > 1:
##numberOfBasesToAddOrRemove -= 1
#UPDATE 2008-08-20: Note that DnaSegment_EditCommand.getCursorText()
#does the job of removing the extra basepair from numberOfBasesToAddOrRemove
#It(subtracting 1 basePair) is not done here as
#self._modifyStructure() needs it without the subtraction. This is
#prone to bugs and need to be cleaned up. -- Ninad
return numberOfBasesToAddOrRemove
|