View Javadoc
1 /***************************************************************************** 2 * Virtual Mockup for Machine Vision 3 * Copyright (C) 2001-2003 Fabio R. de Miranda, João E. Kogler Jr., 4 * Carlos S. Santos. 5 * Virtual Mockup for Machine Vision Project funded by SENAC-SP 6 * 7 * Permission is granted to redistribute and/or modify this 8 * software under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This software is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License (http://www.gnu.org/copyleft/lesser.html) 16 * for more details. 17 * 18 *****************************************************************************/ 19 20 package camera3d.manipulation; 21 22 import javax.vecmath.*; 23 import javax.media.j3d.*; 24 import camera3d.MathUtility; 25 import camera3d.TransformMode; 26 import camera3d.TransformScope; 27 28 /*** 29 * Manipulator for performing translation of scene objects with the mouse.<BR> 30 * 31 * 32 * @author Fábio Roberto de Miranda, Carlos da Silva dos Santos 33 * @version 1.0 34 */ 35 public class TranslationManipulator extends TransformationManipulator { 36 37 private StringBuffer buffer = new StringBuffer(512); 38 39 40 /* 41 * The main idea is always work with points stored as world coordinates. 42 * Every time a new point is informed, convert it ASAP to world 43 * coordinates and leave it stored as such 44 */ 45 /*** Eye position. */ 46 private Point3d eyePos = new Point3d(); 47 48 /*** Holds the virtual world coordinates of the last point the user dragged the mouse on. */ 49 private Point3d currentPoint3D = new Point3d(); 50 /*** Holds the virtual world coordinates of the initial point of each dragging action. */ 51 private Point3d beginPoint3D = new Point3d(); 52 /*** Vector pointing from eye position to current point3d. */ 53 private Vector3d mouseVec = new Vector3d(); 54 55 private Vector3d planeNormal = new Vector3d(); 56 private Point3d planePoint = new Point3d(); 57 private Vector4d pickingPlane = new Vector4d(); 58 59 private Vector3d lineVec = new Vector3d(); 60 private Point3d linePoint = new Point3d(); 61 62 /*** Holds ImagePlate to Virtual World coordinates. */ 63 private Transform3D imagePlateToVworld = new Transform3D(); 64 65 // Preallocated temporary storage variables sometimes come in handy 66 private Transform3D tempT3D = new Transform3D(); 67 private Vector3d tempVec3d = new Vector3d(); 68 private Vector3d tempVec3d2 = new Vector3d(); 69 private Vector3d tempVec3d3 = new Vector3d(); 70 private Point3d tempP3d = new Point3d(); 71 private Point3d tempP3d2 = new Point3d(); 72 private Point3d tempP3d3 = new Point3d(); 73 74 private Vector3d lastIntersectionVec3d = new Vector3d(); 75 private Vector3d intersectionV3d = new Vector3d(); 76 77 private BranchGroup tempBG = new BranchGroup(); 78 79 private boolean checkClipPlanes = true; 80 81 private TranslatableObject translatableObject; 82 83 public TranslationManipulator(){ 84 super(); 85 } 86 87 public void setTranslatableObject(TranslatableObject translatableObject){ 88 this.translatableObject = translatableObject; 89 setTransformationMode(this.mode); 90 setTransformationScope(this.scope); 91 } 92 93 94 /*** 95 * Sets the Canvas3D from which manipulations are performed. 96 * @param canvas Canvas3D object that originates manipulations. 97 */ 98 public void setCanvas3D(Canvas3D canvas){ 99 debugln("translation manip. viewport changed."); 100 if (canvas!=this.canvas3d){ // If canvas was in fact changed... 101 this.canvas3d = canvas; // recompute necessary fields 102 recomputeMatrices(); 103 recomputeEyeVectors(); 104 } 105 } 106 107 /*** 108 * Sets mouse location where manipulation started. 109 * @param x x coordinate of manipulation's initial point. 110 * @param y y coordinate of manipulation's initial point. 111 */ 112 public void setBeginPoint(int x, int y){ 113 if(translatableObject==null){ 114 throw new NullPointerException("TranslationManipulator: translatableObject was null"); 115 } 116 if(!translatableObject.isLive()){ 117 throw new IllegalStateException("TranslationManipulator: translatableObject was not live"); 118 } 119 //debugln("______________________________ set begin point called ______"); 120 super.setBeginPoint(x, y); 121 //debugln("translation manip. begin:("+x+","+y+")"); 122 123 // gets image plate to vworld transform 124 recomputeMatrices(); 125 // calculates eye position in vworld 126 recomputeEyeVectors(); 127 128 // calculates begin point coordinates in vworld 129 canvas3d.getPixelLocationInImagePlate(x, y, beginPoint3D); 130 imagePlateToVworld.transform(beginPoint3D); 131 132 currentPoint3D.set(beginPoint3D); 133 134 // Recompute matrices / planes as needed 135 if (translatableObject!=null&&translatableObject.isLive()){ 136 updateDetectionObjects(); 137 138 // sets last intersection point to current object location 139 translatableObject.getLocalToVworld(tempT3D); 140 tempT3D.get(tempVec3d); 141 //lastIntersectionP3d.set(tempVec3d); 142 lastIntersectionVec3d.set(tempVec3d); 143 144 } 145 debugln("______________________________ end of set begin point ______"); 146 } 147 148 /*** 149 * Sets flag used to determine if the translated object must be kept between the two 150 * clipping planes or no check should be performed to assure that condition.<BR> 151 * The checking process will only be performed if the view that is shown by the Canvas3D 152 * has both the back and front clip policies set to View.VIRTUAL_EYE 153 * @param checkClipPlanes if true, the translated object will be kept in the area 154 * between the two clipping planes; if false that condition 155 * is not assured. 156 */ 157 public void setCheckClipPlanes(boolean checkClipPlanes){ 158 this.checkClipPlanes = checkClipPlanes; 159 } 160 161 /*** 162 * Returns flag that indicates if the translated object will be kept in the area between 163 * the two clipping planes during translation or if it will be allowed to go astray. 164 */ 165 public boolean getCheckClipPlanes(){ 166 return this.checkClipPlanes; 167 } 168 169 /*** 170 * Clears current selection of objects to be translated. 171 */ 172 public void clearSelection(){ 173 this.translatableObject = null; 174 } 175 176 /*** 177 * Decides whether to compute a plane for selected object and a line for user input 178 * (when user selects a plane for translation) 179 * or 180 * Compute a line for selected object and a a plane for user input 181 * (when user selects an axis for translation) 182 */ 183 // This may be a somewhat expensive operation. Maintainers are advised to use only for 184 // recomputing data that became invalid. 185 // This method expects currentPoint3D to contain, in world coordinates, the point where 186 // user has clicked and in eyePos the point where the eye is placed 187 private void updateDetectionObjects(){ 188 //guiControl.clearBagGizmos(ClearGizmosAction.ALL); 189 debugln("Updating detection objects."); 190 // For the sake of clarity clears planes used in debugging 191 //guiControl.clearBagGizmos(ClearGizmosAction.PLANES); 192 mouseVec.sub(currentPoint3D, eyePos); 193 if(scope.isPlane()){ 194 /* 195 If the translation scope is a plane, we will compute the equation 196 of the plane of translation and set the lineVec and linePoint 197 parameters to mouseVec and eyePoint, respectively. Then the 198 intersection of the plane and lineVec will give us the point where 199 the selected object shall be placed. 200 */ 201 // Compute translatableObject's line and picking plane 202 getTranslationPlane(translatableObject, pickingPlane); 203 linePoint.set(eyePos); 204 lineVec.set(mouseVec); 205 206 } else if (scope.isAxis()){ 207 /* 208 If the translation scope is an axis we do the following operations: 209 - compute a vector corresponding to the translation axis, in virtual world 210 coordinates. If the translation is in Absolute mode, the translation axis 211 will be one of the canonical vectors: 212 X = {1,0,0}; Y = {0,1,0}; Z = {0,0,1} 213 If the translation is in Relative mode, these vectors will have to be rotated 214 to reflect the translatableObject own rotation. 215 - compute a plane that contains the mouse vector 216 - the intersection of this plane and the translation axis will determine the 217 point where the selected objects shall be placed. 218 219 */ 220 getTranslationAxis(translatableObject, linePoint, lineVec); 221 // 222 planePoint.set(currentPoint3D); 223 findPickingPlaneNormal(planeNormal, mouseVec, lineVec); 224 MathUtility.buildPlaneFromPointAndNormal(pickingPlane, planePoint, planeNormal); 225 226 /* Commented out 26/07/2002 - used for debugging. 227 guiControl.addPlane(planePoint, planeNormal); 228 guiControl.addPoint(eyePos); 229 guiControl.addPoint(currentPoint3D); 230 guiControl.addLine(linePoint, lineVec); 231 debugln("Chosen axis'': "+ scope.toString()); 232 debugln("Chosen mode'': "+ mode.toString()); 233 debugln("** TransManip.UpdateDetectionObjects - selected object: "+translatableObject.getLabel()); 234 */ 235 } 236 // At this point, linePoint and lineVec will contain the line of interest to 237 // intersection computing, and pickingPlane will contain the proper plane 238 debugln("TransManip.updateDetectionObjects: plane :("+pickingPlane+")"); 239 } 240 241 /*** 242 * Sets current mouse location during manipulation. 243 * @param x x coordinate of current manipulation point. 244 * @param y y coordinate of current manipulation point. 245 */ 246 public void setCurrentPoint(int x, int y){ 247 //debugln("***** set current point called ******** "); 248 //debugln("translation manip. current:("+x+","+y+")"); 249 transform(x,y); 250 //debugln("***** set current point ended **********"); 251 } 252 253 254 void transform(int x, int y){ 255 256 canvas3d.getPixelLocationInImagePlate(x, y, currentPoint3D); 257 258 imagePlateToVworld.transform(currentPoint3D); 259 //debugln("Point where user clicked (currentPoint3D) is ( "+currentPoint3D+")"); 260 261 /* we can only perform translation on live objects */ 262 if (translatableObject==null){ 263 throw new NullPointerException("TranslationManipulator: Selected object was null"); 264 } 265 if(!translatableObject.isLive()){ 266 throw new IllegalStateException("TranslationManipulator: Selected object was not live"); 267 } 268 269 updateDetectionObjects(); 270 // Finds intersection 271 double intersection = MathUtility.computeIntersection(intersectionV3d, 272 linePoint, 273 lineVec, 274 pickingPlane); 275 //debugln("TransManip: Intersection point: "+intersectionP3d); 276 277 if(checkClipPlanes){ 278 checkClippingPlanes(intersectionV3d); 279 } 280 translatableObject.translate(intersectionV3d); 281 lastIntersectionVec3d.set(intersectionV3d); 282 } 283 284 285 /*** 286 * p1, p2 and p3 are supposed to arrive in world coordinates. If it's necessary to obtain the normal to the plane, 287 * all it's necessary to do is assembling a vector with (x,y,z) componentes of the plane Vector4d 288 */ 289 private void buildPickingPlaneDescription(Vector4d plane, Point3d p1, Point3d p2, Point3d p3){ 290 MathUtility.buildPlaneFromPoints(plane, p1, p2, p3); 291 292 } 293 294 /*** 295 * Calculates the eye position in Vworld coordinates. 296 */ 297 private void recomputeEyeVectors(){ 298 canvas3d.getCenterEyeInImagePlate(eyePos); 299 debugln("TranslationManipulator: eye Pos (IP): ("+eyePos.x+","+eyePos.y+","+eyePos.z+")"); 300 imagePlateToVworld.transform(eyePos); 301 debugln("TranslationManipulator: eye Pos (VW): ("+eyePos.x+","+eyePos.y+","+eyePos.z+")"); 302 } 303 304 /*** 305 * Retrives the Image Plate to Vworld transform matrix from the current Canvas3D. 306 * This method needs to be called whenever the Canvas3D is switched or modified, 307 * or the camera is moved. 308 */ 309 private void recomputeMatrices(){ 310 canvas3d.getImagePlateToVworld(imagePlateToVworld); 311 } 312 313 /*** 314 * Converts a plane in the form Ax + By + Cz + D to a plane represented by a normal 315 * and a point. 316 * @param plane is the Ax + By + Cz + D representation of the plan 317 * @param planeNormal is the resulting plane normal (Bet you could've guessed that :) ) 318 * @param planePoint is a point belonging to the plane 319 */ 320 /* 321 void buildPlanePointAndNormal(Vector4d plane, Vector3d planeNormal, Point3d planePoint){ 322 planeNormal.x = plane.x; 323 planeNormal.y = plane.y; 324 planeNormal.z = plane.z; 325 326 double x = -(plane.w/plane.x); 327 double y = -(plane.w/plane.y); 328 double z = -(plane.w/plane.w); 329 330 331 if (!Double.isNaN(y)){ 332 if (!Double.isInfinite(y)){ 333 planePoint.x = 0.0; 334 planePoint.z = 0.0; 335 planePoint.y = y; 336 } 337 } else { 338 339 if (!Double.isNaN(x)){ 340 if (!Double.isInfinite(x)){ 341 planePoint.y = 0.0; 342 planePoint.z = 0.0; 343 planePoint.x = x; 344 } 345 } else { 346 if (!Double.isNaN(z)){ 347 if (!Double.isInfinite(z)){ 348 planePoint.x = 0.0; 349 planePoint.y = 0.0; 350 planePoint.z = z; 351 } 352 } 353 } 354 355 } 356 357 debugln("Building plane in point and normal form.\n From "+plane+"Point: "+planePoint+" Normal: "+planeNormal); 358 }*/ 359 360 361 /*** 362 * Method used when translation along an axis is performed. Finds the normal of a plane 363 * that contains the planeVector parameter. The intersection of this plane and the 364 * selectedAxis parameter will determine the point where the selected object should 365 * be positioned. 366 * @param planeNormal receives the normal of the computed plane. Output parameter. 367 * @param planeVector vetor contained by the computed plane. Input parameter. 368 * @param selectedAxis axis along which translation is performed. 369 */ 370 private void findPickingPlaneNormal(Vector3d planeNormal, Vector3d planeVector, Vector3d selectedAxis){ 371 //lineVec.sub(currentPoint3D, eyePos); 372 debugln("Checking picking vector "+planeVector); 373 /* Comentado 26/7/02 374 guiControl.addLine(currentPoint3D, mouseVec); 375 */ 376 tempVec3d.cross(planeVector, selectedAxis); 377 tempVec3d2.cross(tempVec3d, planeVector); 378 // Now tempVec3d2 holds a vector normal to the plane we wanted 379 tempVec3d2.normalize(); 380 planeNormal.set(tempVec3d2); 381 } 382 383 384 385 /*** 386 * Calculates the plane where the selected object is allowed to move.<BR> 387 * The plane variable will receive a description of a plane which contains the pair 388 * of axis specified by this object's scope field. The plane description is in 389 * the form Ax + By + Cz + D = 0.<BR> 390 * The mode parameter specifies which axis system will be used to calculate the plane: 391 * the absolute (virtual world) one or the local one (meaning the one attached to the 392 * selected object).<BR> 393 * 394 * @param plane receives plane coefficients A,B,C,D. 395 * @param mode types of axis (relative/absolute) to be used to compute plane. 396 * @param axii specifies pairs of axis to be used. 397 * @throws IllegalStateException if the current translation scope is not a plane. 398 */ 399 // This method will use tempP3d1, tempP3d2 and tempP3d3 and tempT3D (spoiling their values) 400 private void getTranslationPlane(TranslatableObject translatableObject, Vector4d plane){ 401 if(!scope.isPlane()){ 402 throw new IllegalStateException("Transformation scope must be a plane."); 403 } 404 tempP3d.set(0.0, 0.0, 0.0); // origin point - common to all computed planes 405 /* Determines points based on the plane specified. */ 406 if(scope == TransformScope.XY){ 407 tempP3d2.set(1.0, 0.0, 0.0); 408 tempP3d3.set(0.0, 1.0, 0.0); 409 }else if(scope == TransformScope.YZ){ 410 tempP3d2.set(0.0, 1.0, 0.0); 411 tempP3d3.set(0.0, 0.0, 1.0); 412 }else if(scope == TransformScope.ZX){ 413 tempP3d2.set(0.0, 0.0, 1.0); 414 tempP3d3.set(1.0, 0.0, 0.0); 415 }// A default case (if pair of axis is invalid) is missing here 416 417 translatableObject.getLocalToVworld(tempT3D); 418 419 if(mode == TransformMode.ABSOLUTE){ 420 /* In absolute mode we will ignore rotation and scaling */ 421 tempT3D.get(tempVec3d); 422 tempT3D.setIdentity(); 423 tempT3D.setTranslation(tempVec3d); 424 } 425 426 // transforms the three points so they are contained in the desired 427 // plane of translation 428 tempT3D.transform(tempP3d); 429 tempT3D.transform(tempP3d2); 430 tempT3D.transform(tempP3d3); 431 432 // creates a plane description from three points which belong to it. 433 MathUtility.buildPlaneFromPoints(plane, tempP3d, tempP3d2, tempP3d3); 434 } 435 436 /*** 437 * Returns parameters describing a line that passes through the origin of the local 438 * coordinates system of one object and whose direction coincides with the current 439 * translation scope, if that is an axis. 440 * 441 * @param translatableObject scene object for which we will compute the translation 442 * axis. Input parameter. 443 * @param linePoint A point contained in the desired line, which is also the origin of 444 * the local coordinates system of selected object. Output parameter 445 * @param lineVec A vector that specifies the direction of the line. Output parameter. 446 */ 447 private void getTranslationAxis(TranslatableObject translatableObject, Point3d linePoint, Vector3d lineVec){ 448 if(translatableObject == null){ 449 throw new NullPointerException("TranslationManipulator: translatableObject was null"); 450 } 451 tempP3d.set(0.0, 0.0, 0.0); 452 if(scope == TransformScope.X){ 453 tempP3d2.set(1.0, 0.0, 0.0); 454 }else if(scope == TransformScope.Y){ 455 tempP3d2.set(0.0, 1.0, 0.0); 456 }else if(scope == TransformScope.Z){ 457 tempP3d2.set(0.0, 0.0, 1.0); 458 } 459 460 translatableObject.getLocalToVworld(tempT3D); 461 462 // transforms to origin of local axis system 463 tempT3D.transform(tempP3d); 464 if(mode == TransformMode.RELATIVE){ 465 // if mode is relative we must transform the second point 466 // and subtract the first point from it in order to get 467 // the correct axis 468 tempT3D.transform(tempP3d2); 469 lineVec.sub(tempP3d2, tempP3d); 470 }else if(mode == TransformMode.ABSOLUTE){ 471 // if mode is absolute the axis corresponds to one of the 472 // canonical X,Y oz Z axis. 473 lineVec.set(tempP3d2); 474 } 475 linePoint.set(tempP3d); 476 } 477 478 private void checkClippingPlanes(Vector3d v3d){ 479 View view = canvas3d.getView(); 480 if(view.getBackClipPolicy()!=View.VIRTUAL_EYE){ 481 //System.out.println("BackClipPolicy != VirtualEye"); 482 return; 483 } 484 if(view.getFrontClipPolicy()!=View.VIRTUAL_EYE){ 485 //System.out.println("FrontClipPolicy != VirtualEye"); 486 return; 487 } 488 double backDistance = view.getBackClipDistance(); 489 double frontDistance = view.getFrontClipDistance(); 490 //System.out.println("__back: "+ backDistance+"; front: "+frontDistance); 491 //coordinates of screen central point 492 int xCenter = canvas3d.getWidth()/2; 493 int yCenter = canvas3d.getHeight()/2; 494 canvas3d.getPixelLocationInImagePlate(xCenter,yCenter,tempP3d); 495 // now we got the center point of screen in Vworld coordinates 496 imagePlateToVworld.transform(tempP3d); 497 498 // got to get vector point from eyePoint to center of screen 499 tempVec3d.set(tempP3d); 500 tempVec3d.sub(eyePos); 501 502 // normalized vetor that points from eye to center of screen 503 tempVec3d.normalize(); 504 505 // vector that points from eye to intersection point 506 tempVec3d2.sub(v3d,eyePos); 507 508 // projection of future object position into eye-to-center vector 509 double projection = tempVec3d2.dot(tempVec3d); 510 511 if(projection > 0.8*backDistance){ 512 //System.out.println("______overflow"); 513 v3d.set(lastIntersectionVec3d); 514 } 515 else if(projection < 1.2*frontDistance){ 516 //System.out.println("_____underflow"); 517 v3d.set(lastIntersectionVec3d); 518 } 519 } 520 521 }

This page was automatically generated by Maven