1 /*
2 * BasicParallelDisplayUI.java
3 *
4 * Created on 19. November 2001, 16:06
5 *
6 * Copyright 2001 Flo Ledermann flo@subnet.at
7 *
8 * Licensed under GNU General Public License (GPL).
9 * See http://www.gnu.org/copyleft/gpl.html
10 */
11
12 package edu.psu.geovista.app.parvis.gui;
13
14 import edu.psu.geovista.app.parvis.model.*;
15
16 import java.awt.*;
17 import java.awt.image.*;
18 import java.awt.event.*;
19 import java.awt.geom.*;
20 import javax.swing.*;
21 import javax.swing.plaf.*;
22 import javax.swing.border.*;
23
24 import java.util.*;
25
26 /***
27 * The UI Delegate, responsible for rendering the ParallelDisplay component.
28 *
29 * @author Flo Ledermann flo@subnet.at
30 * @version 0.1
31 */
32 public class BasicParallelDisplayUI extends ParallelDisplayUI implements MouseListener, MouseMotionListener {
33
34
35 //GeneralPath[] rPaths;
36 int numDimensions;
37 int numRecords;
38
39 Color[] colors;
40
41 int[] conditioning;
42
43 int stepx;
44
45 int hoverAxis = -1;
46 int hoverRecord = -1;
47
48 float axisScale[];
49
50 int borderH = 20;
51 int borderV = 40;
52
53 int width = 0, height = 0;
54
55 String metaText = null;
56 int metaX = 0, metaY = 0;
57
58 boolean dragAxis = false;
59 int dragX = 0;
60
61 BufferedImage bufferImg = null;
62 BufferedImage brushImg = null;
63
64 boolean needsDeepRepaint = true;
65
66 boolean renderQuality = false;
67
68 /*** Begin of area that has to be repainted. */
69 //int repaintStartAxis = 0;
70 /*** End of area that has to be repainted. */
71 //int repaintStopAxis = 0;
72
73 int brushHoverStart = 0;
74 int brushHoverEnd = 0;
75 int brushHoverX = 0;
76 boolean inBrush = false;
77 //ParallelDisplay comp;
78
79 /***
80 * Default Constructor. Creates a new BasicParallelDisplayUI.
81 */
82 public BasicParallelDisplayUI() {
83 }
84
85 /***
86 * Swing method. Returns a new instance.
87 */
88 public static ComponentUI createUI(JComponent c){
89 return new BasicParallelDisplayUI();
90 }
91
92 /***
93 * Installs this instance as UI delegate for the given component.
94 *
95 * @param c The component, a ParallelDisplay in our case.
96 */
97 public void installUI(JComponent c){
98 ParallelDisplay pd = (ParallelDisplay)c;
99
100 pd.addMouseListener(this);
101 pd.addMouseMotionListener(this);
102
103 }
104
105 /***
106 * Uninstalls this instance from its component.
107 *
108 * @param c The component, a ParallelDisplay in our case.
109 */
110 public void uninstallUI(JComponent c){
111 ParallelDisplay pd = (ParallelDisplay)c;
112
113 pd.removeMouseListener(this);
114 pd.removeMouseMotionListener(this);
115
116 numDimensions = 0;
117 numRecords = 0;
118 }
119
120 RenderThread renderThread = null;
121 RenderThread brushThread = null;
122
123 /***
124 * Renders the component on the screen.
125 *
126 * @param g The graphics object to draw on.
127 * @param c The Component, our ParallelDisplay.
128 */
129 public void paint(Graphics g, JComponent c){
130
131 //start our renderThread
132 if (renderThread == null){
133 renderThread = new RenderThread(this);
134 renderThread.setQuality(false, true);
135 renderThread.setStyle(new BasicStroke(.5f), new Color(0.0f, 0.0f, 0.0f, 0.7f));
136 renderThread.start();
137 }
138
139 if (brushThread == null){
140 brushThread = new RenderThread(this);
141 brushThread.setQuality(false, true);
142 brushThread.setStyle(new BasicStroke(1.0f), new Color(0.0f, 0.0f, 0.0f, 0.8f));
143 brushThread.setBrushMode(true);
144 brushThread.start();
145 }
146
147 // set up the environment
148 Graphics2D g2 = (Graphics2D)g;
149 ParallelDisplay comp = (ParallelDisplay)c;
150
151 RenderingHints qualityHints = new RenderingHints(null);
152
153 qualityHints.put(RenderingHints.KEY_ANTIALIASING,
154 RenderingHints.VALUE_ANTIALIAS_ON);
155
156 qualityHints.put(RenderingHints.KEY_RENDERING,
157 RenderingHints.VALUE_RENDER_QUALITY);
158
159 g2.setRenderingHints(qualityHints);
160
161 //workaround flag for model change, resize,...
162 if (comp.deepRepaint){
163 // throw away buffered image -> complete repaint
164
165
166 width = c.getWidth() - 2*borderH;
167 height = c.getHeight() - 2*borderV;
168
169 numDimensions = comp.getNumAxes();
170 numRecords = comp.getNumRecords();
171
172 if(this.conditioning == null){
173 this.conditioning = new int[numRecords];
174 }
175
176 //if (this.rPaths == null || this.rPaths.length != numRecords){
177 //this.rPaths = new GeneralPath[numRecords];
178 //}
179 //this.assembleAllPaths(comp);
180
181 stepx = width / (numDimensions - 1);
182
183 needsDeepRepaint = true;
184
185 bufferImg = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
186 Graphics2D ig = bufferImg.createGraphics();
187 ig.setColor(c.getBackground());
188 ig.fillRect(0,0,c.getWidth(),c.getHeight());
189
190 renderThread.reset();
191 brushThread.reset();
192 renderThread.setCurrentComponent(comp);
193 brushThread.setCurrentComponent(comp);
194
195 if (comp.getBrushedCount() == 0) brushImg = null;
196 renderAll();
197
198 comp.deepRepaint = false;
199 }
200
201 g2.setColor(c.getBackground());
202 g2.fillRect(0, 0, comp.getWidth(), comp.getHeight());
203
204 g2.translate(borderH, borderV);
205
206 // save rendered image in new buffer
207 if (renderThread.getRenderedImage() != null){
208 // we cant do this becase the renderedImage is only a part of the whole
209 // bufferImg = (BufferedImage)renderThread.getRenderedImage();
210 Graphics2D ig = bufferImg.createGraphics();
211 ig.setColor(comp.getBackground());
212 int startAxis = renderThread.getRenderedRegionStart();
213 int stopAxis = renderThread.getRenderedRegionStop();
214
215 //delete area that has been rendered
216 ig.fillRect( startAxis * stepx, 0, (stopAxis - startAxis) * stepx, comp.getHeight());
217 //and paint it new
218 ig.drawImage(renderThread.getRenderedImage(), 0, 0, comp);
219 }
220
221 if (brushThread.getRenderedImage() != null){
222 brushImg = brushThread.getRenderedImage();
223 }
224
225 if (brushImg == null){
226 synchronized (bufferImg){
227 g2.drawImage(bufferImg, 0, 0, comp);
228 }
229 }
230 else {
231 Composite oldcomp = g2.getComposite();
232
233 AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
234 //previous line changed by FAH 29 july 02 from .2 to .5
235 g2.setComposite(ac);
236
237 g2.drawImage(bufferImg, 0, 0, comp);
238
239 g2.setComposite(oldcomp);
240 g2.drawImage(brushImg, 0, 0, comp);
241 }
242
243 // set up
244 g2.setColor(comp.getForeground());
245 g2.setStroke(new BasicStroke(1.0f));
246
247 //draw all the dynamic parts on the screen:
248
249 //axis labels
250 for (int i=0; i<numDimensions; i++){
251 float curx = i*stepx;
252
253
254 //hovering over Axis
255 if (i==hoverAxis){
256 g2.setStroke(new BasicStroke(2.5f));
257 g2.draw(new Line2D.Float(curx, 0, curx, height));
258 g2.setStroke(new BasicStroke(1.0f));
259 }
260 else {
261 g2.draw(new Line2D.Float(curx, 0, curx, height));
262 }
263
264 String label = comp.getAxisLabel(i);
265 if (label != null) {
266 g2.drawString(label, curx - 10, height + 30);
267 }
268
269 g2.drawString("" + comp.getAxisOffset(i), curx + 2, borderV / 2 - 22);
270 g2.drawString("" + (comp.getAxisOffset(i) + comp.getAxisScale(i)), curx + 2, height + borderV / 2 - 5);
271
272 drawArrow(g2, (int)curx, -20, 8, false, (comp.getAxisScale(i) < 0));
273 }
274
275 //brush Hover
276 if (inBrush) {
277 g2.setColor(new Color(0.7f, 0.0f, 0.0f));
278 //g2.setColor(Color.blue);
279 g2.setStroke(new BasicStroke(2.5f));
280 g2.draw(new Line2D.Float(brushHoverX, brushHoverStart, brushHoverX, brushHoverEnd));
281 }
282
283 //hovering over record
284 //added Frank Hardisty 19 July 2002
285
286 boolean paintHoverNative = false;
287 boolean paintHoverComp = false;
288 int paintHoverRecord = -1;
289 if (comp.indication > 0) {
290 paintHoverComp = true;
291 paintHoverRecord = comp.indication;
292 }
293 if (comp.indication < 0) {
294 paintHoverComp = false;
295 paintHoverRecord = -1;
296 }
297
298 if ((comp.getBoolPreference("hoverLine")) && ( hoverRecord != -1)) {
299 paintHoverNative = true;
300 paintHoverRecord = hoverRecord;
301 }
302
303
304 if (paintHoverNative || paintHoverComp){
305 ////System.out.println("Painting record " + i);
306
307 Color col = new Color(1.0f, 1.0f, 0.8f);
308 Font oldfont = g2.getFont();
309 Font newfont = new Font(oldfont.getName(), Font.PLAIN, oldfont.getSize() - 2);
310 g2.setFont(newfont);
311
312 GeneralPath rPath = this.assemblePath(paintHoverRecord,0,numDimensions-1,comp);
313 //float yval = getYValue(paintHoverRecord, 0, comp);
314
315 g2.setStroke(new BasicStroke(2.5f));
316 g2.draw(rPath);
317
318 g2.setStroke(new BasicStroke(1.5f));
319 g2.setColor(Color.red);
320 g2.draw(rPath);
321
322 g2.setFont(oldfont);
323 }
324
325 if ((comp.getBoolPreference("hoverText")) && ( paintHoverRecord != -1)){
326 Color col = new Color(1.0f, 1.0f, 0.8f);
327 for (int j=0; j<numDimensions; j++){
328 float yval = getYValue(paintHoverRecord, j, comp);
329 drawTooltip(g2,comp.getAxisLabel(j) + "=\n" + comp.getValue(paintHoverRecord, j), stepx * j, (int)yval, col);
330 }
331 }
332
333 //dragging axis
334 if (dragAxis) {
335 AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f);
336 g2.setComposite(ac);
337
338 g2.setStroke(new BasicStroke(0.5f));
339 g2.drawLine(dragX - borderH, 0, dragX - borderH, height);
340 }
341
342 //tooltips
343 if ((comp.getBoolPreference("hoverLine")) && (metaText != null)){
344 drawTooltip(g2, metaText, metaX, metaY + 10, new Color(0.7f, 0.7f, 1.0f));
345 }
346
347 g2.translate(-borderH, -borderV);
348
349 }
350
351 void renderRegion(int startAxis, int stopAxis){
352 if (startAxis < 0) startAxis = 0;
353 if (stopAxis >= numDimensions) stopAxis = numDimensions-1;
354
355 renderThread.setRegion(startAxis, stopAxis);
356 renderThread.render();
357
358 if (brushImg != null){
359 //if we do this, we have to add another buffer img for the brush in paint()
360 //brushThread.setRegion(startAxis, stopAxis);
361 brushThread.render();
362 }
363 }
364
365 void renderAll(){
366 renderRegion(0, numDimensions - 1);
367 }
368
369 public void renderBrush(){
370 if (brushThread == null){
371 return;
372 }
373 brushThread.setRegion(0, numDimensions - 1);
374 brushThread.render();
375 }
376 // when do we want to do this? upon 1. init 2. moved axis 3. resized axis
377 private void assembleAllPaths(ParallelDisplay comp){
378 // for (int i = 0; i < this.rPaths.length; i++){
379 // this.rPaths[i] = assemblePath(i,0,this.numDimensions-1,comp);
380 // }
381 }
382 private GeneralPath assemblePath(int num, int startAxis, int stopAxis, ParallelDisplay comp){
383 GeneralPath rPath = new GeneralPath();
384 float val = this.getYValue(num,startAxis,comp);
385 //System.out.println("PCPUI val = " + val);
386 boolean wasNaN = false;
387 if (!Float.isNaN(val)) {
388 rPath.moveTo(stepx * startAxis,val);
389 wasNaN = false;
390 } else {
391 wasNaN = true;
392 }
393 for (int j=startAxis+1; j<=stopAxis; j++){
394 val = getYValue(num, j, comp);
395
396 //System.out.println("PCPUI val = " + val);
397 if (Float.isNaN(val)){//if this one is NaN
398 wasNaN = true;
399 }
400 else {//if this one is not NaN
401 if (wasNaN){//lastone was NaN, so moveTo
402 rPath.moveTo(stepx * j, val);
403 } else {//usual case, usual number following on usual number
404 rPath.lineTo(stepx * j, val);
405 }
406 wasNaN = false;
407 }
408 }
409 return rPath;
410
411 }
412
413 void drawRecord(Graphics2D g2, ParallelDisplay comp, int num, int startAxis, int stopAxis){
414 if (this.conditioning.length != this.numRecords){
415 this.conditioning = new int[numRecords];
416 }
417 if (this.conditioning[num] < 0){
418 return;
419 }
420 GeneralPath rPath = null;
421 // if (startAxis == 0 && stopAxis == this.numDimensions-1){
422 // rPath = this.rPaths[num];
423 // } else {
424 rPath = this.assemblePath(num,startAxis,stopAxis, comp);
425 // }
426 if (this.colors != null) {
427 g2.setColor(this.colors[num]);
428 }
429 if (inBrush){
430 g2.setColor(Color.blue);
431 }
432 g2.draw(rPath);
433 }
434 void drawBrushedRecord(Graphics2D g2, ParallelDisplay comp, int num, int startAxis, int stopAxis){
435 if (this.conditioning[num] < 0){
436 return;
437 }
438 GeneralPath rPath = this.assemblePath(num,startAxis,stopAxis,comp);
439 //if (this.colors != null) {
440 g2.setColor(Color.darkGray);
441 //}
442 g2.draw(rPath);
443 }
444 public void setColors(Color[] colors){
445 this.colors = colors;
446 }
447 public void setConditioning(int[] conditioning){
448 this.conditioning = conditioning;
449 }
450 /***
451 * Helper function to draw a "tooltip" on the given graphics object.
452 *
453 * @param g2 The Graphics2D Object to draw on.
454 * @param text The (multiline) text of the tooltip.
455 * @param x The x coordinate.
456 * @param y The y coordinate.
457 * @param col The background color.
458 */
459 private void drawTooltip(Graphics2D g2, String text, int x, int y, Color col){
460 int i;
461 int mheight, mwidth = 0;
462 int numLines, lineHeight;
463
464 StringTokenizer tok = new StringTokenizer(text,"\n");
465 numLines = tok.countTokens();
466 String lines[] = new String[numLines];
467
468 for (i=0; i<numLines; i++){
469 lines[i] = tok.nextToken();
470
471 int tempwidth = g2.getFontMetrics().stringWidth(lines[i]) + 6;
472 if (tempwidth > mwidth) mwidth = tempwidth;
473 }
474
475 lineHeight = g2.getFontMetrics().getHeight();
476 mheight = numLines * lineHeight + 2;
477
478 x += 10;
479 y += 10;
480 if (x + mwidth > width) x -= (mwidth + 20);
481
482 AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f);
483 g2.setComposite(ac);
484
485 g2.setStroke(new BasicStroke(0.5f));
486 g2.setColor(new Color(0.2f, 0.2f, 0.2f));
487 g2.drawRect(x, y, mwidth, mheight);
488 g2.setColor(col);
489 g2.fillRect(x+1, y+1, mwidth-1, mheight-1);
490
491 g2.setColor(Color.black);
492
493 ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
494 g2.setComposite(ac);
495
496 for (i=0; i<numLines; i++){
497 g2.drawString(lines[i], x + 3, y + (i+1) * lineHeight - 4);
498 }
499
500 }
501
502 /***
503 * Helper function to draw an arrow.
504 *
505 * @param g2 The Graphics2D Object to draw on.
506 * @param x The x coordinate.
507 * @param y The y coordinate.
508 * @param size The size in pixels.
509 * @param horizontal If true, the arrow is drawn horizontally, if false vertically.
510 * @param topright If true, the arrowhead is top/right, if false bottom/left.
511 */
512 private void drawArrow(Graphics2D g2, int x, int y, int size, boolean horizontal, boolean topright){
513
514 if (horizontal){
515
516 g2.drawLine(x-size/2, y, x+size/2, y);
517
518 if (topright){
519 g2.drawLine(x + size/4, y-size/4, x+size/2, y);
520 g2.drawLine(x + size/4, y+size/4, x+size/2, y);
521 }
522 else{
523 g2.drawLine(x - size/4, y-size/4, x-size/2, y);
524 g2.drawLine(x - size/4, y+size/4, x-size/2, y);
525 }
526 }
527 else {
528
529 g2.drawLine(x, y-size/2, x, y+size/2);
530
531 if (topright){
532 g2.drawLine(x + size/4, y-size/4, x, y-size/2);
533 g2.drawLine(x - size/4, y-size/4, x, y-size/2);
534 }
535 else{
536 g2.drawLine(x + size/4, y+size/4, x, y+size/2);
537 g2.drawLine(x - size/4, y+size/4, x, y+size/2);
538 }
539 }
540 }
541
542 /***
543 * Helper function, returns the y value (on screen) for a given record. Scale
544 * factors and translation is applied.
545 *
546 * @param record The recordnumber.
547 * @param axis The axis to calculate the y value for.
548 * @param comp our "parent" component.
549 */
550 private float getYValue(int record, int axis, ParallelDisplay comp){
551 float value = comp.getValue(record, axis);
552
553 value -= comp.getAxisOffset(axis);
554 value *= (comp.getHeight() - 2 * borderV) / comp.getAxisScale(axis);
555
556 return value;
557 }
558
559 // record old coordinates for dragging
560 int oldMouseX, oldMouseY;
561 int activeAxis = -1;
562 float oldScale, oldOffset;
563
564 //0.0 - 1.0 value of loacion of click on axis
565 float clickValue;
566
567 // actual value of point clicked on axis
568 float clickAxisValue;
569
570 int clickModifiers;
571
572 /***
573 * Invoked when the mouse exits the component.
574 *
575 * @param e The mouse event.
576 */
577 public void mouseExited(MouseEvent e) {
578 hoverRecord = -1;
579 }
580
581 /***
582 * Invoked when a mouse button has been released on a component. Checks
583 * if something has been dragged and finishes the drag process.
584 *
585 * @param e The mouse event.
586 */
587 public void mouseReleased(MouseEvent e) {
588 ParallelDisplay comp = (ParallelDisplay)e.getComponent();
589
590 switch (comp.getEditMode()){
591 case ParallelDisplay.REORDER:
592 dragAxis = false;
593 break;
594 case ParallelDisplay.BRUSH:
595 inBrush = false;
596 break;
597 }
598 }
599
600 /***
601 * Invoked when a mouse button has been pressed on a component.
602 * Checks if the user starts dragging something.
603 *
604 * @param e The mouse event.
605 */
606 public void mousePressed(MouseEvent e) {
607 ParallelDisplay comp = (ParallelDisplay)e.getComponent();
608
609 oldMouseX = e.getX();
610 oldMouseY = e.getY();
611
612 activeAxis = hoverAxis;
613
614 clickModifiers = e.getModifiers();
615
616 if (activeAxis != -1){
617
618 oldScale = comp.getAxisScale(activeAxis);
619 oldOffset = comp.getAxisOffset(activeAxis);
620
621 clickValue = ((float)oldMouseY - borderV) / (comp.getHeight() - 2*borderV);
622 clickAxisValue = comp.getAxisOffset(activeAxis) + clickValue * comp.getAxisScale(activeAxis);
623 }
624
625 switch (comp.getEditMode()){
626 case ParallelDisplay.REORDER:
627 dragAxis = true;
628 break;
629 case ParallelDisplay.BRUSH:
630 brushHoverStart = oldMouseY - borderV;
631 brushHoverEnd = oldMouseY - borderV;
632 brushHoverX = oldMouseX - borderH;
633 inBrush = true;
634 hoverRecord = -1;
635 this.createBrushImage(comp);
636 }
637
638 }
639
640 public void createBrushImage(ParallelDisplay comp) {
641 if (brushImg == null) {
642 //brushImg = (BufferedImage)comp.createImage(comp.getWidth(), comp.getHeight());
643 brushImg = new BufferedImage(comp.getWidth(), comp.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
644 Graphics2D ig = brushImg.createGraphics();
645 //fill with transparent white
646 ig.setColor(new Color(1.0f, 1.0f, 1.0f, 0.0f));
647 ig.fillRect(0,0,comp.getWidth(), comp.getHeight());
648 }
649 }
650
651 /***
652 * Invoked when a mouse button is pressed on a component and then
653 * dragged. Mouse drag events will continue to be delivered to
654 * the component where the first originated until the mouse button is
655 * released (regardless of whether the mouse position is within the
656 * bounds of the component).
657 *
658 * Depending on the current mode, this method performs scaling, translating
659 * or reordering of axes.
660 *
661 * @param e The mouse event.
662 */
663 public void mouseDragged(MouseEvent e) {
664 ParallelDisplay comp = (ParallelDisplay)e.getComponent();
665
666 int mouseX = e.getX();
667 int mouseY = e.getY();
668
669 setMetaInfo(null,0,0);
670
671 switch (comp.getEditMode()){
672 case ParallelDisplay.SCALE:
673 if (activeAxis != -1){
674 float way = ((float)(oldMouseY - mouseY)) / (comp.getHeight() - 2*borderV);
675 comp.setAxisScale(activeAxis, oldScale + (way * oldScale)) ;
676 float newValue = clickValue * (comp.getAxisScale(activeAxis) - oldScale);
677 comp.setAxisOffset(activeAxis, oldOffset - newValue);
678
679 renderRegion(activeAxis - 1, activeAxis + 1);
680 }
681 break;
682 case ParallelDisplay.TRANSLATE:
683 if (activeAxis != -1){
684 float way = ((float)(oldMouseY - mouseY)) / (comp.getHeight() - 2*borderV);
685 way *= comp.getAxisScale(activeAxis);
686 comp.setAxisOffset(activeAxis, oldOffset + way);
687
688 renderRegion(activeAxis - 1, activeAxis + 1);
689 }
690 break;
691 case ParallelDisplay.REORDER:
692 if (activeAxis != -1){
693 int deltaX = mouseX - oldMouseX;
694 int num = activeAxis + deltaX / stepx;
695
696 if (num < 0) num = 0;
697 if (num >= numDimensions) num = numDimensions-1;
698
699 dragX = mouseX;
700
701 if (activeAxis != num) {
702 comp.swapAxes(activeAxis, num);
703
704 ////System.out.println("setting repaint axes: " + (Math.min(num,activeAxis) - 1) + ", " + (Math.max(num,activeAxis) + 1));
705
706 renderRegion(Math.min(num,activeAxis) - 1, Math.max(num,activeAxis) + 1);
707
708 activeAxis = num;
709 hoverAxis = num;
710 oldMouseX = stepx * num + borderH;
711 }
712 // to display hoverAxis
713 comp.repaint();
714 }
715 break;
716 case ParallelDisplay.BRUSH:
717 if (activeAxis != -1){
718 brushHoverEnd = mouseY - borderV;
719 float releaseValue = ((float)mouseY - borderV) / (comp.getHeight() - 2*borderV);
720 releaseValue = comp.getAxisOffset(activeAxis) + releaseValue * comp.getAxisScale(activeAxis);
721 float lowerBound = Math.min(clickAxisValue, releaseValue);
722 float upperBound = Math.max(clickAxisValue, releaseValue);
723 boolean doSoft = comp.getBoolPreference("softBrush");
724 float radius = 0.0f;
725 int ids[];
726 if (doSoft){
727 radius = comp.getFloatPreference("brushRadius") * (upperBound - lowerBound);
728 if (radius == 0.0f) {
729 //System.out.println("radius is zero");
730 doSoft = false;
731 }
732 ids = comp.getRecordsByValueRange(activeAxis, lowerBound - radius, upperBound + radius);
733 }
734 else {
735 ids = comp.getRecordsByValueRange(activeAxis, lowerBound, upperBound);
736 }
737 int id = 0;
738 for (int i=0; i<comp.getNumRecords(); i++){
739 if ((ids.length > 0) && (i == ids[id])){
740 //record is inside brush region
741
742 float brushVal = 1.0f;
743
744 if (doSoft){
745 float val = comp.getValue(i, activeAxis);
746 if (val < lowerBound) {
747 brushVal = 1.0f - ( -val + lowerBound ) / radius;
748 }
749 if (val > upperBound) {
750 brushVal = 1.0f - ( val - upperBound ) / radius;
751 }
752 }
753
754 if (e.isShiftDown() && e.isAltDown()){
755 // shift + alt pressed -> intersect mode
756 // we don't have anything to do
757 }
758 else if (e.isShiftDown()){
759 // shift pressed -> expand mode
760 brushVal = brushVal + comp.getBrushValue(i);
761 if (brushVal > 1.0f) brushVal = 1.0f;
762 comp.setBrushValue(i, brushVal);
763 }
764 else if (e.isAltDown()){
765 // alt pressed -> subtract mode
766 comp.setBrushValue(i, 1.0f - brushVal);
767 }
768 else {
769 // no modifiers -> normal mode
770 comp.setBrushValue(i, brushVal);
771 }
772 if (id < ids.length-1) id++;
773 }
774 else {
775 if (e.isShiftDown() && e.isAltDown()){
776 // shift + alt pressed -> intersect mode
777 // clear all values outside our brush
778 if (comp.getBrushValue(i) > 0)
779 comp.setBrushValue(i, 0.0f);
780 }
781 else if (e.isShiftDown()){
782 // shift pressed -> expand mode
783 // do nothing
784 }
785 else if (e.isAltDown()){
786 // alt pressed -> subtract mode
787 // do nothing
788 }
789 else {
790 // no modifiers -> normal mode
791 comp.setBrushValue(i, 0.0f);
792 }
793 }
794 }
795 }
796
797 renderBrush();
798 // to see brush line in realtime
799 comp.repaint();
800
801 comp.fireSelectionChanged();
802 break;
803
804 }
805
806 }
807
808
809 /***
810 * Invoked when the mouse has been clicked on a component.
811 *
812 * Checks if the click hit an arrow and inverts the corresponding axis.
813 *
814 * @param e The mouse event.
815 */
816 public void mouseClicked(MouseEvent e) {
817 ParallelDisplay comp = (ParallelDisplay)e.getComponent();
818
819 //arrow clicked or invert mode
820 if ((comp.getEditMode() == ParallelDisplay.INVERT) || (e.getY() <= 25 && e.getY()>12)) {
821 if (hoverAxis != -1){
822 comp.setAxisOffset(hoverAxis, comp.getAxisOffset(hoverAxis) + comp.getAxisScale(hoverAxis));
823 comp.setAxisScale(hoverAxis, comp.getAxisScale(hoverAxis) * -1);
824
825 renderRegion(activeAxis - 1, activeAxis + 1);
826
827 }
828 }
829
830
831 }
832
833 /***
834 * Invoked when the mouse enters a component.
835 */
836 public void mouseEntered(MouseEvent e) {
837 }
838 /*** Added by Frank Hardisty 19 July 2002
839 *
840 * This method is called to create a hover record from outside the component
841 *
842 */
843
844 public void setHoverRecord(int record, ParallelDisplay comp){
845 if (record != hoverRecord) {
846 hoverRecord = record;
847 //if (hoverRecord != -1) {
848 // setMetaInfo(comp.getRecordLabel(hoverRecord), mousex, mousey);
849 //}
850 comp.repaint();
851 }
852
853 }
854
855 /***
856 * Invoked when the mouse button has been moved on a component
857 * (with no buttons no down).
858 *
859 * Displays tooltips if mouse is hovering over axes or records.
860 *
861 * @param e The mouse event.
862 */
863 public void mouseMoved(MouseEvent e) {
864
865 if (! inBrush ){
866 int mousex = e.getX() - borderH;
867 int mousey = e.getY() - borderV;
868
869 int oldAxis = hoverAxis;
870 int oldRecord = hoverRecord;
871
872 ParallelDisplay comp = (ParallelDisplay)e.getComponent();
873
874 hoverAxis = -1;
875
876 for (int i=0; i<numDimensions; i++){
877 if ((mousex > (i*stepx - 3)) && (mousex < (i*stepx + 3))) {
878 hoverAxis = i;
879 break;
880 }
881 }
882
883 hoverRecord = getRecordByCoordinates(mousex, mousey, comp);
884
885 //added frank Hardisty 19 july 2002
886 if (oldRecord != hoverRecord) {
887 comp.fireIndicationChanged(hoverRecord);
888 }
889
890 if ((oldAxis != hoverAxis) || (oldRecord != hoverRecord)){
891 if (hoverAxis != -1){
892 setMetaInfo(comp.getAxisLabel(hoverAxis), mousex, mousey);
893
894 switch (comp.getEditMode()){
895 case ParallelDisplay.REORDER:
896 comp.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
897 break;
898 case ParallelDisplay.SCALE:
899 comp.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
900 break;
901 case ParallelDisplay.TRANSLATE:
902 comp.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
903 break;
904 case ParallelDisplay.INVERT:
905 comp.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
906 break;
907 case ParallelDisplay.BRUSH:
908 comp.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
909 break;
910 }
911
912 }
913 else {
914 setMetaInfo(null,0,0);
915
916 comp.resetCursor();
917 }
918
919 if (hoverRecord != -1) {
920 setMetaInfo(comp.getRecordLabel(hoverRecord), mousex, mousey);
921 }
922
923 comp.repaint();
924 }
925 }
926
927 }
928
929 /***
930 * Helper method to display a tooltip on hover.
931 */
932 private void setMetaInfo(String text, int x, int y){
933 metaText = text;
934 metaX = x;
935 metaY = y;
936 }
937
938 /***
939 * Returns the record that goes through the screen coordinates x,y. The first
940 * record that is found is returned.
941 *
942 * @param x The x screen coordinate.
943 * @param y The y screen coordinate.
944 * @param comp The "parent" component.
945 *
946 * @return The recordnumber of the first record found passing through the given point.
947 */
948 public int getRecordByCoordinates(int x, int y, ParallelDisplay comp){
949 for (int i=0; i<numDimensions - 1; i++){
950 if ((x >= i*stepx) && (x < (i+1)*stepx)) {
951 float part = (x - i*stepx) / (float)stepx;
952
953 for (int j=0; j<numRecords; j++){
954 float recVal = (1-part) * getYValue(j,i,comp) + part * getYValue(j,i+1,comp);
955 //System.out.println("getRecordByCoordinates" + recVal);
956 if (Math.abs(recVal - y) < 3.0) return j;
957 }
958 break;
959 }
960 }
961
962 return -1;
963 }
964 /***
965 * Returns the record that goes through the screen coordinates x,y. The first
966 * record that is found is returned.
967 *
968 * @param x The x screen coordinate.
969 * @param y The y screen coordinate.
970 * @param comp The "parent" component.
971 *
972 * @return The recordnumber of the first record found passing through the given point.
973 */
974 public int[] getAllRecordsByCoordinates(Rectangle2D hitBox, ParallelDisplay comp){
975 int x = (int)hitBox.getX();
976 int y = (int)hitBox.getY();
977 int width = (int)hitBox.getWidth();
978 int height = (int)hitBox.getHeight();
979 Vector recs = new Vector();
980 for (int i=0; i<numDimensions - 1; i++){
981 if ((x >= i*stepx) && (x < (i+1)*stepx)) {//if x part matches
982 float part = (x - i*stepx) / (float)stepx;
983 //System.out.println(part);
984 for (int j=0; j<numRecords; j++){
985 float recVal = (1-part) * getYValue(j,i,comp) + part * getYValue(j,i+1,comp);
986
987 if (Math.abs(recVal - y) < 3.0) {
988 recs.add(new Integer(j));
989 }
990
991 }
992
993 }
994 }
995
996 return null;
997 }
998
999 }
This page was automatically generated by Maven