1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2019-08-18 15:32:43 -0500 (Sun, 18 Aug 2019) $ 4 * $Revision: 21995 $ 5 * 6 * Copyright (C) 2003-2005 The Jmol Development Team 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 25 package org.jmol.render; 26 27 import java.util.Hashtable; 28 import java.util.Map; 29 30 import org.jmol.api.JmolModulationSet; 31 import org.jmol.modelset.Measurement; 32 import org.jmol.modelset.MeasurementPending; 33 import org.jmol.script.T; 34 import org.jmol.shape.Measures; 35 import org.jmol.util.C; 36 import org.jmol.util.GData; 37 import org.jmol.util.Point3fi; 38 import org.jmol.util.Vibration; 39 40 import javajs.util.A4; 41 import javajs.util.M3; 42 import javajs.util.Measure; 43 import javajs.util.P3; 44 import javajs.util.P3i; 45 46 47 48 public class MeasuresRenderer extends LabelsRenderer { 49 50 private boolean doJustify; 51 private boolean modulating; 52 private short mad0; 53 54 /** 55 * modulation points, which must be refreshed based on 56 * phase of the vibration; keyed on atom index. 57 * 58 */ 59 private Map<Integer, Point3fi> mpts; 60 61 private Measurement m; 62 private Point3fi[] p; 63 private int count; 64 65 private A4 aaT; 66 private M3 matrixT; 67 68 @Override initRenderer()69 protected void initRenderer() { 70 mpts = new Hashtable<Integer, Point3fi>(); 71 p = new Point3fi[4]; 72 } 73 74 @Override render()75 protected boolean render() { 76 if (!g3d.checkTranslucent(false)) 77 return false; 78 if (atomPt == null) 79 atomPt = new Point3fi(); 80 Measures measures = (Measures) shape; 81 if (measures.ms != ms) { 82 System.out.println("!measure wrong modelset!"); 83 measures.clear(); 84 return false; 85 } 86 doJustify = vwr.getBoolean(T.justifymeasurements); 87 modulating = ms.bsModulated != null; 88 // note that this COULD be screen pixels if <= 20. 89 imageFontScaling = vwr.imageFontScaling; 90 mad0 = measures.mad; 91 font3d =vwr.gdata.getFont3DScaled(measures.font3d, imageFontScaling); 92 m = measures.mPending; 93 if (!isExport && m != null && (count = m.count)!= 0) 94 renderPendingMeasurement(); 95 if (!vwr.getBoolean(T.showmeasurements)) 96 return false; 97 boolean showMeasurementLabels = vwr.getBoolean(T.measurementlabels); 98 measures.setVisibilityInfo(); 99 for (int i = measures.measurementCount; --i >= 0;) { 100 m = measures.measurements.get(i); 101 if (!m.isVisible || !m.isValid || (count = m.count) == 1 && m.traceX == Integer.MIN_VALUE) 102 continue; 103 getPoints(); 104 colix = m.colix; 105 if (colix == 0) 106 colix = measures.colix; 107 if (colix == 0) 108 colix = vwr.cm.colixBackgroundContrast; 109 labelColix = m.labelColix; 110 if (labelColix == 0) 111 labelColix = vwr.cm.colixBackgroundContrast; 112 else if (labelColix == -1) 113 labelColix = colix; 114 g3d.setC(colix); 115 colixA = colixB = colix; 116 renderMeasurement(showMeasurementLabels); 117 //checkAtoms("m3"); 118 } 119 return false; 120 } 121 getPoints()122 private void getPoints() { 123 for (int j = count; --j >= 0;) { 124 int i = m.getAtomIndex(j + 1); 125 Point3fi pt = (i >= 0 && modulating ? getModAtom(i) : m.getAtom(j + 1)); 126 if (pt.sD < 0) { 127 tm.transformPtScr(pt, pt0i); 128 pt.sX = pt0i.x; 129 pt.sY = pt0i.y; 130 pt.sZ = pt0i.z; 131 } 132 p[j] = pt; 133 } 134 if (modulating) 135 m.refresh(p); 136 } 137 getModAtom(int i)138 private Point3fi getModAtom(int i) { 139 Integer ii = Integer.valueOf(i); 140 Point3fi pt = mpts.get(ii); 141 if (pt != null) 142 ii = null; 143 JmolModulationSet v = ms.getModulation(i); 144 if (v == null) { 145 pt = ms.at[i]; 146 } else { 147 if (pt == null) 148 pt = new Point3fi(); 149 pt.setT(ms.at[i]); 150 if (vwr.tm.vibrationOn) 151 vwr.tm.getVibrationPoint((Vibration) v, pt, Float.NaN); 152 pt.sD = -1; 153 } 154 if (ii != null) 155 mpts.put(ii, pt); 156 return pt; 157 } 158 renderMeasurement(boolean renderLabel)159 private void renderMeasurement(boolean renderLabel) { 160 String s = (renderLabel ? m.getString() : null); 161 if (s != null) { 162 if (s.length() == 0) { 163 s = null; 164 } else if (m.text != null) { 165 m.text.setText(s); 166 m.text.colix = labelColix; 167 } 168 } 169 if (m.mad == 0) { 170 dotsOrDashes = false; 171 mad = mad0; 172 } else { 173 mad = (short) m.mad; 174 //dashDots = hDashes; 175 dotsOrDashes = true; 176 dashDots = (mad < 0 ? null : ndots); 177 } 178 switch (count) { 179 case 1: 180 drawLine(p[0].sX, p[0].sY, p[0].sZ, m.traceX, m.traceY, 181 p[0].sZ, mad); 182 break; 183 case 2: 184 renderDistance(s, p[0], p[1]); 185 break; 186 case 3: 187 renderAngle(s, p[0], p[1], p[2]); 188 break; 189 case 4: 190 renderTorsion(s, p[0], p[1], p[2], p[3]); 191 break; 192 } 193 p[0] = p[1] = p[2] = p[3] = null; 194 } 195 renderDistance(String s, Point3fi a, Point3fi b)196 void renderDistance(String s, Point3fi a, Point3fi b) { 197 if ((tickInfo = m.tickInfo) != null) { 198 drawLine(a.sX, a.sY, a.sZ, b.sX, 199 b.sY, b.sZ, mad); 200 tickA = a; 201 tickB = b; 202 if (tickAs == null) { 203 tickAs = new P3(); 204 tickBs = new P3(); 205 } 206 tickAs.set(a.sX, a.sY, a.sZ); 207 tickBs.set(b.sX, b.sY, b.sZ); 208 // TODO: z-value error: ONLY APPROXIMATE 209 drawTicks(mad, s != null); 210 return; 211 } 212 int zA = a.sZ - a.sD - 10; 213 int zB = b.sZ - b.sD - 10; 214 int radius = drawLine(a.sX, a.sY, zA, b.sX, 215 b.sY, zB, mad); 216 if (s == null) 217 return; 218 if (mad > 0) 219 radius <<= 1; 220 int z = (zA + zB) / 2; 221 if (z < 1) 222 z = 1; 223 int x = (a.sX + b.sX) / 2; 224 int y = (a.sY + b.sY) / 2; 225 if (m.text == null) { 226 g3d.setC(labelColix); 227 // Correction to center measurement vertically false,true not false,false BH 2019.08.18 228 drawString(x, y, z, radius, doJustify 229 && (x - a.sX) * (y - a.sY) > 0, false, true, 230 (doJustify ? 0 : Integer.MAX_VALUE), s); 231 } else { 232 atomPt.ave(a, b); 233 atomPt.sX = (a.sX + b.sX) / 2; 234 atomPt.sY = (a.sY + b.sY) / 2; 235 renderLabelOrMeasure(m.text, s); 236 } 237 } 238 renderAngle(String s, Point3fi a, Point3fi b, Point3fi c)239 private void renderAngle(String s, Point3fi a, Point3fi b, Point3fi c) { 240 int zOffset = b.sD + 10; 241 int zA = a.sZ - a.sD - 10; 242 int zB = b.sZ - zOffset; 243 int zC = c.sZ - c.sD - 10; 244 int radius = drawLine(a.sX, a.sY, zA, b.sX, 245 b.sY, zB, mad); 246 radius += drawLine(b.sX, b.sY, zB, c.sX, 247 c.sY, zC, mad); 248 if (s == null) 249 return; 250 radius = (radius + 1) / 2; 251 if (m.value > 175) { 252 if (m.text == null) { 253 int offset = (int) Math.floor(5 * imageFontScaling); 254 g3d.setC(labelColix); 255 drawString(b.sX + offset, b.sY - offset, zB, radius, 256 false, false, false, (doJustify ? 0 : Integer.MAX_VALUE), s); 257 } else { 258 atomPt.setT(b); 259 renderLabelOrMeasure(m.text, s); 260 } 261 return; 262 } 263 if (m.isTainted()) { 264 float radians = Measure.computeAngle(p[0], p[1], p[2], 265 vectorT2, vectorT3, false); 266 vectorT.cross(vectorT2, vectorT3); 267 m.renderAxis = A4.new4(vectorT.x, vectorT.y, vectorT.z, radians); 268 vectorT2.normalize(); 269 vectorT2.scale(0.5f); 270 m.renderArc = P3.newP(vectorT2); 271 } 272 if (aaT == null) { 273 aaT = new A4(); 274 matrixT = new M3(); 275 } 276 int dotCount = (int) Math.floor((m.renderAxis.angle / (2 * Math.PI)) * 64); 277 float stepAngle = m.renderAxis.angle / dotCount; 278 aaT.setAA(m.renderAxis); 279 int iMid = dotCount / 2; 280 for (int i = dotCount; --i >= 0;) { 281 aaT.angle = i * stepAngle; 282 pointT.setT(m.renderArc); 283 matrixT.setAA(aaT).rotate(pointT); 284 pointT.add(b); 285 // NOTE! Point3i screen is just a pointer 286 // to tm.transformManager.point3iScreenTemp 287 P3i p3i = tm.transformPt(pointT); 288 int zArc = p3i.z - zOffset; 289 if (zArc < 0) 290 zArc = 0; 291 g3d.drawPixel(p3i.x, p3i.y, zArc); 292 if (i != iMid) 293 continue; 294 pointT.setT(m.renderArc); 295 pointT.scale(1.1f); 296 // next line modifies Point3i point3iScreenTemp 297 matrixT.rotate(pointT); 298 pointT.add(b); 299 tm.transformPt(pointT); 300 int zLabel = p3i.z - zOffset; 301 if (m.text == null) { 302 g3d.setC(labelColix); 303 drawString(p3i.x, p3i.y, zLabel, radius, p3i.x < b.sX, false, 304 false, (doJustify ? b.sY : Integer.MAX_VALUE), s); 305 } else { 306 atomPt.setT(pointT); 307 renderLabelOrMeasure(m.text, s); 308 } 309 } 310 } 311 renderTorsion(String s, Point3fi a, Point3fi b, Point3fi c, Point3fi d)312 private void renderTorsion(String s, Point3fi a, Point3fi b, Point3fi c, Point3fi d) { 313 int zA = a.sZ - a.sD - 10; 314 int zB = b.sZ - b.sD - 10; 315 int zC = c.sZ - c.sD - 10; 316 int zD = d.sZ - d.sD - 10; 317 int radius = drawLine(a.sX, a.sY, zA, b.sX, 318 b.sY, zB, mad); 319 radius += drawLine(b.sX, b.sY, zB, c.sX, 320 c.sY, zC, mad); 321 radius += drawLine(c.sX, c.sY, zC, d.sX, 322 d.sY, zD, mad); 323 if (s == null) 324 return; 325 int zLabel = (zA + zB + zC + zD) / 4; 326 radius /= 3; 327 if (m.text == null) { 328 g3d.setC(labelColix); 329 drawString((a.sX + b.sX + c.sX + d.sX) / 4, 330 (a.sY + b.sY + c.sY + d.sY) / 4, zLabel, radius, false, false, false, 331 (doJustify ? 0 : Integer.MAX_VALUE), s); 332 } else { 333 atomPt.add2(a, b); 334 atomPt.add(c); 335 atomPt.add(d); 336 atomPt.scale(0.25f); 337 renderLabelOrMeasure(m.text, s); 338 } 339 } 340 renderPendingMeasurement()341 private void renderPendingMeasurement() { 342 try { 343 getPoints(); 344 } catch (Exception e) { 345 ((Measures) shape).mPending = null; 346 return; 347 } 348 boolean renderLabel = (m.traceX == Integer.MIN_VALUE); 349 g3d.setC(labelColix = (renderLabel ? vwr.cm.colixRubberband 350 : count == 2 ? C.MAGENTA : C.GOLD)); 351 if (((MeasurementPending) m).haveTarget) { 352 renderMeasurement(renderLabel); 353 return; 354 } 355 Point3fi atomLast = p[count - 1]; 356 if (count > 1) 357 renderMeasurement(false); 358 int lastZ = atomLast.sZ - atomLast.sD - 10; 359 int x = vwr.getCursorX(); 360 int y = vwr.getCursorY(); 361 if (g3d.isAntialiased()) { 362 x <<= 1; 363 y <<= 1; 364 } 365 drawLine(atomLast.sX, atomLast.sY, lastZ, x, y, lastZ, mad); 366 } 367 368 //TODO: I think the 20 here is the cutoff for pixels -- check this 369 @Override drawLine(int x1, int y1, int z1, int x2, int y2, int z2, int mad)370 protected int drawLine(int x1, int y1, int z1, int x2, int y2, int z2, 371 int mad) { 372 // small numbers refer to pixels already? 373 int diameter = (int) (mad >= 20 && exportType != GData.EXPORT_CARTESIAN ? 374 vwr.tm.scaleToScreen((z1 + z2) / 2, mad) : mad); 375 if (dotsOrDashes && (dashDots == null || dashDots == ndots)) 376 width = diameter; 377 return drawLine2(x1, y1, z1, x2, y2, z2, diameter); 378 } 379 } 380