1 /* $RCSfile$
2  * $Author: egonw $
3  * $Date: 2005-11-10 09:52:44 -0600 (Thu, 10 Nov 2005) $
4  * $Revision: 4255 $
5  *
6  * Copyright (C) 2002-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 package org.jmol.modelset;
25 
26 import javajs.util.P3;
27 import javajs.util.P3i;
28 import javajs.util.PT;
29 import javajs.util.SB;
30 import javajs.util.BS;
31 
32 import org.jmol.shape.Shape;
33 import org.jmol.util.C;
34 import org.jmol.util.Font;
35 import org.jmol.util.Txt;
36 import org.jmol.viewer.JC;
37 import org.jmol.viewer.Viewer;
38 
39 public class Text {
40 
41   private Viewer vwr;
42 
43   public boolean doFormatText;
44 
45   public Font font;
46   private byte fid;
47   private int ascent;
48   public int descent;
49   private int lineHeight;
50 
51   protected int offsetX; // Labels only
52   protected int offsetY; // Labels only
53   public  int boxYoff2;
54 
55   private int[] widths;
56 
57   private int textWidth;
58   private int textHeight;
59   public String text;
60   public String textUnformatted;
61   public String[] lines;
62 
63   public Object image;
64   public float imageScale = 1;
65 
66 
67 
Text()68   public Text() {
69     // public for reflection
70     // requires .newLabel or .newEcho
71     boxXY =  new float[5];
72   }
73 
newLabel(Viewer vwr, Font font, String text, short colix, short bgcolix, int align, float scalePixelsPerMicron)74   static public Text newLabel(Viewer vwr, Font font, String text,
75                               short colix, short bgcolix, int align, float scalePixelsPerMicron) {
76     // for labels and hover
77     Text t = new Text();
78     t.vwr = vwr;
79     t.set(font, colix, align, scalePixelsPerMicron);
80     t.setText(text);
81     t.bgcolix = bgcolix;
82     return t;
83   }
84 
newMeasure(Viewer vwr, Font font, short colix)85   static public Text newMeasure(Viewer vwr, Font font,
86                               short colix) {
87     Text t = new Text();
88     t.vwr = vwr;
89     t.set(font, colix, 0, 0);
90     t.isMeasure = true;
91     return t;
92   }
93 
newEcho(Viewer vwr, Font font, String target, short colix, int valign, int align, float scalePixelsPerMicron)94   public static Text newEcho(Viewer vwr, Font font, String target,
95                       short colix, int valign, int align,
96                       float scalePixelsPerMicron) {
97     Text t = new Text();
98     t.isEcho = true;
99     t.vwr = vwr;
100     t.set(font, colix, align, scalePixelsPerMicron);
101     t.target = target;
102     t.valign = valign;
103     t.z = 2;
104     t.zSlab = Integer.MIN_VALUE;
105     return t;
106   }
107 
set(Font font, short colix, int align, float scalePixelsPerMicron)108   private void set(Font font, short colix, int align,
109                    float scalePixelsPerMicron) {
110     this.scalePixelsPerMicron = scalePixelsPerMicron;
111     this.isEcho = isEcho;
112     this.colix = colix;
113     this.align = align;
114     this.setFont(font, !isEcho);
115   }
116 
setOffset(int offset)117   public void setOffset(int offset) {
118     //Labels only
119     offsetX = JC.getXOffset(offset);
120     offsetY = JC.getYOffset(offset);
121     pymolOffset = null;
122     valign = JC.ECHO_XY;
123   }
124 
getFontMetrics()125   private void getFontMetrics() {
126     descent = font.getDescent();
127     ascent = font.getAscent();
128     lineHeight = ascent + descent;
129   }
130 
setFontFromFid(byte fid)131   public void setFontFromFid(byte fid) { //labels only
132     if (this.fid == fid)
133       return;
134     fontScale = 0;
135     setFont(Font.getFont3D(fid), true);
136   }
137 
setText(String text)138   public void setText(String text) {
139     if (image != null)
140       getFontMetrics();
141     image = null;
142     if (text != null && text.length() == 0)
143       text = null;
144     if (this.text != null && this.text.equals(text))
145       return;
146     this.text = textUnformatted = text;
147     doFormatText = (isEcho && text != null && (text.indexOf("%{") >= 0 || text
148         .indexOf("@{") >= 0));
149     if (!doFormatText)
150       recalc();
151   }
152 
setImage(Object image)153   public void setImage(Object image) {
154     this.image = image;
155     // this.text will be file name
156     recalc();
157   }
158 
setScale(float scale)159   public void setScale(float scale) {
160     imageScale = scale;
161     recalc();
162   }
163 
setFont(Font f3d, boolean doAll)164   public void setFont(Font f3d, boolean doAll) {
165     font = f3d;
166     if (font == null)
167       return;
168     getFontMetrics();
169     if (!doAll)
170       return;
171     fid = font.fid;
172     recalc();
173   }
174 
setFontScale(float scale)175   public void setFontScale(float scale) {
176     if (fontScale == scale)
177       return;
178     fontScale = scale;
179     if (fontScale != 0)
180       setFont(vwr.gdata.getFont3DScaled(font, scale), true);
181   }
182 
recalc()183   private void recalc() {
184     if (image != null) {
185       textWidth = textHeight = 0;
186       boxWidth = vwr.apiPlatform.getImageWidth(image) * fontScale * imageScale;
187       boxHeight = vwr.apiPlatform.getImageHeight(image) * fontScale * imageScale;
188       ascent = 0;
189       return;
190     }
191     if (text == null) {
192       text = null;
193       lines = null;
194       widths = null;
195       return;
196     }
197     if (font == null)
198       return;
199     lines = PT.split(text, (text.indexOf("\n") >= 0 ? "\n" : "|"));
200     textWidth = 0;
201     widths = new int[lines.length];
202     for (int i = lines.length; --i >= 0;)
203       textWidth = Math.max(textWidth, widths[i] = stringWidth(lines[i]));
204     textHeight = lines.length * lineHeight;
205     boxWidth = textWidth + (fontScale >= 2 ? 16 : 8);
206     boxHeight = textHeight + (fontScale >= 2 ? 16 : 8);
207   }
208 
setPosition(float scalePixelsPerMicron, float imageFontScaling, boolean isAbsolute, float[] boxXY)209   public void setPosition(float scalePixelsPerMicron, float imageFontScaling,
210                           boolean isAbsolute, float[] boxXY) {
211     if (boxXY == null)
212       boxXY = this.boxXY;
213     else
214       this.boxXY = boxXY;
215     setWindow(vwr.gdata.width, vwr.gdata.height, scalePixelsPerMicron);
216     if (scalePixelsPerMicron != 0 && this.scalePixelsPerMicron != 0)
217       setFontScale(scalePixelsPerMicron / this.scalePixelsPerMicron);
218     else if (fontScale != imageFontScaling)
219       setFontScale(imageFontScaling);
220     if (doFormatText) {
221       text = (isEcho ? Txt.formatText(vwr, textUnformatted) : textUnformatted);
222       recalc();
223     }
224     float dx = offsetX * imageFontScaling;
225     float dy = offsetY * imageFontScaling;
226     xAdj = (fontScale >= 2 ? 8 : 4);
227     yAdj = ascent - lineHeight + xAdj;
228     if (!isEcho || pymolOffset != null) {
229       boxXY[0] = movableX;
230       boxXY[1] = movableY;
231       if (pymolOffset != null && pymolOffset[0] != 2 && pymolOffset[0] != 3) {
232         // [1,2,3] are in Angstroms, not screen pixels
233         float pixelsPerAngstrom = vwr.tm.scaleToScreen(z, 1000);
234         float pz = pymolOffset[3];
235         float dz = (pz < 0 ? -1 : 1) * Math.max(pz == 0 ? 0.5f : 0, Math.abs(pz) - 1)
236             * pixelsPerAngstrom;
237         z -= (int) dz;
238         pixelsPerAngstrom = vwr.tm.scaleToScreen(z, 1000);
239 
240         /* for whatever reason, Java returns an
241          * ascent that is considerably higher than a capital X
242          * forget leading!
243          * ______________________________________________
244          *                    leading
245          *                   ________
246          *     X X
247          *      X    ascent
248          * __  X X _________ _________
249          * _________ descent
250          *                                   textHeight
251          * _________
252          *     X X           lineHeight
253          *      X    ascent
254          * __  X X__________ _________        ___________
255          * _________ descent
256          *
257          *
258          *
259          */
260         // dx and dy are the overall object offset, with text
261         dx = getPymolXYOffset(pymolOffset[1], textWidth, pixelsPerAngstrom);
262         int dh = ascent - descent;
263         dy = -getPymolXYOffset(-pymolOffset[2], dh, pixelsPerAngstrom)
264             - (textHeight + dh) / 2;
265 
266         //dy: added -lineHeight (for one line)
267         if (pymolOffset[0] == 1) {
268           // from PyMOL - back to original plan
269           dy -= descent;
270         }
271 
272         // xAdj and yAdj are the adjustments for the box itself relative to the text
273         xAdj = (fontScale >= 2 ? 8 : 4);
274         yAdj = -descent;
275         boxXY[0] = movableX - xAdj;
276         boxXY[1] = movableY - yAdj;
277         isAbsolute = true;
278         boxYoff2 = -2; // empirical fudge factor
279       } else {
280         boxYoff2 = 0;
281       }
282       if (pymolOffset == null)
283         switch (align) {
284         case JC.TEXT_ALIGN_CENTER:
285           dy = 0;
286           dx = 0;
287           break;
288         case JC.TEXT_ALIGN_RIGHT:
289           boxXY[0] -= boxWidth;
290           //$FALL-THROUGH$
291         case JC.TEXT_ALIGN_LEFT:
292           dy = 0;
293         }
294       //System.out.println(dx + " Text " + dy + " " + boxWidth + " " + boxHeight);
295       setBoxXY(boxWidth, boxHeight, dx, dy, boxXY, isAbsolute);
296     } else {
297       setPos(fontScale);
298     }
299     boxX = boxXY[0];
300     boxY = boxXY[1];
301 
302     // adjust positions if necessary
303 
304     if (adjustForWindow)
305       setBoxOffsetsInWindow(/*image == null ? fontScale * 5 :*/0,
306           isEcho ? 0 : 16 * fontScale + lineHeight, boxY - textHeight);
307     //if (!isAbsolute)
308     y0 = boxY + yAdj;
309     if (isMeasure && align != JC.TEXT_ALIGN_CENTER)
310       y0 += ascent + (lines.length - 1)/2f * lineHeight;
311   }
312 
getPymolXYOffset(float off, int width, float ppa)313   private float getPymolXYOffset(float off, int width, float ppa) {
314     float f = (off < -1 ? -1 : off > 1 ? 0 : (off - 1) / 2);
315     // offset
316     // -3     -2
317     // -2     -1
318     // -1      0 absolute, -1 width
319     //-0.5    -3/4  width
320     //  0     -1/2 width
321     // 0.5    -1/4 width
322     //  1      0
323     //  2      1
324     //  3      2
325     off = (off < -1 || off > 1 ? off + (off < 0 ? 1 : -1) : 0);
326     return f * width + off * ppa;
327   }
328 
setPos(float scale)329   private void setPos(float scale) {
330     float xLeft, xCenter, xRight;
331     boolean is3dEcho = (xyz != null);
332     if (valign == JC.ECHO_XY || valign == JC.ECHO_XYZ) {
333       float x = (movableXPercent != Integer.MAX_VALUE ? movableXPercent
334           * windowWidth / 100 : is3dEcho ? movableX : movableX * scale);
335       float offsetX = this.offsetX * scale;
336       xLeft = xRight = xCenter = x + offsetX;
337     } else {
338       xLeft = 5 * scale;
339       xCenter = windowWidth / 2;
340       xRight = windowWidth - xLeft;
341     }
342 
343     // set box X from alignments
344 
345     boxXY[0] = xLeft;
346     switch (align) {
347     case JC.TEXT_ALIGN_CENTER:
348       boxXY[0] = xCenter - boxWidth / 2;
349       break;
350     case JC.TEXT_ALIGN_RIGHT:
351       boxXY[0] = xRight - boxWidth;
352     }
353 
354     // set box Y from alignments
355 
356     boxXY[1] = 0;
357     switch (valign) {
358     case JC.ECHO_TOP:
359       break;
360     case JC.ECHO_MIDDLE:
361       boxXY[1] = windowHeight / 2;
362       break;
363     case JC.ECHO_BOTTOM:
364       boxXY[1] = windowHeight;
365       break;
366     default:
367       float y = (movableYPercent != Integer.MAX_VALUE ? movableYPercent
368           * windowHeight / 100 : is3dEcho ? movableY : movableY * scale);
369       boxXY[1] = (is3dEcho ? y : (windowHeight - y)) + offsetY * scale;
370    }
371 
372     if (align == JC.TEXT_ALIGN_CENTER)
373       boxXY[1] -= (image != null ? boxHeight : xyz != null ? boxHeight
374           : ascent - boxHeight) / 2;
375     else if (image != null)
376       boxXY[1] -= 0;
377     else if (xyz != null)
378       boxXY[1] -= ascent / 2;
379   }
380 
setBoxXY(float boxWidth, float boxHeight, float xOffset, float yOffset, float[] boxXY, boolean isAbsolute)381   public static void setBoxXY(float boxWidth, float boxHeight, float xOffset,
382                                float yOffset, float[] boxXY, boolean isAbsolute) {
383     float xBoxOffset, yBoxOffset;
384 
385     // these are based on a standard |_ grid, so y is reversed.
386     if (xOffset > 0 || isAbsolute) {
387       xBoxOffset = xOffset;
388     } else {
389       xBoxOffset = -boxWidth;
390       if (xOffset == 0)
391         xBoxOffset /= 2;
392       else
393         xBoxOffset += xOffset;
394     }
395     if (isAbsolute || yOffset > 0) {
396       yBoxOffset = -boxHeight - yOffset;
397     } else if (yOffset == 0) {
398       yBoxOffset = -boxHeight / 2; // - 2; removed in Jmol 11.7.45 06/24/2009
399     } else {
400       yBoxOffset = -yOffset;
401     }
402     boxXY[0] += xBoxOffset;
403     boxXY[1] += yBoxOffset;
404     boxXY[2] = boxWidth;
405     boxXY[3] = boxHeight;
406   }
407 
stringWidth(String str)408   private int stringWidth(String str) {
409     int w = 0;
410     int f = 1;
411     int subscale = 1; //could be something less than that
412     if (str == null)
413       return 0;
414     if (str.indexOf("<su") < 0 && str.indexOf("<color") < 0)
415       return font.stringWidth(str);
416     int len = str.length();
417     String s;
418     for (int i = 0; i < len; i++) {
419       if (str.charAt(i) == '<') {
420         if (i + 8 <= len &&
421             (str.substring(i, i + 7).equals("<color ") || str.substring(i, i + 8).equals("</color>"))) {
422           int i1 = str.indexOf(">", i);
423           if (i1 >= 0) {
424             i = i1;
425             continue;
426           }
427         }
428         if (i + 5 <= len
429             && ((s = str.substring(i, i + 5)).equals("<sub>") || s
430                 .equals("<sup>"))) {
431           i += 4;
432           f = subscale;
433           continue;
434         }
435         if (i + 6 <= len
436             && ((s = str.substring(i, i + 6)).equals("</sub>") || s
437                 .equals("</sup>"))) {
438           i += 5;
439           f = 1;
440           continue;
441         }
442       }
443       w += font.stringWidth(str.substring(i, i + 1)) * f;
444     }
445     return w;
446   }
447 
448   private float xAdj, yAdj;
449 
450   private float y0;
451 
452   public P3 pointerPt; // for echo
453 
setXYA(float[] xy, int i)454   public void setXYA(float[] xy, int i) {
455     if (i == 0) {
456       xy[2] = boxX;
457       switch (align) {
458       case JC.TEXT_ALIGN_CENTER:
459         xy[2] += boxWidth / 2;
460         break;
461       case JC.TEXT_ALIGN_RIGHT:
462         xy[2] += boxWidth - xAdj;
463         break;
464       default:
465         xy[2] += xAdj;
466       }
467       xy[0] = xy[2];
468       xy[1] = y0;
469     }
470     switch (align) {
471     case JC.TEXT_ALIGN_CENTER:
472       xy[0] = xy[2] - widths[i] / 2;
473       break;
474     case JC.TEXT_ALIGN_RIGHT:
475       xy[0] = xy[2] - widths[i];
476     }
477     xy[1] += lineHeight;
478   }
479 
appendFontCmd(SB s)480   public void appendFontCmd(SB s) {
481     s.append("  " + Shape.getFontCommand("echo", font));
482     if (scalePixelsPerMicron > 0)
483       s.append(" " + (10000f / scalePixelsPerMicron)); // Angstroms per pixel
484   }
485 
486   public boolean isMeasure;
487   public boolean isEcho;
488   public P3 xyz;
489   public String target;
490   public String script;
491   public short colix;
492   public short bgcolix;
493   public int pointer;
494   public float fontScale;
495 
496   public int align;
497   public int valign;
498   public int atomX, atomY, atomZ = Integer.MAX_VALUE;
499   public int movableX, movableY, movableZ; // Echo only
500   public int movableXPercent = Integer.MAX_VALUE; // Echo only
501   public int movableYPercent = Integer.MAX_VALUE; // Echo only
502   public int movableZPercent = Integer.MAX_VALUE; // Echo only
503 
504   public int z = 1; // front plane
505   public int zSlab = Integer.MIN_VALUE; // z for slabbing purposes -- may be near an atom
506 
507   // PyMOL-type offset
508   // [mode, screenoffsetx,y,z (applied after tranform), positionOffsetx,y,z (applied before transform)]
509   public float[] pymolOffset;
510 
511   protected int windowWidth;
512   protected int windowHeight;
513   public boolean adjustForWindow;
514   public float boxWidth;
515   public float boxHeight;
516   public float boxX;
517   public float boxY;
518 
519   public int modelIndex = -1;
520   public boolean visible = true;
521   public boolean hidden = false;
522 
523   public float[] boxXY;
524 
525   public float scalePixelsPerMicron;
526 
setScalePixelsPerMicron(float scalePixelsPerMicron)527   public void setScalePixelsPerMicron(float scalePixelsPerMicron) {
528     fontScale = 0;//fontScale * this.scalePixelsPerMicron / scalePixelsPerMicron;
529     this.scalePixelsPerMicron = scalePixelsPerMicron;
530   }
531 
setXYZ(P3 xyz, boolean doAdjust)532   public void setXYZ(P3 xyz, boolean doAdjust) {
533     this.xyz = xyz;
534     if (xyz == null)
535       this.zSlab = Integer.MIN_VALUE;
536     if (doAdjust) {
537       valign = (xyz == null ? JC.ECHO_XY : JC.ECHO_XYZ);
538      adjustForWindow = (xyz == null);
539     }
540   }
541 
setTranslucent(float level, boolean isBackground)542   public void setTranslucent(float level, boolean isBackground) {
543     if (isBackground) {
544       if (bgcolix != 0)
545         bgcolix = C.getColixTranslucent3(bgcolix, !Float.isNaN(level), level);
546     } else {
547       colix = C.getColixTranslucent3(colix, !Float.isNaN(level), level);
548     }
549   }
550 
setMovableX(int x)551   public void setMovableX(int x) {
552     valign = (valign == JC.ECHO_XYZ ? JC.ECHO_XYZ : JC.ECHO_XY);
553     movableX = x;
554     movableXPercent = Integer.MAX_VALUE;
555   }
556 
setMovableY(int y)557   public void setMovableY(int y) {
558     valign = (valign == JC.ECHO_XYZ ? JC.ECHO_XYZ : JC.ECHO_XY);
559     movableY = y;
560     movableYPercent = Integer.MAX_VALUE;
561   }
562 
563   //  public void setMovableZ(int z) {
564   //    if (valign != VALIGN_XYZ)
565   //      valign = VALIGN_XY;
566   //    movableZ = z;
567   //    movableZPercent = Integer.MAX_VALUE;
568   //  }
569 
setMovableXPercent(int x)570   public void setMovableXPercent(int x) {
571     valign = (valign == JC.ECHO_XYZ ? JC.ECHO_XYZ : JC.ECHO_XY);
572     movableX = Integer.MAX_VALUE;
573     movableXPercent = x;
574   }
575 
setMovableYPercent(int y)576   public void setMovableYPercent(int y) {
577     valign = (valign == JC.ECHO_XYZ ? JC.ECHO_XYZ : JC.ECHO_XY);
578     movableY = Integer.MAX_VALUE;
579     movableYPercent = y;
580   }
581 
setMovableZPercent(int z)582   public void setMovableZPercent(int z) {
583     if (valign != JC.ECHO_XYZ)
584       valign = JC.ECHO_XY;
585     movableZ = Integer.MAX_VALUE;
586     movableZPercent = z;
587   }
588 
setZs(int z, int zSlab)589   public void setZs(int z, int zSlab) {
590     this.z = z;
591     this.zSlab = zSlab;
592   }
593 
setXYZs(int x, int y, int z, int zSlab)594   public void setXYZs(int x, int y, int z, int zSlab) {
595     setMovableX(x);
596     setMovableY(y);
597     setZs(z, zSlab);
598   }
599 
setScript(String script)600   public void setScript(String script) {
601     this.script = (script == null || script.length() == 0 ? null : script);
602   }
603 
setAlignmentLCR(String align)604   public boolean setAlignmentLCR(String align) {
605     if ("left".equals(align))
606       return setAlignment(JC.TEXT_ALIGN_LEFT);
607     if ("center".equals(align))
608       return setAlignment(JC.TEXT_ALIGN_CENTER);
609     if ("right".equals(align))
610       return setAlignment(JC.TEXT_ALIGN_RIGHT);
611     return false;
612   }
613 
setAlignment(int align)614   public boolean setAlignment(int align) {
615     if (this.align != align) {
616       this.align = align;
617       recalc();
618     }
619     return true;
620   }
621 
setBoxOffsetsInWindow(float margin, float vMargin, float vTop)622   public void setBoxOffsetsInWindow(float margin, float vMargin, float vTop) {
623     // not labels
624 
625     // these coordinates are (0,0) in top left
626     // (user coordinates are (0,0) in bottom left)
627     float bw = boxWidth + margin;
628     float x = boxX;
629     if (x + bw > windowWidth)
630       x = windowWidth - bw;
631     if (x < margin)
632       x = margin;
633     boxX = x;
634 
635     float bh = boxHeight;
636     float y = vTop;
637     if (y + bh > windowHeight)
638       y = windowHeight - bh;
639     if (y < vMargin)
640       y = vMargin;
641     boxY = y;
642   }
643 
setWindow(int width, int height, float scalePixelsPerMicron)644   public void setWindow(int width, int height, float scalePixelsPerMicron) {
645     windowWidth = width;
646     windowHeight = height;
647     if (pymolOffset == null && this.scalePixelsPerMicron < 0
648         && scalePixelsPerMicron != 0)
649       setScalePixelsPerMicron(scalePixelsPerMicron);
650   }
651 
checkObjectClicked(boolean isAntialiased, int x, int y, BS bsVisible)652   public boolean checkObjectClicked(boolean isAntialiased, int x, int y,
653                                     BS bsVisible) {
654     if (hidden || script == null || modelIndex >= 0 && !bsVisible.get(modelIndex))
655       return false;
656     if (isAntialiased) {
657       x <<= 1;
658       y <<= 1;
659     }
660     return (x >= boxX && x <= boxX + boxWidth && y >= boxY && y <= boxY
661         + boxHeight);
662   }
663 
664 //
665 //  new feature: set labelOffset [mode sx sy sz ax ay az] (3.1.15, never documented)
666 //  new feature: PyMOL-like label offset options:
667 //
668 //     set labelOffset [sx, sy, sz]
669 //     set labelOffset [mode, sx, sy, sz, ax, ay, az]
670 //
671 //   where
672 //
673 //     sx,sy,sz are screen coord offsets
674 //      -- applied after view rotation
675 //      -- sy > 0 LOWERS label
676 //     ax,ay,az are xyz position (in Angstroms; applied before view rotation)
677 //     mode == 0 indicates xyz position is absolute and sx sy sz are Angstroms
678 //     mode == 1 indicates xyz position is relative to atom position and sx sy sz are Angstroms
679 //     mode == 2 indicates xyz is absolute, and sx sy sz positions are screen pixels
680 //     mode == 3 indicates xyz is relative, and sx sy sz positions are screen pixels
681 //     defaults: mode == 1; ax = ay = az = 0
682 //
683 //
684 
685   /**
686    * PyMOL will use 1 here for pymolOffset[0] for relative, 0 or absolute. Jmol
687    * set labelOffset or set echo offset or measure offset will set -1, when
688    * using {sx sy sz}.
689    *
690    *
691    * @param atomPt
692    * @param screen
693    * @param zSlab
694    * @param pTemp
695    * @param sppm
696    */
getPymolScreenOffset(P3 atomPt, P3i screen, int zSlab, P3 pTemp, float sppm)697   public void getPymolScreenOffset(P3 atomPt, P3i screen, int zSlab, P3 pTemp,
698                                    float sppm) {
699     float mode = pymolOffset[0];
700     if (atomPt != null && (Math.abs(mode) % 2) == 1)
701       pTemp.setT(atomPt);
702     else
703       pTemp.set(0, 0, 0);
704     pTemp.add3(pymolOffset[4], pymolOffset[5], pymolOffset[6]);
705     vwr.tm.transformPtScr(pTemp, screen);
706     if (mode == 2 || mode == 3) {
707       screen.x += pymolOffset[1];
708       screen.y += pymolOffset[2];
709       screen.z += pymolOffset[3];
710     }
711     setXYZs(screen.x, screen.y, screen.z, zSlab);
712     setScalePixelsPerMicron(sppm);
713   }
714 
715 }
716