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