~ubuntu-branches/ubuntu/maverick/cdk/maverick

« back to all changes in this revision

Viewing changes to src/org/openscience/cdk/modeling/builder3d/AtomTetrahedralLigandPlacer3D.java

  • Committer: Bazaar Package Importer
  • Author(s): Paul Cager
  • Date: 2008-04-09 21:17:53 UTC
  • Revision ID: james.westby@ubuntu.com-20080409211753-46lmjw5z8mx5pd8d
Tags: upstream-1.0.2
ImportĀ upstreamĀ versionĀ 1.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  $RCSfile$
 
2
 *  $Author: egonw $
 
3
 *  $Date: 2007-04-16 10:40:19 +0200 (Mon, 16 Apr 2007) $
 
4
 *  $Revision: 8201 $
 
5
 *
 
6
 *  Copyright (C) 2005-2007  Christian Hoppe <chhoppe@users.sf.net>
 
7
 *
 
8
 *  Contact: cdk-devel@lists.sourceforge.net
 
9
 *
 
10
 *  This program is free software; you can redistribute it and/or
 
11
 *  modify it under the terms of the GNU Lesser General Public License
 
12
 *  as published by the Free Software Foundation; either version 2.1
 
13
 *  of the License, or (at your option) any later version.
 
14
 *  All we ask is that proper credit is given for our work, which includes
 
15
 *  - but is not limited to - adding the above copyright notice to the beginning
 
16
 *  of your source code files, and to any copyright notice that you may distribute
 
17
 *  with programs based on this work.
 
18
 *
 
19
 *  This program is distributed in the hope that it will be useful,
 
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
22
 *  GNU Lesser General Public License for more details.
 
23
 *
 
24
 *  You should have received a copy of the GNU Lesser General Public License
 
25
 *  along with this program; if not, write to the Free Software
 
26
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
27
 *
 
28
 */
 
29
package org.openscience.cdk.modeling.builder3d;
 
30
 
 
31
import java.io.IOException;
 
32
import java.util.Hashtable;
 
33
import java.util.Vector;
 
34
 
 
35
import javax.vecmath.AxisAngle4d;
 
36
import javax.vecmath.Matrix3d;
 
37
import javax.vecmath.Point3d;
 
38
import javax.vecmath.Vector3d;
 
39
 
 
40
import org.openscience.cdk.interfaces.IAtom;
 
41
import org.openscience.cdk.interfaces.IAtomContainer;
 
42
import org.openscience.cdk.interfaces.IBond;
 
43
import org.openscience.cdk.CDKConstants;
 
44
 
 
45
/**
 
46
 *  A set of static utility classes for geometric calculations on Atoms.
 
47
 *
 
48
 *@author         Peter Murray-Rust,chhoppe,egonw
 
49
 *@cdk.created    2003-??-??
 
50
 * @cdk.module    builder3d
 
51
 */
 
52
public class AtomTetrahedralLigandPlacer3D {
 
53
 
 
54
        private Hashtable pSet = null;
 
55
        private final double DEFAULT_BOND_LENGTH_H = 1.0;
 
56
        //private final double DEFAULT_BOND_LENGTH_HA = 1.3;
 
57
 
 
58
        public final double TETRAHEDRAL_ANGLE =
 
59
                        2.0 * Math.acos(1.0 / Math.sqrt(3.0));
 
60
 
 
61
        private final double SP2_ANGLE = 120 * Math.PI / 180;
 
62
        private final double SP_ANGLE = Math.PI;
 
63
 
 
64
        final static Vector3d XV = new Vector3d(1.0, 0.0, 0.0);
 
65
        final static Vector3d YV = new Vector3d(0.0, 1.0, 0.0);
 
66
 
 
67
 
 
68
        /**
 
69
         *  Constructor for the AtomTetrahedralLigandPlacer3D object
 
70
         */
 
71
        AtomTetrahedralLigandPlacer3D() { }
 
72
 
 
73
 
 
74
        /**
 
75
         *  Constructor for the setParameterSet object
 
76
         *
 
77
         *@param  moleculeParameter  Description of the Parameter
 
78
         */
 
79
        public void setParameterSet(Hashtable moleculeParameter) {
 
80
                pSet = moleculeParameter;
 
81
        }
 
82
 
 
83
 
 
84
        /**
 
85
         *  Generate coordinates for all atoms which are singly bonded and have no
 
86
         *  coordinates. This is useful when hydrogens are present but have no coords.
 
87
         *  It knows about C, O, N, S only and will give tetrahedral or trigonal
 
88
         *  geometry elsewhere. Bond lengths are computed from covalent radii or taken
 
89
         *  out of a paramter set if available. Angles are tetrahedral or trigonal
 
90
         *
 
91
         *@param  atomContainer  the set of atoms involved
 
92
         *@exception  Exception  Description of the Exception
 
93
         *@cdk.keyword           coordinate calculation
 
94
         *@cdk.keyword           3D model
 
95
         */
 
96
        public void add3DCoordinatesForSinglyBondedLigands(IAtomContainer atomContainer) throws Exception {
 
97
                IAtomContainer noCoords = new org.openscience.cdk.AtomContainer();
 
98
                IAtomContainer withCoords = new org.openscience.cdk.AtomContainer();
 
99
                IAtom refAtom = null;
 
100
                IAtom atomC = null;
 
101
                int nwanted = 0;
 
102
                for (int i = 0; i < atomContainer.getAtomCount(); i++) {
 
103
                        refAtom = atomContainer.getAtom(i);
 
104
                        if (!refAtom.getSymbol().equals("H") && hasUnsetNeighbour(refAtom, atomContainer)) {
 
105
                                noCoords = getUnsetAtomsInAtomContainer(refAtom, atomContainer);
 
106
                                withCoords = getPlacedAtomsInAtomContainer(refAtom, atomContainer);
 
107
                                if (withCoords.getAtomCount() > 0) {
 
108
                                        atomC = getPlacedHeavyAtomInAtomContainer(withCoords.getAtom(0), refAtom, atomContainer);
 
109
                                }
 
110
                                if (refAtom.getFormalNeighbourCount() == 0 && refAtom.getSymbol().equals("C")) {
 
111
                                        nwanted = noCoords.getAtomCount();
 
112
                                } else if (refAtom.getFormalNeighbourCount() == 0 && !refAtom.getSymbol().equals("C")) {
 
113
                                        nwanted = 4;
 
114
                                } else {
 
115
                                        nwanted = refAtom.getFormalNeighbourCount() - withCoords.getAtomCount();
 
116
                                }
 
117
                                Point3d[] newPoints = get3DCoordinatesForLigands(refAtom,
 
118
                                                noCoords, withCoords, atomC, nwanted, DEFAULT_BOND_LENGTH_H, -1);
 
119
                                for (int j = 0; j < noCoords.getAtomCount(); j++) {
 
120
                                        IAtom ligand = noCoords.getAtom(j);
 
121
                                        Point3d newPoint = rescaleBondLength(refAtom, ligand, newPoints[j]);
 
122
                                        ligand.setPoint3d(newPoint);
 
123
                                        ligand.setFlag(CDKConstants.ISPLACED, true);
 
124
                                }
 
125
 
 
126
                                noCoords.removeAllElements();
 
127
                                withCoords.removeAllElements();
 
128
                        }
 
129
                }
 
130
        }
 
131
 
 
132
 
 
133
        /**
 
134
         *  Rescales Point2 so that length 1-2 is sum of covalent radii. 
 
135
         *  If covalent radii cannot be found, use bond length of 1.0
 
136
         *
 
137
         *@param  atom1          stationary atom
 
138
         *@param  atom2          moveable atom
 
139
         *@param  point2         coordinates for atom 2
 
140
         *@return                new coords for atom 2
 
141
         *@exception  Exception  Description of the Exception
 
142
         */
 
143
        public Point3d rescaleBondLength(IAtom atom1, IAtom atom2, Point3d point2) throws Exception {
 
144
                Point3d point1 = atom1.getPoint3d();
 
145
                double d1 = atom1.getCovalentRadius();
 
146
                double d2 = atom2.getCovalentRadius();
 
147
                // in case we have no covalent radii, set to 1.0
 
148
                double distance = (d1 < 0.1 || d2 < 0.1) ? 1.0 :
 
149
                                atom1.getCovalentRadius() + atom2.getCovalentRadius();
 
150
                if (pSet != null) {
 
151
                        distance = getDistanceValue(atom1.getAtomTypeName(), atom2.getAtomTypeName());
 
152
                }
 
153
                Vector3d vect = new Vector3d(point2);
 
154
                vect.sub(point1);
 
155
                vect.normalize();
 
156
                vect.scale(distance);
 
157
                Point3d newPoint = new Point3d(point1);
 
158
                newPoint.add(vect);
 
159
                return newPoint;
 
160
        }
 
161
 
 
162
 
 
163
        /**
 
164
         *  Adds 3D coordinates for singly-bonded ligands of a reference atom (A).
 
165
         *  Initially designed for hydrogens. The ligands of refAtom are identified and
 
166
         *  those with 3D coordinates used to generate the new points. (This allows
 
167
         *  strucures with partially known 3D coordinates to be used, as when groups
 
168
         *  are added.) "Bent" and "non-planar" groups can be formed by taking a subset
 
169
         *  of the calculated points. Thus R-NH2 could use 2 of the 3 points calculated
 
170
         *  from (1,iii) nomenclature: A is point to which new ones are "attached". A
 
171
         *  may have ligands B, C... B may have ligands J, K.. points X1, X2... are
 
172
         *  returned The cases (see individual routines, which use idealised geometry
 
173
         *  by default): (0) zero ligands of refAtom. The resultant points are randomly
 
174
         *  oriented: (i) 1 points required; +x,0,0 (ii) 2 points: use +x,0,0 and
 
175
         *  -x,0,0 (iii) 3 points: equilateral triangle in xy plane (iv) 4 points
 
176
         *  x,x,x, x,-x,-x, -x,x,-x, -x,-x,x (1a) 1 ligand(B) of refAtom which itself
 
177
         *  has a ligand (J) (i) 1 points required; vector along AB vector (ii) 2
 
178
         *  points: 2 vectors in ABJ plane, staggered and eclipsed wrt J (iii) 3
 
179
         *  points: 1 staggered wrt J, the others +- gauche wrt J (1b) 1 ligand(B) of
 
180
         *  refAtom which has no other ligands. A random J is generated and (1a)
 
181
         *  applied (2) 2 ligands(B, C) of refAtom A (i) 1 points required; vector in
 
182
         *  ABC plane bisecting AB, AC. If ABC is linear, no points (ii) 2 points: 2
 
183
         *  vectors at angle ang, whose resultant is 2i (3) 3 ligands(B, C, D) of
 
184
         *  refAtom A (i) 1 points required; if A, B, C, D coplanar, no points. else
 
185
         *  vector is resultant of BA, CA, DA fails if atom itself has no coordinates
 
186
         *  or >4 ligands
 
187
         *
 
188
         *@param  refAtom        (A) to which new ligands coordinates could be added
 
189
         *@param  length         A-X length
 
190
         *@param  angle          B-A-X angle (used in certain cases)
 
191
         *@param  nwanted        Description of the Parameter
 
192
         *@param  noCoords       Description of the Parameter
 
193
         *@param  withCoords     Description of the Parameter
 
194
         *@param  atomC          Description of the Parameter
 
195
         *@return                Point3D[] points calculated. If request could not be
 
196
         *      fulfilled (e.g. too many atoms, or strange geometry, returns empty
 
197
         *      array (zero length, not null)
 
198
         *@exception  Exception  Description of the Exception
 
199
         *@cdk.keyword           coordinate generation
 
200
         */
 
201
 
 
202
        public Point3d[] get3DCoordinatesForLigands(IAtom refAtom,
 
203
                        IAtomContainer noCoords, IAtomContainer withCoords, IAtom atomC, int nwanted, double length, double angle) throws Exception {
 
204
                Point3d newPoints[] = new Point3d[1];
 
205
 
 
206
                if (noCoords.getAtomCount() == 0 && withCoords.getAtomCount() == 0) {
 
207
                        return newPoints;
 
208
                }
 
209
 
 
210
                // too many ligands at present
 
211
                if (withCoords.getAtomCount() > 3) {
 
212
                        return newPoints;
 
213
                }
 
214
 
 
215
                if (refAtom.getFormalNeighbourCount() == 1 || refAtom.getMaxBondOrder() > 4) {
 
216
                } else if (refAtom.getFormalNeighbourCount() == 2 || refAtom.getMaxBondOrder() == 3) {
 
217
                        //sp
 
218
                        if (angle == -1){
 
219
                                angle=SP_ANGLE;
 
220
                        }
 
221
                        newPoints[0] = get3DCoordinatesForSPLigands(refAtom, withCoords, length, angle);
 
222
                } else if (refAtom.getFormalNeighbourCount() == 3 || (refAtom.getMaxBondOrder() > 1 && refAtom.getMaxBondOrder() < 3)) {
 
223
                        //sp2
 
224
                        if (angle == -1){
 
225
                                angle=SP2_ANGLE;
 
226
                        }
 
227
                        try {
 
228
                                newPoints = get3DCoordinatesForSP2Ligands(refAtom, noCoords, withCoords, atomC, length, angle);
 
229
                        } catch (Exception ex1) {
 
230
//                              logger.debug("Get3DCoordinatesForLigandsERROR: Cannot place SP2 Ligands due to:" + ex1.toString());
 
231
                                throw new IOException("Cannot place sp2 substituents");
 
232
                        }
 
233
 
 
234
                } else {
 
235
                        //sp3
 
236
                        try {
 
237
                                newPoints = get3DCoordinatesForSP3Ligands(refAtom, noCoords, withCoords, atomC, nwanted, length, angle);
 
238
                        } catch (Exception ex1) {
 
239
//                              logger.debug("Get3DCoordinatesForLigandsERROR: Cannot place SP3 Ligands due to:" + ex1.toString());
 
240
                                throw new IOException("Cannot place sp3 substituents");
 
241
                        }
 
242
                }
 
243
                //logger.debug("...Ready "+newPoints.length+" "+newPoints[0].toString());
 
244
                return newPoints;
 
245
        }
 
246
 
 
247
        public Point3d get3DCoordinatesForSPLigands(IAtom refAtom, IAtomContainer withCoords, double length, double angle) {
 
248
                //logger.debug(" SP Ligands start "+refAtom.getPoint3d()+" "+(withCoords.getAtomAt(0)).getPoint3d());
 
249
                Vector3d ca = new Vector3d(refAtom.getPoint3d());
 
250
                ca.sub((withCoords.getAtom(0)).getPoint3d());
 
251
                ca.normalize();
 
252
                ca.scale(length);
 
253
                Point3d newPoint = new Point3d(refAtom.getPoint3d());
 
254
                newPoint.add(ca);
 
255
                return newPoint;
 
256
        }
 
257
 
 
258
 
 
259
        /**
 
260
         *  Main method for the calculation of the ligand coordinates for sp2 atoms.
 
261
         *  Decides if one or two coordinates should be created
 
262
         *
 
263
         *@param  refAtom            central atom (Atom)
 
264
         *@param  noCoords           Description of the Parameter
 
265
         *@param  withCoords         Description of the Parameter
 
266
         *@param  atomC              Description of the Parameter
 
267
         *@param  length             Description of the Parameter
 
268
         *@param  angle              Description of the Parameter
 
269
         *@return                    coordinates as Points3d []
 
270
         */
 
271
        public Point3d[] get3DCoordinatesForSP2Ligands(IAtom refAtom, IAtomContainer noCoords, IAtomContainer withCoords, IAtom atomC, double length, double angle) {
 
272
                //logger.debug(" SP2 Ligands start");
 
273
                Point3d newPoints[] = new Point3d[1];
 
274
                if (angle < 0) {
 
275
                        angle = SP2_ANGLE;
 
276
                }
 
277
                if (withCoords.getAtomCount() >= 2) {
 
278
                        //logger.debug("Wanted:1 "+noCoords.getAtomCount());
 
279
                        newPoints[0] = calculate3DCoordinatesSP2_1(refAtom.getPoint3d(), (withCoords.getAtom(0)).getPoint3d(),
 
280
                                        (withCoords.getAtom(1)).getPoint3d(), length, -1 * angle);
 
281
 
 
282
                } else if (withCoords.getAtomCount() <= 1) {
 
283
                        //logger.debug("NoCoords 2:"+noCoords.getAtomCount());
 
284
                        newPoints = calculate3DCoordinatesSP2_2(refAtom.getPoint3d(), (withCoords.getAtom(0)).getPoint3d(),
 
285
                                        (atomC != null) ? atomC.getPoint3d() : null, length, angle);
 
286
                }
 
287
                //logger.debug("Ready SP2");
 
288
                return newPoints;
 
289
        }
 
290
 
 
291
 
 
292
        /**
 
293
         *  Main method for the calculation of the ligand coordinates for sp3 atoms.
 
294
         *  Decides how many coordinates should be created
 
295
         *
 
296
         *@param  refAtom            central atom (Atom)
 
297
         *@param  nwanted            how many ligands should be created
 
298
         *@param  length             bond length
 
299
         *@param  angle              angle in a B-A-(X) system; a=central atom;
 
300
         *      x=ligand with unknown coordinates
 
301
         *@param  noCoords           Description of the Parameter
 
302
         *@param  withCoords         Description of the Parameter
 
303
         *@param  atomC              Description of the Parameter
 
304
         *@return                    Description of the Return Value
 
305
         */
 
306
        public Point3d[] get3DCoordinatesForSP3Ligands(IAtom refAtom, IAtomContainer noCoords, IAtomContainer withCoords, IAtom atomC, int nwanted, double length, double angle) {
 
307
                //logger.debug("SP3 Ligands start ");
 
308
                Point3d newPoints[] = new Point3d[0];
 
309
                Point3d aPoint = refAtom.getPoint3d();
 
310
                int nwithCoords = withCoords.getAtomCount();
 
311
                if (angle < 0) {
 
312
                        angle = TETRAHEDRAL_ANGLE;
 
313
                }
 
314
                if (nwithCoords == 0) {
 
315
                        newPoints = calculate3DCoordinates0(refAtom.getPoint3d(), nwanted, length);
 
316
                } else if (nwithCoords == 1) {
 
317
                        newPoints = calculate3DCoordinates1(aPoint, (withCoords.getAtom(0)).getPoint3d(), (atomC != null) ? atomC.getPoint3d() : null, nwanted, length, angle);
 
318
                } else if (nwithCoords == 2) {
 
319
                        Point3d bPoint = withCoords.getAtom(0).getPoint3d();
 
320
                        Point3d cPoint = withCoords.getAtom(1).getPoint3d();
 
321
                        newPoints = calculate3DCoordinates2(aPoint, bPoint, cPoint, nwanted, length, angle);
 
322
                } else if (nwithCoords == 3) {
 
323
                        Point3d bPoint = withCoords.getAtom(0).getPoint3d();
 
324
                        Point3d cPoint = withCoords.getAtom(1).getPoint3d();
 
325
                        newPoints = new Point3d[1];
 
326
                        Point3d dPoint = withCoords.getAtom(2).getPoint3d();
 
327
                        newPoints[0] = calculate3DCoordinates3(aPoint, bPoint, cPoint, dPoint, length);
 
328
                }
 
329
                //logger.debug("...Ready");
 
330
                return newPoints;
 
331
        }
 
332
 
 
333
 
 
334
        /**
 
335
         *  Calculates substituent points. Calculate substituent points for (0) zero
 
336
         *  ligands of aPoint. The resultant points are randomly oriented: (i) 1 points
 
337
         *  required; +x,0,0 (ii) 2 points: use +x,0,0 and -x,0,0 (iii) 3 points:
 
338
         *  equilateral triangle in xy plane (iv) 4 points x,x,x, x,-x,-x, -x,x,-x,
 
339
         *  -x,-x,x where 3x**2 = bond length
 
340
         *
 
341
         *@param  aPoint   to which substituents are added
 
342
         *@param  nwanted  number of points to calculate (1-4)
 
343
         *@param  length   from aPoint
 
344
         *@return          Point3d[] nwanted points (or zero if failed)
 
345
         */
 
346
        public Point3d[] calculate3DCoordinates0(Point3d aPoint, int nwanted, double length) {
 
347
                Point3d points[] = new Point3d[0];
 
348
                if (nwanted == 1) {
 
349
                        points = new Point3d[1];
 
350
                        points[0] = new Point3d(aPoint);
 
351
                        points[0].add(new Vector3d(length, 0.0, 0.0));
 
352
                } else if (nwanted == 2) {
 
353
                        points[0] = new Point3d(aPoint);
 
354
                        points[0].add(new Vector3d(length, 0.0, 0.0));
 
355
                        points[1] = new Point3d(aPoint);
 
356
                        points[1].add(new Vector3d(-length, 0.0, 0.0));
 
357
                } else if (nwanted == 3) {
 
358
                        points[0] = new Point3d(aPoint);
 
359
                        points[0].add(new Vector3d(length, 0.0, 0.0));
 
360
                        points[1] = new Point3d(aPoint);
 
361
                        points[1].add(new Vector3d(-length * 0.5, -length * 0.5 * Math.sqrt(3.0), 0.0f));
 
362
                        points[2] = new Point3d(aPoint);
 
363
                        points[2].add(new Vector3d(-length * 0.5, length * 0.5 * Math.sqrt(3.0), 0.0f));
 
364
                } else if (nwanted == 4) {
 
365
                        double dx = length / Math.sqrt(3.0);
 
366
                        points[0] = new Point3d(aPoint);
 
367
                        points[0].add(new Vector3d(dx, dx, dx));
 
368
                        points[1] = new Point3d(aPoint);
 
369
                        points[1].add(new Vector3d(dx, -dx, -dx));
 
370
                        points[2] = new Point3d(aPoint);
 
371
                        points[2].add(new Vector3d(-dx, -dx, dx));
 
372
                        points[3] = new Point3d(aPoint);
 
373
                        points[3].add(new Vector3d(-dx, dx, -dx));
 
374
                }
 
375
                return points;
 
376
        }
 
377
 
 
378
 
 
379
        /**
 
380
         *  Calculate new point(s) X in a B-A system to form B-A-X. Use C as reference
 
381
         *  for * staggering about the B-A bond (1a) 1 ligand(B) of refAtom (A) which
 
382
         *  itself has a ligand (C) (i) 1 points required; vector along AB vector (ii)
 
383
         *  2 points: 2 vectors in ABC plane, staggered and eclipsed wrt C (iii) 3
 
384
         *  points: 1 staggered wrt C, the others +- gauche wrt C If C is null, a
 
385
         *  random non-colinear C is generated
 
386
         *
 
387
         *@param  aPoint   to which substituents are added
 
388
         *@param  nwanted  number of points to calculate (1-3)
 
389
         *@param  length   A-X length
 
390
         *@param  angle    B-A-X angle
 
391
         *@param  bPoint   Description of the Parameter
 
392
         *@param  cPoint   Description of the Parameter
 
393
         *@return          Point3d[] nwanted points (or zero if failed)
 
394
         */
 
395
        public Point3d[] calculate3DCoordinates1(
 
396
                        Point3d aPoint, Point3d bPoint, Point3d cPoint,
 
397
                        int nwanted, double length, double angle
 
398
                        ) {
 
399
                Point3d points[] = new Point3d[nwanted];
 
400
                // BA vector
 
401
                Vector3d ba = new Vector3d(aPoint);
 
402
                ba.sub(bPoint);
 
403
                ba.normalize();
 
404
                // if no cPoint, generate a random reference
 
405
                if (cPoint == null) {
 
406
                        Vector3d cVector = getNonColinearVector(ba);
 
407
                        cPoint = new Point3d(cVector);
 
408
                }
 
409
                // CB vector
 
410
                Vector3d cb = new Vector3d(bPoint);
 
411
                cb.sub(cPoint);
 
412
                cb.normalize();
 
413
                // if A, B, C colinear, replace C by random point
 
414
                double cbdotba = cb.dot(ba);
 
415
                if (cbdotba > 0.999999) {
 
416
                        Vector3d cVector = getNonColinearVector(ba);
 
417
                        cPoint = new Point3d(cVector);
 
418
                        cb = new Vector3d(bPoint);
 
419
                        cb.sub(cPoint);
 
420
                }
 
421
                // cbxba = c x b
 
422
                Vector3d cbxba = new Vector3d();
 
423
                cbxba.cross(cb, ba);
 
424
                cbxba.normalize();
 
425
                // create three perp axes ba, cbxba, and ax
 
426
                Vector3d ax = new Vector3d();
 
427
                ax.cross(cbxba, ba);
 
428
                ax.normalize();
 
429
                double drot = Math.PI * 2.0 / (double) nwanted;
 
430
                for (int i = 0; i < nwanted; i++) {
 
431
                        double rot = (double) i * drot;
 
432
                        points[i] = new Point3d(aPoint);
 
433
                        Vector3d vx = new Vector3d(ba);
 
434
                        vx.scale(-Math.cos(angle) * length);
 
435
                        Vector3d vy = new Vector3d(ax);
 
436
                        vy.scale(Math.cos(rot) * length);
 
437
                        Vector3d vz = new Vector3d(cbxba);
 
438
                        vz.scale(Math.sin(rot) * length);
 
439
                        points[i].add(vx);
 
440
                        points[i].add(vy);
 
441
                        points[i].add(vz);
 
442
                }
 
443
                /*ax = null;
 
444
                cbxba = null;
 
445
                ba = null;
 
446
                cb = null;*/
 
447
                return points;
 
448
        }
 
449
 
 
450
 
 
451
        /**
 
452
         *  Calculate new point(s) X in a B-A-C system, it forms a B-A(-C)-X
 
453
         *  system. (2) 2 ligands(B, C) of refAtom A (i) 1 points required; vector in
 
454
         *  ABC plane bisecting AB, AC. If ABC is linear, no points (ii) 2 points: 2
 
455
         *  points X1, X2, X1-A-X2 = angle about 2i vector
 
456
         *
 
457
         *@param  aPoint   to which substituents are added
 
458
         *@param  bPoint   first ligand of A
 
459
         *@param  cPoint   second ligand of A
 
460
         *@param  nwanted  number of points to calculate (1-2)
 
461
         *@param  length   A-X length
 
462
         *@param  angle    B-A-X angle
 
463
         *@return          Point3d[] nwanted points (or zero if failed)
 
464
         */
 
465
        public Point3d[] calculate3DCoordinates2(
 
466
                        Point3d aPoint, Point3d bPoint, Point3d cPoint,
 
467
                        int nwanted, double length, double angle) {
 
468
                //logger.debug("3DCoordinates2");
 
469
                Point3d newPoints[] = new Point3d[0];
 
470
                double ang2 = angle / 2.0;
 
471
 
 
472
                Vector3d ba = new Vector3d(aPoint);
 
473
                ba.sub(bPoint);
 
474
                Vector3d ca = new Vector3d(aPoint);
 
475
                ca.sub(cPoint);
 
476
                Vector3d baxca = new Vector3d();
 
477
                baxca.cross(ba, ca);
 
478
                if (baxca.length() < 0.00000001) {
 
479
                        ;
 
480
                        // linear
 
481
                } else if (nwanted == 1) {
 
482
                        newPoints = new Point3d[1];
 
483
                        Vector3d ax = new Vector3d(ba);
 
484
                        ax.add(ca);
 
485
                        ax.normalize();
 
486
                        ax.scale(length);
 
487
                        newPoints[0] = new Point3d(aPoint);
 
488
                        newPoints[0].add(ax);
 
489
                } else if (nwanted >= 2) {
 
490
                        newPoints = new Point3d[2];
 
491
                        Vector3d ax = new Vector3d(ba);
 
492
                        ax.add(ca);
 
493
                        ax.normalize();
 
494
                        baxca.normalize();
 
495
                        baxca.scale(Math.sin(ang2) * length);
 
496
                        ax.scale(Math.cos(ang2) * length);
 
497
                        newPoints[0] = new Point3d(aPoint);
 
498
                        newPoints[0].add(ax);
 
499
                        newPoints[0].add(baxca);
 
500
                        newPoints[1] = new Point3d(aPoint);
 
501
                        newPoints[1].add(ax);
 
502
                        newPoints[1].sub(baxca);
 
503
                }
 
504
                baxca = null;
 
505
                ba = null;
 
506
                ca = null;
 
507
                return newPoints;
 
508
        }
 
509
 
 
510
 
 
511
        /**
 
512
         *  Calculate new point X in a B-A(-D)-C system. It forms a B-A(-D)(-C)-X
 
513
         *  system. (3) 3 ligands(B, C, D) of refAtom A (i) 1 points required; if A, B,
 
514
         *  C, D coplanar, no points. else vector is resultant of BA, CA, DA
 
515
         *
 
516
         *@param  aPoint  to which substituents are added
 
517
         *@param  bPoint  first ligand of A
 
518
         *@param  cPoint  second ligand of A
 
519
         *@param  dPoint  third ligand of A
 
520
         *@param  length  A-X length
 
521
         *@return         Point3d nwanted points (or null if failed (coplanar))
 
522
         */
 
523
        public Point3d calculate3DCoordinates3(
 
524
                        Point3d aPoint, Point3d bPoint, Point3d cPoint, Point3d dPoint,
 
525
                        double length) {
 
526
                //logger.debug("3DCoordinates3");
 
527
                Vector3d bc = new Vector3d(bPoint);
 
528
                bc.sub(cPoint);
 
529
                Vector3d dc = new Vector3d(dPoint);
 
530
                dc.sub(cPoint);
 
531
                Vector3d ca = new Vector3d(cPoint);
 
532
                ca.sub(aPoint);
 
533
 
 
534
                Vector3d n1 = new Vector3d();
 
535
                Vector3d n2 = new Vector3d();
 
536
 
 
537
                n1.cross(bc, dc);
 
538
                n1.normalize();
 
539
                n1.scale(length);
 
540
 
 
541
                Vector3d ax = new Vector3d(aPoint);
 
542
                ax.add(n1);
 
543
                ax.sub(aPoint);
 
544
 
 
545
                Vector3d ax2 = new Vector3d(aPoint);
 
546
                ax2.add(n2);
 
547
                ax2.sub(aPoint);
 
548
 
 
549
                Point3d point = new Point3d(aPoint);
 
550
 
 
551
                double dotProduct = ca.dot(ax);
 
552
                double angle = Math.acos((dotProduct) / (ax.length() * ca.length()));
 
553
 
 
554
                if (angle < 1.5) {
 
555
                        n2.cross(dc, bc);
 
556
                        n2.normalize();
 
557
                        n2.scale(length);
 
558
                        point.add(n2);
 
559
                } else {
 
560
                        point.add(n1);
 
561
                }
 
562
 
 
563
                bc = null;
 
564
                dc = null;
 
565
                ca = null;
 
566
                n1 = null;
 
567
                n2 = null;
 
568
                return point;
 
569
        }
 
570
 
 
571
 
 
572
        /**
 
573
         *  Calculate new point in B-A-C system. It forms B-A(-X)-C system, where A is
 
574
         *  sp2
 
575
         *
 
576
         *@param  aPoint  central point A (Point3d)
 
577
         *@param  bPoint  B (Point3d)
 
578
         *@param  cPoint  C (Point3d)
 
579
         *@param  length  bond length
 
580
         *@param  angle   angle between B(C)-A-X
 
581
         *@return         new Point (Point3d)
 
582
         */
 
583
        public Point3d calculate3DCoordinatesSP2_1(Point3d aPoint, Point3d bPoint, Point3d cPoint,
 
584
                        double length, double angle) {
 
585
                //logger.debug("3DCoordinatesSP2_1");
 
586
                Vector3d ba = new Vector3d(bPoint);
 
587
                ba.sub(aPoint);
 
588
                Vector3d ca = new Vector3d(cPoint);
 
589
                ca.sub(aPoint);
 
590
 
 
591
                Vector3d n1 = new Vector3d();
 
592
                n1.cross(ba, ca);
 
593
                n1.normalize();
 
594
 
 
595
                Vector3d n2 = rotate(ba, n1, angle);
 
596
                n2.normalize();
 
597
 
 
598
                n2.scale(length);
 
599
                Point3d point = new Point3d(aPoint);
 
600
                point.add(n2);
 
601
                n1 = null;
 
602
                n2 = null;
 
603
                ba = null;
 
604
                ca = null;
 
605
                return point;
 
606
        }
 
607
 
 
608
 
 
609
        /**
 
610
         *  Calculate two new points in B-A system. It forms B-A(-X)(-X) system, where
 
611
         *  A is sp2
 
612
         *
 
613
         *@param  aPoint  central point A (Point3d)
 
614
         *@param  bPoint  B (Point3d)
 
615
         *@param  cPoint  C (Point3d)
 
616
         *@param  length  bond length
 
617
         *@param  angle   angle between B(C)-A-X
 
618
         *@return         new Points (Point3d [])
 
619
         */
 
620
        public Point3d[] calculate3DCoordinatesSP2_2(Point3d aPoint, Point3d bPoint, Point3d cPoint, double length, double angle) {
 
621
                //logger.debug("3DCoordinatesSP_2");
 
622
                Vector3d ca = new Vector3d();
 
623
                Point3d newPoints[] = new Point3d[2];
 
624
                Vector3d ba = new Vector3d(bPoint);
 
625
                ba.sub(aPoint);
 
626
 
 
627
                if (cPoint != null) {
 
628
                        ca.x = cPoint.x - aPoint.x;
 
629
                        ca.y = cPoint.y - aPoint.y;
 
630
                        ca.z = cPoint.z - aPoint.z;
 
631
                } else {
 
632
                        ca.x = -1 * ba.x;
 
633
                        ca.y = -1 * ba.y;
 
634
                        ca.z = -1.5 * ba.z;
 
635
                }
 
636
 
 
637
                Vector3d crossProduct = new Vector3d();
 
638
                crossProduct.cross(ba, ca);
 
639
 
 
640
                Vector3d n1 = rotate(ba, crossProduct, 2 * angle);
 
641
                n1.normalize();
 
642
                n1.scale(length);
 
643
                newPoints[0] = new Point3d(aPoint);
 
644
                newPoints[0].add(n1);
 
645
 
 
646
                Vector3d n2 = rotate(n1, ba, Math.PI);
 
647
                n2.normalize();
 
648
                n2.scale(length);
 
649
                newPoints[1] = new Point3d(aPoint);
 
650
                newPoints[1].add(n2);
 
651
                n1 = null;
 
652
                n2 = null;
 
653
                ba = null;
 
654
                ca = null;
 
655
                return newPoints;
 
656
        }
 
657
 
 
658
 
 
659
        /**
 
660
         *  Gets the nonColinearVector attribute of the AtomLigandPlacer3D class
 
661
         *
 
662
         *@param  ab  Description of the Parameter
 
663
         *@return     The nonColinearVector value
 
664
         */
 
665
        private Vector3d getNonColinearVector(Vector3d ab) {
 
666
                Vector3d cr = new Vector3d();
 
667
                cr.cross(ab, XV);
 
668
                if (cr.length() > 0.00001) {
 
669
                        return XV;
 
670
                } else {
 
671
                        return YV;
 
672
                }
 
673
        }
 
674
 
 
675
 
 
676
        /**
 
677
         *  Rotates a vector around an axis
 
678
         *
 
679
         *@param  vector  vector to be rotated around axis
 
680
         *@param  axis    axis of rotation
 
681
         *@param  angle   angle to vector rotate around
 
682
         *@return         rotated vector
 
683
         *author:         egonw
 
684
         */
 
685
        public static Vector3d rotate(Vector3d vector, Vector3d axis, double angle) {
 
686
                Matrix3d rotate = new Matrix3d();
 
687
                rotate.set(new AxisAngle4d(axis.x, axis.y, axis.z, angle));
 
688
                Vector3d result = new Vector3d();
 
689
                rotate.transform(vector, result);
 
690
                return result;
 
691
        }
 
692
 
 
693
 
 
694
        /**
 
695
         * Gets the distance between two atoms out of the parameter set.
 
696
         *
 
697
         *@param  id1            id of the paramter set for atom1 (atom1.getAtomTypeName())
 
698
         *@param  id2            id of the paramter set for atom2
 
699
         *@return                The distanceValue value
 
700
         *@exception  Exception  Description of the Exception
 
701
         */
 
702
        private double getDistanceValue(String id1, String id2) throws Exception {
 
703
                String dkey = "";
 
704
                if (pSet.containsKey(("bond" + id1 + ";" + id2))) {
 
705
                        dkey = "bond" + id1 + ";" + id2;
 
706
                } else if (pSet.containsKey(("bond" + id2 + ";" + id1))) {
 
707
                        dkey = "bond" + id2 + ";" + id1;
 
708
                } else {
 
709
//                      logger.debug("DistanceKEYError:pSet has no key:" + id2 + " ; " + id1 + " take default bond length:" + DEFAULT_BOND_LENGTH_H);
 
710
                        return DEFAULT_BOND_LENGTH_H;
 
711
                }
 
712
                return ((Double) (((Vector) pSet.get(dkey)).get(0))).doubleValue();
 
713
        }
 
714
 
 
715
 
 
716
        /**
 
717
         *  Gets the angleKey attribute of the AtomPlacer3D object
 
718
         *
 
719
         *@param  id1            Description of the Parameter
 
720
         *@param  id2            Description of the Parameter
 
721
         *@param  id3            Description of the Parameter
 
722
         *@return                The angleKey value
 
723
         *@exception  Exception  Description of the Exception
 
724
         */
 
725
        public double getAngleValue(String id1, String id2, String id3) throws Exception {
 
726
                String akey = "";
 
727
                if (pSet.containsKey(("angle" + id1 + ";" + id2 + ";" + id3))) {
 
728
                        akey = "angle" + id1 + ";" + id2 + ";" + id3;
 
729
                } else if (pSet.containsKey(("angle" + id3 + ";" + id2 + ";" + id1))) {
 
730
                        akey = "angle" + id3 + ";" + id2 + ";" + id1;
 
731
                } else if (pSet.containsKey(("angle" + id2 + ";" + id1 + ";" + id3))) {
 
732
                        akey = "angle" + id2 + ";" + id1 + ";" + id3;
 
733
                } else if (pSet.containsKey(("angle" + id1 + ";" + id3 + ";" + id2))) {
 
734
                        akey = "angle" + id1 + ";" + id3 + ";" + id2;
 
735
                } else if (pSet.containsKey(("angle" + id3 + ";" + id1 + ";" + id2))) {
 
736
                        akey = "angle" + id3 + ";" + id1 + ";" + id2;
 
737
                } else if (pSet.containsKey(("angle" + id2 + ";" + id3 + ";" + id1))) {
 
738
                        akey = "angle" + id2 + ";" + id3 + ";" + id1;
 
739
                } else {
 
740
                        System.out.println("AngleKEYError:Unknown angle " + id1 + " " + id2 + " " + id3 + " take default angle:" + TETRAHEDRAL_ANGLE);
 
741
                        return TETRAHEDRAL_ANGLE;
 
742
                }
 
743
                return ((Double) (((Vector) pSet.get(akey)).get(0))).doubleValue();
 
744
        }
 
745
 
 
746
 
 
747
        /**
 
748
         *  set Atoms in respect to stereoinformation.
 
749
         *      take placed neighbours to stereocenter
 
750
         *              create a x b
 
751
         *           if right handed system (spatproduct >0)
 
752
         *                      if unplaced is not up (relativ to stereocenter)
 
753
         *                              n=b x a
 
754
         *           Determine angle between n and possible ligand place points
 
755
         *           if angle smaller than 90 degrees take this branch point
 
756
         *
 
757
         *@param  atomA         placed Atom - stereocenter
 
758
         *@param  ax            bond between stereocenter and unplaced atom
 
759
         *@param  atomB         neighbour of atomA (in plane created by atomA, atomB and atomC)
 
760
         *@param  atomC         neighbour of atomA
 
761
         *@param  branchPoints  the two possible placement points for unplaced atom (up and down) 
 
762
         *@return               int value of branch point position
 
763
         */
 
764
        public int makeStereocenter(Point3d atomA, IBond ax, Point3d atomB, Point3d atomC, Point3d[] branchPoints) {
 
765
                
 
766
                Vector3d b = new Vector3d((atomB.x - atomA.x), (atomB.y - atomA.y), (atomB.z - atomA.z));
 
767
                Vector3d c = new Vector3d((atomC.x - atomA.x), (atomC.y - atomA.y), (atomC.z - atomA.z));
 
768
                Vector3d n1 = new Vector3d();
 
769
                Vector3d n2 = null;
 
770
                n1.cross(b, c);
 
771
                n1.normalize();
 
772
 
 
773
                if (getSpatproduct(b, c, n1) >= 0) {
 
774
                        if (ax.getStereo() != CDKConstants.STEREO_BOND_UP_INV) {
 
775
                                n1.cross(c, b);
 
776
                                n1.normalize();
 
777
                        }
 
778
                }
 
779
                double dotProduct = 0;
 
780
                for (int i = 0; i < branchPoints.length; i++) {
 
781
                        n2 = new Vector3d(branchPoints[0].x, branchPoints[0].y, branchPoints[0].z);
 
782
                        dotProduct = n1.dot(n2);
 
783
                        if (Math.acos(dotProduct / (n1.length() * n2.length())) < 1.6) {
 
784
                                return i;
 
785
                        }
 
786
                }
 
787
                return -1;
 
788
        }
 
789
 
 
790
 
 
791
        /**
 
792
         *  Gets the spatproduct of three vectors
 
793
         *
 
794
         *@param  a  vector a
 
795
         *@param  b  vector b
 
796
         *@param  c  vector c
 
797
         *@return    double value of the spatproduct
 
798
         */
 
799
        public double getSpatproduct(Vector3d a, Vector3d b, Vector3d c) {
 
800
                return (c.x * (b.y * a.z - b.z * a.y) + c.y * (b.z * a.x - b.x * a.z) + c.z * (b.x * a.y - b.y * a.x));
 
801
        }
 
802
 
 
803
 
 
804
        /**
 
805
         *  Calculates the torsionAngle of a-b-c-d 
 
806
         *
 
807
         *@param  a  Point3d
 
808
         *@param  b  Point3d
 
809
         *@param  c  Point3d
 
810
         *@param  d  Point3d
 
811
         *@return    The torsionAngle value
 
812
         */
 
813
        public double getTorsionAngle(Point3d a, Point3d b, Point3d c, Point3d d) {
 
814
                Vector3d ab = new Vector3d(a.x - b.x, a.y - b.y, a.z - b.z);
 
815
                Vector3d cb = new Vector3d(c.x - b.x, c.y - b.y, c.z - b.z);
 
816
                Vector3d dc = new Vector3d(d.x - c.x, d.y - c.y, d.z - c.z);
 
817
                Vector3d bc = new Vector3d(b.x - c.x, b.y - c.y, b.z - c.z);
 
818
                Vector3d n1 = new Vector3d();
 
819
                Vector3d n2 = new Vector3d();
 
820
 
 
821
                n1.cross(ab, cb);
 
822
                if (getSpatproduct(ab, cb, n1) > 0) {
 
823
                        n1.cross(cb, ab);
 
824
                }
 
825
                n1.normalize();
 
826
                n2.cross(dc, bc);
 
827
                if (getSpatproduct(dc, bc, n2) < 0) {
 
828
                        n2.cross(bc, dc);
 
829
                }
 
830
                n2.normalize();
 
831
                return n1.dot(n2);
 
832
        }
 
833
 
 
834
 
 
835
        /**
 
836
         *  Gets all placed neighbouring atoms of a atom
 
837
         *
 
838
         *@param  atom  central atom (Atom)
 
839
         *@param  ac    the molecul
 
840
         *@return       all connected and placed atoms to the central atom
 
841
         *      ((AtomContainer)
 
842
         */
 
843
        public IAtomContainer getPlacedAtomsInAtomContainer(IAtom atom, IAtomContainer ac) {
 
844
 
 
845
                java.util.List bonds = ac.getConnectedBondsList(atom);
 
846
                IAtomContainer connectedAtoms = new org.openscience.cdk.AtomContainer();
 
847
                IAtom connectedAtom = null;
 
848
                for (int i = 0; i < bonds.size(); i++) {
 
849
                        connectedAtom = ((IBond)bonds.get(i)).getConnectedAtom(atom);
 
850
                        if (connectedAtom.getFlag(CDKConstants.ISPLACED)) {
 
851
                                connectedAtoms.addAtom(connectedAtom);
 
852
                        }
 
853
                }
 
854
                return connectedAtoms;
 
855
        }
 
856
 
 
857
 
 
858
        /**
 
859
         *  Gets the unsetAtomsInAtomContainer attribute of the
 
860
         *  AtomTetrahedralLigandPlacer3D object
 
861
         *
 
862
         *@param  atom  Description of the Parameter
 
863
         *@param  ac    Description of the Parameter
 
864
         *@return       The unsetAtomsInAtomContainer value
 
865
         */
 
866
        public IAtomContainer getUnsetAtomsInAtomContainer(IAtom atom, IAtomContainer ac) {
 
867
                java.util.List atoms = ac.getConnectedAtomsList(atom);
 
868
                IAtomContainer connectedAtoms = new org.openscience.cdk.AtomContainer();
 
869
                for (int i = 0; i < atoms.size(); i++) {
 
870
                        IAtom curAtom = (IAtom)atoms.get(i);
 
871
                        if (!curAtom.getFlag(CDKConstants.ISPLACED)){//&& atoms[i].getPoint3d() == null) {
 
872
                                connectedAtoms.addAtom(curAtom);
 
873
                        }
 
874
                }
 
875
                return connectedAtoms;
 
876
        }
 
877
 
 
878
        public boolean hasUnsetNeighbour(IAtom atom, IAtomContainer ac) {
 
879
                java.util.List atoms = ac.getConnectedAtomsList(atom);
 
880
                for (int i = 0; i < atoms.size(); i++) {
 
881
                        IAtom curAtom = (IAtom)atoms.get(i);
 
882
                        if (!curAtom.getFlag(CDKConstants.ISPLACED)) {//&& atoms[i].getPoint3d() == null) {
 
883
                                return true;
 
884
                        }
 
885
                }
 
886
                return false;
 
887
        }
 
888
 
 
889
 
 
890
        /**
 
891
         *  Returns a placed neighbouring atom of a central atom atomA, which is not
 
892
         *  atomB
 
893
         *
 
894
         *@param  atomA  central atom (Atom)
 
895
         *@param  atomB  atom connected to atomA (Atom)
 
896
         *@param  ac     molecule
 
897
         *@return        returns a connected atom (Atom)
 
898
         */
 
899
        public IAtom getPlacedHeavyAtomInAtomContainer(IAtom atomA, IAtom atomB, IAtomContainer ac) {
 
900
                java.util.List atoms = ac.getConnectedAtomsList(atomA);
 
901
                IAtom atom=null;
 
902
                for (int i = 0; i < atoms.size(); i++) {
 
903
                        IAtom curAtom = (IAtom)atoms.get(i);
 
904
                        if (curAtom.getFlag(CDKConstants.ISPLACED) && !curAtom.getSymbol().equals("H")
 
905
                                         && curAtom != atomB) {
 
906
                                return curAtom;
 
907
                        }
 
908
                }
 
909
                return atom;
 
910
        }
 
911
}
 
912