1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2007-08-05 16:26:58 -0500 (Sun, 05 Aug 2007) $
4  * $Revision: 8032 $
5  *
6  * Copyright (C) 2002-2006  Miguel, Jmol Development, www.jmol.org
7  *
8  * Contact: jmol-developers@lists.sf.net
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 package org.jmol.render;
25 
26 
27 import org.jmol.modelset.TickInfo;
28 import org.jmol.script.T;
29 import org.jmol.util.Font;
30 import org.jmol.util.GData;
31 
32 import javajs.util.P3;
33 import javajs.util.P3i;
34 import javajs.util.PT;
35 
36 import org.jmol.util.SimpleUnitCell;
37 import javajs.util.V3;
38 
39 public abstract class FontLineShapeRenderer extends ShapeRenderer {
40 
41   // Axes, Bbcage, Measures, Uccage, also Sticks, Echo, Measures, Labels
42 
43   protected float imageFontScaling;
44   protected P3 tickA, tickB, tickAs, tickBs;
45   protected Font font3d;
46 
47   final protected P3i pt0i = new P3i();
48   final protected P3i pt2i = new P3i();
49   protected final P3i s1 = new P3i();
50   protected final P3i s2 = new P3i();
51 
52   final protected P3 pointT = new P3();
53   final protected P3 pointT2 = new P3();
54   final protected P3 pointT3 = new P3();
55   final protected V3 vectorT = new V3();
56   final protected V3 vectorT2 = new V3();
57   final protected V3 vectorT3 = new V3();
58 
59   //final Rectangle box = new Rectangle();
60 
61   protected TickInfo tickInfo;
62 
63   protected boolean draw000 = true;
64   protected int width;
65   protected byte endcap = GData.ENDCAPS_SPHERICAL;
66   protected P3 pt0 = new P3();
67   protected P3 pt1 = new P3();
68 
69   //protected void clearBox() {
70   //  box.setBounds(0, 0, 0, 0);
71   //}
72 
getDiameter(int z, int mad10OrPixels)73   protected int getDiameter(int z, int mad10OrPixels) {
74     int diameter;
75     boolean isMad10 = (mad10OrPixels > 20);
76     switch (exportType) {
77     case GData.EXPORT_CARTESIAN:
78       diameter = (isMad10 ? mad10OrPixels
79           : (int) Math.floor(vwr.tm.unscaleToScreen(z, mad10OrPixels * 2/10f) * 1000));
80       break;
81     default:
82       if (isMad10) {
83         // mad
84         diameter = (int) vwr.tm.scaleToScreen(z, mad10OrPixels/10);
85       } else {
86         // pixels, and that's what we want
87         if (g3d.isAntialiased())
88           mad10OrPixels += mad10OrPixels;
89         diameter = mad10OrPixels;
90       }
91     }
92     return diameter;
93   }
94 
renderLine(P3 p0, P3 p1, int diameter, boolean drawTicks)95   protected void renderLine(P3 p0, P3 p1, int diameter,
96                             boolean drawTicks) {
97     // used by Bbcage, Uccage, and axes
98     if (diameter < 0)
99       g3d.drawDashedLineBits(8, 4, p0, p1);
100     else
101       g3d.fillCylinderBits(endcap, diameter, p0, p1);
102     if (!drawTicks || tickInfo == null)
103       return;
104     // AtomA and AtomB molecular coordinates must be set previously
105     checkTickTemps();
106     tickAs.setT(p0);
107     tickBs.setT(p1);
108     drawTicks(diameter, true);
109   }
110 
checkTickTemps()111   protected void checkTickTemps() {
112     if (tickA == null) {
113       tickA = new P3();
114       tickB = new P3();
115       tickAs = new P3();
116       tickBs = new P3();
117     }
118   }
119 
drawTicks(int diameter, boolean withLabels)120   protected void drawTicks(int diameter, boolean withLabels) {
121     if (Float.isNaN(tickInfo.first))
122       tickInfo.first = 0;
123     drawTicks2(tickInfo.ticks.x, 8, diameter, (!withLabels ? null : tickInfo.tickLabelFormats == null ?
124             new String[] { "%0.2f" } : tickInfo.tickLabelFormats));
125     drawTicks2(tickInfo.ticks.y, 4, diameter, null);
126     drawTicks2(tickInfo.ticks.z, 2, diameter, null);
127   }
128 
drawTicks2(float dx, int length, int diameter, String[] formats)129   private void drawTicks2(float dx, int length,
130                          int diameter, String[] formats) {
131 
132     if (dx == 0)
133       return;
134     /*
135     boolean isOut = true;
136     if (dx < 0) {
137       isOut = false;
138       dx = -dx;
139     }
140     */
141     if (g3d.isAntialiased())
142       length *= 2;
143     // perpendicular to line on screen:
144     vectorT2.set(tickBs.x, tickBs.y, 0);
145     vectorT.set(tickAs.x, tickAs.y, 0);
146     vectorT2.sub(vectorT);
147     if (vectorT2.length() < 50)
148       return;
149 
150     float signFactor = tickInfo.signFactor;
151     vectorT.sub2(tickB, tickA);
152     float d0 = vectorT.length();
153     if (tickInfo.scale != null) {
154       if (Float.isNaN(tickInfo.scale.x)) { // unitcell
155         float a = vwr.getUnitCellInfo(SimpleUnitCell.INFO_A);
156         if (!Float.isNaN(a))
157           vectorT.set(vectorT.x / a, vectorT.y
158               / vwr.getUnitCellInfo(SimpleUnitCell.INFO_B), vectorT.z
159               / vwr.getUnitCellInfo(SimpleUnitCell.INFO_C));
160       } else {
161         vectorT.set(vectorT.x * tickInfo.scale.x, vectorT.y * tickInfo.scale.y,
162             vectorT.z * tickInfo.scale.z);
163       }
164     }
165     // d is in scaled units
166     float d = vectorT.length() + 0.0001f * dx;
167     if (d < dx)
168       return;
169     float f = dx / d * d0 / d;
170     vectorT.scale(f);
171     float dz = (tickBs.z - tickAs.z) / (d / dx);
172     // TODO: z-value error: ONLY APPROXIMATE
173     // vectorT is now the length of the spacing between ticks
174     // but we may have an offset.
175     d += tickInfo.first;
176     float p = ((int) Math.floor(tickInfo.first / dx)) * dx - tickInfo.first;
177     pointT.scaleAdd2(p / dx, vectorT, tickA);
178     p += tickInfo.first;
179     float z = tickAs.z;
180     if (diameter < 0)
181       diameter = 1;
182     vectorT2.set(-vectorT2.y, vectorT2.x, 0);
183     vectorT2.scale(length / vectorT2.length());
184     P3 ptRef = tickInfo.reference; // not implemented
185     if (ptRef == null) {
186       pointT3.setT(vwr.getBoundBoxCenter());
187       if (vwr.g.axesMode == T.axeswindow) {
188         pointT3.add3(1, 1, 1);
189       }
190     } else {
191       pointT3.setT(ptRef);
192     }
193     tm.transformPtScr(pointT3, pt2i);
194     //too annoying! float tx = vectorT2.x * ((ptA.screenX + ptB.screenX) / 2 - pt2.x);
195     //float ty = vectorT2.y * ((ptA.screenY + ptB.screenY) / 2 - pt2.y);
196     //if (tx + ty < -0.1)
197       //vectorT2.scale(-1);
198     //if (!isOut)
199       //vectorT2.scale(-1);
200     boolean horizontal = (Math.abs(vectorT2.x / vectorT2.y) < 0.2);
201     boolean centerX = horizontal;
202     boolean centerY = !horizontal;
203     boolean rightJustify = !centerX && (vectorT2.x < 0);
204     boolean drawLabel = (formats != null && formats.length > 0);
205     int x, y;
206     Object[] val = new Object[1];
207     int i = (draw000 ? 0 : -1);
208     while (p < d) {
209       if (p >= tickInfo.first) {
210         pointT2.setT(pointT);
211         tm.transformPt3f(pointT2, pointT2);
212         drawLine((int) Math.floor(pointT2.x), (int) Math.floor(pointT2.y), (int) z,
213             (x = (int) Math.floor(pointT2.x + vectorT2.x)),
214             (y = (int) Math.floor(pointT2.y + vectorT2.y)), (int) z, diameter);
215         if (drawLabel && (draw000 || p != 0)) {
216           val[0] = Float.valueOf((p == 0 ? 0 : p * signFactor));
217           String s = PT.sprintf(formats[i % formats.length], "f", val);
218           drawString(x, y, (int) z, 4, rightJustify, centerX, centerY,
219               (int) Math.floor(pointT2.y), s);
220         }
221       }
222       pointT.add(vectorT);
223       p += dx;
224       z += dz;
225       i++;
226     }
227   }
228 
drawLine(int x1, int y1, int z1, int x2, int y2, int z2, int diameter)229   protected int drawLine(int x1, int y1, int z1, int x2, int y2, int z2,
230                          int diameter) {
231     return drawLine2(x1, y1, z1, x2, y2, z2, diameter);
232   }
233 
drawLine2(int x1, int y1, int z1, int x2, int y2, int z2, int diameter)234   protected int drawLine2(int x1, int y1, int z1, int x2, int y2, int z2, int diameter) {
235     pt0.set(x1, y1, z1);
236     pt1.set(x2, y2, z2);
237     if (dotsOrDashes) {
238       if (dashDots != null)
239         drawDashed(x1, y1, z1, x2, y2, z2, dashDots);
240     } else {
241       if (diameter < 0) {
242         g3d.drawDashedLineBits(8, 4, pt0 , pt1);
243         return 1;
244       }
245       g3d.fillCylinderBits(GData.ENDCAPS_FLAT, diameter, pt0, pt1);
246     }
247     return (diameter + 1) / 2;
248   }
249 
drawString(int x, int y, int z, int radius, boolean rightJustify, boolean centerX, boolean centerY, int yRef, String sVal)250   protected void drawString(int x, int y, int z, int radius,
251                             boolean rightJustify, boolean centerX,
252                             boolean centerY, int yRef, String sVal) {
253     if (sVal == null)
254       return;
255     int width = font3d.stringWidth(sVal);
256     int height = font3d.getAscent();
257     int xT = x;
258     if (rightJustify)
259       xT -= radius / 2 + 2 + width;
260     else if (centerX)
261       xT -= radius / 2 + 2 + width / 2;
262     else
263       xT += radius / 2 + 2;
264     int yT = y;
265     if (centerY)
266       yT += height / 2;
267     else if (yRef == 0 || yRef < y)
268       yT += height;
269     else
270       yT -= radius / 2;
271     int zT = z - radius - 2;
272     if (zT < 1)
273       zT = 1;
274     //if (!box.contains(xT, yT) && !box.contains(xT + width, yT)
275       //  && !box.contains(xT, yT + height)
276         //&& !box.contains(xT + width, yT + height)) {
277       g3d.drawString(sVal, font3d, xT, yT, zT, zT, (short) 0);
278    //   box.setBounds(xT, yT, width, height);
279    // }
280   }
281 
282   protected final static int[] dashes =   { 12, 0, 0, 2, 5, 7, 10 };
283   protected final static int[] hDashes =  { 10, 7, 6, 1, 3, 4, 6, 7, 9 };
284 
285   protected final static int[] ndots =  { 0, 3, 1000 };
286   protected final static int[] sixdots =  { 12, 3, 6, 1, 3, 5, 7, 9, 11 };
287   protected final static int[] fourdots = { 13, 3, 5, 2, 5, 8, 11 };
288   protected final static int[] twodots =  { 12, 3, 4, 3, 9 };
289 
290   protected short colixA, colixB;
291   protected boolean dotsOrDashes;
292   protected int[] dashDots;
293 
drawDashed(int xA, int yA, int zA, int xB, int yB, int zB, int[] array)294   protected void drawDashed(int xA, int yA, int zA, int xB, int yB, int zB,
295                           int[] array) {
296     if (array == null || width < 0)
297       return;
298     // for sticks and measures
299     float f = array[0];
300     float dx = xB - xA;
301     float dy = yB - yA;
302     float dz = zB - zA;
303     int n = 0;
304     boolean isNdots = (array == ndots);
305     boolean isDots = (isNdots || array == sixdots);
306     if (isDots) {
307       float d2 = (dx * dx + dy * dy)  / (width * width);
308       if (isNdots) {
309         f = (float) (Math.sqrt(d2) / 1.5);
310         n = (int) f + 2;
311       } else if (d2 < 8) {
312         array = twodots;
313       } else if (d2 < 32) {
314         array = fourdots;
315       }
316     }
317     int ptS = array[1];
318     int ptE = array[2];
319     short colixS = colixA;
320     short colixE = (ptE == 0 ? colixB : colixA);
321     if (n == 0)
322       n = array.length;
323     for (int i = 0, pt = 3; pt < n; pt++) {
324       i = (isNdots ? i + 1 : array[pt]);
325       int xS = (int) Math.floor(xA + dx * i / f);
326       int yS = (int) Math.floor(yA + dy * i / f);
327       int zS = (int) Math.floor(zA + dz * i / f);
328       if (isDots) {
329         s1.set(xS, yS, zS);
330         if (pt == ptS)
331           g3d.setC(colixA);
332         else if (pt == ptE)
333           g3d.setC(colixB);
334         g3d.fillSphereI(width, s1);
335         continue;
336       }
337       if (pt == ptS)
338         colixS = colixB;
339       i = array[++pt];
340       if (pt == ptE)
341         colixE = colixB;
342       int xE = (int) Math.floor(xA + dx * i / f);
343       int yE = (int) Math.floor(yA + dy * i / f);
344       int zE = (int) Math.floor(zA + dz * i / f);
345       fillCylinder(colixS, colixE, GData.ENDCAPS_FLAT, width, xS, yS, zS,
346           xE, yE, zE);
347     }
348   }
349 
350   protected boolean asLineOnly;
351 
fillCylinder(short colixA, short colixB, byte endcaps, int diameter, int xA, int yA, int zA, int xB, int yB, int zB)352   protected void fillCylinder(short colixA, short colixB, byte endcaps,
353                               int diameter, int xA, int yA, int zA, int xB,
354                               int yB, int zB) {
355     if (asLineOnly)
356       g3d.drawLine(colixA, colixB, xA, yA, zA, xB, yB, zB);
357     else
358       g3d.fillCylinderXYZ(colixA, colixB, endcaps,
359           (!isExport || mad == 1 ? diameter : mad),
360           xA, yA, zA, xB, yB, zB);
361   }
362 
363 
364 
365 
366 }
367