/* 
 * This code is part of Project Waterloo from King's College London
 * 
 *
 * Copyright King's College London 2011-
 * 
 * @author Malcolm Lidierth, King's College London  [Contact]
 * 
 * Project Waterloo is free software:  you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Project Waterloo 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 *
 */
package kcl.waterloo.graphics;

import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import kcl.waterloo.actions.GJEventManager;
import kcl.waterloo.defaults.GJDefaults;
import kcl.waterloo.common.deploy.AbstractDeployableGraphics2D;
import kcl.waterloo.math.geom.Ellipse;
import kcl.waterloo.math.ArrayMath;

/**
 * Concrete class extending the {@code GJAbstractGraph} class.
 *
 * While {@code GJGraph} instances can be independent of their containers, they
 * will normally be added to a {@code GJGraphContainer} instance.
 *
 * The {@code GJGraph} class supports adding of plots, provides objects to
 * transform the data held in those plots as well as coordinating the painting
 * of grids and internal axes.
 *
 * Axes shown outside of the viewable area of the graph are painted by the
 * {@code GraphContainer} if it has one. These components are properties of the
 * graph, but are members of the Swing hierarchy of the container.
 *
 * @author Malcolm Lidierth
 */
public final class GJGraph extends GJAbstractGraph {

    private transient Stroke minorGridStroke;
    private transient Stroke localAxisStroke;
    private final ArrayList v = new ArrayList();
    private final ArrayList h = new ArrayList();

    /**
     * 

* Creates a new graph display. The following properties are automatically * set:

  • view: -1.0 to +1.0 on both axis
  • *
  • origin: At (0, 0)
  • grid: Spacing * of 0.2 between major lines; minor lines count is 4
*/ public GJGraph() { this(0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.2, 4, 0.2, 4); } /** *

* Creates a new graph display with the specified view, origin and grid * lines.

* * @param originX the coordinate of the inner axis * @param originY the coordinate of the inner axis * @param xleft the left coordinate on the X axis for the view * @param xright the right coordinate on the X axis for the view * @param ybottom the bottom coordinate on the Y axis for the view * @param ytop the top coordinate on the Y axis for the view * @param majorX the majorX interval * @param minorCountX number of minor divisions to a major division * @param majorY the majorY interval * @param minorCountY number of minor divisions to a major division */ private GJGraph(double originX, double originY, double xleft, double xright, double ybottom, double ytop, double majorX, int minorCountX, double majorY, int minorCountY) { super(); this.originX = originX; this.originY = originY; this.xLeft = xleft; this.xRight = xright; this.yBottom = ybottom; this.yTop = ytop; this.setMajorXHint(majorX); this.setMinorCountXHint(minorCountX); this.setMajorYHint(majorY); this.setMinorCountYHint(minorCountY); setBackground(Color.WHITE); setFocusable(true); //addKeyListener(GraphCycler.getInstance()); getMainFormatter().setMaximumFractionDigits(2); //getLayers().add(this); setCurrentLayer(this); // addPropertyChangeListener(this); } public static GJGraph createInstance() { GJGraph gr = new GJGraph(); gr.setLeftAxisPainted(((Boolean) GJDefaults.getMap().get("GJGraph.leftAxisPainted"))); gr.setRightAxisPainted(((Boolean) GJDefaults.getMap().get("GJGraph.rightAxisPainted"))); gr.setTopAxisPainted(((Boolean) GJDefaults.getMap().get("GJGraph.topAxisPainted"))); gr.setBottomAxisPainted(((Boolean) GJDefaults.getMap().get("GJGraph.bottomAxisPainted"))); gr.setLeftAxisLabelled(((Boolean) GJDefaults.getMap().get("GJGraph.leftAxisLabelled"))); gr.setRightAxisLabelled(((Boolean) GJDefaults.getMap().get("GJGraph.rightAxisLabelled"))); gr.setTopAxisLabelled(((Boolean) GJDefaults.getMap().get("GJGraph.topAxisLabelled"))); gr.setBottomAxisLabelled(((Boolean) GJDefaults.getMap().get("GJGraph.bottomAxisLabelled"))); gr.addMouseListener(GJEventManager.getInstance()); gr.addListeners(); gr.addPropertyChangeListener(gr); return gr; } /** * Draw the grid. First draw the vertical lines, then the horizontal lines. * * @param g2 Graphics2D instance */ private void drawGrid(Graphics2D g2) { Stroke stroke = g2.getStroke(); minorGridStroke = new BasicStroke(super.minorGridStrokeWeight); Stroke majorGridStroke = new BasicStroke(super.majorGridStrokeWeight); localAxisStroke = new BasicStroke(getAxisStrokeWeight()); if (isMajorGridPainted() || isMinorGridPainted() || isInnerAxisPainted()) { if (isPolar()) { polarDrawVerticalGrid(g2); polarDrawHorizontalGrid(g2); } else { // Draw minor and major grids with single calls to draw on g2. // This creates them as a single editable unit (group/clipping mask etc) // in vector graphics based editors. drawVerticalGrid(g2); drawHorizontalGrid(g2); g2.setStroke(minorGridStroke); g2.setPaint(getMinorGridColor()); g2.setColor(getMinorGridColor()); v.get(0).append(h.get(0), false); g2.draw(v.get(0)); v.get(1).append(h.get(1), false); g2.setStroke(majorGridStroke); g2.setPaint(getMajorGridColor()); g2.draw(v.get(1)); v.get(2).append(h.get(2), false); g2.setPaint(getForeground()); g2.setStroke(localAxisStroke); g2.draw(v.get(2)); } } g2.setStroke(stroke); } /** * Draw all labels. First draws labels on the horizontal axis, then labels * on the vertical axis. If the axis is set not to be painted, this method * draws the origin as a straight cross. * * @param g2 Graphics2D instance */ private void drawLabels(Graphics2D g2) { if (isInnerAxisLabelled()) { g2.setPaint(getForeground()); drawHorizontalAxisLabels(g2); drawVerticalAxisLabels(g2); } } /** * Draws labels on the vertical axis. First draws labels below the origin, * then draw labels on top of the origin. * * @param g2 Graphics2D instance */ private void drawVerticalAxisLabels(Graphics2D g2) { if (isInnerAxisLabelled()) { double axisV = xPositionToPixel(getOriginX()); ArrayList it; if (isPolar()) { it = getPolarAxisLabels(); } else { double startY = Math.floor(getYMin() / getMajorYHint()) * getMajorYHint(); it = getYTransform().getAxisTickPositions( startY, getYMax() + getMajorYHint(), getMajorYHint()); } double y; for (Double s : it) { y = s; if (((y - getMajorYHint() / 2.0) < getOriginY()) && ((y + getMajorYHint() / 2.0) > getOriginY())) { continue; } int position = (int) yPositionToPixel(y); String str = format(y); FontMetrics metrics = g2.getFontMetrics(); Shape clip = g2.getClip(); if (clip == null) { return; } if (clip.contains(axisV + 5, position - metrics.getMaxAscent() / 2) && clip.contains(axisV + 5, position + metrics.getMaxAscent() / 2 + metrics.getMaxDescent())) { g2.drawString(str, (int) axisV + 5, position + metrics.getMaxAscent() / 2); } } } } /** * Draws the horizontal lines of the grid. Draws both minor and major grid * lines. * * @param g2 Graphics2D instance */ private void drawHorizontalGrid(Graphics2D g2) { double axisV = xPositionToPixel(getOriginX()); Rectangle clip = g2.getClipBounds(); if (clip == null) { return; } int position; Path2D path0 = new Path2D.Double(); Path2D path1 = new Path2D.Double(); Path2D path2 = new Path2D.Double(); if (isInnerAxisPainted()) { g2.setStroke(localAxisStroke); g2.setPaint(getAxisColor()); position = (int) xPositionToPixel(getOriginX()); if (position >= clip.x && position <= clip.x + clip.width) { g2.setColor(getMajorGridColor()); g2.drawLine(position, clip.y, position, clip.y + clip.height); } } final double myh = getMajorYHint(); double startY = Math.floor(getYMin() / myh) * myh; double y; if (isMinorGridPainted()) { ArrayList MinorGrid = getYTransform().getMinorGridPositions( startY, getYMax() + myh, myh, this.getMinorCountYHint()); for (Double s : MinorGrid) { y = s.doubleValue(); position = (int) yPositionToPixel(y); if (position >= clip.y && position <= clip.y + clip.height) { path0.moveTo(clip.x, position); path0.lineTo(clip.x + clip.width, position); } } } if (isMajorGridPainted() || isInnerAxisPainted()) { ArrayList MajorGrid = getYTransform().getAxisTickPositions( startY, getYMax() + myh, myh); for (Double s : MajorGrid) { y = s; position = (int) yPositionToPixel(y); if (position >= clip.y && position <= clip.y + clip.height) { if (isMajorGridPainted()) { path1.moveTo(clip.x, position); path1.lineTo(clip.x + clip.width, position); } // Draw the inner axis if required if (isInnerAxisPainted()) { path2.moveTo(axisV - 3, position); path2.lineTo(axisV + 3, position); } } } } h.clear(); h.add(path0); h.add(path1); h.add(path2); } /** * Draws labels on the horizontal axis. First draws labels on the right of * the origin, then on the left. * * @param g2 Graphics2D instance */ private void drawHorizontalAxisLabels(Graphics2D g2) { double axisH = yPositionToPixel(getOriginY()); FontMetrics metrics = g2.getFontMetrics(); final double mxh = getMajorXHint(); ArrayList it; if (isPolar()) { it = getPolarAxisLabels(); } else { double startX = Math.floor(getXMin() / mxh) * mxh; it = getXTransform().getAxisTickPositions( startX, getXMax() + mxh, mxh); } double x; for (Double s : it) { x = s.doubleValue(); if (((x - mxh / 2.0) < getOriginX()) && ((x + mxh / 2.0) > getOriginX())) { continue; } int position = (int) xPositionToPixel(x); String str = formatXAxisLabel(x); // GJAbstractGraph change Shape clip = g2.getClip(); if (clip == null) { return; } position = position - metrics.stringWidth(str) / 2; if (clip.contains(position, axisH + metrics.getHeight()) && clip.contains(position + metrics.stringWidth(str), axisH + metrics.getHeight())) { g2.drawString(str, position, (int) axisH + metrics.getHeight()); } else if (clip.contains(position, axisH - metrics.getHeight()) && clip.contains(position + metrics.stringWidth(str), axisH - 2 * metrics.getDescent())) { g2.drawString(str, position, (int) axisH - 2 * metrics.getDescent()); } } } /** * Draws the vertical lines of the grid. Draws both minor and major grid * lines. * * @param g2 Graphics2D instance */ private void drawVerticalGrid(Graphics2D g2) { double axisH = yPositionToPixel(getOriginY()); Rectangle clip = g2.getClipBounds(); if (clip == null) { return; } int position; Path2D path0 = new Path2D.Double(); Path2D path1 = new Path2D.Double(); Path2D path2 = new Path2D.Double(); if (isInnerAxisPainted()) { g2.setStroke(localAxisStroke); g2.setPaint(getAxisColor()); position = (int) yPositionToPixel(getOriginY()); if (position >= clip.y && position <= clip.y + clip.height) { g2.drawLine(clip.x, position, clip.x + clip.width, position); } } double mxh = getMajorXHint(); double startX = Math.floor(getXMin() / mxh) * mxh; double x; if (isMinorGridPainted()) { ArrayList MinorGrid = getXTransform().getMinorGridPositions( startX, getXMax() + mxh, mxh, getMinorCountXHint()); // Minor Grid for (Double s : MinorGrid) { x = s.doubleValue(); position = (int) xPositionToPixel(x); if (position >= clip.x && position <= clip.x + clip.width) { path0.moveTo(position, clip.y); path0.lineTo(position, clip.y + clip.height); } } } if (isMajorGridPainted() || isInnerAxisPainted()) { ArrayList MajorGrid = getXTransform().getAxisTickPositions( startX, getXMax() + mxh, mxh); for (Double s : MajorGrid) { x = s; position = (int) xPositionToPixel(x); if (position >= clip.x && position <= clip.x + clip.width) { if (isMajorGridPainted()) { path1.moveTo(position, clip.y); path1.lineTo(position, clip.y + clip.height); } if (isInnerAxisPainted()) { path2.moveTo(position, (int) axisH - 3); path2.lineTo(position, (int) axisH + 3); } } } } v.clear(); v.add(path0); v.add(path1); v.add(path2); } /** * Draws the main axis. * * @param g2 Graphics2D instance */ private void drawAxis(Graphics2D g2) { if (isPolar()) { polarDrawAxis(g2); } if (isInnerAxisPainted()) { double axisH = yPositionToPixel(getOriginY()); double axisV = xPositionToPixel(getOriginX()); Rectangle clip = g2.getClipBounds(); if (clip == null) { return; } g2.setPaint(getForeground()); Stroke stroke = g2.getStroke(); g2.setStroke(localAxisStroke); if (axisH >= clip.y && axisH <= clip.y + clip.height) { g2.drawLine(clip.x, (int) axisH, clip.x + clip.width, (int) axisH); } if (axisV >= clip.x && axisV <= clip.x + clip.width) { g2.drawLine((int) axisV, clip.y, (int) axisV, clip.y + clip.height); } g2.setStroke(stroke); } } private void polarDrawVerticalGrid(Graphics2D g2) { double position; if (isMajorGridPainted()) { Shape clip = g2.getClip(); if (clip == null) { return; } g2.clip(new Ellipse2D.Double(0, 0, getWidth(), getHeight())); ArrayList it = getPolarAxisLabels(); g2.setStroke(minorGridStroke); g2.setPaint(getMinorGridColor()); for (Double s : it) { int x, y, w, h; position = s; if (position < 0) { x = (int) xPositionToPixel(position); y = (int) yPositionToPixel(-position); } else { x = (int) xPositionToPixel(-position); y = (int) yPositionToPixel(position); } w = (int) (xPositionToPixel(position) - x); h = (int) (yPositionToPixel(-position) - y); g2.drawOval(x, y, w, h); //g2.drawArc(x, y, w, h, 0, 360); } g2.setClip(clip); } } private ArrayList getPolarAxisLabels() { // if (getMajorYHint() < 1e-14) { // updatePlots(); // } double factor = getMajorXHint() % 2 == 0 ? 2d : 1d; double start = ArrayMath.min(new double[]{-getYMax(), getYMin(), -getXMax(), getXMin()}) + getMajorYHint() / factor; double stop = ArrayMath.max(new double[]{getYMax(), -getYMin(), getXMax(), -getXMin()}) + getMajorYHint() / factor; start = Math.floor(start / getMajorYHint() * getMajorYHint()); stop = Math.ceil(stop / getMajorYHint() * getMajorYHint()); if (Math.abs(start) > stop) { stop = Math.abs(start); } start = 0; ArrayList arr = getXTransform().getAxisTickPositions( start, stop, getMajorXHint() / factor); ArrayList arr2 = new ArrayList(); arr2.addAll(arr); for (Double x : arr2) { arr.add(-x); } return arr; } private void polarDrawHorizontalGrid(Graphics2D g2) { } private void polarDrawAxis(Graphics2D g2) { Paint paint = g2.getPaint(); g2.setPaint(getMajorGridColor()); Stroke stroke = g2.getStroke(); g2.setStroke(minorGridStroke); Shape clip = g2.getClip(); if (clip == null) { return; } g2.clip(new Ellipse2D.Double(0, 0, getWidth(), getHeight())); for (double theta = 0; theta < 2 * Math.PI; theta += Math.PI / 6) { g2.drawLine((int) xPositionToPixel(0), (int) yPositionToPixel(0), (int) xPositionToPixel(Math.cos(theta) * getWidth() / 2d), (int) yPositionToPixel(Math.sin(theta) * getHeight() / 2d)); } g2.setClip(clip); g2.setStroke(stroke); g2.setPaint(paint); } HashMap> getPolarRayLabels(Graphics2D g) { HashMap> map = new HashMap>(); Ellipse2D e0 = new Ellipse2D.Double(-5, -5, getWidth() + 10, getHeight() + 10); double x0 = xPositionToPixel(0); double y0 = yPositionToPixel(0); for (double theta = 0; theta < 2 * Math.PI; theta += Math.PI / 6) { String s = String.format("%3.0f\u00b0", theta * 180 / Math.PI); double x1 = xPositionToPixel(Math.cos(theta) * getWidth() / 2d); double y1 = yPositionToPixel(Math.sin(theta) * getHeight() / 2d); Point2D xy = Ellipse.intersections(e0, x0, y0, x1, y1); map.put(xy, new AbstractMap.SimpleEntry(s, theta)); } return map; } @Override public void paint(Graphics g) { paintComponent(g); paintBorder(g); } /** * {@inheritDoc} * * @param g */ @Override public final void paintComponent(Graphics g) { if (!isVisible()) { return; } super.paintComponent(g); Graphics2D g2 = (Graphics2D) g.create(); if (g2 instanceof AbstractDeployableGraphics2D) { ((AbstractDeployableGraphics2D) g2).setPaintObject(this); } setupGraphics(g2); paintBackground(g2); if (g2 instanceof AbstractDeployableGraphics2D) { ((AbstractDeployableGraphics2D) g).addComment("Painting grid"); } drawGrid(g2); drawAxis(g2); if (getPlots() != null) { drawPlots(g2); } drawLabels(g2); if (getCurrentROI() != null && getCurrentROI().getWidth() > 0) { g2.setPaint(new Color(245, 222, 179, 100)); Rectangle2D r = getCurrentROI(); int x1 = (int) xPositionToPixel(r.getX()); int y1 = (int) yPositionToPixel(r.getY()); int w1 = (int) (r.getWidth() / getPixelWidth()); int h1 = (int) (r.getHeight() / getPixelHeight()); g2.fillRect(x1, y1, w1, h1); g2.setPaint(Color.DARK_GRAY); g2.drawRect(x1, y1, w1, h1); } if (getAvailableROI() != null) { for (Rectangle2D r : getAvailableROI()) { if (r.getWidth() > 0) { g2.setPaint(new Color(0f, 1f, 1f, 0.3f)); int x1 = (int) xPositionToPixel(r.getX()); int y1 = (int) yPositionToPixel(r.getY()); int w1 = (int) (r.getWidth() / getPixelWidth()); int h1 = (int) (r.getHeight() / getPixelHeight()); g2.fillRect(x1, y1, w1, h1); g2.setPaint(Color.BLACK); g2.drawRect(x1, y1, w1, h1); } } } paintChildren(g2); g2.dispose(); } public void paintGrid() { if (!isVisible()) { return; } Graphics g = getGraphics(); Graphics2D g2 = (Graphics2D) g.create(); g2.setClip(0, 0, getWidth(), getHeight()); super.paintBackground(g2); super.paintComponent(g2); if (g2 instanceof AbstractDeployableGraphics2D) { ((AbstractDeployableGraphics2D) g).setPaintObject(this); } setupGraphics(g2); paintBackground(g2); if (g2 instanceof AbstractDeployableGraphics2D) { ((AbstractDeployableGraphics2D) g).addComment("Painting grid"); } drawGrid(g2); drawAxis(g2); } @Override public LocalTransferable getTransferable() { if (getGraphContainer() != null) { return getGraphContainer().getTransferable(); } else { return super.getTransferable(); } } }

Ads help maintain this website.