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