Source code: com/xerox/VTM/glyphs/VQdCurve.java
1 /* FILE: VQdCurve.java
2 * DATE OF CREATION: Oct 02 2001
3 * AUTHOR : Emmanuel Pietriga (emmanuel.pietriga@xrce.xerox.com)
4 * MODIF: Thu Jul 10 16:55: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.Font;
24 import java.awt.Graphics2D;
25 import java.awt.Polygon;
26 import java.awt.Stroke;
27 import java.awt.geom.*;
28 import java.awt.geom.AffineTransform;
29 import java.lang.Math;
30 import java.util.Vector;
31 import com.xerox.VTM.engine.*;
32
33 /**
34 * Quadratic Curve - can be reoriented - CANNOT DETECT ENTRY/EXIT in curves, even when filled (they look as, but are not, closed shapes) <br> a quadratic curve is a curved segment that has two endpoints and one control point. The control point determines the shape of the curve by controlling both of the endpoint tangent vectors <br> for this particular glyph, vx and vy correspond to the center of the imaginary segment linking the curve's start and end points <br> the coordinates of the control point are expressed w.r.t this point in polar coordinates (orient=0 on segment linking start and end points, meaning that if orient=0, start control and end points are aligned)
35 * @author Emmanuel Pietriga
36 **/
37
38 public class VQdCurve extends Glyph implements Cloneable {
39
40 /**size (distance between start and end point)*/
41 long vs;
42
43 /**control point, polar coordinates - origin is vx,vy - orient=0 on segment linking start and end points*/
44 long vrad;
45 float ang;
46
47 /**array of projected coordinates - index of camera in virtual space is equal to index of projected coords in this array*/
48 ProjQdCurve[] pc;
49
50 /**
51 *@param x coordinate in virtual space
52 *@param y coordinate in virtual space
53 *@param z altitude
54 *@param s size (width=height) in virtual space
55 *@param c fill color
56 *@param or orientation
57 *@param ctrlDist1 distance of control point (polar coords origin=(x,y) provided in this constructor)
58 *@param or1 orientation of control point (polar coords origin=(x,y) provided in this constructor)
59 */
60 public VQdCurve(long x,long y,float z,long s,Color c,float or,long ctrlDist1,float or1){
61 vx=x;
62 vy=y;
63 vz=z;
64 vs=s;
65 sensit=false;
66 orient=or;
67 vrad=ctrlDist1;
68 ang=or1;
69 computeSize();
70 filled=false;
71 setColor(c);
72 setBorderColor(bColor);
73 }
74
75 /**set position of control point (polar coords w.r.t center of segment linking start and end points)*/
76 public void setCtrlPoint(long d,float o){
77 vrad=d;
78 // if (o>2*Math.PI){ang=o%2*(float)Math.PI;}
79 // else {ang=o;}
80 ang=o;
81 try{vsm.repaintNow();}catch(NullPointerException e){/*System.err.println("VSM null in Glyph "+e);*/}
82 }
83
84 /**get distance from center of segment linking start and end points to control point (polar coords)*/
85 public long getCtrlPointRadius(){return vrad;}
86
87 /**get orientation of control point (polar coords)*/
88 public float getCtrlPointAngle(){return ang;}
89
90 /**called when glyph is created in order to create the initial set of projected coordinates wrt the number of cameras in the space
91 *@param nbCam current number of cameras in the virtual space
92 */
93 public void initCams(int nbCam){
94 pc=new ProjQdCurve[nbCam];
95 for (int i=0;i<nbCam;i++){
96 pc[i]=new ProjQdCurve();
97 }
98 }
99
100 /**used internally to create new projected coordinates to use with the new camera
101 *@param verifIndex camera index, just to be sure that the number of projected coordinates is consistent with the number of cameras
102 */
103 public void addCamera(int verifIndex){
104 if (pc!=null){
105 if (verifIndex==pc.length){
106 ProjQdCurve[] ta=pc;
107 pc=new ProjQdCurve[ta.length+1];
108 for (int i=0;i<ta.length;i++){
109 pc[i]=ta[i];
110 }
111 pc[pc.length-1]=new ProjQdCurve();
112 }
113 else {System.err.println("VQdCurve:Error while adding camera "+verifIndex);}
114 }
115 else {
116 if (verifIndex==0){
117 pc=new ProjQdCurve[1];
118 pc[0]=new ProjQdCurve();
119 }
120 else {System.err.println("VQdCurve:Error while adding camera "+verifIndex);}
121 }
122 }
123
124 /**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*/
125 public void removeCamera(int index){
126 pc[index]=null;
127 }
128
129 /**reset prevMouseIn for projected coordinates nb i*/
130 public void resetMouseIn(int i){
131 if (pc[i]!=null){pc[i].prevMouseIn=false;}
132 }
133
134 /**get orientation*/
135 public float getOrient(){return orient;}
136
137 /**set orientation (absolute)*/
138 public void orientTo(float angle){
139 orient=angle;
140 try{vsm.repaintNow();}catch(NullPointerException e){/*System.err.println("VSM null in Glyph "+e);*/}
141 // vsm.constMgr.suggestAValue(this.ID,"or",orient);
142 //computeOrientCoords();
143 }
144
145 /**set orientation (absolute)*/
146 // public void orientToNS(float angle){
147 // orient=angle;
148 // //computeOrientCoords();
149 // }
150
151 /**get size (bounding circle radius)*/
152 public float getSize(){return size;}
153
154 /**compute curve from VTM object model*/
155 void computeSize(){
156 size=(float)vs;
157 }
158
159 /**set absolute size by setting bounding circle radius*/
160 public void sizeTo(float radius){
161 vrad=Math.round(vrad*radius/size);
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 // vrad=Math.round(vrad*radius/size);
171 // size=radius;
172 // vs=Math.round(size);
173 // }
174
175 /**multiply bounding circle radius by factor*/
176 public void reSize(float factor){
177 size*=factor;
178 vs=(long)Math.round(size);
179 vrad=Math.round(vrad*factor);
180 try{vsm.repaintNow();}catch(NullPointerException e){/*System.err.println("VSM null in Glyph "+e);*/}
181 // vsm.constMgr.suggestAValue(this.ID,"sz",size);
182 }
183
184 /**used to find out if glyph completely fills the view (in which case it is not necessary to repaint objects at a lower altitude)*/
185 public boolean fillsView(long w,long h,int camIndex){
186 return false;
187 }
188
189 /**detects whether the given point is inside this glyph or not
190 *@param x EXPECTS PROJECTED JPanel COORDINATE
191 *@param y EXPECTS PROJECTED JPanel COORDINATE
192 */
193 public boolean coordInside(int x,int y,int camIndex){
194 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 return 0;
200 }
201
202 /**project shape in camera coord sys prior to actual painting*/
203 public void project(Camera c,ViewPanel v){
204 int i=c.getIndex();
205 coef=(float)(c.focal/(c.focal+c.altitude));
206 //find coordinates of object's geom center wrt to camera center and project
207 pc[i].cx=Math.round((vx-c.posx)*coef);
208 pc[i].cy=Math.round((vy-c.posy)*coef);
209 //translate in JPanel coords
210 pc[i].cx=(v.getSize().width/2)+pc[i].cx;
211 pc[i].cy=(v.getSize().height/2)-pc[i].cy;
212 //project height and construct curve
213 pc[i].cs=Math.round(vs*coef);
214 if (pc[i].cs>1){
215 pc[i].start.setLocation(pc[i].cx+pc[i].cs*Math.cos(orient),pc[i].cy+pc[i].cs*Math.sin(orient));
216 pc[i].end.setLocation(pc[i].cx-pc[i].cs*Math.cos(orient),pc[i].cy-pc[i].cs*Math.sin(orient));
217 pc[i].ctrl.setLocation(pc[i].cx+(int)Math.round(coef*vrad*Math.cos(orient-ang)),pc[i].cy+(int)Math.round(coef*vrad*Math.sin(orient-ang)));
218 pc[i].quad.setCurve(pc[i].start,pc[i].ctrl,pc[i].end);
219 }
220 }
221
222 /**draw text associated with this glyph
223 *@param i camera index in the virtual space
224 */
225 void textDraw(Graphics2D g,int i){
226 if ((fontSizePolicy>=0) && (text!=null)) {
227 if (!text.equals("")){
228 //g.setColor(this.color);
229 textWidth=(int)g.getFontMetrics().getStringBounds(text,g).getWidth();
230 if ((fontSizePolicy==1) || ((fontSizePolicy==0) && (textWidth<2*pc[i].cs))) {
231 if (textPos==1){
232 //textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
233 g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy-pc[i].cs-4);
234 }
235 else if (textPos==-1){
236 textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
237 g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy+pc[i].cs+textHeight);
238 }
239 else {g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy);}
240 }
241 else if (fontSizePolicy==2) { //modify font size to make string fit in glyph THIS OPTION IS RATHER TIME CONSUMING
242 Font tf=new Font(g.getFont().getName(),g.getFont().getStyle(),g.getFont().getSize());
243 int s=1;
244 while (textWidth>2*pc[i].cs){
245 s=g.getFont().getSize()-2; if (s<0){s=0;break;}
246 g.setFont(new Font(tf.getName(),tf.getStyle(),s));
247 textWidth=(int)g.getFontMetrics().getStringBounds(text,g).getWidth();
248 }
249 if (s>0) {
250 if (textPos==1){
251 //textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
252 g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy-pc[i].cs-4);
253 }
254 else if (textPos==-1){
255 textHeight=(int)g.getFontMetrics().getStringBounds(text,g).getHeight();
256 g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy+pc[i].cs+textHeight);
257 }
258 else {g.drawString(text,pc[i].cx-textWidth/2,pc[i].cy);}
259 }
260 g.setFont(tf);
261 }
262 }
263 }
264 }
265
266 /**draw glyph
267 *@param i camera index in the virtual space
268 */
269 public void draw(Graphics2D g,int vW,int vH,int i,Stroke stdS,AffineTransform stdT){
270 if (pc[i].cs>1){//repaint only if object is visible
271 if (filled) {
272 g.setColor(this.color);
273 g.fill(pc[i].quad);
274 g.setColor(borderColor);
275 g.drawLine((int)pc[i].start.x,(int)pc[i].start.y,(int)pc[i].end.x,(int)pc[i].end.y);
276 }
277 else {//if not filled (common case), paint curve with main color, not border color
278 g.setColor(this.color);
279 }
280 if (stroke!=null) {
281 g.setStroke(stroke);
282 g.draw(pc[i].quad);
283 g.setStroke(stdS);
284 }
285 else {
286 g.draw(pc[i].quad);
287 }
288 this.textDraw(g,i);
289 }
290 else g.fillRect(pc[i].cx,pc[i].cy,1,1);
291 }
292
293 /**returns a clone of this object (only basic information is cloned for now: shape, orientation, position, size)*/
294 public Object clone(){
295 VQdCurve res=new VQdCurve(vx,vy,0,vs,color,orient,vrad,ang);
296 res.borderColor=this.borderColor;
297 res.selectedColor=this.selectedColor;
298 res.mouseInsideColor=this.mouseInsideColor;
299 res.bColor=this.bColor;
300 return res;
301 }
302
303 }
304