/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.display; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.Rectangle2D; import org.opensourcephysics.controls.XML; import org.opensourcephysics.controls.XMLControl; import org.opensourcephysics.controls.XMLLoader; /** * A Dataset that can highlight selected points. * * @author Doug Brown * @created Dec 14 2005 * @version 1.0 */ public class HighlightableDataset extends Dataset implements Interactive { // instance fields boolean[] highlighted = new boolean[1]; // true if highlighted boolean[] previous; Color highlightColor = new Color(255, 255, 0, 128); Shape highlightShape; Shape[] hitShapes = new Shape[0]; int hitIndex = -1; double[][] screenCoordinates = new double[2][]; /** * Default constructor. */ public HighlightableDataset() { super(); } /** * Constructor specifying the marker color. * * @param markerColor marker color */ public HighlightableDataset(Color markerColor) { super(markerColor); } /** * Constructor specifying the marker color, line color, and whether * points are connected. * * @param markerColor marker color * @param lineColor line color * @param connected true to connect points with line */ public HighlightableDataset(Color markerColor, Color lineColor, boolean connected) { super(markerColor, lineColor, connected); } /** * Appends an (x,y) datum to the Dataset. * * @param x the x value * @param y the y value */ public void append(double x, double y) { super.append(x, y); adjustCapacity(xpoints.length); } /** * Appends (x,y) arrays to the Dataset. * * @param xarray the x array * @param yarray the y array */ public void append(double[] xarray, double[] yarray) { super.append(xarray, yarray); adjustCapacity(xpoints.length); } /** * Clear all data from this Dataset. */ public void clear() { super.clear(); previous = highlighted; highlighted = new boolean[xpoints.length]; } /** * Restores previous highlights. */ public void restoreHighlights() { if((previous!=null)&&(previous.length==highlighted.length)) { highlighted = previous; } } /** * Clears highlights. */ public void clearHighlights() { for(int i = 0; i<highlighted.length; i++) { highlighted[i] = false; } } /** * Sets the highlighted flag for the specified point. * * @param i the array index * @param highlight true to highlight the point */ public void setHighlighted(int i, boolean highlight) { if(i>=highlighted.length) { adjustCapacity(i+1); } highlighted[i] = highlight; } /** * Gets the highlighted flag for the specified point. * * @param i the array index * @return true if point is highlighted */ public boolean isHighlighted(int i) { if(i>=highlighted.length) { adjustCapacity(i+1); } return highlighted[i]; } /** * Sets the highlight color. * * @param color the color */ public void setHighlightColor(Color color) { highlightColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), 128); } /** * Move an out-of-place datum into its correct position. * * @param loc the datum */ protected void moveDatum(int loc) { super.moveDatum(loc); } /** * Sets the highlighted array size to larger of xpoints.length and minLength. * * @param minLength minimum capacity required */ private synchronized void adjustCapacity(int minLength) { int len = Math.max(xpoints.length, minLength); if(highlighted.length==len) { return; } boolean[] temp = highlighted; highlighted = new boolean[len]; int count = Math.min(temp.length, len); System.arraycopy(temp, 0, highlighted, 0, count); } /** * Draw this Dataset in the drawing panel. * * @param drawingPanel the drawing panel * @param g the graphics */ public void draw(DrawingPanel drawingPanel, Graphics g) { super.draw(drawingPanel, g); Graphics2D g2 = (Graphics2D) g; int offset = getMarkerSize()+4; int edge = 2*offset; // increase the clip to include the entire highlight Shape clipShape = g2.getClip(); g2.setClip(drawingPanel.leftGutter-offset-1, drawingPanel.topGutter-offset-1, drawingPanel.getWidth()-drawingPanel.leftGutter-drawingPanel.rightGutter+2+2*offset, drawingPanel.getHeight()-drawingPanel.bottomGutter-drawingPanel.topGutter+2+2*offset); Rectangle viewRect = drawingPanel.getViewRect(); if(viewRect!=null) { // decrease the clip if we are in a scroll pane g2.clipRect(viewRect.x, viewRect.y, viewRect.x+viewRect.width, viewRect.y+viewRect.height); } hitShapes = new Shape[index]; double[] xValues = getXPoints(); double[] yValues = getYPoints(); if (screenCoordinates[0]==null || screenCoordinates[0].length!=index) { screenCoordinates[0] = new double[index]; screenCoordinates[1] = new double[index]; } for(int i = 0; i<index; i++) { if(Double.isNaN(yValues[i])) { screenCoordinates[1][i] = Double.NaN; continue; } double xp = drawingPanel.xToPix(xValues[i]); double yp = drawingPanel.yToPix(yValues[i]); screenCoordinates[0][i] = xp; screenCoordinates[1][i] = yp; hitShapes[i] = new Rectangle2D.Double(xp-offset, yp-offset, edge, edge); if(!isHighlighted(i)) { continue; } g2.setColor(highlightColor); g2.fill(hitShapes[i]); } g2.setClip(clipShape); // restore the original clip } /** * Returns the Interactive object at a specified pixel position. * Implements Interactive. * * @param panel the drawing panel * @param xpix the x pixel position on the panel * @param ypix the y pixel position on the panel * @return the object */ public Interactive findInteractive(DrawingPanel panel, int xpix, int ypix) { // return hits only within active plot area int l = panel.getLeftGutter(); int r = panel.getRightGutter(); int t = panel.getTopGutter(); int b = panel.getBottomGutter(); Dimension dim = panel.getSize(); if((xpix<l)||(xpix>dim.width-r)) { return null; } if((ypix<t)||(ypix>dim.height-b)) { return null; } hitIndex = -1; for (int i = 0; i<hitShapes.length; i++) { if(hitShapes[i]!=null && hitShapes[i].contains(xpix, ypix)) { hitIndex = i; return this; } } return null; } /** * Gets the most recent hit index. * * @return the hit index */ public int getHitIndex() { return hitIndex; } /** * Gets the screen coordinates of all data points. * * @return screen coordinates */ public double[][] getScreenCoordinates() { return screenCoordinates; } /** * Implements Interactive. * * @param enabled ignored */ public void setEnabled(boolean enabled) {} /** * Implements Interactive. * * @return true */ public boolean isEnabled() { return true; } /** * Implements Interactive. * * @param x ignored * @param y ignored */ public void setXY(double x, double y) {} /** * Implements Interactive. * * @param x ignored */ public void setX(double x) {} /** * Implements Interactive. * * @param y ignored */ public void setY(double y) {} /** * Implements Interactive. * * @return the x value at the current hit index, or Double.NaN if none */ public double getX() { if(hitIndex>-1) { return xpoints[hitIndex]; } return Double.NaN; } /** * Implements Interactive. * * @return the y value at the current hit index, or Double.NaN if none */ public double getY() { if(hitIndex>-1) { return ypoints[hitIndex]; } return Double.NaN; } /** * Returns the XML.ObjectLoader for this class. * * @return the object loader */ public static XML.ObjectLoader getLoader() { return new Loader(); } /** * A class to save and load Dataset data in an XMLControl. */ private static class Loader extends XMLLoader { public void saveObject(XMLControl control, Object obj) { XML.getLoader(Dataset.class).saveObject(control, obj); HighlightableDataset data = (HighlightableDataset) obj; control.setValue("highlighted", data.highlighted); //$NON-NLS-1$ } public Object createObject(XMLControl control) { return new HighlightableDataset(); } public Object loadObject(XMLControl control, Object obj) { XML.getLoader(Dataset.class).loadObject(control, obj); HighlightableDataset data = (HighlightableDataset) obj; boolean[] highlighted = (boolean[]) control.getObject("highlighted"); //$NON-NLS-1$ if(highlighted!=null) { data.highlighted = highlighted; } return data; } } } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */