Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.appletgui

File RotatableCanvas.java

 

Coverage histogram

../../img/srcFileCovDistChart0.png
60% of files have more coverage

Code metrics

80
197
29
1
677
475
80
0.41
6.79
29
2.76

Classes

Class Line # Actions
RotatableCanvas 46 197 80
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.appletgui;
22   
23    import jalview.api.RotatableCanvasI;
24    import jalview.datamodel.Point;
25    import jalview.datamodel.SequenceGroup;
26    import jalview.datamodel.SequenceI;
27    import jalview.datamodel.SequencePoint;
28    import jalview.math.RotatableMatrix;
29    import jalview.math.RotatableMatrix.Axis;
30    import jalview.util.MessageManager;
31    import jalview.viewmodel.AlignmentViewport;
32   
33    import java.awt.Color;
34    import java.awt.Dimension;
35    import java.awt.Font;
36    import java.awt.Graphics;
37    import java.awt.Image;
38    import java.awt.Panel;
39    import java.awt.event.KeyEvent;
40    import java.awt.event.KeyListener;
41    import java.awt.event.MouseEvent;
42    import java.awt.event.MouseListener;
43    import java.awt.event.MouseMotionListener;
44    import java.util.List;
45   
 
46    public class RotatableCanvas extends Panel implements MouseListener,
47    MouseMotionListener, KeyListener, RotatableCanvasI
48    {
49    private static final int DIMS = 3;
50   
51    String tooltip;
52   
53    int toolx;
54   
55    int tooly;
56   
57    // RubberbandRectangle rubberband;
58   
59    boolean drawAxes = true;
60   
61    int mouseX = 0;
62   
63    int mouseY = 0;
64   
65    Image img;
66   
67    Graphics ig;
68   
69    Dimension prefsize;
70   
71    Point centre;
72   
73    float[] width = new float[DIMS];
74   
75    float[] max = new float[DIMS];
76   
77    float[] min = new float[DIMS];
78   
79    float maxwidth;
80   
81    float scale;
82   
83    int npoint;
84   
85    List<SequencePoint> points;
86   
87    Point[] orig;
88   
89    Point[] axisEndPoints;
90   
91    int startx;
92   
93    int starty;
94   
95    int lastx;
96   
97    int lasty;
98   
99    int rectx1;
100   
101    int recty1;
102   
103    int rectx2;
104   
105    int recty2;
106   
107    float scalefactor = 1;
108   
109    AlignmentViewport av;
110   
111    boolean showLabels = false;
112   
 
113  0 toggle public RotatableCanvas(AlignmentViewport viewport)
114    {
115  0 this.av = viewport;
116  0 axisEndPoints = new Point[DIMS];
117    }
118   
 
119  0 toggle public void showLabels(boolean b)
120    {
121  0 showLabels = b;
122  0 repaint();
123    }
124   
 
125  0 toggle @Override
126    public void setPoints(List<SequencePoint> points, int npoint)
127    {
128  0 this.points = points;
129  0 this.npoint = npoint;
130  0 PaintRefresher.Register(this, av.getSequenceSetId());
131   
132  0 prefsize = getPreferredSize();
133  0 orig = new Point[npoint];
134   
135  0 for (int i = 0; i < npoint; i++)
136    {
137  0 SequencePoint sp = points.get(i);
138  0 orig[i] = sp.coord;
139    }
140   
141  0 resetAxes();
142   
143  0 findCentre();
144  0 findWidth();
145   
146  0 scale = findScale();
147   
148    // jalview.bin.Console.outPrintln("Scale factor = " + scale);
149   
150  0 addMouseListener(this);
151  0 addKeyListener(this);
152    // if (getParent() != null) {
153    // getParent().addKeyListener(this);
154    // }
155  0 addMouseMotionListener(this);
156   
157    // Add rubberband
158    // rubberband = new RubberbandRectangle(this);
159    // rubberband.setActive(true);
160    // rubberband.addListener(this);
161    }
162   
163    /*
164    * public boolean handleSequenceSelectionEvent(SequenceSelectionEvent evt) {
165    * redrawneeded = true; repaint(); return true; }
166    *
167    * public void removeNotify() { controller.removeListener(this);
168    * super.removeNotify(); }
169    */
170   
171    /**
172    * Resets axes to the initial state: x-axis to the right, y-axis up, z-axis to
173    * back (so obscured in a 2-D display)
174    */
 
175  0 toggle public void resetAxes()
176    {
177  0 axisEndPoints[0] = new Point(1f, 0f, 0f);
178  0 axisEndPoints[1] = new Point(0f, 1f, 0f);
179  0 axisEndPoints[2] = new Point(0f, 0f, 1f);
180    }
181   
182    /**
183    * Computes and saves the maximum and minimum (x, y, z) positions of any
184    * sequence point, and also the min-max range (width) for each dimension, and
185    * the maximum width for all dimensions
186    */
 
187  0 toggle public void findWidth()
188    {
189  0 max = new float[3];
190  0 min = new float[3];
191   
192  0 max[0] = Float.MIN_VALUE;
193  0 max[1] = Float.MIN_VALUE;
194  0 max[2] = Float.MIN_VALUE;
195   
196  0 min[0] = Float.MAX_VALUE;
197  0 min[1] = Float.MAX_VALUE;
198  0 min[2] = Float.MAX_VALUE;
199   
200  0 for (SequencePoint sp : points)
201    {
202  0 max[0] = Math.max(max[0], sp.coord.x);
203  0 max[1] = Math.max(max[1], sp.coord.y);
204  0 max[2] = Math.max(max[2], sp.coord.z);
205  0 min[0] = Math.min(min[0], sp.coord.x);
206  0 min[1] = Math.min(min[1], sp.coord.y);
207  0 min[2] = Math.min(min[2], sp.coord.z);
208    }
209   
210  0 width[0] = Math.abs(max[0] - min[0]);
211  0 width[1] = Math.abs(max[1] - min[1]);
212  0 width[2] = Math.abs(max[2] - min[2]);
213   
214  0 maxwidth = Math.max(width[0], Math.max(width[1], width[2]));
215    }
216   
 
217  0 toggle public float findScale()
218    {
219  0 int dim, w, height;
220  0 if (getSize().width != 0)
221    {
222  0 w = getSize().width;
223  0 height = getSize().height;
224    }
225    else
226    {
227  0 w = prefsize.width;
228  0 height = prefsize.height;
229    }
230   
231  0 if (w < height)
232    {
233  0 dim = w;
234    }
235    else
236    {
237  0 dim = height;
238    }
239   
240  0 return dim * scalefactor / (2 * maxwidth);
241    }
242   
243    /**
244    * Computes and saves the position of the centre of the view
245    */
 
246  0 toggle public void findCentre()
247    {
248  0 findWidth();
249   
250  0 float x = (max[0] + min[0]) / 2;
251  0 float y = (max[1] + min[1]) / 2;
252  0 float z = (max[2] + min[2]) / 2;
253   
254  0 centre = new Point(x, y, z);
255    }
256   
 
257  0 toggle @Override
258    public Dimension getPreferredSize()
259    {
260  0 if (prefsize != null)
261    {
262  0 return prefsize;
263    }
264    else
265    {
266  0 return new Dimension(400, 400);
267    }
268    }
269   
 
270  0 toggle @Override
271    public Dimension getMinimumSize()
272    {
273  0 return getPreferredSize();
274    }
275   
 
276  0 toggle @Override
277    public void update(Graphics g)
278    {
279  0 paint(g);
280    }
281   
 
282  0 toggle @Override
283    public void paint(Graphics g)
284    {
285  0 if (points == null)
286    {
287  0 g.setFont(new Font("Verdana", Font.PLAIN, 18));
288  0 g.drawString(
289    MessageManager.getString("label.calculating_pca") + "....",
290    20, getSize().height / 2);
291    }
292    else
293    {
294   
295    // Only create the image at the beginning -
296  0 if ((img == null) || (prefsize.width != getSize().width)
297    || (prefsize.height != getSize().height))
298    {
299  0 prefsize.width = getSize().width;
300  0 prefsize.height = getSize().height;
301   
302  0 scale = findScale();
303   
304    // jalview.bin.Console.outPrintln("New scale = " + scale);
305  0 img = createImage(getSize().width, getSize().height);
306  0 ig = img.getGraphics();
307   
308    }
309   
310  0 drawBackground(ig, Color.black);
311  0 drawScene(ig);
312  0 if (drawAxes)
313    {
314  0 drawAxes(ig);
315    }
316   
317  0 if (tooltip != null)
318    {
319  0 ig.setColor(Color.red);
320  0 ig.drawString(tooltip, toolx, tooly);
321    }
322   
323  0 g.drawImage(img, 0, 0, this);
324    }
325    }
326   
 
327  0 toggle public void drawAxes(Graphics g)
328    {
329   
330  0 g.setColor(Color.yellow);
331  0 for (int i = 0; i < 3; i++)
332    {
333  0 g.drawLine(getSize().width / 2, getSize().height / 2,
334    (int) (axisEndPoints[i].x * scale * max[0]
335    + getSize().width / 2),
336    (int) (axisEndPoints[i].y * scale * max[1]
337    + getSize().height / 2));
338    }
339    }
340   
 
341  0 toggle public void drawBackground(Graphics g, Color col)
342    {
343  0 g.setColor(col);
344  0 g.fillRect(0, 0, prefsize.width, prefsize.height);
345    }
346   
 
347  0 toggle public void drawScene(Graphics g)
348    {
349  0 for (int i = 0; i < npoint; i++)
350    {
351  0 SequencePoint sp = points.get(i);
352  0 SequenceI sequence = sp.getSequence();
353  0 Color sequenceColour = av.getSequenceColour(sequence);
354  0 g.setColor(
355  0 sequenceColour == Color.black ? Color.white : sequenceColour);
356  0 if (av.getSelectionGroup() != null)
357    {
358  0 if (av.getSelectionGroup().getSequences(null).contains(sequence))
359    {
360  0 g.setColor(Color.gray);
361    }
362    }
363   
364  0 if (sp.coord.z < centre.z)
365    {
366  0 g.setColor(g.getColor().darker());
367    }
368   
369  0 int halfwidth = getSize().width / 2;
370  0 int halfheight = getSize().height / 2;
371  0 int x = (int) ((sp.coord.x - centre.x) * scale) + halfwidth;
372  0 int y = (int) ((sp.coord.y - centre.y) * scale) + halfheight;
373  0 g.fillRect(x - 3, y - 3, 6, 6);
374   
375  0 if (showLabels)
376    {
377  0 g.setColor(Color.red);
378  0 g.drawString(sequence.getName(), x - 3, y - 4);
379    }
380    }
381    }
382   
 
383  0 toggle @Override
384    public void keyTyped(KeyEvent evt)
385    {
386    }
387   
 
388  0 toggle @Override
389    public void keyReleased(KeyEvent evt)
390    {
391    }
392   
 
393  0 toggle @Override
394    public void keyPressed(KeyEvent evt)
395    {
396  0 boolean shiftDown = evt.isShiftDown();
397  0 int keyCode = evt.getKeyCode();
398  0 if (keyCode == KeyEvent.VK_UP)
399    {
400  0 if (shiftDown)
401    {
402  0 rotate(0f, -1f);
403    }
404    else
405    {
406  0 zoom(1.1f);
407    }
408    }
409  0 else if (keyCode == KeyEvent.VK_DOWN)
410    {
411  0 if (shiftDown)
412    {
413  0 rotate(0f, 1f);
414    }
415    else
416    {
417  0 zoom(0.9f);
418    }
419    }
420  0 else if (shiftDown && keyCode == KeyEvent.VK_LEFT)
421    {
422  0 rotate(1f, 0f);
423    }
424  0 else if (shiftDown && keyCode == KeyEvent.VK_RIGHT)
425    {
426  0 rotate(-1f, 0f);
427    }
428  0 else if (evt.getKeyChar() == 's')
429    {
430  0 jalview.bin.Console.errPrintln("DEBUG: Rectangle selection"); // log.debug
431  0 if (rectx2 != -1 && recty2 != -1)
432    {
433  0 rectSelect(rectx1, recty1, rectx2, recty2);
434   
435    }
436    }
437  0 repaint();
438    }
439   
 
440  0 toggle @Override
441    public void mouseClicked(MouseEvent evt)
442    {
443    }
444   
 
445  0 toggle @Override
446    public void mouseEntered(MouseEvent evt)
447    {
448    }
449   
 
450  0 toggle @Override
451    public void mouseExited(MouseEvent evt)
452    {
453    }
454   
 
455  0 toggle @Override
456    public void mouseReleased(MouseEvent evt)
457    {
458    }
459   
 
460  0 toggle @Override
461    public void mousePressed(MouseEvent evt)
462    {
463  0 int x = evt.getX();
464  0 int y = evt.getY();
465   
466  0 mouseX = x;
467  0 mouseY = y;
468   
469  0 startx = x;
470  0 starty = y;
471   
472  0 rectx1 = x;
473  0 recty1 = y;
474   
475  0 rectx2 = -1;
476  0 recty2 = -1;
477   
478  0 SequenceI found = findSequenceAtPoint(x, y);
479   
480  0 if (found != null)
481    {
482    // TODO: applet PCA is not associatable with multi-panels - only parent
483    // view
484  0 if (av.getSelectionGroup() != null)
485    {
486  0 av.getSelectionGroup().addOrRemove(found, true);
487  0 av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
488    }
489    else
490    {
491  0 av.setSelectionGroup(new SequenceGroup());
492  0 av.getSelectionGroup().addOrRemove(found, true);
493  0 av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
494   
495    }
496  0 PaintRefresher.Refresh(this, av.getSequenceSetId());
497  0 av.sendSelection();
498    }
499  0 repaint();
500    }
501   
 
502  0 toggle @Override
503    public void mouseMoved(MouseEvent evt)
504    {
505  0 SequenceI found = findSequenceAtPoint(evt.getX(), evt.getY());
506  0 if (found == null)
507    {
508  0 tooltip = null;
509    }
510    else
511    {
512  0 tooltip = found.getName();
513  0 toolx = evt.getX();
514  0 tooly = evt.getY();
515    }
516  0 repaint();
517    }
518   
 
519  0 toggle @Override
520    public void mouseDragged(MouseEvent evt)
521    {
522  0 int xPos = evt.getX();
523  0 int yPos = evt.getY();
524   
525  0 if (xPos == mouseX && yPos == mouseY)
526    {
527  0 return;
528    }
529   
530  0 int xDelta = xPos - mouseX;
531  0 int yDelta = yPos - mouseY;
532   
533  0 rotate(xDelta, yDelta);
534  0 repaint();
535    }
536   
 
537  0 toggle public void rectSelect(int x1, int y1, int x2, int y2)
538    {
539    // boolean changedSel = false;
540  0 for (int i = 0; i < npoint; i++)
541    {
542  0 SequencePoint sp = points.get(i);
543  0 int tmp1 = (int) ((sp.coord.x - centre.x) * scale
544    + getSize().width / 2.0);
545  0 int tmp2 = (int) ((sp.coord.y - centre.y) * scale
546    + getSize().height / 2.0);
547   
548  0 SequenceI sequence = sp.getSequence();
549  0 if (tmp1 > x1 && tmp1 < x2 && tmp2 > y1 && tmp2 < y2)
550    {
551  0 if (av != null)
552    {
553  0 if (!av.getSelectionGroup().getSequences(null).contains(sequence))
554    {
555  0 av.getSelectionGroup().addSequence(sequence, true);
556    }
557    }
558    }
559    }
560    }
561   
562    /**
563    * Answers the first sequence found whose point on the display is within 2
564    * pixels of the given coordinates, or null if none is found
565    *
566    * @param x
567    * @param y
568    *
569    * @return
570    */
 
571  0 toggle public SequenceI findSequenceAtPoint(int x, int y)
572    {
573  0 int halfwidth = getSize().width / 2;
574  0 int halfheight = getSize().height / 2;
575   
576  0 int found = -1;
577   
578  0 for (int i = 0; i < npoint; i++)
579    {
580   
581  0 SequencePoint sp = points.get(i);
582  0 int px = (int) ((sp.coord.x - centre.x) * scale) + halfwidth;
583  0 int py = (int) ((sp.coord.y - centre.y) * scale) + halfheight;
584   
585  0 if (Math.abs(px - x) < 3 && Math.abs(py - y) < 3)
586    {
587  0 found = i;
588  0 break;
589    }
590    }
591   
592  0 if (found != -1)
593    {
594  0 return points.get(found).getSequence();
595    }
596    else
597    {
598  0 return null;
599    }
600    }
601   
602    /**
603    * Resets the view to initial state (no rotation)
604    */
 
605  0 toggle public void resetView()
606    {
607  0 img = null;
608  0 resetAxes();
609    }
610   
 
611  0 toggle @Override
612    public void zoom(float factor)
613    {
614  0 if (factor > 0f)
615    {
616  0 scalefactor *= factor;
617    }
618  0 scale = findScale();
619    }
620   
 
621  0 toggle @Override
622    public void rotate(float x, float y)
623    {
624  0 if (x == 0f && y == 0f)
625    {
626  0 return;
627    }
628   
629    /*
630    * get the identity transformation...
631    */
632  0 RotatableMatrix rotmat = new RotatableMatrix();
633   
634    /*
635    * rotate around the X axis for change in Y
636    * (mouse movement up/down); note we are equating a
637    * number of pixels with degrees of rotation here!
638    */
639  0 if (y != 0)
640    {
641  0 rotmat.rotate(y, Axis.X);
642    }
643   
644    /*
645    * rotate around the Y axis for change in X
646    * (mouse movement left/right)
647    */
648  0 if (x != 0)
649    {
650  0 rotmat.rotate(x, Axis.Y);
651    }
652   
653    /*
654    * apply the composite transformation to sequence points
655    */
656  0 for (int i = 0; i < npoint; i++)
657    {
658  0 SequencePoint sp = points.get(i);
659  0 sp.translate(-centre.x, -centre.y, -centre.z);
660   
661    // Now apply the rotation matrix
662  0 sp.coord = rotmat.vectorMultiply(sp.coord);
663   
664    // Now translate back again
665  0 sp.translate(centre.x, centre.y, centre.z);
666    }
667   
668    /*
669    * rotate the x/y/z axis positions
670    */
671  0 for (int i = 0; i < DIMS; i++)
672    {
673  0 axisEndPoints[i] = rotmat.vectorMultiply(axisEndPoints[i]);
674    }
675    }
676   
677    }