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