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