1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2006-02-25 17:19:14 -0600 (Sat, 25 Feb 2006) $
4  * $Revision: 4529 $
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 
25 package org.jmol.shapespecial;
26 
27 import javajs.util.AU;
28 import javajs.util.Lst;
29 import javajs.util.SB;
30 
31 import java.util.Hashtable;
32 
33 import java.util.Map;
34 
35 
36 import org.jmol.util.BSUtil;
37 import org.jmol.util.C;
38 import org.jmol.util.Escape;
39 import org.jmol.util.Logger;
40 import org.jmol.util.MeshSurface;
41 
42 import javajs.util.Measure;
43 import javajs.util.P3;
44 import javajs.util.P3i;
45 import javajs.util.P4;
46 import javajs.util.PT;
47 import javajs.util.T3;
48 import javajs.util.V3;
49 
50 import org.jmol.viewer.ActionManager;
51 import org.jmol.viewer.JC;
52 import javajs.util.BS;
53 import org.jmol.script.SV;
54 import org.jmol.script.T;
55 import org.jmol.shape.Mesh;
56 import org.jmol.shape.MeshCollection;
57 
58 
59 public class Draw extends MeshCollection {
60 
61   // bob hanson hansonr@stolaf.edu 3/2006
62 
Draw()63   public Draw() {
64     // from reflection
65     htObjects = new Hashtable<String, Mesh>();
66   }
67 
68   DrawMesh[] dmeshes = new DrawMesh[4];
69   private DrawMesh thisMesh;
70 
71   @Override
allocMesh(String thisID, Mesh m)72   public void allocMesh(String thisID, Mesh m) {
73     int index = meshCount++;
74     meshes = dmeshes = (DrawMesh[]) AU.ensureLength(dmeshes,
75         meshCount * 2);
76     currentMesh = thisMesh = dmeshes[index] = (m == null ? new DrawMesh(vwr, thisID,
77         colix, index) : (DrawMesh) m);
78     currentMesh.color = color;
79     currentMesh.index = index;
80     if (thisID != null && thisID != MeshCollection.PREVIOUS_MESH_ID
81         && htObjects != null)
82       htObjects.put(thisID.toUpperCase(), currentMesh);
83   }
84 
setPropertySuper(String propertyName, Object value, BS bs)85   private void setPropertySuper(String propertyName, Object value, BS bs) {
86     currentMesh = thisMesh;
87     setPropMC(propertyName, value, bs);
88     thisMesh = (DrawMesh)currentMesh;
89   }
90 
91  @Override
initShape()92 public void initShape() {
93     super.initShape();
94     myType = "draw";
95   }
96 
97   private P3[] ptList;
98   private V3 offset = new V3();
99   private int nPoints;
100   private int diameter;
101   private float width;
102   private float newScale;
103   private float length;
104   private boolean isCurve;
105   private boolean isArc;
106   private boolean isArrow;
107   private boolean isLine;
108   private boolean isVector;
109   private boolean isCircle;
110   private boolean isPerpendicular;
111   private boolean isCylinder;
112   private boolean isVertices;
113   private boolean isPlane;
114   private boolean isReversed;
115   private boolean isRotated45;
116   private boolean isCrossed;
117   private boolean isValid;
118   private boolean noHead;
119   private boolean isBarb;
120   private int indicatedModelIndex = -1;
121   private int[] modelInfo;
122   private boolean makePoints;
123   //private int nidentifiers;
124   //private int nbitsets;
125   private P4 plane;
126   private BS bsAllModels;
127   private Lst<Object> polygon;
128 
129   private Lst<Object[]> vData;
130   private String intersectID;
131   private P3[] boundBox;
132 
133   private Lst<P3[]> lineData;
134   private final static int PT_COORD = 1;
135   private final static int PT_IDENTIFIER = 2;
136   private final static int PT_BITSET = 3;
137   private final static int PT_MODEL_INDEX = 4;
138   private final static int PT_MODEL_BASED_POINTS = 5;
139 
140   @SuppressWarnings("unchecked")
141   @Override
setProperty(String propertyName, Object value, BS bs)142   public void setProperty(String propertyName, Object value, BS bs) {
143 
144     if ("init" == propertyName) {
145       initDraw();
146       setPropertySuper("init", value, bs);
147       return;
148     }
149 
150     if ("length" == propertyName) {
151       length = ((Float) value).floatValue();
152       return;
153     }
154 
155     if ("fixed" == propertyName) {
156       isFixed = ((Boolean) value).booleanValue();
157       return;
158     }
159 
160     if ("intersect" == propertyName) {
161       if (value instanceof String)
162         intersectID = (String) value;
163       else
164         boundBox = (P3[]) value;
165       return;
166     }
167 
168     if ("slab" == propertyName) {
169       int meshIndex = getIndexFromName((String) value);
170       if (meshIndex < 0) {
171         // could be isosurface?
172         return;
173       }
174       Mesh m = meshes[meshIndex];
175       if (m.checkByteCount != 1)
176         return;
177       MeshSurface ms = new MeshSurface();
178       ms.vs = m.vs;
179       ms.vvs = new float[m.vc];
180       ms.vc = m.vc;
181       ms.pis = m.pis;
182       ms.pc = m.pc;
183       ms.dataOnly = true;
184       slabData = ms;
185     }
186 
187     if ("lineData" == propertyName) {
188       lineData = new Lst<P3[]>();
189       if (indicatedModelIndex < 0)
190         indicatedModelIndex = vwr.am.cmi;
191       float[] fdata = (float[]) value;
192       int n = fdata.length / 6;
193       for (int i = 0, pt = 0; i < n; i++)
194         lineData.addLast(new P3[] {
195             P3.new3(fdata[pt++], fdata[pt++], fdata[pt++]),
196             P3.new3(fdata[pt++], fdata[pt++], fdata[pt++]) });
197       return;
198     }
199 
200     if ("modelIndex" == propertyName) {
201       //from saved state -- used to set modelVertices
202       indicatedModelIndex = ((Integer) value).intValue();
203       if (indicatedModelIndex < 0 || indicatedModelIndex >= vwr.ms.mc)
204         return;
205       vData.addLast(new Object[] { Integer.valueOf(PT_MODEL_INDEX),
206           (modelInfo = new int[] { indicatedModelIndex, 0 }) });
207       return;
208     }
209 
210     if ("planedef" == propertyName) {
211       plane = (P4) value;
212       if (intersectID != null || boundBox != null || slabData != null)
213         return;
214       if (isCircle || isArc)
215         isPlane = true;
216       vData.addLast(new Object[] { Integer.valueOf(PT_COORD),
217           P3.new3(Float.NaN, Float.NaN, Float.NaN) });
218       return;
219     }
220 
221     if ("perp" == propertyName) {
222       isPerpendicular = true;
223       return;
224     }
225 
226     if ("cylinder" == propertyName) {
227       isCylinder = true;
228       return;
229     }
230 
231     if ("plane" == propertyName) {
232       isPlane = true;
233       return;
234     }
235 
236     if ("curve" == propertyName) {
237       isCurve = true;
238       return;
239     }
240 
241     if ("arrow" == propertyName) {
242       isArrow = true;
243       return;
244     }
245 
246     if ("line" == propertyName) {
247       isLine = true;
248       isCurve = true;
249       return;
250     }
251 
252     if ("arc" == propertyName) {
253       isCurve = true;
254       isArc = true;
255       if (isArrow) {
256         isArrow = false;
257         isVector = true;
258       }
259       return;
260     }
261 
262     if ("circle" == propertyName) {
263       isCircle = true;
264       return;
265     }
266 
267     if ("vector" == propertyName) {
268       isArrow = true;
269       isVector = true;
270       return;
271     }
272 
273     if ("vertices" == propertyName) {
274       isVertices = true;
275       return;
276     }
277 
278     if ("reverse" == propertyName) {
279       isReversed = true;
280       return;
281     }
282 
283     if ("nohead" == propertyName) {
284       noHead = true;
285       return;
286     }
287 
288     if ("isbarb" == propertyName) {
289       isBarb = true;
290       return;
291     }
292 
293     if ("rotate45" == propertyName) {
294       isRotated45 = true;
295       return;
296     }
297 
298     if ("crossed" == propertyName) {
299       isCrossed = true;
300       return;
301     }
302 
303     if ("points" == propertyName) {
304       newScale = ((Integer) value).floatValue() / 100;
305       if (newScale == 0)
306         newScale = 1;
307       return;
308     }
309 
310     if ("scale" == propertyName) {
311       newScale = ((Integer) value).floatValue() / 100;
312       if (newScale == 0)
313         newScale = 0.01f; // very tiny but still sizable;
314       if (thisMesh != null) {
315         // no points in this script statement
316         scale(thisMesh, newScale);
317         thisMesh.initialize(T.fullylit, null, null);
318       }
319       return;
320     }
321 
322     if ("diameter" == propertyName) {
323       diameter = ((Float) value).intValue();
324       return;
325     }
326 
327     if ("width" == propertyName) {
328       width = ((Float) value).floatValue();
329       return;
330     }
331 
332     if ("identifier" == propertyName) {
333       String thisID = (String) value;
334       int meshIndex = getIndexFromName(thisID);
335       if (meshIndex >= 0) {
336         vData.addLast(new Object[] { Integer.valueOf(PT_IDENTIFIER),
337             new int[] { meshIndex, isReversed ? 1 : 0, isVertices ? 1 : 0 } });
338         isReversed = isVertices = false;
339         //nidentifiers++;
340       } else {
341         Logger.error("draw identifier " + value + " not found");
342         isValid = false;
343       }
344       return;
345     }
346 
347     if ("polygon" == propertyName) {
348       polygon = (Lst<Object>) value;
349       if (polygon == null)
350         polygon = new Lst<Object>();
351       return;
352     }
353 
354     if ("coord" == propertyName) {
355       vData.addLast(new Object[] { Integer.valueOf(PT_COORD), value });
356       if (indicatedModelIndex >= 0)
357         modelInfo[1]++; // counts vertices
358       return;
359     }
360 
361     if ("offset" == propertyName) {
362       offset = V3.newV((P3) value);
363       if (thisMesh != null)
364         thisMesh.offset(offset);
365       return;
366     }
367 
368     if ("atomSet" == propertyName) {
369       BS bsAtoms = (BS) value;
370       if (bsAtoms.isEmpty())
371         return;
372       vData.addLast(new Object[] { Integer.valueOf(PT_BITSET), bsAtoms });
373       //nbitsets++;
374       if (isCircle && diameter == 0 && width == 0)
375         width = vwr.ms.calcRotationRadiusBs(bsAtoms) * 2.0f;
376       return;
377     }
378 
379     if ("coords" == propertyName) {
380       addPoints(PT_COORD, value);
381       return;
382     }
383 
384     if ("modelBasedPoints" == propertyName) {
385       addPoints(PT_MODEL_BASED_POINTS, value);
386       return;
387     }
388 
389     if ("set" == propertyName) {
390       if (thisMesh == null) {
391         allocMesh(null, null);
392         thisMesh.colix = colix;
393         thisMesh.color = color;
394       }
395       thisMesh.isValid = (isValid ? setDrawing((int[]) value) : false);
396       if (thisMesh.isValid) {
397         if (thisMesh.vc > 2 && length != Float.MAX_VALUE && newScale == 1)
398           newScale = length;
399         scale(thisMesh, newScale);
400         thisMesh.initialize(T.fullylit, null, null);
401         setAxes(thisMesh);
402         thisMesh.title = title;
403         thisMesh.visible = true;
404       }
405       nPoints = -1; // for later scaling
406       vData = null;
407       lineData = null;
408       return;
409     }
410 
411     if (propertyName == "deleteModelAtoms") {
412       deleteModels(((int[]) ((Object[]) value)[2])[0]);
413       return;
414     }
415 
416     setPropertySuper(propertyName, value, bs);
417   }
418 
addPoints(int type, Object value)419   private void addPoints(int type, Object value) {
420     @SuppressWarnings("unchecked")
421     Lst<SV> pts = (Lst<SV>) value;
422     Integer key = Integer.valueOf(type);
423     boolean isModelPoints = (type == PT_MODEL_BASED_POINTS);
424     if (isModelPoints)
425       vData.addLast(new Object[] { key, pts });
426     for (int i = 0, n = pts.size(); i < n; i++) {
427       Object o = pts.get(i);
428       P3 pt;
429       if (o instanceof P3) {
430         // from Draw HKL
431         pt = (P3) o;
432       } else {
433         SV v = (SV) o;
434         switch (v.tok) {
435         case T.bitset:
436           if (!isModelPoints && ((BS) v.value).isEmpty())
437             continue;
438           pt = vwr.ms.getAtomSetCenter((BS) v.value);
439           break;
440         case T.point3f:
441           if (isModelPoints)
442             continue;
443           //$FALL-THROUGH$
444         default:
445           pt = SV.ptValue(v);
446         }
447       }
448       if (isModelPoints) {
449         pts.set(i, SV.getVariable(pt));
450       } else {
451         vData.addLast(new Object[] { key, pt });
452       }
453     }
454   }
455 
deleteModels(int modelIndex)456 private void deleteModels(int modelIndex) {
457    //int firstAtomDeleted = ((int[])((Object[])value)[2])[1];
458    //int nAtomsDeleted = ((int[])((Object[])value)[2])[2];
459    for (int i = meshCount; --i >= 0;) {
460      DrawMesh m = dmeshes[i];
461      if (m == null)
462        continue;
463      boolean deleteMesh = (m.modelIndex == modelIndex);
464      if (m.modelFlags != null) {
465        m.deleteAtoms(modelIndex);
466        deleteMesh = (m.modelFlags.length() == 0);
467        if (!deleteMesh)
468          continue;
469      }
470      if (deleteMesh) {
471        meshCount--;
472        deleteMeshElement(i);
473      } else if (meshes[i].modelIndex > modelIndex) {
474        meshes[i].modelIndex--;
475      }
476    }
477    resetObjects();
478   }
479 
deleteMeshElement(int i)480  private void deleteMeshElement(int i) {
481    if (meshes[i] == currentMesh)
482      currentMesh = thisMesh = null;
483    meshes = dmeshes = (DrawMesh[]) AU
484        .deleteElements(meshes, i, 1);
485 }
486 
initDraw()487 private void initDraw() {
488    boundBox = null;
489    bsAllModels = null;
490    colix = C.ORANGE;
491    color = 0xFFFFFFFF;
492    diameter = 0;
493    explicitID = false;
494    indicatedModelIndex = -1;
495    intersectID = null;
496    isCurve = isArc = isArrow = isPlane = isCircle = isCylinder = isLine = false;
497    isFixed = isReversed = isRotated45 = isCrossed = noHead = isBarb = false;
498    isPerpendicular = isVertices = isVector = false;
499    isValid = true;
500    length = Float.MAX_VALUE;
501    lineData = null;
502    newScale = 0;
503    //nidentifiers = nbitsets = 0;
504    offset = null;
505    plane = null;
506    polygon = null;
507    slabData = null;
508    vData = new  Lst<Object[]>();
509    width = 0;
510    setPropertySuper("thisID", MeshCollection.PREVIOUS_MESH_ID, null);
511   }
512 
513   @Override
getPropertyData(String property, Object[] data)514   public boolean getPropertyData(String property, Object[] data) {
515     if (property == "keys") {
516       @SuppressWarnings("unchecked")
517       Lst<String> keys = (data[1] instanceof Lst<?> ? (Lst<String>) data[1] : new Lst<String>());
518       data[1] = keys;
519       keys.addLast("getSpinAxis");
520       // will continue on to super
521     }
522     if (property == "getCenter") {
523       String id = (String) data[0];
524       int index = ((Integer) data[1]).intValue();
525       int modelIndex = ((Integer) data[2]).intValue();
526       data[2] = getSpinCenter(id, index, modelIndex);
527       return (data[2] != null);
528     }
529     if (property == "getSpinAxis") {
530       String id = (String) data[0];
531       int index = ((Integer) data[1]).intValue();
532       data[2] =  getSpinAxis(id, index);
533       return (data[2] != null);
534     }
535     return getPropDataMC(property, data);
536   }
537 
538   @Override
getProperty(String property, int index)539   public Object getProperty(String property, int index) {
540     DrawMesh m = thisMesh;
541     if (index >= 0 && (index >= meshCount || (m = (DrawMesh) meshes[index]) == null))
542       return null;
543     if (property == "command")
544       return getCommand(m);
545     if (property == "type")
546       return Integer.valueOf(m == null ? EnumDrawType.NONE.id : m.drawType.id);
547     return getPropMC(property, index);
548   }
549 
getSpinCenter(String axisID, int vertexIndex, int modelIndex)550   private T3 getSpinCenter(String axisID, int vertexIndex, int modelIndex) {
551     String id;
552     int pt = axisID.indexOf("[");
553     int pt2;
554     if (pt > 0) {
555       id = axisID.substring(0, pt);
556       if ((pt2 = axisID.lastIndexOf("]")) < pt)
557         pt2 = axisID.length();
558       try {
559         vertexIndex = Integer.parseInt(axisID.substring(pt + 1, pt2));
560       } catch (Exception e) {
561         // ignore
562       }
563     } else {
564       id = axisID;
565     }
566     DrawMesh m = (DrawMesh) getMesh(id);
567     if (m == null || m.vs == null)
568       return null;
569     // >= 0 ? that vertexIndex
570     // < 0 and no ptCenters or modelIndex < 0 -- center point
571     // < 0 center for modelIndex
572     if (vertexIndex == Integer.MAX_VALUE)
573       return P3.new3(m.index + 1, meshCount, m.vc);
574     if (vertexIndex != Integer.MIN_VALUE)
575       vertexIndex = m.getVertexIndexFromNumber(vertexIndex);
576     return (vertexIndex >= 0 ? m.vs[vertexIndex] : m.ptCenters == null
577         || modelIndex < 0 || modelIndex >= m.ptCenters.length
578         ? m.ptCenter : m.ptCenters[modelIndex]);
579   }
580 
getSpinAxis(String axisID, int modelIndex)581   private V3 getSpinAxis(String axisID, int modelIndex) {
582     DrawMesh m = (DrawMesh) getMesh(axisID);
583     return (m == null || m.vs == null ? null
584         : m.ptCenters == null || modelIndex < 0 ? m.axis : m.axes[modelIndex]);
585    }
586 
setDrawing(int[] connections)587   private boolean setDrawing(int[] connections) {
588     if (thisMesh == null)
589       allocMesh(null, null);
590     thisMesh.clear("draw");
591     thisMesh.diameter = diameter;
592     thisMesh.width = width;
593     if (intersectID != null || boundBox != null) {
594       if (boundBox != null) {
595         // TODO
596         if (plane == null) {
597 
598         }
599       } else if (plane != null && intersectID != null) {
600         Lst<P3[]> vData = new Lst<P3[]>();
601         Object[] data = new Object[] { intersectID, plane, vData, null };
602         vwr.shm.getShapePropertyData(JC.SHAPE_ISOSURFACE, "intersectPlane",
603             data);
604         if (vData.size() > 0) {
605           indicatedModelIndex = ((Integer) data[3]).intValue();
606           lineData = vData;
607         }
608       }
609     } else if (slabData != null && plane != null) {
610       slabData.getMeshSlicer().getIntersection(0, plane, null, null, null,
611           null, null, false, true, T.plane, false);
612       polygon = new Lst<Object>();
613       polygon.addLast(slabData.vs);
614       polygon.addLast(slabData.pis);
615     }
616     if (polygon == null
617         && (lineData != null ? lineData.size() == 0
618             : (vData.size() == 0) == (connections == null)) || !isArrow
619         && connections != null)
620       return false; // connections only for arrows at this point
621     int modelCount = vwr.ms.mc;
622     if (polygon != null
623         || lineData != null
624         || indicatedModelIndex < 0
625         && (isFixed || isArrow || isCurve || isCircle || isCylinder || modelCount == 1)) {
626       // make just ONE copy
627 
628       // arrows and curves simply can't be handled as
629       // multiple frames yet
630       thisMesh.modelIndex = (lineData == null ? vwr.am.cmi
631           : indicatedModelIndex);
632       thisMesh.isFixed = (isFixed || lineData == null
633           && thisMesh.modelIndex < 0 && modelCount > 1);
634       if (isFixed && modelCount > 1)
635         thisMesh.modelIndex = -1;
636       else if (lineData == null && thisMesh.modelIndex < 0)
637         thisMesh.modelIndex = 0;
638       thisMesh.ptCenters = null;
639       thisMesh.modelFlags = null;
640       thisMesh.drawTypes = null;
641       thisMesh.drawVertexCounts = null;
642       thisMesh.connectedAtoms = connections;
643       if (polygon != null) {
644         if (polygon.size() == 0)
645           return false;
646         thisMesh.isDrawPolygon = true;
647         thisMesh.vs = (P3[]) polygon.get(0);
648         thisMesh.pis = (int[][]) polygon.get(1);
649         thisMesh.drawVertexCount = thisMesh.vc = thisMesh.vs.length;
650         thisMesh.pc = (thisMesh.pis == null ? -1 : thisMesh.pis.length);
651         for (int i = 0; i < thisMesh.pc; i++) {
652           for (int j = 0; j < 3; j++)
653             if (thisMesh.pis[i][j] >= thisMesh.vc)
654               return false;
655         }
656         thisMesh.drawType = EnumDrawType.POLYGON;
657         thisMesh.checkByteCount = 1;
658       } else if (lineData != null) {
659         thisMesh.lineData = lineData;
660       } else {
661         thisMesh.setPolygonCount(1);
662         if (setPoints(-1, -1))
663           setPoints(-1, nPoints);
664         setPolygon(0);
665       }
666     } else {
667       // multiple copies, one for each model involved
668       thisMesh.modelIndex = -1;
669       thisMesh.setPolygonCount(modelCount);
670       thisMesh.ptCenters = new P3[modelCount];
671       thisMesh.modelFlags = new BS();
672       thisMesh.drawTypes = new EnumDrawType[modelCount];
673       thisMesh.drawVertexCounts = new int[modelCount];
674       thisMesh.vc = 0;
675       if (indicatedModelIndex >= 0) {
676         setPoints(-1, 0);
677         thisMesh.drawType = EnumDrawType.MULTIPLE;
678         thisMesh.drawVertexCount = -1;
679         thisMesh.modelFlags.set(indicatedModelIndex);
680         indicatedModelIndex = -1;
681       } else {
682         BS bsModels = vwr.getVisibleFramesBitSet();
683         for (int iModel = 0; iModel < modelCount; iModel++) {
684           if (bsModels.get(iModel) && setPoints(iModel, -1)) {
685             setPoints(iModel, nPoints);
686             setPolygon(iModel);
687             thisMesh.setCenter(iModel);
688             thisMesh.drawTypes[iModel] = thisMesh.drawType;
689             thisMesh.drawVertexCounts[iModel] = thisMesh.drawVertexCount;
690             thisMesh.drawType = EnumDrawType.MULTIPLE;
691             thisMesh.drawVertexCount = -1;
692             thisMesh.modelFlags.set(iModel);
693           } else {
694             thisMesh.drawTypes[iModel] = EnumDrawType.NONE;
695             thisMesh.pis[iModel] = new int[0];
696           }
697         }
698       }
699     }
700     thisMesh.isVector = isVector;
701     thisMesh.noHead = noHead;
702     thisMesh.isBarb = isBarb;
703     thisMesh.width = (thisMesh.drawType == EnumDrawType.CYLINDER
704         || thisMesh.drawType == EnumDrawType.CIRCULARPLANE ? -Math.abs(width)
705         : width);
706     thisMesh.setCenter(-1);
707     if (offset != null)
708       thisMesh.offset(offset);
709     if (thisMesh.thisID == null) {
710       thisMesh.thisID = thisMesh.drawType.name + (++nUnnamed);
711       htObjects.put(thisMesh.thisID, thisMesh);
712     }
713     clean();
714     return true;
715   }
716 
717   @Override
clean()718   protected void clean() {
719     for (int i = meshCount; --i >= 0;)
720       if (meshes[i] == null || meshes[i].vc == 0
721           && meshes[i].connectedAtoms == null && meshes[i].lineData == null)
722         deleteMeshI(i);
723   }
724 
725   MeshSurface slabData;
726 
addPoint(T3 newPt, int iModel)727   private void addPoint(T3 newPt, int iModel) {
728     if (makePoints) {
729       if (newPt == null || iModel >= 0 && !bsAllModels.get(iModel))
730         return;
731       ptList[nPoints] = P3.newP(newPt);
732       if (newPt.z == Float.MAX_VALUE || newPt.z == -Float.MAX_VALUE)
733         thisMesh.haveXyPoints = true;
734     } else if (iModel >= 0) {
735       bsAllModels.set(iModel);
736     }
737     nPoints++;
738   }
739 
setPoints(int iModel, int n)740   private boolean setPoints(int iModel, int n) {
741     // {x,y,z} points are already defined in ptList
742     // $drawID references may be fixed or not
743     // Prior to 11.5.37, points were created in the order:
744     //  1) all {x,y,z} points
745     //  2) all $drawID points
746     //  3) all {atomExpression} points
747     //  4) all {atomExpression}.split() points
748     // Order is only important when there are four points,
749     // where they may become crossed, so
750     // we also provide a flag CROSSED to uncross them
751     this.makePoints = (n >= 0);
752     if (makePoints) {
753       ptList = new P3[Math.max(5,n)];
754       if (bsAllModels == null)
755         bsAllModels = vwr.getVisibleFramesBitSet();
756     }
757     nPoints = 0;
758     int nData = vData.size();
759     int modelIndex = 0;
760     BS bs;
761     BS bsModel = (iModel < 0 ? null : vwr.getModelUndeletedAtomsBitSet(iModel));
762     for (int i = 0; i < nData; i++) {
763       Object[] info = vData.get(i);
764       switch (((Integer) info[0]).intValue()) {
765       case PT_MODEL_INDEX:
766         // from the saved state
767         int[] modelInfo = (int[]) info[1];
768         modelIndex = modelInfo[0];
769         nPoints = modelInfo[1];
770         int nVertices = Math.max(nPoints, 3);
771         int n0 = thisMesh.vc;
772         if (nPoints > 0) {
773           int[] p = thisMesh.pis[modelIndex] = new int[nVertices];
774           for (int j = 0; j < nPoints; j++) {
775             info = vData.get(++i);
776             p[j] = thisMesh.addV((P3) info[1], false);
777           }
778           for (int j = nPoints; j < 3; j++) {
779             p[j] = n0 + nPoints - 1;
780           }
781           thisMesh.drawTypes[modelIndex] = EnumDrawType.getType(nPoints);
782           thisMesh.drawVertexCounts[modelIndex] = nPoints;
783           thisMesh.modelFlags.set(modelIndex);
784         }
785         break;
786       case PT_COORD:
787         addPoint((P3) info[1], (makePoints ? iModel : -1));
788         break;
789       case PT_BITSET:
790         // (atom set) references must be filtered for relevant model
791         // note that if a model doesn't have a relevant point, one may
792         // get a line instead of a plane, a point instead of a line, etc.
793         bs = BSUtil.copy((BS) info[1]);
794         if (bsModel != null)
795           bs.and(bsModel);
796         if (bs.length() > 0)
797           addPoint(vwr.ms.getAtomSetCenter(bs), (makePoints ? iModel : -1));
798         break;
799       case PT_IDENTIFIER:
800         int[] idInfo = (int[]) info[1];
801         DrawMesh m = dmeshes[idInfo[0]];
802         boolean isReversed = (idInfo[1] == 1);
803         boolean isVertices = (idInfo[2] == 1);
804         if (m.modelIndex > 0 && m.modelIndex != iModel)
805           return false;
806         if (bsAllModels == null)
807           bsAllModels = new BS();
808         if (isPlane && !isCircle || isPerpendicular || isVertices) {
809           if (isReversed) {
810             if (iModel < 0 || iModel >= m.pc)
811               for (int ipt = m.drawVertexCount; --ipt >= 0;)
812                 addPoint(m.vs[ipt], iModel);
813             else if (m.pis[iModel] != null)
814               for (int ipt = m.drawVertexCounts[iModel]; --ipt >= 0;)
815                 addPoint(m.vs[m.pis[iModel][ipt]], iModel);
816           } else {
817             if (iModel < 0 || iModel >= m.pc)
818               for (int ipt = 0; ipt < m.drawVertexCount; ipt++)
819                 addPoint(m.vs[ipt], iModel);
820             else if (m.pis[iModel] != null)
821               for (int ipt = 0; ipt < m.drawVertexCounts[iModel]; ipt++)
822                 addPoint(m.vs[m.pis[iModel][ipt]], iModel);
823           }
824         } else {
825           if (iModel < 0 || m.ptCenters == null || m.ptCenters[iModel] == null)
826             addPoint(m.ptCenter, iModel);
827           else
828             addPoint(m.ptCenters[iModel], iModel);
829         }
830         break;
831       case PT_MODEL_BASED_POINTS:
832         // from list variables
833         @SuppressWarnings("unchecked")
834         Lst<SV> modelBasedPoints = (Lst<SV>)info[1];
835         if (bsAllModels == null)
836           bsAllModels = new BS();
837         for (int j = 0; j < modelBasedPoints.size(); j++)
838           if (iModel < 0 || j == iModel) {
839             Object point = modelBasedPoints.get(j);
840             bsAllModels.set(j);
841             if (point instanceof P3) {
842               addPoint((P3) point, j);
843             } else if (point instanceof BS) {
844               bs = (BS) point;
845               if (bsModel != null)
846                 bs.and(bsModel);
847               if (bs.length() > 0)
848                 addPoint(vwr.ms.getAtomSetCenter(bs), j);
849             } else if (point instanceof SV) {
850               addPoint(SV.ptValue((SV) point), j);
851             }
852           }
853         break;
854       }
855     }
856     if (makePoints && isCrossed && nPoints == 4) {
857       P3 pt = ptList[1];
858       ptList[1] = ptList[2];
859       ptList[2] = pt;
860     }
861     return (nPoints > 0);
862   }
863 
864   private final V3 vAB = new V3();
865 
setPolygon(int nPoly)866   private void setPolygon(int nPoly) {
867     int nVertices = nPoints;
868     EnumDrawType drawType = EnumDrawType.POINT;
869     if (isArc) {
870       if (nVertices >= 2) {
871         drawType = EnumDrawType.ARC;
872       } else {
873         isArc = false;
874         isVector = false;
875         isCurve = false;
876         isArrow = true;
877       }
878     }
879     if (isCircle) {
880       length = 0;
881       if (nVertices == 2)
882         isPlane = true;
883       if (!isPlane)
884         drawType = EnumDrawType.CIRCLE;
885       if (width == 0)
886         width = 1;
887     } else if ((isCurve || isArrow) && nVertices >= 2 && !isArc) {
888       drawType = (isLine ? EnumDrawType.LINE_SEGMENT
889           : isCurve ? EnumDrawType.CURVE : EnumDrawType.ARROW);
890     }
891     if (isVector && !isArc) {
892       if (nVertices > 2)
893         nVertices = 2;
894       else if (plane == null && nVertices != 2)
895         isVector = false;
896     }
897     if (thisMesh.haveXyPoints) {
898       isPerpendicular = false;
899       if (nVertices == 3 && isPlane)
900         isPlane = false;
901       length = Float.MAX_VALUE;
902       if (isVector)
903         thisMesh.diameter = 0;
904     } else if (nVertices == 2 && isVector) {
905       ptList[1].add(ptList[0]);
906     }
907     float dist = 0;
908     if (isArc || plane != null && isCircle) {
909       if (plane != null) {
910         dist = Measure.distanceToPlane(plane, ptList[0]);
911         V3 vAC = V3.new3(-plane.x, -plane.y, -plane.z);
912         vAC.normalize();
913         if (dist < 0)
914           vAC.scale(-1);
915         if (isCircle) {
916           vAC.scale(0.005f);
917           ptList[0].sub(vAC);
918           vAC.scale(2);
919         }
920         vAC.add(ptList[0]);
921         ptList[1] = P3.newP(vAC);
922         drawType = (isArrow ? EnumDrawType.ARROW
923             : isArc ? EnumDrawType.ARC : EnumDrawType.CIRCULARPLANE);
924       }
925       if (isArc) {
926         dist = Math.abs(dist);
927         if (nVertices > 3) {
928           // draw arc {center} {pt2} {ptRef} {angleOffset theta
929           // fractionalAxisOffset}
930         } else if (nVertices == 3) {
931           // draw arc {center} {pt2} {angleOffset theta fractionalAxisOffset}
932           ptList[3] = P3.newP(ptList[2]);
933           ptList[2] = randomPoint();
934         } else {
935           if (nVertices == 2) {
936             // draw arc {center} {pt2}
937             ptList[2] = randomPoint();
938           }
939           ptList[3] = P3.new3(0, 360, 0);
940         }
941         if (plane != null)
942           ptList[3].z *= dist;
943         nVertices = 4;
944       }
945       plane = null;
946     } else if (drawType == EnumDrawType.POINT) {
947       P3 pt;
948       P3 center = new P3();
949       V3 normal = new V3();
950       if (nVertices == 2 && plane != null) {
951         ptList[1] = P3.newP(ptList[0]);
952         V3 vTemp = new V3();
953         Measure.getPlaneProjection(ptList[1], plane, ptList[1], vTemp);
954         nVertices = -2;
955         if (isArrow)
956           drawType = EnumDrawType.ARROW;
957         plane = null;
958       }
959       if (nVertices == 3 && isPlane && !isPerpendicular) {
960         // three points define a plane
961         pt = P3.newP(ptList[1]);
962         pt.sub(ptList[0]);
963         pt.scale(0.5f);
964         ptList[3] = P3.newP(ptList[2]);
965         ptList[2].add(pt);
966         ptList[3].sub(pt);
967         nVertices = 4;
968       } else if (nVertices >= 3 && !isPlane && isPerpendicular) {
969         // normal to plane
970         Measure.calcNormalizedNormal(ptList[0], ptList[1], ptList[2], normal,
971             vAB);
972         center = new P3();
973         Measure.calcAveragePointN(ptList, nVertices, center);
974         dist = (length == Float.MAX_VALUE ? ptList[0].distance(center) : length);
975         normal.scale(dist);
976         ptList[0].setT(center);
977         ptList[1].add2(center, normal);
978         nVertices = 2;
979       } else if (nVertices == 2 && isPerpendicular) {
980         // perpendicular line to line or plane to line
981         Measure.calcAveragePoint(ptList[0], ptList[1], center);
982         dist = (length == Float.MAX_VALUE ? ptList[0].distance(center) : length);
983         if (isPlane && length != Float.MAX_VALUE)
984           dist /= 2f;
985         if (isPlane && isRotated45)
986           dist *= 1.4142f;
987         Measure.getNormalToLine(ptList[0], ptList[1], normal);
988         normal.scale(dist);
989         if (isPlane) {
990           ptList[2] = P3.newP(center);
991           ptList[2].sub(normal);
992           pt = P3.newP(center);
993           pt.add(normal);
994           // pt
995           // |
996           // 0-------+--------1
997           // |
998           // 2
999           Measure.calcNormalizedNormal(ptList[0], ptList[1], ptList[2], normal,
1000               vAB);
1001           normal.scale(dist);
1002           ptList[3] = P3.newP(center);
1003           ptList[3].add(normal);
1004           ptList[1].sub2(center, normal);
1005           ptList[0].setT(pt);
1006           //
1007           // pt,0 1
1008           // |/
1009           // -------+--------
1010           // /|
1011           // 3 2
1012 
1013           if (isRotated45) {
1014             Measure.calcAveragePoint(ptList[0], ptList[1], ptList[0]);
1015             Measure.calcAveragePoint(ptList[1], ptList[2], ptList[1]);
1016             Measure.calcAveragePoint(ptList[2], ptList[3], ptList[2]);
1017             Measure.calcAveragePoint(ptList[3], pt, ptList[3]);
1018           }
1019           nVertices = 4;
1020         } else {
1021           ptList[0].sub2(center, normal);
1022           ptList[1].add2(center, normal);
1023         }
1024         if (isArrow && nVertices != -2)
1025           isArrow = false;
1026       } else if (nVertices == 2 && length != Float.MAX_VALUE) {
1027         Measure.calcAveragePoint(ptList[0], ptList[1], center);
1028         normal.sub2(ptList[1], center);
1029         normal.scale(0.5f / normal.length() * (length == 0 ? 0.01f : length));
1030         if (length == 0)
1031           center.setT(ptList[0]);
1032         ptList[0].sub2(center, normal);
1033         ptList[1].add2(center, normal);
1034       }
1035       if (nVertices > 4)
1036         nVertices = 4; // for now
1037 
1038       switch (nVertices) {
1039       case -2:
1040         nVertices = 2;
1041         break;
1042       case 1:
1043         break;
1044       case 2:
1045         drawType = (isArc ? EnumDrawType.ARC
1046             : isPlane && isCircle ? EnumDrawType.CIRCULARPLANE
1047                 : isCylinder ? EnumDrawType.CYLINDER
1048                     : EnumDrawType.LINE);
1049         break;
1050       default:
1051         drawType = (thisMesh.connectedAtoms == null ? EnumDrawType.PLANE
1052             : EnumDrawType.ARROW);
1053       }
1054     }
1055     thisMesh.drawType = drawType;
1056     thisMesh.drawVertexCount = nVertices;
1057 
1058     if (nVertices == 0)
1059       return;
1060     int nVertices0 = thisMesh.vc;
1061     for (int i = 0; i < nVertices; i++) {
1062       thisMesh.addV(ptList[i], false);
1063     }
1064     int npoints = (nVertices < 3 ? 3 : nVertices);
1065     thisMesh.setPolygonCount(nPoly + 1);
1066     thisMesh.pis[nPoly] = new int[npoints];
1067     for (int i = 0; i < npoints; i++) {
1068       thisMesh.pis[nPoly][i] = nVertices0
1069           + (i < nVertices ? i : nVertices - 1);
1070     }
1071     return;
1072   }
1073 
scale(Mesh mesh, float newScale)1074   private void scale(Mesh mesh, float newScale) {
1075     DrawMesh dmesh = (DrawMesh) mesh;
1076     /*
1077      * allows for Draw to scale object
1078      * have to watch out for double-listed vertices
1079      *
1080      */
1081     if (newScale == 0 || dmesh.vc == 0 && dmesh.connectedAtoms == null
1082         || dmesh.scale == newScale)
1083       return;
1084     float f = newScale / dmesh.scale;
1085     dmesh.scale = newScale;
1086     dmesh.isScaleSet = true;
1087     if (dmesh.isRenderScalable())
1088       return; // done in renderer
1089     V3 diff = new V3();
1090     int iptlast = -1;
1091     int ipt = 0;
1092     try {
1093       for (int i = dmesh.pc; --i >= 0;) {
1094         T3 center = (dmesh.isVector ? dmesh.vs[0]
1095             : dmesh.ptCenters == null ? dmesh.ptCenter : dmesh.ptCenters[i]);
1096         if (center == null)
1097           return;
1098         if (dmesh.pis[i] == null)
1099           continue;
1100         iptlast = -1;
1101         for (int iV = dmesh.pis[i].length; --iV >= 0;) {
1102           ipt = dmesh.pis[i][iV];
1103           if (ipt == iptlast)
1104             continue;
1105           iptlast = ipt;
1106           diff.sub2(dmesh.vs[ipt], center);
1107           diff.scale(f);
1108           diff.add(center);
1109           dmesh.vs[ipt].setT(diff);
1110         }
1111       }
1112     } catch (Exception e) {
1113       Logger.info("Error executing DRAW command: " + e);
1114       dmesh.isValid = false;
1115     }
1116   }
1117 
setAxes(DrawMesh m)1118   private final static void setAxes(DrawMesh m) {
1119     m.axis = V3.new3(0, 0, 0);
1120     m.axes = new V3[m.pc > 0 ? m.pc : 1];
1121     if (m.vs == null)
1122       return;
1123     int n = 0;
1124     for (int i = m.pc; --i >= 0;) {
1125       int[] p = m.pis[i];
1126       m.axes[i] = new V3();
1127       if (p == null || p.length == 0) {
1128       } else if (m.drawVertexCount == 2 || m.drawVertexCount < 0
1129           && m.drawVertexCounts[i] == 2) {
1130         m.axes[i].sub2(m.vs[p[0]],
1131             m.vs[p[1]]);
1132         n++;
1133       } else {
1134         Measure.calcNormalizedNormal(m.vs[p[0]],
1135             m.vs[p[1]],
1136             m.vs[p[2]], m.axes[i], m.vAB);
1137         n++;
1138       }
1139       m.axis.add(m.axes[i]);
1140     }
1141     if (n == 0)
1142       return;
1143     m.axis.scale(1f / n);
1144   }
1145 
1146   @Override
setModelVisibilityFlags(BS bsModels)1147   public void setModelVisibilityFlags(BS bsModels) {
1148     /*
1149      * set all fixed objects visible; others based on model being displayed note
1150      * that this is NOT done with atoms and bonds, because they have mads. When
1151      * you say "frame 0" it is just turning on all the mads.
1152      */
1153     for (int i = 0; i < meshCount; i++) {
1154       DrawMesh m = dmeshes[i];
1155       if (m == null) {
1156         continue;
1157       }
1158       m.visibilityFlags = (m.isValid && m.visible ? vf : 0);
1159       if (m.modelIndex >= 0 && !bsModels.get(m.modelIndex) || m.modelFlags != null
1160           && !BSUtil.haveCommon(bsModels, m.modelFlags)) {
1161         m.visibilityFlags = 0;
1162       } else if (m.modelFlags != null) {
1163         m.bsMeshesVisible.clearAll();
1164         m.bsMeshesVisible.or(m.modelFlags);
1165         m.bsMeshesVisible.and(bsModels);
1166       }
1167 
1168     }
1169   }
1170 
1171   private final static int MAX_OBJECT_CLICK_DISTANCE_SQUARED = 10 * 10;
1172 
1173   private final P3i ptXY = new P3i();
1174 
1175   @Override
checkObjectClicked(int x, int y, int action, BS bsVisible, boolean drawPicking)1176   public Map<String, Object> checkObjectClicked(int x, int y, int action,
1177                                                 BS bsVisible,
1178                                                 boolean drawPicking) {
1179     boolean isPickingMode = (vwr.getPickingMode() == ActionManager.PICKING_DRAW);
1180     boolean isSpinMode = (vwr.getPickingMode() == ActionManager.PICKING_SPIN);
1181     if (!isPickingMode && !drawPicking && !isSpinMode
1182         || C.isColixTranslucent(colix))
1183       return null;
1184     if (!findPickedObject(x, y, false, bsVisible))
1185       return null;
1186     T3 v = pickedMesh.vs[pickedMesh.pis[pickedModel][pickedVertex]];
1187     int modelIndex = pickedMesh.modelIndex;
1188     BS bs = ((DrawMesh) pickedMesh).modelFlags;
1189     if (modelIndex < 0 && BSUtil.cardinalityOf(bs) == 1)
1190       modelIndex = bs.nextSetBit(0);
1191     Map<String, Object> map = null;
1192     if (action != 0)
1193       map = getPickedPoint(v, modelIndex);
1194     if (drawPicking && !isPickingMode) {
1195       if (action != 0) // not mouseMove
1196         setStatusPicked(-2, v, map);
1197       return getPickedPoint(v, modelIndex);
1198     }
1199     if (action == 0
1200         || pickedMesh.pis[pickedModel][0] == pickedMesh.pis[pickedModel][1]) {
1201       return map;
1202     }
1203     boolean isClockwise = vwr.isBound(action,
1204         ActionManager.ACTION_spinDrawObjectCW);
1205     if (pickedVertex == 0) {
1206       vwr.startSpinningAxis(
1207           pickedMesh.vs[pickedMesh.pis[pickedModel][1]],
1208           pickedMesh.vs[pickedMesh.pis[pickedModel][0]],
1209           isClockwise);
1210     } else {
1211       vwr.startSpinningAxis(
1212           pickedMesh.vs[pickedMesh.pis[pickedModel][0]],
1213           pickedMesh.vs[pickedMesh.pis[pickedModel][1]],
1214           isClockwise);
1215     }
1216     return getPickedPoint(null, 0);
1217   }
1218 
1219   @Override
checkObjectHovered(int x, int y, BS bsVisible)1220   public boolean checkObjectHovered(int x, int y, BS bsVisible) {
1221     if (!vwr.getDrawHover())
1222       return false;
1223     if (C.isColixTranslucent(colix))
1224       return false;
1225     if (!findPickedObject(x, y, false, bsVisible))
1226       return false;
1227     if (vwr.gdata.antialiasEnabled) {
1228       //because hover rendering is done in FIRST pass only
1229       x <<= 1;
1230       y <<= 1;
1231     }
1232     String s = (pickedMesh.title == null ? pickedMesh.thisID
1233         : pickedMesh.title[0]);
1234     if (s.length() > 1 && s.charAt(0) == '>')
1235       s = s.substring(1);
1236     vwr.hoverOnPt(x, y, s, pickedMesh.thisID, pickedPt);
1237     return true;
1238   }
1239 
1240   @Override
checkObjectDragged(int prevX, int prevY, int x, int y, int dragAction, BS bsVisible)1241   public synchronized boolean checkObjectDragged(int prevX, int prevY, int x,
1242                                                  int y, int dragAction,
1243                                                  BS bsVisible) {
1244     //TODO -- can dispense with this first check:
1245     if (vwr.getPickingMode() != ActionManager.PICKING_DRAW)
1246       return false;
1247     boolean moveAll = vwr.isBound(dragAction,
1248         ActionManager.ACTION_dragDrawObject);
1249     boolean movePoint = vwr.isBound(dragAction,
1250         ActionManager.ACTION_dragDrawPoint);
1251     if (!moveAll && !movePoint)
1252       return false;
1253     // mouse down ?
1254     if (prevX == Integer.MIN_VALUE)
1255       return findPickedObject(x, y, true, bsVisible);
1256     // mouse up ?
1257     if (prevX == Integer.MAX_VALUE) {
1258       pickedMesh = null;
1259       return false;
1260     }
1261     if (pickedMesh == null)
1262       return false;
1263     DrawMesh dm = (DrawMesh) pickedMesh;
1264     move2D(dm, dm.pis[pickedModel], pickedVertex, x,
1265         y, moveAll);
1266     thisMesh = dm;
1267     return true;
1268   }
1269 
move2D(DrawMesh mesh, int[] vertexes, int iVertex, int x, int y, boolean moveAll)1270   private void move2D(DrawMesh mesh, int[] vertexes,
1271                       int iVertex, int x, int y,
1272                       boolean moveAll) {
1273     if (vertexes == null || vertexes.length == 0)
1274       return;
1275     if (vwr.gdata.isAntialiased()) {
1276       x <<= 1;
1277       y <<= 1;
1278     }
1279     int action = moveAll ? ActionManager.ACTION_dragDrawObject : ActionManager.ACTION_dragDrawPoint;
1280     if (vwr.acm.userActionEnabled(action)
1281         && !vwr.acm.userAction(action, new Object[] { mesh.thisID, new int[] {x, y, iVertex} } ))
1282       return;
1283     P3 pt = new P3();
1284     int ptVertex = vertexes[iVertex];
1285     P3 coord = P3.newP(mesh.altVertices == null ? mesh.vs[ptVertex] : (P3) mesh.altVertices[ptVertex]);
1286     P3 newcoord = new P3();
1287     V3 move = new V3();
1288     vwr.tm.transformPt3f(coord, pt);
1289     pt.x = x;
1290     pt.y = y;
1291     vwr.tm.unTransformPoint(pt, newcoord);
1292     move.sub2(newcoord, coord);
1293     if (mesh.isDrawPolygon)
1294       iVertex = ptVertex; // operate on entire set of vertices, not just the
1295                           // one for this model
1296     int n = (!moveAll ? iVertex + 1
1297         : mesh.isDrawPolygon ? mesh.vs.length : vertexes.length);
1298     BS bsMoved = new BS();
1299     for (int i = (moveAll ? 0 : iVertex); i < n; i++)
1300       if (moveAll || i == iVertex) {
1301         int k = (mesh.isDrawPolygon ? i : vertexes[i]);
1302         if (bsMoved.get(k))
1303           continue;
1304         bsMoved.set(k);
1305         mesh.vs[k].add(move);
1306       }
1307     if (mesh.altVertices != null)
1308       mesh.recalcAltVertices = true;
1309     mesh.setCenters();
1310   }
1311 
1312   /**
1313    *
1314    * @param x
1315    * @param y
1316    * @param isPicking
1317    *        IGNORED
1318    * @param bsVisible
1319    * @return true if found
1320    */
findPickedObject(int x, int y, boolean isPicking, BS bsVisible)1321   private boolean findPickedObject(int x, int y, boolean isPicking, BS bsVisible) {
1322     int dmin2 = MAX_OBJECT_CLICK_DISTANCE_SQUARED;
1323     if (vwr.gdata.isAntialiased()) {
1324       x <<= 1;
1325       y <<= 1;
1326       dmin2 <<= 1;
1327     }
1328     pickedModel = 0;
1329     pickedVertex = 0;
1330     pickedMesh = null;
1331     for (int i = 0; i < meshCount; i++) {
1332       DrawMesh m = dmeshes[i];
1333       if (m.visibilityFlags != 0) {
1334         int mCount = (m.isDrawPolygon ? m.pc
1335             : m.modelFlags == null ? 1 : vwr.ms.mc);
1336         for (int iModel = mCount; --iModel >= 0;) {
1337           if (m.modelFlags != null
1338               && !m.modelFlags.get(iModel)
1339               || m.pis == null
1340               || !m.isDrawPolygon
1341               && (iModel >= m.pis.length || m.pis[iModel] == null))
1342             continue;
1343           for (int iVertex = (m.isDrawPolygon ? 3
1344               : m.pis[iModel].length); --iVertex >= 0;) {
1345             try {
1346               int iv = m.pis[iModel][iVertex];
1347               T3 pt = (m.altVertices == null ? m.vs[iv]
1348                   : (P3) m.altVertices[iv]);
1349               int d2 = coordinateInRange(x, y, pt, dmin2, ptXY);
1350               if (d2 >= 0) {
1351                 pickedMesh = m;
1352                 dmin2 = d2;
1353                 pickedModel = iModel;
1354                 pickedVertex = iVertex;
1355                 pickedPt = pt;
1356               }
1357             } catch (Exception e) {
1358               System.out.println(e);
1359             }
1360           }
1361         }
1362       }
1363     }
1364     return (pickedMesh != null);
1365   }
1366 
getCommand(Mesh mesh)1367   private String getCommand(Mesh mesh) {
1368     if (mesh != null)
1369       return getCommand2(mesh, mesh.modelIndex);
1370 
1371     SB sb = new SB();
1372     String key = (explicitID && previousMeshID != null
1373         && PT.isWild(previousMeshID) ? previousMeshID : null);
1374     Lst<Mesh> list = getMeshList(key, false);
1375     // counting down on list will be up in order
1376     for (int i = list.size(); --i >= 0;) {
1377       Mesh m = list.get(i);
1378       sb.append(getCommand2(m, m.modelIndex));
1379     }
1380     return sb.toString();
1381   }
1382 
getCommand2(Mesh mesh, int iModel)1383   private String getCommand2(Mesh mesh, int iModel) {
1384     DrawMesh dmesh = (DrawMesh) mesh;
1385     if (!dmesh.isValid ||
1386         dmesh.drawType == EnumDrawType.NONE
1387         && dmesh.lineData == null
1388         && dmesh.drawVertexCount == 0 && dmesh.drawVertexCounts == null
1389         )
1390       return "";
1391     SB str = new SB();
1392     int modelCount = vwr.ms.mc;
1393     if (!dmesh.isFixed && iModel >= 0 && modelCount > 1)
1394       appendCmd(str, "frame " + vwr.getModelNumberDotted(iModel));
1395     str.append("  draw ID ").append(PT.esc(dmesh.thisID));
1396     if (dmesh.isFixed)
1397       str.append(" fixed");
1398     if (iModel < 0)
1399       iModel = 0;
1400     if (dmesh.noHead)
1401       str.append(" noHead");
1402     else if (dmesh.isBarb)
1403       str.append(" barb");
1404     if (dmesh.scale != 1 && dmesh.isScaleSet
1405         && (dmesh.haveXyPoints || dmesh.connectedAtoms != null || dmesh.drawType == EnumDrawType.CIRCLE || dmesh.drawType == EnumDrawType.ARC))
1406       str.append(" scale ").appendF(dmesh.scale);
1407     if (dmesh.width != 0)
1408       str.append(" diameter ").appendF(
1409           (dmesh.drawType == EnumDrawType.CYLINDER ? Math.abs(dmesh.width)
1410               : dmesh.drawType == EnumDrawType.CIRCULARPLANE ? Math
1411                   .abs(dmesh.width * dmesh.scale) : dmesh.width));
1412     else if (dmesh.diameter > 0)
1413       str.append(" diameter ").appendI(dmesh.diameter);
1414     if (dmesh.lineData != null) {
1415       str.append("  lineData [");
1416       int n = dmesh.lineData.size();
1417       for (int j = 0; j < n;) {
1418         P3[] pts = dmesh.lineData.get(j);
1419         String s = Escape.eP(pts[0]);
1420         str.append(s.substring(1, s.length() - 1));
1421         str.append(",");
1422         s = Escape.eP(pts[1]);
1423         str.append(s.substring(1, s.length() - 1));
1424         if (++j < n)
1425           str.append(", ");
1426       }
1427       str.append("]");
1428     } else {
1429       int nVertices = dmesh.drawVertexCount > 0  || dmesh.drawVertexCounts == null ? dmesh.drawVertexCount
1430           : dmesh.drawVertexCounts[iModel >= 0 ? iModel : 0];
1431       switch (dmesh.drawTypes == null || dmesh.drawTypes[iModel] == null ? dmesh.drawType : dmesh.drawTypes[iModel]) {
1432       case NONE:
1433       case MULTIPLE:
1434         break;
1435       case POLYGON:
1436         str.append(" POLYGON ").appendI(nVertices);
1437         break;
1438       case PLANE:
1439         if (nVertices == 4)
1440           str.append(" PLANE");
1441         break;
1442       case LINE_SEGMENT:
1443         str.append(" LINE");
1444         break;
1445       case ARC:
1446         str.append(dmesh.isVector ? " ARROW ARC" : " ARC");
1447         break;
1448       case ARROW:
1449         str.append(dmesh.isVector ? " VECTOR" : " ARROW");
1450         if (dmesh.connectedAtoms != null)
1451           str.append(" connect ").append(Escape.eAI(dmesh.connectedAtoms));
1452         break;
1453       case CIRCLE:
1454         str.append(" CIRCLE");
1455         break;
1456       case CURVE:
1457         str.append(" CURVE");
1458         break;
1459       case CIRCULARPLANE:
1460       case CYLINDER:
1461         str.append(" CYLINDER");
1462         break;
1463       case POINT:
1464         nVertices = 1; // because this might be multiple points
1465         break;
1466       case LINE:
1467         nVertices = 2; // because this might be multiple lines
1468         break;
1469       }
1470       if (dmesh.modelIndex < 0 && !dmesh.isFixed) {
1471         for (int i = 0; i < modelCount; i++)
1472           if (isPolygonDisplayable(dmesh, i)) {
1473             if (nVertices == 0)
1474               nVertices = dmesh.drawVertexCounts[i];
1475             str.append(" [ " + i);
1476             String s = getVertexList(dmesh, i, nVertices);
1477             if (s.indexOf("NaN") >= 0)
1478               return "";
1479             str.append(s);
1480             str.append(" ] ");
1481           }
1482       } else if (dmesh.drawType == EnumDrawType.POLYGON) {
1483         for (int i = 0; i < dmesh.vc; i++)
1484           str.append(" ").append(Escape.eP(dmesh.vs[i]));
1485         str.append(" ").appendI(dmesh.pc);
1486         for (int i = 0; i < dmesh.pc; i++)
1487           if (dmesh.pis[i] == null)
1488             str.append(" [0 0 0 0]");
1489           else
1490             str.append(" ").append(Escape.eAI(dmesh.pis[i]));
1491       } else {
1492         String s = getVertexList(dmesh, iModel, nVertices);
1493         if (s.indexOf("NaN") >= 0)
1494           return "";
1495         str.append(s);
1496       }
1497     }
1498     if (dmesh.mat4 != null) {
1499       V3 v = new V3();
1500       dmesh.mat4.getTranslation(v);
1501       str.append(" offset ").append(Escape.eP(v));
1502     }
1503     if (dmesh.title != null) {
1504       String s = "";
1505       for (int i = 0; i < dmesh.title.length; i++)
1506         s += "|" + dmesh.title[i];
1507       str.append(PT.esc(s.substring(1)));
1508     }
1509     str.append(";\n");
1510     appendCmd(str, dmesh.getState("draw"));
1511     appendCmd(str, getColorCommandUnk("draw", dmesh.colix, translucentAllowed));
1512     return str.toString();
1513   }
1514 
isPolygonDisplayable(Mesh mesh, int i)1515   public static boolean isPolygonDisplayable(Mesh mesh, int i) {
1516     return (i < mesh.pis.length
1517         && mesh.pis[i] != null
1518         && mesh.pis[i].length > 0);
1519   }
1520 
getVertexList(DrawMesh mesh, int iModel, int nVertices)1521   private static String getVertexList(DrawMesh mesh, int iModel, int nVertices) {
1522     String str = "";
1523     try {
1524       if (iModel >= mesh.pis.length)
1525         iModel = 0; // arrows and curves may not have multiple model representations
1526       boolean adjustPt = (mesh.isVector && mesh.drawType != EnumDrawType.ARC);
1527       for (int i = 0; i < nVertices; i++) {
1528         T3 pt = mesh.vs[mesh.pis[iModel][i]];
1529         if (pt.z == Float.MAX_VALUE || pt.z == -Float.MAX_VALUE) {
1530           str += (i == 0 ? " " : " ,") + "[" + (int) pt.x + " " + (int) pt.y + (pt.z < 0 ? " %]" : "]");
1531         } else if (adjustPt && i == 1){
1532           P3 pt1 = P3.newP(pt);
1533           pt1.sub(mesh.vs[mesh.pis[iModel][0]]);
1534           str += " " + Escape.eP(pt1);
1535         } else {
1536           str += " " + Escape.eP(pt);
1537         }
1538       }
1539     } catch (Exception e) {
1540       Logger.error("Unexpected error in Draw.getVertexList");
1541     }
1542     return str;
1543   }
1544 
1545   @Override
getShapeDetail()1546   public Object getShapeDetail() {
1547     Lst<Map<String, Object>> V = new  Lst<Map<String,Object>>();
1548     for (int i = 0; i < meshCount; i++) {
1549       DrawMesh mesh = dmeshes[i];
1550       if (mesh.vc == 0)
1551         continue;
1552       Map<String, Object> info = new Hashtable<String, Object>();
1553       info.put("visible", mesh.visible ? Boolean.TRUE : Boolean.FALSE);
1554       info.put("fixed", mesh.ptCenters == null ? Boolean.TRUE : Boolean.FALSE);
1555       info.put("ID", (mesh.thisID == null ? "<noid>" : mesh.thisID));
1556       info.put("drawType", mesh.drawType.name);
1557       if (mesh.diameter > 0)
1558         info.put("diameter", Integer.valueOf(mesh.diameter));
1559       if (mesh.width != 0)
1560         info.put("width", Float.valueOf(mesh.width));
1561       info.put("scale", Float.valueOf(mesh.scale));
1562       if (mesh.drawType == EnumDrawType.MULTIPLE) {
1563         Lst<Map<String, Object>> m = new  Lst<Map<String,Object>>();
1564         int modelCount = vwr.ms.mc;
1565         for (int k = 0; k < modelCount; k++) {
1566           if (mesh.ptCenters[k] == null)
1567             continue;
1568           Map<String, Object> mInfo = new Hashtable<String, Object>();
1569           mInfo.put("modelIndex", Integer.valueOf(k));
1570           mInfo.put("command", getCommand2(mesh, k));
1571           mInfo.put("center", mesh.ptCenters[k]);
1572           int nPoints = mesh.drawVertexCounts[k];
1573           mInfo.put("vertexCount", Integer.valueOf(nPoints));
1574           if (nPoints > 1)
1575             mInfo.put("axis", mesh.axes[k]);
1576           Lst<T3> v = new  Lst<T3>();
1577           for (int ipt = 0; ipt < nPoints; ipt++)
1578             v.addLast(mesh.vs[mesh.pis[k][ipt]]);
1579           mInfo.put("vertices", v);
1580           if (mesh.drawTypes[k] == EnumDrawType.LINE) {
1581             float d = mesh.vs[mesh.pis[k][0]]
1582                 .distance(mesh.vs[mesh.pis[k][1]]);
1583             mInfo.put("length_Ang", Float.valueOf(d));
1584           }
1585           m.addLast(mInfo);
1586         }
1587         info.put("models", m);
1588       } else {
1589         info.put("command", getCommand(mesh));
1590         info.put("center", mesh.ptCenter);
1591         if (mesh.drawVertexCount > 1)
1592           info.put("axis", mesh.axis);
1593         Lst<T3> v = new  Lst<T3>();
1594         for (int j = 0; j < mesh.vc; j++)
1595           v.addLast(mesh.vs[j]);
1596         info.put("vertices", v);
1597         if (mesh.drawType == EnumDrawType.LINE)
1598           info.put("length_Ang", Float.valueOf(mesh.vs[0]
1599               .distance(mesh.vs[1])));
1600       }
1601       V.addLast(info);
1602     }
1603     return V;
1604   }
1605 
1606   @Override
getShapeState()1607   public String getShapeState() {
1608     SB s = new SB();
1609     s.append("\n");
1610     appendCmd(s, myType + " delete");
1611     for (int i = 0; i < meshCount; i++) {
1612       DrawMesh mesh = dmeshes[i];
1613       if (mesh.vc == 0 && mesh.lineData == null)
1614         continue;
1615       s.append(getCommand2(mesh, mesh.modelIndex));
1616       if (!mesh.visible)
1617         s.append(" " + myType + " ID " + PT.esc(mesh.thisID) + " off;\n");
1618     }
1619     return s.toString();
1620   }
1621 
randomPoint()1622   public static P3 randomPoint() {
1623     return P3.new3((float) Math.random(), (float) Math.random(), (float) Math.random());
1624   }
1625 
1626   public enum EnumDrawType {
1627     MULTIPLE(-1,"multiple"),
1628     NONE(0,"none"),
1629 
1630     POINT(1,"point"),
1631     LINE(2,"line"),
1632     PLANE(4,"plane"),
1633 
1634     CYLINDER(14,"cylinder"),
1635     ARROW(15,"arrow"),
1636     CIRCLE(16,"circle"),
1637     CURVE(17,"curve"),
1638     CIRCULARPLANE(18,"circularPlane"),
1639     ARC(19,"arc"),
1640     LINE_SEGMENT(20,"lineSegment"),
1641     POLYGON(21,"polygon");
1642 
1643     final int id;
1644     final String name;
1645 
EnumDrawType(int id, String name)1646     EnumDrawType(int id, String name) {
1647       this.id = id;
1648       this.name = name;
1649     }
1650 
getType(int nPoints)1651     public static EnumDrawType getType(int nPoints) {
1652       switch (nPoints) {
1653       case 1:
1654         return POINT;
1655       case 2:
1656         return LINE;
1657       case 4:
1658         return PLANE;
1659       default:
1660         return NONE;
1661       }
1662     }
1663   }
1664 }
1665