302
302
self.bond_width = self.auto_bond_sign * sign * abs( self.bond_width)
303
303
if automatic == "both":
304
304
self.center = center
305
# the following lines ensure proper drawing in case 3D coordinates are involved
307
self._transform = oasa.transform3d.transform3d()
308
if self.order != 1 or self.type != 'n':
309
for n in self.atom1.neighbors + self.atom2.neighbors:
310
# self.atom1 and self.atom2 are in this list as well
312
# engage 3d transform prior to detection of where to draw
313
transform = self._get_3dtransform_for_drawing()
316
for n in self.molecule.atoms:
317
n.transform( transform)
318
self._transform = transform.get_inverse()
320
# we call the draw method
305
321
self.__class__.__dict__[ method]( self)
322
# we have to cleanup after 3D stuff
324
# if transform was used, we need to transform back
325
for n in self.molecule.atoms:
326
n.transform( self._transform)
327
self._transform = None
309
330
# THE DRAW HELPER METHODS
337
358
# the bond is too short to draw it
339
360
x1, y1, x2, y2 = where
341
361
# decide the capstyle
342
362
if self.atom1.show or self.atom2.show:
343
363
capstyle = "butt"
345
365
capstyle = "round"
347
self.item = self.paper.create_line( (x1, y1, x2, y2), tags=('bond',), width=self.line_width, fill=self.line_color, capstyle=capstyle)
367
self.item = self._create_line_with_transform( (x1, y1, x2, y2), tags=('bond',), width=self.line_width, fill=self.line_color, capstyle=capstyle)
348
368
# draw helper items
349
369
self.second = self.third = []
350
370
self.paper.register_id( self.item, self)
457
477
x, y, x0, y0 = geometry.find_parallel( x1, y1, x2, y2, self.wedge_width/2.0)
458
478
xa, ya, xb, yb = geometry.find_parallel( x1, y1, x2, y2, self.line_width/2.0)
459
d = sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond
479
d = math.sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond
461
481
return [] # to prevent division by zero
462
482
dx1 = (x0 - xa)/d
588
608
x1, y1, x2, y2 = coords
590
610
dashing = (5, 5) # pixels full, pixels empty
591
d = sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond
611
d = math.sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond
592
612
# we adjust the dashing lengths
594
614
while not _d > d:
606
626
xn = x + dx*dashing[0]
607
627
yn = y + dy*dashing[0]
608
628
coords = (x, y, xn, yn)
609
items.append( self.paper.create_line( coords, width=self.line_width, fill=self.line_color))
629
items.append( self._create_line_with_transform( coords, width=self.line_width, fill=self.line_color))
610
630
x = xn + dx*dashing[1]
611
631
y = yn + dy*dashing[1]
616
635
def _draw_second_line( self, coords):
617
636
my_x1, my_y1 = self.atom1.get_xy()
618
637
my_x2, my_y2 = self.atom2.get_xy()
649
return [self.paper.create_line( x, y, x0, y0, width=self.line_width, fill=self.line_color)]
668
return [self._create_line_with_transform( (x, y, x0, y0), width=self.line_width, fill=self.line_color)]
672
def _get_3dtransform_for_drawing( self):
673
"""this is a helper method that returns a transform3d which rotates
674
self to coincide with the x-axis and rotates neighbors to be in (x,y)
676
x1,y1,z1 = self.atom1.get_xyz()
677
x2,y2,z2 = self.atom2.get_xyz()
678
t = geometry.create_transformation_to_coincide_point_with_z_axis( [x1,y1,z1],[x2,y2,z2])
679
x,y,z = t.transform_xyz( x2,y2,z2)
680
# now rotate to make the plane of neighbor atoms coincide with x,y plane
682
for n in self.atom1.neighbors + self.atom2.neighbors:
683
if n is not self.atom1 and n is not self.atom2:
684
nx,ny,nz = t.transform_xyz( *n.get_xyz())
685
ang = math.atan2( ny, nx)
689
ang = sum( angs) / len( angs)
690
t.set_rotation_z( ang + math.pi/2.0)
691
t.set_rotation_y( math.pi/2.0)
715
757
x, y, x0, y0 = geometry.find_parallel( x1, y1, x2, y2, self.wedge_width/2.0)
716
758
xa, ya, xb, yb = geometry.find_parallel( x1, y1, x2, y2, self.line_width/2.0)
717
return [self.paper.create_polygon( (xa, ya, x0, y0, 2*x2-x0, 2*y2-y0, 2*x1-xa, 2*y1-ya), width=0, fill=self.line_color, joinstyle="miter")]
759
return [self._create_polygon_with_transform( (xa, ya, x0, y0, 2*x2-x0, 2*y2-y0, 2*x1-xa, 2*y1-ya), width=0, fill=self.line_color, joinstyle="miter")]
720
762
def _draw_a1( self):
782
824
x1, y1, x2, y2 = coords
784
826
x, y, x0, y0 = geometry.find_parallel( x1, y1, x2, y2, self.wedge_width/2.0)
785
d = sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond
827
d = math.sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond
786
828
if self.equithick:
787
829
step_size = 1.8*self.line_width
816
858
coords2.extend((coords[2], coords[3]))
817
859
coords2.extend((x2, y2))
818
860
if self.equithick:
819
return [self.paper.create_line( coords2, width=self.line_width, fill=self.line_color, smooth=1)]
861
return [self._create_line_with_transform( coords2, width=self.line_width, fill=self.line_color, smooth=1)]
821
return [self.paper.create_line( coords2, width=self.line_width, fill=self.line_color)]
863
return [self._create_line_with_transform( coords2, width=self.line_width, fill=self.line_color)]
823
865
def _draw_b1( self):
824
866
where = self._draw_n1()
956
998
radius = 0.5*diameter
957
999
while min(x1,x2) <= x <= max(x1, x2) and min(y1,y2) <= y <= max(y1,y2):
958
1000
coords = (x-radius, y-radius, x+radius, y+radius)
959
items.append( self.paper.create_oval( coords, width=self.line_width, fill=self.line_color))
1001
items.append( self._create_oval_with_transform( coords, width=self.line_width, fill=self.line_color))
960
1002
x += dx*( diameter + spacing)
961
1003
y += dy*( diameter + spacing)
1001
1037
x1, y1 = self.atom1.get_xy()
1002
1038
x2, y2 = self.atom2.get_xy()
1003
1039
x1, y1, x2, y2 = map( round, [x1, y1, x2, y2])
1040
if self.item and not self.paper.type( self.item) == "line":
1041
self.paper.unregister_id( self.item)
1042
self.paper.delete( self.item)
1004
1044
if not self.item:
1005
1045
# the bond might not be drawn because it was too short
1046
self.item = self.paper.create_line( (x1,y1,x2,y2))
1047
self.paper.register_id( self.item, self)
1008
1049
self.paper.coords( self.item, x1, y1, x2, y2)
1009
1050
self.paper.itemconfig( self.item, width = self.line_width, fill=self.line_color)
1318
1355
def _compute_sign_and_center( self):
1319
1356
"""returns tuple of (sign, center) where sign is the default sign of the self.bond_width"""
1357
# check if we need to transform 3D before computation
1359
for n in self.atom1.neighbors + self.atom2.neighbors:
1360
# self.atom1 and self.atom2 are in this list as well
1362
# engage 3d transform prior to detection of where to draw
1363
transform = self._get_3dtransform_for_drawing()
1366
for n in self.atom1.neighbors + self.atom2.neighbors:
1367
n.transform( transform)
1320
1369
line = self.atom1.get_xy() + self.atom2.get_xy()
1321
1370
atms = self.atom1.get_neighbors() + self.atom2.get_neighbors()
1322
1371
atms = misc.difference( atms, [self.atom1, self.atom2])
1323
1372
coords = [a.get_xy() for a in atms]
1325
1373
# searching for circles
1329
1375
for ring in self.molecule.get_smallest_independent_cycles_dangerous_and_cached():
1330
1376
if self.atom1 in ring and self.atom2 in ring:
1331
1377
on_which_side = lambda xy: geometry.on_which_side_is_point( line, xy)
1332
1378
circles += reduce( operator.add, map( on_which_side, [a.get_xy() for a in ring if a not in self.atoms]))
1340
1385
if side == 0 and (len( self.atom1.get_neighbors()) == 1 or
1341
1386
len( self.atom2.get_neighbors()) == 1):
1342
1387
# maybe we should center, but this is usefull only when one of the atoms has no other substitution
1345
1390
if not circles:
1346
1391
# we center when both atoms have visible symbol and are not in circle
1355
1400
sides[i] *= 0.2 # this makes "non C" less then C but more then H
1356
1401
side = reduce( operator.add, sides, 0)
1406
# transform back if necessary
1408
inv = transform.get_inverse()
1409
for n in self.atom1.neighbors + self.atom2.neighbors:
1411
# /end of back transform
1414
1466
if self.selector:
1415
1467
self.unselect()
1417
# we need to check if the sing of double bond width has not changed
1469
# we need to check if the sign of double bond width has not changed
1418
1470
# this happens during 3d rotation
1419
1471
if self.order == 2 and not self.center:
1420
1472
line = list( self.atom1.get_xy())
1433
1485
items are items that are exported according to the bond type;
1434
1486
as this code is a ugly mix of conditionals it makes sense to put it into one
1435
1487
place co that the exporters do not have to reinvent it themself."""
1437
1488
# items to be exported
1438
1489
if self.type == 'd':
1439
1490
# d is a little bit twisted
1466
1517
line_items = []
1468
1519
line_items = self.second + self.third
1470
1520
return line_items, items
1523
def _create_line_with_transform( self, coords, **kw):
1524
"""this is a private method intended to pass things to self.paper.create_line,
1525
but ensuring that a proper transformation takes place in case it is needed.
1526
It is used during drawing of bonds in 3D"""
1528
coords = self._transform.transform_xy_flat_list( coords)
1529
return self.paper.create_line( coords, **kw)
1531
def _create_oval_with_transform( self, coords, **kw):
1532
"""this is a private method intended to pass things to self.paper.create_oval,
1533
but ensuring that a proper transformation takes place in case it is needed.
1534
It is used during drawing of bonds in 3D"""
1536
coords = self._transform.transform_xy_flat_list( coords)
1537
return self.paper.create_oval( coords, **kw)
1539
def _create_polygon_with_transform( self, coords, **kw):
1540
"""this is a private method intended to pass things to self.paper.create_polygon,
1541
but ensuring that a proper transformation takes place in case it is needed.
1542
It is used during drawing of bonds in 3D"""
1544
coords = self._transform.transform_xy_flat_list( coords)
1545
return self.paper.create_polygon( coords, **kw)