001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.nbio.structure.contact;
022
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026import javax.vecmath.Point3d;
027import javax.vecmath.Vector3d;
028import java.io.Serializable;
029import java.util.Arrays;
030
031
032/**
033 * A bounding box for short cutting some geometrical calculations.
034 *
035 * See http://en.wikipedia.org/wiki/Bounding_volume
036 *
037 * @author Jose Duarte
038 *
039 */
040public class BoundingBox implements Serializable {
041
042        private static final long serialVersionUID = 1L;
043        private static final Logger logger = LoggerFactory.getLogger(StructureInterfaceList.class);
044
045
046        public double xmin;
047        public double xmax;
048        public double ymin;
049        public double ymax;
050        public double zmin;
051        public double zmax;
052
053        public BoundingBox(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) {
054                this.xmin = xmin;
055                this.xmax = xmax;
056                this.ymin = ymin;
057                this.ymax = ymax;
058                this.zmin = zmin;
059                this.zmax = zmax;
060        }
061
062        public BoundingBox(BoundingBox bb) {
063                this.xmin = bb.xmin;
064                this.xmax = bb.xmax;
065                this.ymin = bb.ymin;
066                this.ymax = bb.ymax;
067                this.zmin = bb.zmin;
068                this.zmax = bb.zmax;
069        }
070
071        /**
072         * Constructs a BoundingBox by calculating maxs and mins of given array of atoms.
073         * @param atoms
074         */
075        public BoundingBox (Point3d[] atoms) {
076
077                if (atoms.length==0) logger.error("Error! Empty list of atoms");
078
079                xmax = atoms[0].x;
080                xmin = xmax;
081                ymax = atoms[0].y;
082                ymin = ymax;
083                zmax = atoms[0].z;
084                zmin = zmax;
085
086                for(int i=1;i<atoms.length;i++) {
087                        if(atoms[i].x > xmax) xmax = atoms[i].x;
088                        else if(atoms[i].x < xmin) xmin = atoms[i].x;
089
090                        if(atoms[i].y > ymax) ymax = atoms[i].y;
091                        else if(atoms[i].y < ymin) ymin = atoms[i].y;
092
093                        if(atoms[i].z > zmax) zmax = atoms[i].z;
094                        else if(atoms[i].z < zmin) zmin = atoms[i].z;
095                }
096
097        }
098
099
100        /** Returns the dimensions of this bounding box.
101         *
102         * @return a double array (x,y,z) with the dimensions of the box.
103         */
104        public double[] getDimensions(){
105
106                double[] dim = new double[3];
107
108                dim[0] = xmax-xmin;
109                dim[1] = ymax-ymin;
110                dim[2] = zmax-zmin;
111
112                return dim;
113
114        }
115
116        /**
117         * Given a set of bounding boxes returns a bounding box that bounds all of them.
118         * @param boxes
119         */
120        public BoundingBox(BoundingBox[] boxes) {
121
122                if (boxes.length==0) logger.error("Error! Empty list of bounding boxes");
123
124                xmax = boxes[0].xmax;
125                xmin = boxes[0].xmin;
126                ymax = boxes[0].ymax;
127                ymin = boxes[0].ymin;
128                zmax = boxes[0].zmax;
129                zmin = boxes[0].zmin;
130
131                for (int i=1;i<boxes.length;i++) {
132                        if(boxes[i].xmax > xmax) xmax = boxes[i].xmax;
133                        else if(boxes[i].xmin < xmin) xmin = boxes[i].xmin;
134                        if(boxes[i].ymax > ymax) ymax = boxes[i].ymax;
135                        else if(boxes[i].ymin < ymin) ymin = boxes[i].ymin;
136                        if(boxes[i].zmax > zmax) zmax = boxes[i].zmax;
137                        else if(boxes[i].zmin < zmin) zmin = boxes[i].zmin;
138                }
139
140        }
141
142        private class Bound implements Comparable<Bound> {
143                int cardinal;
144                double value;
145                public Bound(int cardinal,double value) {
146                        this.cardinal = cardinal;
147                        this.value = value;
148                }
149                @Override
150                public int compareTo(Bound o) {
151                        return Double.compare(this.value,o.value);
152                }
153                @Override
154                public String toString() {
155                        return "["+cardinal+","+value+"]";
156                }
157        }
158
159        /**
160         * Returns true if this bounding box overlaps given one, i.e. they are within
161         * one cutoff distance in one of their 3 dimensions.
162         * @param cutoff
163         * @return
164         */
165        public boolean overlaps(BoundingBox o, double cutoff) {
166                if (this==o) return true;
167                // x dimension
168                if (!areOverlapping(xmin,xmax,o.xmin,o.xmax,cutoff)) {
169                        return false;
170                }
171                // y dimension
172                if (!areOverlapping(ymin,ymax,o.ymin,o.ymax,cutoff)) {
173                        return false;
174                }
175                // z dimension
176                if (!areOverlapping(zmin,zmax,o.zmin,o.zmax,cutoff)) {
177                        return false;
178                }
179                return true;
180        }
181
182        private boolean areOverlapping(double imin, double imax, double jmin, double jmax, double cutoff) {
183
184                Bound[] bounds = {new Bound(0,imin), new Bound(1,imax),
185                                new Bound(2,jmin), new Bound(3,jmax)};
186
187                Arrays.sort(bounds);
188
189                if ((bounds[0].cardinal==0 && bounds[1].cardinal==1)) {
190                        if ((bounds[2].value-bounds[1].value)>cutoff) {
191                                return false;
192                        }
193                } else if (bounds[0].cardinal==2 && bounds[1].cardinal==3) {
194                        if ((bounds[2].value-bounds[1].value)>cutoff) {
195                                return false;
196                        }
197                }
198
199                return true;
200
201        }
202
203        /**
204         * Check if a given point falls within this box
205         * @param atom
206         * @return
207         */
208        public boolean contains(Point3d atom) {
209                double x = atom.x;
210                double y = atom.y;
211                double z = atom.z;
212                return xmin <= x && x <= xmax
213                                && ymin <= y && y <= ymax
214                                && zmin <= z && z <= zmax;
215        }
216
217        public void translate(Vector3d translation) {
218                xmin+=translation.x;
219                xmax+=translation.x;
220                ymin+=translation.y;
221                ymax+=translation.y;
222                zmin+=translation.z;
223                zmax+=translation.z;
224        }
225
226        /**
227         * Returns an array of size 2 with min and max values of given double array
228         * @param array
229         * @return
230         */
231        public double[] getMinMax(double[] array) {
232                double[] minmax = new double[2];
233
234                double max = Double.MIN_VALUE;
235                double min = Double.MAX_VALUE;
236
237                for(double value : array) {
238                        if(value > max) max = value;
239                        if(value < min) min = value;
240                }
241
242                minmax[0] = min;
243                minmax[1] = max;
244                return minmax;
245        }
246
247        @Override
248        public String toString() {
249                return String.format("[(%7.2f,%7.2f),(%7.2f,%7.2f),(%7.2f,%7.2f)]", xmin,xmax,ymin,ymax,zmin,zmax);
250        }
251}