/******************************************************************************* * Mission Control Technologies, Copyright (c) 2009-2012, United States Government * as represented by the Administrator of the National Aeronautics and Space * Administration. All rights reserved. * * The MCT platform is licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * MCT includes source code licensed under additional open source licenses. See * the MCT Open Source Licenses file included with this distribution or the About * MCT Licenses dialog available at runtime from the MCT Help menu for additional * information. *******************************************************************************/ package plotter.xy; import java.awt.BasicStroke; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Stroke; import javax.swing.JComponent; import javax.swing.SwingUtilities; /** * Draws a line at a particular value on an {@link XYPlot}. * @author Adam Crume */ public class XYMarkerLine extends JComponent { private static final long serialVersionUID = 1L; /** If the line moves by fewer than this many pixels, only one repaint is issued. * Otherwise, two separate repaints (to erase the old and draw the new) are issued. */ private static final int REPAINT_COMBINE_THRESHOLD = 5; /** Default stroke used to draw lines. */ private static final Stroke DEFAULT_STROKE = new BasicStroke(1, 0, 0, 1, new float[] { 4, 4 }, 0); /** Used to draw the line. */ private Stroke stroke = DEFAULT_STROKE; /** Axis this line corresponds to. */ private XYAxis axis; /** Value this line is drawn at. */ private double value; /** Period length of the stroke, used to optimize drawing. */ private double strokeLength = 8; /** * Creates a marker line. * @param axis axis the value lies on * @param value value to draw the line at */ public XYMarkerLine(XYAxis axis, double value) { this.axis = axis; this.value = value; } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g.create(); g2.setColor(getForeground()); g2.setStroke(stroke); int height = getHeight(); Rectangle clip = g2.getClipBounds(); int loc = axis.toPhysical(value); if(axis.getPlotDimension() == XYDimension.X) { Point p = SwingUtilities.convertPoint(axis, loc, 0, this); loc = p.x; int xmin = clip.x; int xmax = clip.x + clip.width; if(loc >= xmin && loc <= xmax) { // Draw shorter lines when possible. // This makes drawing dashed lines faster. int lineymin = 0; int lineymax = height; if(strokeLength != 0) { // We have to adjust the starting points so that the dashes are in phase // with what they would have been if we drew the whole line. lineymin = (int) (strokeLength * (int) (clip.y / strokeLength)); lineymax = clip.y + clip.height; } g2.drawLine(loc, lineymin, loc, lineymax); } } else { Point p = SwingUtilities.convertPoint(axis, 0, loc, this); loc = p.y; int width = getWidth(); int ymin = clip.y; int ymax = clip.y + clip.height; if(loc >= ymin && loc <= ymax) { // Draw shorter lines when possible. // This makes drawing dashed lines faster. int linexmin = 0; int linexmax = width; if(strokeLength != 0) { // We have to adjust the starting points so that the dashes are in phase // with what they would have been if we drew the whole line. linexmin = (int) (strokeLength * (int) (clip.x / strokeLength)); linexmax = clip.x + clip.width; } g2.drawLine(linexmin, loc, linexmax, loc); } } } /** * Returns the stroke used to draw the lines. * @return the stroke used to draw the lines */ public Stroke getStroke() { return stroke; } /** * Sets the stroke used to draw the lines. * @param stroke the stroke used to draw the lines */ public void setStroke(Stroke stroke) { this.stroke = stroke; if(stroke instanceof BasicStroke) { float[] dashes = ((BasicStroke) stroke).getDashArray(); float strokeLength = 0; for(float f : dashes) { strokeLength += f; } this.strokeLength = strokeLength; } else { this.strokeLength = 0; } } /** * Returns the value this line is drawn at. * @return the value this line is drawn at */ public double getValue() { return value; } /** * Sets the value this line is drawn at. * @param value the value this line is drawn at */ public void setValue(double value) { double oldValue = this.value; if(value != oldValue) { this.value = value; int loc = axis.toPhysical(oldValue); int loc2 = axis.toPhysical(value); if(loc == loc2) { // Old and new values do not match exactly, but they paint the same. return; } int minLoc = Math.min(loc, loc2); int maxLoc = Math.max(loc, loc2); int span = maxLoc - minLoc + 1; // If the old and new values are close together, just issue one paint request. // Otherwise, issue two smaller paint requests. if(span < REPAINT_COMBINE_THRESHOLD) { if(axis.getPlotDimension() == XYDimension.X) { Point p = SwingUtilities.convertPoint(axis, minLoc, 0, this); repaint(p.x, 0, span, getHeight()); } else { Point p = SwingUtilities.convertPoint(axis, 0, minLoc, this); repaint(0, p.y, getWidth(), span); } } else { if(axis.getPlotDimension() == XYDimension.X) { int xo = SwingUtilities.convertPoint(axis, 0, 0, this).x; int height = getHeight(); repaint(loc + xo, 0, 1, height); repaint(loc2 + xo, 0, 1, height); } else { int yo = SwingUtilities.convertPoint(axis, 0, 0, this).y; int width = getWidth(); repaint(0, loc + yo, width, 1); repaint(0, loc2 + yo, width, 1); } } } } }