Source code: com/xerox/VTM/glyphs/RectangleNR.java
1 /* FILE: RectangleNR.java
2 * DATE OF CREATION: Thu Dec 05 13:53:36 2002
3 * AUTHOR : Emmanuel Pietriga (emmanuel@w3.org)
4 * MODIF: Thu Jul 10 16:20:55 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.Stroke;
15 import java.awt.geom.AffineTransform;
16 import java.lang.Math;
17 import java.util.Vector;
18 import com.xerox.VTM.engine.*;
19 import com.xerox.VTM.glyphs.Glyph;
20 import com.xerox.VTM.glyphs.RectangularShape;
21 import com.xerox.VTM.glyphs.ProjRectangle;
22
23 /**
24 * RectangleNR - not sensitive to zoom (will keep its size no matter the camera's altitude) - cannot be reoriented <br> used for instance to create resizing handles
25 * @author Emmanuel Pietriga
26 **/
27
28 public class RectangleNR extends Glyph implements RectangularShape,Cloneable {
29
30 /**half width and height in virtual space*/
31 long vw,vh;
32 /**aspect ratio (width divided by height)*/
33 float ar;
34
35 ProjRectangle[] pc;
36
37 public RectangleNR(){
38 vx=0;
39 vy=0;
40 vz=0;
41 vw=5;
42 vh=5;
43 computeSize();
44 ar=(float)vw/(float)vh;
45 orient=0;
46 setColor(Color.black);
47 setBorderColor(Color.black);
48 }
49
50 /**
51 *@param x coordinate in virtual space
52 *@param y coordinate in virtual space
53 *@param z altitude
54 *@param w half width in virtual space
55 *@param h half height in virtual space
56 *@param c fill color
57 */
58 public RectangleNR(long x,long y,float z,long w,long h,Color c){
59 vx=x;
60 vy=y;
61 vz=z;
62 vw=w;
63 vh=h;
64 computeSize();
65 if (vw==0 && vh==0){ar=1.0f;}
66 else {ar=(float)vw/(float)vh;}
67 //if (vh!=0){ar=vw/vh;}else{ar=0;}
68 orient=0;
69 setColor(c);
70 setBorderColor(Color.black);
71 }
72
73 /**called when glyph is created in order to create the initial set of projected coordinates wrt the number of cameras in the space
74 *@param nbCam current number of cameras in the virtual space
75 */
76 public void initCams(int nbCam){
77 pc=new ProjRectangle[nbCam];
78 for (int i=0;i<nbCam;i++){
79 pc[i]=new ProjRectangle();
80 pc[i].cw=(int)vw;
81 pc[i].ch=(int)vh;
82 }
83 }
84
85 /**used internally to create new projected coordinates to use with the new camera
86 *@param verifIndex camera index, just to be sure that the number of projected coordinates is consistent with the number of cameras
87 */
88 public void addCamera(int verifIndex){
89 if (pc!=null){
90 if (verifIndex==pc.length){
91 ProjRectangle[] ta=pc;
92 pc=new ProjRectangle[ta.length+1];
93 for (int i=0;i<ta.length;i++){
94 pc[i]=ta[i];
95 }
96 pc[pc.length-1]=new ProjRectangle();
97 pc[pc.length-1].cw=(int)vw;
98 pc[pc.length-1].ch=(int)vh;
99 }
100 else {System.err.println("RectangleNR:Error while adding camera "+verifIndex);}
101 }
102 else {
103 if (verifIndex==0){
104 pc=new ProjRectangle[1];
105 pc[0]=new ProjRectangle();
106 pc[0].cw=(int)vw;
107 pc[0].ch=(int)vh;
108 }
109 else {System.err.println("RectangleNR:Error while adding camera "+verifIndex);}
110 }
111 }
112
113 /**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*/
114 public void removeCamera(int index){
115 pc[index]=null;
116 }
117
118 /**reset prevMouseIn for projected coordinates nb i*/
119 public void resetMouseIn(int i){
120 if (pc[i]!=null){pc[i].prevMouseIn=false;}
121 }
122
123 /**get orientation*/
124 public float getOrient(){return 0;}
125
126 /**set orientation (absolute) - has no effect*/
127 public void orientTo(float angle){}
128
129 /**get size (bounding circle radius)*/
130 public float getSize(){return size;}
131
132 /**get half width*/
133 public long getWidth(){return vw;}
134
135 /**get half height*/
136 public long getHeight(){return vh;}
137
138 /**compute size (bounding circle radius)*/
139 void computeSize(){
140 size=(float)Math.sqrt(Math.pow(vw,2)+Math.pow(vh,2));
141 }
142
143 /**set absolute size by setting bounding circle radius*/
144 public void sizeTo(float radius){ //new bounding circle radius
145 size=radius;
146 vw=(long)Math.round((size*ar)/(Math.sqrt(Math.pow(ar,2)+1)));
147 vh=(long)Math.round((size)/(Math.sqrt(Math.pow(ar,2)+1)));
148 updateProjectedWH();
149 try{vsm.repaintNow();}catch(NullPointerException e){}
150 }
151
152 /**set absolute half width*/
153 public void setWidth(long w){
154 vw=w;
155 computeSize();
156 updateProjectedWH();
157 try{vsm.repaintNow();}catch(NullPointerException e){}
158 }
159
160 /**set absolute half height*/
161 public void setHeight(long h){
162 vh=h;
163 computeSize();
164 updateProjectedWH();
165 try{vsm.repaintNow();}catch(NullPointerException e){}
166 }
167
168 /**multiply bounding circle radius by factor*/
169 public void reSize(float factor){//resizing factor
170 size*=factor;
171 vw=(long)Math.round((size*ar)/(Math.sqrt(Math.pow(ar,2)+1)));
172 vh=(long)Math.round((size)/(Math.sqrt(Math.pow(ar,2)+1)));
173 updateProjectedWH();
174 try{vsm.repaintNow();}catch(NullPointerException e){}
175 }
176
177 private void updateProjectedWH(){
178 if (pc!=null){
179 for (int i=0;i<pc.length;i++){
180 try {
181 pc[i].cw=(int)vw;
182 pc[i].ch=(int)vh;
183 }//some pc[i] might be null (if cameras were deleted from the virtual space)
184 catch (NullPointerException e){}
185 }
186 }
187 }
188
189 /**used to find out if it is necessary to project and draw the glyph in the current view*/
190 public boolean drawMe(long w1,long h1,long w2,long h2,int i){//i should be the camera's index (used only by some glyph classes redefining this method)
191 if ((vx>=w2) && (vx<=w1) && (vy>=h2) && (vy<=h1)){ //if glyph hotspot is in the region, it is obviously visible
192 return true;
193 }
194 else {
195 if (((vx-pc[i].cw)<=w1) && ((vx+pc[i].cw)>=w2) && ((vy-pc[i].ch)<=h1) && ((vy+pc[i].ch)>=h2)){
196 //if glyph is at least partially in region (we approximate using the glyph
197 // bounding circle, meaning that some glyphs not actually visible can
198 return true; //be projected and drawn (but they won't be displayed))
199 }
200 else return false; //otherwise the glyph is not visible
201 }
202 }
203
204 /**used to find out if glyph completely fills the view (in which case it is not necessary to repaint objects at a lower altitude)*/
205 public boolean fillsView(long w,long h,int camIndex){//width and height of view - pc[i].c? are JPanel coords
206 if ((w<=pc[camIndex].cx+pc[camIndex].cw) && (0>=pc[camIndex].cx-pc[camIndex].cw) && (h<=pc[camIndex].cy+pc[camIndex].ch) && (0>=pc[camIndex].cy-pc[camIndex].ch)){return true;}
207 else {return false;}
208 }
209
210 /**detects whether the given point is inside this glyph or not
211 *@param x EXPECTS PROJECTED JPanel COORDINATE
212 *@param y EXPECTS PROJECTED JPanel COORDINATE
213 */
214 public boolean coordInside(int x,int y,int camIndex){
215 if ((x>=(pc[camIndex].cx-pc[camIndex].cw)) && (x<=(pc[camIndex].cx+pc[camIndex].cw)) && (y>=(pc[camIndex].cy-pc[camIndex].ch)) && (y<=(pc[camIndex].cy+pc[camIndex].ch))){return true;}
216 else {return false;}
217 }
218
219 /**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)*/
220 public int mouseInOut(int x,int y,int camIndex){
221 if (coordInside(x,y,camIndex)){//if the mouse is inside the glyph
222 if (!pc[camIndex].prevMouseIn){//if it was not inside it last time, mouse has entered the glyph
223 pc[camIndex].prevMouseIn=true;
224 return 1;
225 }
226 else {return 0;} //if it was inside last time, nothing has changed
227 }
228 else{//if the mouse is not inside the glyph
229 if (pc[camIndex].prevMouseIn){//if it was inside it last time, mouse has exited the glyph
230 pc[camIndex].prevMouseIn=false;
231 return -1;
232 }
233 else {return 0;} //if it was not inside last time, nothing has changed
234 }
235 }
236
237 /**project shape in camera coord sys prior to actual painting*/
238 public void project(Camera c,ViewPanel v){
239 int i=c.getIndex();
240 coef=(float)(c.focal/(c.focal+c.altitude));
241 //find coordinates of object's geom center wrt to camera center and project
242 pc[i].cx=Math.round((vx-c.posx)*coef);
243 pc[i].cy=Math.round((vy-c.posy)*coef);
244 //translate in JPanel coords
245 pc[i].cx=(v.getSize().width/2)+pc[i].cx;
246 pc[i].cy=(v.getSize().height/2)-pc[i].cy;
247 }
248
249 /**draw text associated with this glyph
250 *@param i camera index in the virtual space
251 */
252 void textDraw(Graphics2D g,int i){
253 if ((fontSizePolicy>=0) && (text!=null)) { //if appli wants text drawn and if there is a text for this glyph
254 if (!text.equals("")){
255 //g.setColor(this.color);
256 textWidth=(int)g.getFontMetrics().getStringBounds(text,g).getWidth(); //get width of this text : draw if conditions are met
257 if ((fontSizePolicy==1) || ((fontSizePolicy==0) && (textWidth<2*pc[i].cw))) {
258 if (textPos==1){
259 //textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
260 g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy-pc[i].ch-4); //-4 for things like g, j, p, q, y
261 }
262 else if (textPos==-1){
263 textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
264 g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy+pc[i].ch+textHeight);
265 }
266 else {g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy);}
267 }
268 else if (fontSizePolicy==2) { //modify font size to make string fit in glyph THIS OPTION IS RATHER TIME CONSUMING
269 Font tf=new Font(g.getFont().getName(),g.getFont().getStyle(),g.getFont().getSize());
270 int s=1;
271 while (textWidth>2*pc[i].cw){
272 s=g.getFont().getSize()-2; if (s<0){s=0;break;}
273 g.setFont(new Font(tf.getName(),tf.getStyle(),s));
274 textWidth=(int)g.getFontMetrics().getStringBounds(text,g).getWidth();
275 }
276 if (s>0) {
277 if (textPos==1){
278 //textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
279 g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy-pc[i].ch-4);
280 }
281 else if (textPos==-1){
282 textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
283 g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy+pc[i].ch+textHeight);
284 }
285 else {g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy);}
286 }
287 g.setFont(tf);
288 }
289 }
290 }
291 }
292
293 /**draw glyph
294 *@param i camera index in the virtual space
295 *@param vW view width - used to determine if contour should be drawn or not (when it is dashed and object too big)
296 *@param vH view height - used to determine if contour should be drawn or not (when it is dashed and object too big)
297 */
298 public void draw(Graphics2D g,int vW,int vH,int i,Stroke stdS,AffineTransform stdT){
299 if ((pc[i].cw>1) && (pc[i].ch>1)) {//repaint only if object is visible
300 if (filled) {
301 g.setColor(this.color);
302 g.fillRect(pc[i].cx-pc[i].cw,pc[i].cy-pc[i].ch,2*pc[i].cw,2*pc[i].ch);
303 }
304 g.setColor(borderColor);
305 if (paintBorder){
306 g.drawRect(pc[i].cx-pc[i].cw,pc[i].cy-pc[i].ch,2*pc[i].cw-1,2*pc[i].ch-1);
307 }
308 this.textDraw(g,i);
309 }
310 else if ((pc[i].cw<=1) ^ (pc[i].ch<=1)) {//repaint only if object is visible (^ means xor)
311 g.setColor(this.color);
312 if (pc[i].cw<=1){
313 g.fillRect(pc[i].cx,pc[i].cy-pc[i].ch,1,2*pc[i].ch);
314 }
315 else if (pc[i].ch<=1){
316 g.fillRect(pc[i].cx-pc[i].cw,pc[i].cy,2*pc[i].cw,1);
317 }
318 }
319 else g.fillRect(pc[i].cx,pc[i].cy,1,1);
320 }
321
322 /**returns a clone of this object (only basic information is cloned for now: shape, orientation, position, size)*/
323 public Object clone(){
324 RectangleNR res=new RectangleNR(vx,vy,0,vw,vh,color);
325 res.borderColor=this.borderColor;
326 res.selectedColor=this.selectedColor;
327 res.mouseInsideColor=this.mouseInsideColor;
328 res.bColor=this.bColor;
329 return res;
330 }
331
332 }