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