Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/xerox/VTM/glyphs/VShape.java


1   /*   FILE: VShape.java
2    *   DATE OF CREATION:   Aug 01 2001
3    *   AUTHOR :            Emmanuel Pietriga (emmanuel.pietriga@xrce.xerox.com)
4    *   MODIF:              Thu Jul 10 17:05:16 2003 by Emmanuel Pietriga (emmanuel@w3.org, emmanuel@claribole.net)
5    *   Copyright (c) Xerox Corporation, XRCE/Contextual Computing, 2002. All Rights Reserved
6    *
7    * This library is free software; you can redistribute it and/or
8    * modify it 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 library 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 for more details.
16   *
17   * For full terms see the file COPYING.
18   */
19  
20  package com.xerox.VTM.glyphs;
21  
22  import java.awt.Color;
23  import java.awt.Graphics2D;
24  import java.awt.Font;
25  import java.awt.Polygon;
26  import java.awt.Stroke;
27  import java.awt.geom.AffineTransform;
28  import java.awt.geom.Point2D;
29  import java.lang.Math;
30  import java.util.Vector;
31  import com.xerox.VTM.engine.*;
32  
33  /**
34   * Custom shape implementing Jean-Yves Vion-Dury's model - defined by its N vertices (every vertex is between 0 (distance from shape's center=0) and 1.0 (distance from shape's center equals bounding circle radius)) - angle between each vertices is 2*Pi/N - can be reoriented
35   * @author Emmanuel Pietriga
36   **/
37  
38  public class VShape extends Glyph implements Cloneable {
39  
40      /**height=width in virtual space*/
41      long vs;
42  
43      /**array of projected coordinates - index of camera in virtual space is equal to index of projected coords in this array*/
44      ProjShape[] pc;
45  
46      /**list of vertex distance to the shape's center in the 0-1.0 range (relative to bounding circle) --vertices are layed out counter clockwise, with the first vertex placed at the same Y coord as the shape's center (provided orient=0)*/
47      float[] vertices;
48  
49      int[] xcoords;
50      int[] ycoords;
51  
52      /**
53       *@param v list of vertex distance to the shape's center in the 0-1.0 range (relative to bounding circle)
54       */
55      public VShape(float[] v){
56    vx=0;
57    vy=0;
58    vz=0;
59    vs=10;
60    vertices=v;
61    xcoords=new int[vertices.length];
62    ycoords=new int[vertices.length];
63    computeSize();
64    orient=0;
65    setColor(Color.white);
66    setBorderColor(Color.black);
67      }
68  
69      /**
70       *@param x coordinate in virtual space
71       *@param y coordinate in virtual space
72       *@param z altitude
73       *@param s size (width=height) in virtual space
74       *@param v list of vertex distance to the shape's center in the 0-1.0 range (relative to bounding circle) --vertices are layed out counter clockwise, with the first vertex placed at the same Y coord as the shape's center (provided orient=0)
75       *@param c fill color
76       */
77      public VShape(long x,long y,float z,long s,float[] v,Color c,float or){
78    vx=x;
79    vy=y;
80    vz=z;
81    vs=s;
82    vertices=v;
83    xcoords=new int[vertices.length];
84    ycoords=new int[vertices.length];
85    computeSize();
86    orient=or;
87    setColor(c);
88    setBorderColor(bColor);
89      }
90  
91      /**called when glyph is created in order to create the initial set of projected coordinates wrt the number of cameras in the space
92       *@param nbCam current number of cameras in the virtual space
93       */
94      public void initCams(int nbCam){
95    pc=new ProjShape[nbCam];
96    for (int i=0;i<nbCam;i++){
97        pc[i]=new ProjShape();
98    }
99      }
100 
101     /**used internally to create new projected coordinates to use with the new camera
102      *@param verifIndex camera index, just to be sure that the number of projected coordinates is consistent with the number of cameras
103      */
104     public void addCamera(int verifIndex){
105   if (pc!=null){
106       if (verifIndex==pc.length){
107     ProjShape[] ta=pc;
108     pc=new ProjShape[ta.length+1];
109     for (int i=0;i<ta.length;i++){
110         pc[i]=ta[i];
111     }
112     pc[pc.length-1]=new ProjShape();
113       }
114       else {System.err.println("VShape:Error while adding camera "+verifIndex);}
115   }
116   else {
117       if (verifIndex==0){
118     pc=new ProjShape[1];
119     pc[0]=new ProjShape();
120       }
121       else {System.err.println("VShape:Error while adding camera "+verifIndex);}
122   }
123     }
124 
125     /**if a camera is removed from the virtual space, we should delete the corresponding projected coordinates, but do not modify the array it self because we do not want to change other cameras' index - just point to null*/
126     public void removeCamera(int index){
127   pc[index]=null;
128     }
129 
130     /**reset prevMouseIn for projected coordinates nb i*/
131     public void resetMouseIn(int i){
132   if (pc[i]!=null){pc[i].prevMouseIn=false;}
133     }
134 
135     /**get orientation*/
136     public float getOrient(){return orient;}
137 
138     /**set orientation (absolute)*/
139     public void orientTo(float angle){
140   orient=angle;
141   try{vsm.repaintNow();}catch(NullPointerException e){/*System.err.println("VSM null in Glyph "+e);*/}
142 //   vsm.constMgr.suggestAValue(this.ID,"or",orient);
143   //computeOrientCoords();
144     }
145 
146     /**set orientation (absolute)*/
147 //     public void orientToNS(float angle){
148 //   orient=angle;
149 //   //computeOrientCoords();
150 //     }
151 
152     /**get size (bounding circle radius)*/
153     public float getSize(){return size;}
154 
155     /**compute size (bounding circle radius)*/
156     void computeSize(){
157   size=(float)vs;
158     }
159 
160     /**set absolute size by setting bounding circle radius*/
161     public void sizeTo(float radius){
162   size=radius;
163   vs=Math.round(size);
164   try{vsm.repaintNow();}catch(NullPointerException e){/*System.err.println("VSM null in Glyph "+e);*/}
165 //   vsm.constMgr.suggestAValue(this.ID,"sz",size);
166     }
167 
168     /**set absolute size by setting bounding circle radius*/
169 //     public void sizeToNS(float radius){
170 //   size=radius;
171 //   vs=Math.round(size);
172 //     }
173 
174     /**multiply bounding circle radius by factor*/
175     public void reSize(float factor){
176   size*=factor;
177   vs=(long)Math.round(size);
178   try{vsm.repaintNow();}catch(NullPointerException e){/*System.err.println("VSM null in Glyph "+e);*/}
179 //   vsm.constMgr.suggestAValue(this.ID,"sz",size);  
180     }
181 
182     /**used to find out if glyph completely fills the view (in which case it is not necessary to repaint objects at a lower altitude)*/
183     public boolean fillsView(long w,long h,int camIndex){
184   if ((pc[camIndex].p.contains(0,0)) && (pc[camIndex].p.contains(w,0)) && (pc[camIndex].p.contains(0,h)) && (pc[camIndex].p.contains(w,h))){return true;}
185   else {return false;}
186     }
187 
188     /**detects whether the given point is inside this glyph or not 
189      *@param x EXPECTS PROJECTED JPanel COORDINATE
190      *@param y EXPECTS PROJECTED JPanel COORDINATE
191      */
192     public boolean coordInside(int x,int y,int camIndex){
193   if (pc[camIndex].p.contains(x,y)){return true;}
194   else {return false;}
195     }
196 
197     /**returns 1 if mouse has entered the glyph, -1 if it has exited the glyph, 0 if nothing has changed (meaning it was already inside or outside it)*/
198     public int mouseInOut(int x,int y,int camIndex){
199   if (coordInside(x,y,camIndex)){//if the mouse is inside the glyph
200       if (!pc[camIndex].prevMouseIn){//if it was not inside it last time, mouse has entered the glyph
201     pc[camIndex].prevMouseIn=true;
202     return 1;
203       }
204       else {return 0;}  //if it was inside last time, nothing has changed
205   }
206   else{//if the mouse is not inside the glyph
207       if (pc[camIndex].prevMouseIn){//if it was inside it last time, mouse has exited the glyph
208     pc[camIndex].prevMouseIn=false;
209     return -1;
210       }
211       else {return 0;}  //if it was not inside last time, nothing has changed
212   }
213     }
214 
215     /**list of vertex distance to the shape's center in the 0-1.0 range (relative to bounding circle) --vertices are layed out counter clockwise, with the first vertex placed at the same Y coord as the shape's center (provided orient=0)*/
216     public float[] getVertices(){
217   return vertices;
218     }
219 
220     /**
221      *returns a comma-separated string representation of the vertex distance to the shape's center
222      */
223     public String getVerticesAsText(){
224   StringBuffer res=new StringBuffer();
225   for (int i=0;i<vertices.length-1;i++){
226       res.append(vertices[i]+",");
227   }
228   res.append(vertices[vertices.length-1]);
229   return res.toString();
230     }
231 
232     /**project shape in camera coord sys prior to actual painting*/
233     public void project(Camera c,ViewPanel v){
234   int i=c.getIndex();
235   coef=(float)(c.focal/(c.focal+c.altitude));
236   //find coordinates of object's geom center wrt to camera center and project
237   pc[i].cx=Math.round((vx-c.posx)*coef);
238   pc[i].cy=Math.round((vy-c.posy)*coef);
239   //translate in JPanel coords
240   pc[i].cx=(v.getSize().width/2)+pc[i].cx;
241   pc[i].cy=(v.getSize().height/2)-pc[i].cy;
242   //project height and construct polygon
243   pc[i].cs=Math.round(vs*coef);
244   float vertexAngle=orient;
245   for (int j=0;j<vertices.length-1;j++){
246       xcoords[j]=(int)Math.round(pc[i].cx+pc[i].cs*Math.cos(vertexAngle)*vertices[j]);
247       ycoords[j]=(int)Math.round(pc[i].cy-pc[i].cs*Math.sin(vertexAngle)*vertices[j]);
248       vertexAngle+=2*Math.PI/vertices.length;
249   }//last iteration outside to loop to avoid one vertexAngle computation too many
250   xcoords[vertices.length-1]=(int)Math.round(pc[i].cx+pc[i].cs*Math.cos(vertexAngle)*vertices[vertices.length-1]);
251   ycoords[vertices.length-1]=(int)Math.round(pc[i].cy-pc[i].cs*Math.sin(vertexAngle)*vertices[vertices.length-1]);
252   pc[i].p=new Polygon(xcoords,ycoords,vertices.length);
253     }
254 
255     /**draw text associated with this glyph
256      *@param i camera index in the virtual space
257      */
258     void textDraw(Graphics2D g,int i){
259   if ((fontSizePolicy>=0) && (text!=null)) {
260       if (!text.equals("")){
261     //g.setColor(this.color);
262     textWidth=(int)g.getFontMetrics().getStringBounds(text,g).getWidth();
263     if ((fontSizePolicy==1) || ((fontSizePolicy==0) && (textWidth<2*pc[i].cs))) {
264         if (textPos==1){
265       //textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
266       g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy-pc[i].cs-4);
267         }
268         else if (textPos==-1){
269       textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
270       g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy+pc[i].cs+textHeight);
271         }
272         else {g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy);}
273     }
274     else if (fontSizePolicy==2) {  //modify font size to make string fit in glyph   THIS OPTION IS RATHER TIME CONSUMING
275         Font tf=new Font(g.getFont().getName(),g.getFont().getStyle(),g.getFont().getSize());
276         int s=1;
277         while (textWidth>2*pc[i].cs){
278       s=g.getFont().getSize()-2; if (s<0){s=0;break;}
279       g.setFont(new Font(tf.getName(),tf.getStyle(),s));
280       textWidth=(int)g.getFontMetrics().getStringBounds(text,g).getWidth();
281         }
282         if (s>0) {
283       if (textPos==1){
284           //textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
285           g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy-pc[i].cs-4);
286       }
287       else if (textPos==-1){
288           textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
289           g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy+pc[i].cs+textHeight);
290       }
291       else {g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy);}
292         }
293         g.setFont(tf);
294     }
295       }
296   }
297     }
298 
299     /**draw glyph 
300      *@param i camera index in the virtual space
301      */
302     public void draw(Graphics2D g,int vW,int vH,int i,Stroke stdS,AffineTransform stdT){
303   if (pc[i].cs>1){//repaint only if object is visible
304       if (filled) {
305     g.setColor(this.color);
306     g.fillPolygon(pc[i].p);
307       }
308       g.setColor(borderColor);
309       if (paintBorder){
310     if (stroke!=null) {
311         g.setStroke(stroke);
312         g.drawPolygon(pc[i].p);
313         g.setStroke(stdS);
314     }
315     else {
316         g.drawPolygon(pc[i].p);
317     }
318       }
319       this.textDraw(g,i);
320   }
321   else g.fillRect(pc[i].cx,pc[i].cy,1,1);
322     }
323 
324     /**
325      * returns a given VShape's area
326      */
327     public double getArea(){
328   long[] xcoordsForArea=new long[vertices.length];
329   long[] ycoordsForArea=new long[vertices.length];
330   float vertexAngle=orient;
331   for (int i=0;i<vertices.length-1;i++){
332       xcoordsForArea[i]=Math.round(vx+vs*Math.cos(vertexAngle)*vertices[i]);
333       ycoordsForArea[i]=Math.round(vy+vs*Math.sin(vertexAngle)*vertices[i]);
334       vertexAngle+=2*Math.PI/vertices.length;
335   }//last iteration outside to loop to avoid one vertexAngle computation too many
336   xcoordsForArea[vertices.length-1]=Math.round(vx+vs*Math.cos(vertexAngle)*vertices[vertices.length-1]);
337   ycoordsForArea[vertices.length-1]=Math.round(vy+vs*Math.sin(vertexAngle)*vertices[vertices.length-1]);
338   int j,k;
339   double res=0;
340   for (j=0;j<vertices.length;j++){
341       k=(j+1) % vertices.length;
342       res+=(xcoordsForArea[j]*ycoordsForArea[k]-ycoordsForArea[j]*xcoordsForArea[k]);
343   }
344   res=res/2.0;
345   return ((res<0) ? -res : res);
346     }
347 
348     /**
349      *return the double precision coordinates of this VShape's centroid
350      */
351     public Point2D.Double getPreciseCentroid(){
352   //compute polygon vertices
353   long[] xcoordsForArea=new long[vertices.length];
354   long[] ycoordsForArea=new long[vertices.length];
355   float vertexAngle=orient;
356   for (int i=0;i<vertices.length-1;i++){
357       xcoordsForArea[i]=Math.round(vx+vs*Math.cos(vertexAngle)*vertices[i]);
358       ycoordsForArea[i]=Math.round(vy+vs*Math.sin(vertexAngle)*vertices[i]);
359       vertexAngle+=2*Math.PI/vertices.length;
360   }//last iteration outside to loop to avoid one vertexAngle computation too many
361   xcoordsForArea[vertices.length-1]=Math.round(vx+vs*Math.cos(vertexAngle)*vertices[vertices.length-1]);
362   ycoordsForArea[vertices.length-1]=Math.round(vy+vs*Math.sin(vertexAngle)*vertices[vertices.length-1]);
363   //compute polygon area
364   int j,k;
365   double area=0;
366   for (j=0;j<vertices.length;j++){
367       k=(j+1) % vertices.length;
368       area+=(xcoordsForArea[j]*ycoordsForArea[k]-ycoordsForArea[j]*xcoordsForArea[k]);
369   }
370   area=area/2.0;
371   //area=((area<0) ? -area : area);  //do not do that!!! it can change the centroid's coordinates
372                                      //(-x,-y instead of x,y) depending on the order in which the
373                                      //sequence of vertex coords
374   //compute centroid
375   double factor=0;
376   double cx=0;
377   double cy=0;
378   for (j=0;j<vertices.length;j++){
379       k=(j+1) % vertices.length;
380       factor=xcoordsForArea[j]*ycoordsForArea[k]-xcoordsForArea[k]*ycoordsForArea[j];
381       cx+=(xcoordsForArea[j]+xcoordsForArea[k])*factor;
382       cy+=(ycoordsForArea[j]+ycoordsForArea[k])*factor;
383   }
384   area*=6.0;
385   factor=1/area;
386   cx*=factor;
387   cy*=factor;
388   Point2D.Double res=new Point2D.Double(cx,cy);
389   return res;
390     }
391 
392     /**
393      *return the coordinates of this VShape's centroid in virtual space
394      */
395     public LongPoint getCentroid(){
396   Point2D.Double p2dd=this.getPreciseCentroid();
397   return new LongPoint(Math.round(p2dd.getX()),Math.round(p2dd.getY()));
398     }
399 
400     /**returns a clone of this object (only basic information is cloned for now: shape, orientation, position, size)*/
401     public Object clone(){
402   VShape res=new VShape(vx,vy,0,vs,(float[])vertices.clone(),color,orient);
403   res.borderColor=this.borderColor;
404   res.selectedColor=this.selectedColor;
405   res.mouseInsideColor=this.mouseInsideColor;
406   res.bColor=this.bColor;
407   return res;
408     }
409 
410 }