~ubuntu-branches/ubuntu/trusty/bkchem/trusty

« back to all changes in this revision

Viewing changes to bkchem/bond.py

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Leidert (dale)
  • Date: 2009-02-23 22:43:18 UTC
  • mfrom: (1.1.3 upstream) (2.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20090223224318-7rs4x9kqrdhjynz5
Tags: 0.13.0-1
* New upstream release 0.13.0.

* debian/control (Vcs-Svn): Fixed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
from __future__ import division
24
24
 
25
 
from math import sqrt
 
25
import math
26
26
import misc
27
 
import geometry
 
27
from oasa import geometry
28
28
from warnings import warn
29
29
from ftext import ftext
30
30
import dom_extensions
89
89
    self.center = None
90
90
    self.auto_bond_sign = 1
91
91
    self.simple_double = simple_double
92
 
    self.equithick = 1
 
92
    self.equithick = 0
93
93
 
94
94
    if package:
95
95
      self.read_package( package)
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
 
306
    transform = None
 
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
 
311
        if n.z != 0:
 
312
          # engage 3d transform prior to detection of where to draw
 
313
          transform = self._get_3dtransform_for_drawing()
 
314
          break
 
315
      if transform:
 
316
        for n in self.molecule.atoms:
 
317
          n.transform( transform)
 
318
        self._transform = transform.get_inverse()
 
319
    # / end of 3D
 
320
    # we call the draw method
305
321
    self.__class__.__dict__[ method]( self)
306
 
 
 
322
    # we have to cleanup after 3D stuff
 
323
    if self._transform:
 
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
307
328
 
308
329
 
309
330
  # THE DRAW HELPER METHODS
337
358
      # the bond is too short to draw it
338
359
      return None
339
360
    x1, y1, x2, y2 = where
340
 
 
341
361
    # decide the capstyle
342
362
    if self.atom1.show or self.atom2.show:
343
363
      capstyle = "butt"
344
364
    else:
345
365
      capstyle = "round"
346
 
 
347
 
    self.item = self.paper.create_line( (x1, y1, x2, y2), tags=('bond',), width=self.line_width, fill=self.line_color, capstyle=capstyle)
 
366
    # draw the item
 
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)
456
476
    # main item
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
460
480
    if d == 0:  
461
481
      return []  # to prevent division by zero
462
482
    dx1 = (x0 - xa)/d 
499
519
            coords[0] += 1
500
520
          else:
501
521
            coords[1] += 1
502
 
      items.append( self.paper.create_line( coords, width=self.line_width, fill=self.line_color))
 
522
      items.append( self._create_line_with_transform( coords, width=self.line_width, fill=self.line_color))
503
523
 
504
524
    return items
505
525
 
588
608
    x1, y1, x2, y2 = coords
589
609
    # main item
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
593
613
    _d = dashing[0]
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]
612
632
    return items
613
633
 
614
634
 
615
 
 
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()
646
665
        else:
647
666
          # parallel
648
667
          pass
649
 
    return [self.paper.create_line( x, y, x0, y0, width=self.line_width, fill=self.line_color)]
650
 
 
 
668
    return [self._create_line_with_transform( (x, y, x0, y0), width=self.line_width, fill=self.line_color)]
 
669
 
 
670
 
 
671
 
 
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)
 
675
    plane."""
 
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
 
681
    angs = []
 
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)
 
686
        if ang < -0.00001:
 
687
          ang += math.pi
 
688
        angs.append( ang)
 
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)
 
692
    return t
651
693
 
652
694
  # wedge bonds
653
695
 
714
756
    # main item
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")] 
718
760
 
719
761
 
720
762
  def _draw_a1( self):
782
824
    x1, y1, x2, y2 = coords
783
825
    # main item
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
788
830
    else:
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)]
820
862
    else:
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)]
822
864
 
823
865
  def _draw_b1( self):
824
866
    where = self._draw_n1()
934
976
    # main item
935
977
    diameter = self.line_width
936
978
    spacing = 2*self.line_width
937
 
    d = sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond
 
979
    d = math.sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond
938
980
    # we adjust the spacing
939
981
    _d = spacing + diameter
940
982
    i = 1.0
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)
962
1004
    return items
964
1006
 
965
1007
 
966
1008
 
967
 
 
968
 
 
969
 
 
970
 
 
971
1009
  ## // DRAW HELPER METHODS
972
1010
 
973
 
 
974
 
 
975
1011
  def redraw( self, recalc_side=0):
976
1012
    if not self.__dirty:
977
1013
      pass
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)
 
1043
      self.item = None
1004
1044
    if not self.item:
1005
1045
      # the bond might not be drawn because it was too short
1006
 
      self.draw()
1007
 
    if self.item:
 
1046
      self.item = self.paper.create_line( (x1,y1,x2,y2))
 
1047
      self.paper.register_id( self.item, self)
 
1048
    else:
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)
1010
1051
    
1295
1336
 
1296
1337
 
1297
1338
 
1298
 
 
1299
 
 
1300
1339
  def _decide_distance_and_center( self):
1301
1340
    """according to molecular geometry decide what bond.center and bond.bond_width should be"""
1302
1341
    line = self.atom1.get_xy() + self.atom2.get_xy()
1313
1352
 
1314
1353
 
1315
1354
 
1316
 
 
1317
 
 
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
 
1358
    transform = None
 
1359
    for n in self.atom1.neighbors + self.atom2.neighbors:
 
1360
      # self.atom1 and self.atom2 are in this list as well
 
1361
      if n.z != 0:
 
1362
        # engage 3d transform prior to detection of where to draw
 
1363
        transform = self._get_3dtransform_for_drawing()
 
1364
        break
 
1365
    if transform:
 
1366
      for n in self.atom1.neighbors + self.atom2.neighbors:
 
1367
        n.transform( transform)
 
1368
    # /end of check
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]
1324
 
 
1325
1373
    # searching for circles
1326
 
 
1327
1374
    circles = 0
1328
 
 
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]))
1333
 
 
1334
1379
    if circles:
1335
1380
      side = circles
1336
1381
    else:
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
1343
 
      return (1 ,1)
 
1388
      ret = (1 ,1)
1344
1389
    else:
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)
1357
1402
      if side < 0:
1358
 
        return (-1, 0)
 
1403
        ret = (-1, 0)
1359
1404
      else:
1360
 
        return (1, 0)
 
1405
        ret = (1, 0)
 
1406
    # transform back if necessary
 
1407
    if transform:
 
1408
      inv = transform.get_inverse()
 
1409
      for n in self.atom1.neighbors + self.atom2.neighbors:
 
1410
        n.transform( inv)
 
1411
    # /end of back transform
 
1412
    return ret
1361
1413
    
1362
1414
 
1363
1415
 
1414
1466
    if self.selector:
1415
1467
      self.unselect()
1416
1468
      self.select()
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."""
1436
 
 
1437
1488
    # items to be exported
1438
1489
    if self.type == 'd':
1439
1490
      # d is a little bit twisted
1466
1517
        line_items = []
1467
1518
      else:
1468
1519
        line_items = self.second + self.third
1469
 
 
1470
1520
    return line_items, items
 
1521
 
 
1522
 
 
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"""
 
1527
    if self._transform:
 
1528
      coords = self._transform.transform_xy_flat_list( coords)
 
1529
    return self.paper.create_line( coords, **kw)
 
1530
 
 
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"""
 
1535
    if self._transform:
 
1536
      coords = self._transform.transform_xy_flat_list( coords)
 
1537
    return self.paper.create_oval( coords, **kw)
 
1538
 
 
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"""
 
1543
    if self._transform:
 
1544
      coords = self._transform.transform_xy_flat_list( coords)
 
1545
    return self.paper.create_polygon( coords, **kw)
 
1546