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; 030import java.util.Locale; 031import java.util.Objects; 032 033 034/** 035 * A bounding box for short cutting some geometrical calculations. 036 * 037 * See http://en.wikipedia.org/wiki/Bounding_volume 038 * 039 * @author Jose Duarte 040 * 041 */ 042public class BoundingBox implements Serializable { 043 044 private static final long serialVersionUID = 1L; 045 private static final Logger logger = LoggerFactory.getLogger(StructureInterfaceList.class); 046 047 048 public double xmin; 049 public double xmax; 050 public double ymin; 051 public double ymax; 052 public double zmin; 053 public double zmax; 054 055 public BoundingBox(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) { 056 this.xmin = xmin; 057 this.xmax = xmax; 058 this.ymin = ymin; 059 this.ymax = ymax; 060 this.zmin = zmin; 061 this.zmax = zmax; 062 } 063 064 public BoundingBox(BoundingBox bb) { 065 this.xmin = bb.xmin; 066 this.xmax = bb.xmax; 067 this.ymin = bb.ymin; 068 this.ymax = bb.ymax; 069 this.zmin = bb.zmin; 070 this.zmax = bb.zmax; 071 } 072 073 /** 074 * Constructs a BoundingBox by calculating maxs and mins of given array of atoms. 075 * @param atoms the atom array 076 * @throws IllegalArgumentException if atom array is empty 077 * @throws NullPointerException if input is null 078 */ 079 public BoundingBox (Point3d[] atoms) { 080 081 if (atoms.length==0) 082 throw new IllegalArgumentException("Empty list of atoms is not allowed for BoundingBox construction"); 083 084 xmax = atoms[0].x; 085 xmin = xmax; 086 ymax = atoms[0].y; 087 ymin = ymax; 088 zmax = atoms[0].z; 089 zmin = zmax; 090 091 for(int i=1;i<atoms.length;i++) { 092 if(atoms[i].x > xmax) xmax = atoms[i].x; 093 else if(atoms[i].x < xmin) xmin = atoms[i].x; 094 095 if(atoms[i].y > ymax) ymax = atoms[i].y; 096 else if(atoms[i].y < ymin) ymin = atoms[i].y; 097 098 if(atoms[i].z > zmax) zmax = atoms[i].z; 099 else if(atoms[i].z < zmin) zmin = atoms[i].z; 100 } 101 102 } 103 104 /** 105 * Given a set of bounding boxes returns a bounding box that bounds all of them. 106 * @param boxes an array of bounding boxes 107 * @throws IllegalArgumentException if input array is empty 108 * @throws NullPointerException if input is null 109 */ 110 public BoundingBox(BoundingBox[] boxes) { 111 112 if (boxes.length==0) 113 throw new IllegalArgumentException("Empty list of bounding boxes is not allowed for BoundingBox construction"); 114 115 xmax = boxes[0].xmax; 116 xmin = boxes[0].xmin; 117 ymax = boxes[0].ymax; 118 ymin = boxes[0].ymin; 119 zmax = boxes[0].zmax; 120 zmin = boxes[0].zmin; 121 122 for (int i=1;i<boxes.length;i++) { 123 if(boxes[i].xmax > xmax) xmax = boxes[i].xmax; 124 else if(boxes[i].xmin < xmin) xmin = boxes[i].xmin; 125 if(boxes[i].ymax > ymax) ymax = boxes[i].ymax; 126 else if(boxes[i].ymin < ymin) ymin = boxes[i].ymin; 127 if(boxes[i].zmax > zmax) zmax = boxes[i].zmax; 128 else if(boxes[i].zmin < zmin) zmin = boxes[i].zmin; 129 } 130 131 } 132 133 private static class Bound implements Comparable<Bound> { 134 int cardinal; 135 double value; 136 public Bound(int cardinal,double value) { 137 this.cardinal = cardinal; 138 this.value = value; 139 } 140 @Override 141 public int compareTo(Bound o) { 142 return Double.compare(this.value,o.value); 143 } 144 @Override 145 public String toString() { 146 return "["+cardinal+","+value+"]"; 147 } 148 } 149 150 /** 151 * Returns the dimensions of this bounding box. 152 * 153 * @return a double array (x,y,z) with the dimensions of the box. 154 */ 155 public double[] getDimensions(){ 156 double[] dim = new double[3]; 157 dim[0] = xmax-xmin; 158 dim[1] = ymax-ymin; 159 dim[2] = zmax-zmin; 160 return dim; 161 } 162 163 /** 164 * Returns true if this bounding box overlaps given one, i.e. they are within 165 * one cutoff distance in one of their 3 dimensions. 166 * @param cutoff 167 * @return 168 */ 169 public boolean overlaps(BoundingBox o, double cutoff) { 170 if (this==o) return true; 171 // x dimension 172 if (!areOverlapping(xmin,xmax,o.xmin,o.xmax,cutoff)) { 173 return false; 174 } 175 // y dimension 176 if (!areOverlapping(ymin,ymax,o.ymin,o.ymax,cutoff)) { 177 return false; 178 } 179 // z dimension 180 if (!areOverlapping(zmin,zmax,o.zmin,o.zmax,cutoff)) { 181 return false; 182 } 183 return true; 184 } 185 186 private boolean areOverlapping(double imin, double imax, double jmin, double jmax, double cutoff) { 187 188 Bound[] bounds = {new Bound(0,imin), new Bound(1,imax), 189 new Bound(2,jmin), new Bound(3,jmax)}; 190 191 Arrays.sort(bounds); 192 193 if ((bounds[0].cardinal==0 && bounds[1].cardinal==1)) { 194 if ((bounds[2].value-bounds[1].value)>cutoff) { 195 return false; 196 } 197 } else if (bounds[0].cardinal==2 && bounds[1].cardinal==3) { 198 if ((bounds[2].value-bounds[1].value)>cutoff) { 199 return false; 200 } 201 } 202 203 return true; 204 205 } 206 207 /** 208 * Check if a given point falls within this box 209 * @param atom 210 * @return 211 */ 212 public boolean contains(Point3d atom) { 213 double x = atom.x; 214 double y = atom.y; 215 double z = atom.z; 216 return xmin <= x && x <= xmax 217 && ymin <= y && y <= ymax 218 && zmin <= z && z <= zmax; 219 } 220 221 public void translate(Vector3d translation) { 222 xmin+=translation.x; 223 xmax+=translation.x; 224 ymin+=translation.y; 225 ymax+=translation.y; 226 zmin+=translation.z; 227 zmax+=translation.z; 228 } 229 230 /** 231 * Returns an array of size 2 with min and max values of given double array 232 * @param array 233 * @return 234 */ 235 public double[] getMinMax(double[] array) { 236 double[] minmax = new double[2]; 237 238 double max = Double.MIN_VALUE; 239 double min = Double.MAX_VALUE; 240 241 for(double value : array) { 242 if(value > max) max = value; 243 if(value < min) min = value; 244 } 245 246 minmax[0] = min; 247 minmax[1] = max; 248 return minmax; 249 } 250 251 @Override 252 public String toString() { 253 return String.format(Locale.US, "[(%7.2f,%7.2f),(%7.2f,%7.2f),(%7.2f,%7.2f)]", xmin,xmax,ymin,ymax,zmin,zmax); 254 } 255}