1 /* 2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab 3 * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet 4 * 5 * Copyright (C) 2012 - 2016 - Scilab Enterprises 6 * 7 * This file is hereby licensed under the terms of the GNU GPL v2.0, 8 * pursuant to article 5.3.4 of the CeCILL v.2.1. 9 * This file was originally licensed under the terms of the CeCILL v2.1, 10 * and continues to be available under such terms. 11 * For more information, see the COPYING file which you should have received 12 * along with this program. 13 */ 14 15 package org.scilab.forge.scirenderer.implementation.g2d.motor; 16 17 import java.awt.Color; 18 import java.awt.Graphics2D; 19 import java.awt.Shape; 20 import java.util.ArrayList; 21 import java.util.List; 22 23 import org.scilab.forge.scirenderer.tranformations.Vector3d; 24 import org.scilab.forge.scirenderer.tranformations.Vector4d; 25 26 /** 27 * @author Calixte DENIZET 28 * 29 * Class to represent a convex object. 30 * Collision and relative positions of convexs object are relatively easy to determinate. 31 * About the method isBehind, it could be interesting to use the algorithm of Chung-Wang. 32 */ 33 public abstract class ConvexObject extends AbstractDrawable3DObject implements Clippable { 34 35 private List<ConvexObject> areas; 36 37 /** 38 * Default constructor 39 * @param vertices the vertices 40 * @param colors the colors 41 */ ConvexObject(Vector3d[] vertices, Color[] colors)42 public ConvexObject(Vector3d[] vertices, Color[] colors) throws InvalidPolygonException { 43 super(vertices, colors); 44 } 45 46 /** 47 * Abstract method 48 * Break this ConvexObject against the ConvexObject o 49 * @param o a ConvexObject 50 * @return a list of ConvexObject. 51 */ breakObject(ConvexObject o)52 public abstract List<ConvexObject> breakObject(ConvexObject o); 53 54 /** 55 * {@inheritDoc} 56 */ breakObject(Vector4d v)57 public abstract List<ConvexObject> breakObject(Vector4d v); 58 addArea(ConvexObject co)59 public void addArea(ConvexObject co) { 60 if (areas == null) { 61 areas = new ArrayList<ConvexObject>(); 62 } 63 areas.add(co); 64 } 65 drawAreas(Graphics2D g2d)66 protected void drawAreas(Graphics2D g2d) { 67 if (areas != null) { 68 for (ConvexObject co : areas) { 69 Shape oldClip = g2d.getClip(); 70 g2d.clip(this.getProjectedContour()); 71 co.draw(g2d); 72 g2d.setClip(oldClip); 73 } 74 } 75 } 76 77 /** 78 * Test the coplanarity of two objects 79 * @param o a ConvexObject 80 * @return true if the two objects are coplanar 81 */ areCoplanar(ConvexObject o)82 public boolean areCoplanar(ConvexObject o) { 83 if (!(this instanceof Segment)) { 84 double sc = vertices[0].scalar(getNormal()); 85 if (o instanceof Segment) { 86 return isEqual(sc, o.vertices[0].scalar(v0v1)) && isEqual(sc, o.vertices[1].scalar(v0v1)); 87 } 88 return isEqual(sc, o.vertices[0].scalar(v0v1)) && isEqual(sc, o.vertices[1].scalar(v0v1)) && isEqual(sc, o.vertices[2].scalar(v0v1)); 89 } 90 91 if (!(o instanceof Segment)) { 92 return o.areCoplanar(this); 93 } 94 95 if (o.vertices[0].equals(vertices[0]) || o.vertices[1].equals(vertices[0]) || o.vertices[0].equals(vertices[1]) || o.vertices[1].equals(vertices[1])) { 96 return true; 97 } 98 99 getNormal(); 100 o.getNormal(); 101 Vector3d v = Vector3d.product(v0, o.v0); 102 return isNull(v.scalar(vertices[0].minus(o.vertices[0]))); 103 } 104 105 /** 106 * Check if o is behind this. 107 * Take care: the algorithms used are for convex objects (typically tri-tri, seg-seg or tri-seg) 108 * @return true if o is behind this 109 */ isBehind(ConvexObject o)110 public int isBehind(ConvexObject o) { 111 BoundingBox bbox = getBBox(); 112 BoundingBox obbox = o.getBBox(); 113 // Quick test in using bounding boxes 114 if (bbox.isNonZOverlapping(obbox)) { 115 return 0; 116 } 117 118 // Check if the two objects intersect in projection plane or not 119 if (check2DIntersection(o)) { 120 // We have a strict intersection or an intersection on an edge 121 if (areCoplanar(o)) { 122 return getPrecedence() > o.getPrecedence() ? 1 : -1; 123 } 124 125 // Quick test with bounding-box along z-axis 126 int ret = bbox.zCompare(obbox); 127 if (ret != 0) { 128 return ret; 129 } 130 131 // In the most of the cases, one of the two following test are sufficient to determinate 132 // if one object is behind or on front of the plane containing this or o 133 ret = check(o, getNormal()); 134 if (ret != 0) { 135 return ret; 136 } 137 138 ret = check(o, o.getNormal()); 139 if (ret != 0) { 140 return ret; 141 } 142 143 // Check against the cross product of one edge of this and one edge of o 144 int M = vertices.length == 2 ? 1 : vertices.length; 145 int N = o.vertices.length == 2 ? 1 : o.vertices.length; 146 for (int j = 0; j < M; j++) { 147 int l = (j + 1 < vertices.length) ? j + 1 : 0; 148 Vector3d e = vertices[l].minus(vertices[j]); 149 for (int k = 0; k < N; k++) { 150 int m = (k + 1 < o.vertices.length) ? k + 1 : 0; 151 Vector3d oe = o.vertices[m].minus(o.vertices[k]); 152 ret = check(o, Vector3d.product(e, oe).getNormalized()); 153 if (ret != 0) { 154 return ret; 155 } 156 } 157 } 158 159 // At this point: there is a collision between the two objects 160 return 2; 161 } else { 162 return 0; 163 } 164 } 165 166 /** 167 * Check the intersections of the projection on the xOy-plane of this and o 168 * The algorithm is the following: for each edge, determinate the normal vector and project all the points 169 * of this and o on the normal. If the intersection of [this.min,this.max] and [o.min, o.max] is empty, then 170 * we have a separating line so the two objects are separated. 171 * @param o the object to test with this 172 * @return true if there is a collision 173 */ check2DIntersection(final ConvexObject o)174 public boolean check2DIntersection(final ConvexObject o) { 175 int ret = check2D(this, o); 176 if (ret != -1) { 177 return false; 178 } 179 180 ret = check2D(o, this); 181 if (ret != -1) { 182 return false; 183 } 184 185 return true; 186 } 187 188 /** 189 * Check the intersections of the projection on the xOy-plane of this and o 190 * The algorithm is the following: for each edge, determinate the normal vector and project all the points 191 * of this and o on the normal. If the intersection of [this.min,this.max] and [o.min, o.max] is empty, then 192 * we have a separating line so the two objects are separated. 193 * @param o the object to test with this 194 * @return true if there is a collision 195 */ check2DTrueIntersection(final ConvexObject o)196 public boolean check2DTrueIntersection(final ConvexObject o) { 197 int ret = check2D2(this, o); 198 if (ret == 1) { 199 return true; 200 } else if (ret == 0) { 201 return false; 202 } 203 204 ret = check2D2(o, this); 205 if (ret == 1) { 206 return true; 207 } else if (ret == 0) { 208 return false; 209 } 210 211 return true; 212 } 213 214 /** 215 * Check 2D intersection of two convex objects 216 * @param o1 first object 217 * @param o2 second object 218 * @return -1 if strict intersection, 1 if intersection on an edge and 0 if no intersection 219 */ check2D(final ConvexObject o1, final ConvexObject o2)220 private static final int check2D(final ConvexObject o1, final ConvexObject o2) { 221 // When o1 is a Segment (i.e. o1.vertices;length == 2) it is mandatory to check against ortho(v1-v0) and ortho(v0-v1) 222 int M = o1.vertices.length == 2 ? 1 : o1.vertices.length; 223 for (int i = 0; i < M; i++) { 224 int j = (i + 1 < o1.vertices.length) ? i + 1 : 0; 225 double xN = o1.vertices[i].getY() - o1.vertices[j].getY(); 226 double yN = o1.vertices[j].getX() - o1.vertices[i].getX(); 227 double n = Math.hypot(xN, yN); 228 xN /= n; 229 yN /= n; 230 double[] mM = minmax2D(o1, xN, yN); 231 double min = mM[0]; 232 double max = mM[1]; 233 234 mM = minmax2D(o2, xN, yN); 235 double omin = mM[0]; 236 double omax = mM[1]; 237 238 if (max < omin || omax < min) { 239 return 0; 240 } 241 242 if (isEqual(max, omin) || isEqual(omax, min)) { 243 return 1; 244 } 245 } 246 247 return -1; 248 } 249 250 /** 251 * Check 2D intersection of two convex objects 252 * @param o1 first object 253 * @param o2 second object 254 * @return -1 if strict intersection, 1 if intersection on an edge and 0 if no intersection 255 */ check2D2(final ConvexObject o1, final ConvexObject o2)256 private static final int check2D2(final ConvexObject o1, final ConvexObject o2) { 257 // When o1 is a Segment (i.e. o1.vertices;length == 2) it is mandatory to check against ortho(v1-v0) and ortho(v0-v1) 258 int M = o1.vertices.length == 2 ? 1 : o1.vertices.length; 259 boolean bool = false; 260 for (int i = 0; i < M; i++) { 261 int j = (i + 1 < o1.vertices.length) ? i + 1 : 0; 262 double xN = o1.vertices[i].getY() - o1.vertices[j].getY(); 263 double yN = o1.vertices[j].getX() - o1.vertices[i].getX(); 264 double n = Math.hypot(xN, yN); 265 xN /= n; 266 yN /= n; 267 double[] mM = minmax2D(o1, xN, yN); 268 double min = mM[0]; 269 double max = mM[1]; 270 271 mM = minmax2D(o2, xN, yN); 272 double omin = mM[0]; 273 double omax = mM[1]; 274 275 if (max < omin || omax < min) { 276 return 0; 277 } 278 279 if (!bool && (isEqual(max, omin) || isEqual(omax, min))) { 280 bool = true; 281 } 282 } 283 284 if (bool) { 285 return 1; 286 } 287 288 return -1; 289 } 290 291 /** 292 * Check the intersection this and o against vector v. 293 * The algorithm is just to project this and o on the vector v and to check if the two projected sets 294 * can be separated. 295 * @param v the vector where to project 296 * @return 1 if o is behind this, 0 if it is undeterminated and -1 if this is behind o. 297 */ check(final ConvexObject o, final Vector3d v)298 protected int check(final ConvexObject o, final Vector3d v) { 299 if (!v.isNearZero()) { 300 double[] mM = minmax3D(this, v); 301 double min = mM[0]; 302 double max = mM[1]; 303 304 mM = minmax3D(o, v); 305 double omin = mM[0]; 306 double omax = mM[1]; 307 double z = v.getZ(); 308 309 if (Math.signum(z) == 0) { 310 return 0; 311 } 312 313 if (isLowerOrEqual(max, omin)) { 314 return (int) Math.signum(z); 315 } 316 317 if (isLowerOrEqual(omax, min)) { 318 return (int) - Math.signum(z); 319 } 320 } 321 322 return 0; 323 } 324 } 325