Java tutorial
/** * Squidy Interaction Library 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. * * Squidy Interaction Library 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 Squidy Interaction Library. If not, see * <http://www.gnu.org/licenses/>. * * 2009 Human-Computer Interaction Group, University of Konstanz. * <http://hci.uni-konstanz.de> * * Please contact info@squidy-lib.de or visit our website * <http://www.squidy-lib.de> for further information. */ package org.squidy.designer.zoom.impl; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Ellipse2D; import java.awt.geom.RectangularShape; import java.util.HashMap; import java.util.Map; import java.util.Queue; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentLinkedQueue; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.squidy.designer.component.button.ImageButton; import org.squidy.designer.constant.Constants; import org.squidy.designer.event.ZoomActionEvent; import org.squidy.designer.event.ZoomActionListener; import org.squidy.designer.model.PipeShape; import org.squidy.designer.piccolo.JComponentWrapper; import org.squidy.designer.shape.VisualShape; import org.squidy.designer.shape.visualization.PlotContext; import org.squidy.designer.shape.visualization.Visualization; import org.squidy.designer.shape.visualization.impl.ImagePlot; import org.squidy.designer.shape.visualization.impl.ScatterPlot; import org.squidy.designer.shape.visualization.impl.ThermoPlot; import org.squidy.designer.util.FontUtils; import org.squidy.designer.util.ShapeUtils; import org.squidy.designer.zoom.ActionShape; import org.squidy.manager.ProcessException; import org.squidy.manager.ProcessingFeedback; import org.squidy.manager.data.IData; import org.squidy.manager.data.IDataContainer; import org.squidy.manager.data.impl.DefaultDataContainer; import org.squidy.manager.model.Processable; import edu.umd.cs.piccolo.nodes.PImage; import edu.umd.cs.piccolo.util.PAffineTransform; import edu.umd.cs.piccolo.util.PBounds; import edu.umd.cs.piccolo.util.PPaintContext; import edu.umd.cs.piccolo.util.PPickPath; import edu.umd.cs.piccolox.pswing.PSwing; /** * <code>ZoomVisualization</code>. * * <pre> * Date: Feb 18, 2009 * Time: 6:09:39 PM * </pre> * * @author Roman Rädle, <a * href="mailto:Roman.Raedle@uni-konstanz.de">Roman. * Raedle@uni-konstanz.de</a>, University of Konstanz * @version $Id: VisualizationShape.java 772 2011-09-16 15:39:44Z raedle $ * @since 1.0.0 */ public class VisualizationShape extends ActionShape<VisualShape<?>, Processable> implements ProcessingFeedback { /** * Generated serial version UID. */ private static final long serialVersionUID = -649926873755554231L; // Logger to log info, error, debug,... messages. private static final Log LOG = LogFactory.getLog(VisualizationShape.class); // class DataWrapper { // // long timestamp; // IData[] data; // // DataWrapper(long timestamp, IData... data) { // this.timestamp = timestamp; // this.data = data; // } // } // ############################################################################# // BEGIN ProcessingFeedback // ############################################################################# // private final Queue<DataWrapper> DATA_QUEUE = new // ConcurrentLinkedQueue<DataWrapper>(); private boolean active = false; private final Queue<IDataContainer> DATA_QUEUE = new ConcurrentLinkedQueue<IDataContainer>(); private int frameCount = 0; private int currentFPS = 0; private final Map<Class<? extends IData>, IData> LAST_DATA_CACHE = new HashMap<Class<? extends IData>, IData>(); /* * (non-Javadoc) * * @see * org.squidy.manager.ProcessingFeedback#feedback(org.squidy * .manager.data.IData) */ public void feedback(IData... data) { if (!active) { return; } frameCount++; if (freeze) { return; } // if (DATA_QUEUE.size() > 500) { // DATA_QUEUE.poll(); // } for (IData d : data) { LAST_DATA_CACHE.put(d.getClass(), d); } DATA_QUEUE.add(new DefaultDataContainer(data)); } // ############################################################################# // END ProcessingFeedback // ############################################################################# // ############################################################################# // BEGIN ILaunchable // ############################################################################# // ############################################################################# // END ILaunchable // ############################################################################# /* * (non-Javadoc) * * @see org.squidy.manager.ILaunchable#delete() */ public void delete() throws ProcessException { throw new UnsupportedOperationException(); } /* * (non-Javadoc) * * @see org.squidy.manager.ILaunchable#start() */ public void start() throws ProcessException { throw new UnsupportedOperationException(); } /* * (non-Javadoc) * * @see org.squidy.manager.ILaunchable#stop() */ public void stop() throws ProcessException { throw new UnsupportedOperationException(); } // ############################################################################# // BEGIN INTERNAL // ############################################################################# private final PAffineTransform TRANSLATION = new PAffineTransform(); private static final int SCALING = 10; private static final double SCALE_TRANSFORM_SCALING = 1.0 / (double) SCALING; private static final PAffineTransform SCALE_TRANSFROM = new PAffineTransform(); static { SCALE_TRANSFROM.setScale(SCALE_TRANSFORM_SCALING); } private int visualizationIndex = 0; private Visualization[] visualizations = new Visualization[] { new ScatterPlot(), new ThermoPlot(), new ImagePlot() }; // private Visualization visualization = new ThermoPlot(); private Timer timer = new Timer(); private TimerTask repaintTask; private TimerTask frameCountTask; private PSwing timeSpinner; private long sampleTime = 2500; private PImage image; private RectangularShape shape; private boolean freeze = false; private PipeShape pipeShape; /** * @param pipeShape */ public VisualizationShape(PipeShape pipeShape) { setBounds(Constants.DEFAULT_CIRCLE_BOUNDS); setTitle("Visualization"); // setShowNavigation(false); setShowTitle(false); this.pipeShape = pipeShape; // addInputEventListener(new PBasicInputEventHandler() { // @Override // public void mouseClicked(PInputEvent event) { // // if (event.isRightMouseButton() && event.getClickCount() == 2) { // if (!event.isHandled()) { // if (++visualizationIndex == visualizations.length) { // visualizationIndex = 0; // } // invalidatePaint(); // // event.setHandled(true); // } // } // } // }); initSemanticImage(); initTimeSpinner(); PBounds bounds = getBoundsReference(); PBounds imageBounds = image.getBoundsReference(); image.setOffset(bounds.getCenterX() - (imageBounds.getCenterX() * image.getScale()), bounds.getCenterY() - (imageBounds.getCenterY() * image.getScale())); } /* * (non-Javadoc) * * @see org.squidy.designer.zoom.ActionShape#initializeLayout() */ @Override public void initializeLayout() { super.initializeLayout(); setShowActionsOnMouseOver(false); final ImageButton runAction = new ImageButton( VisualizationShape.class.getResource("/images/24x24/stopwatch_run.png"), "Run"); final ImageButton pauseAction = new ImageButton( VisualizationShape.class.getResource("/images/24x24/stopwatch_pause.png"), "Pause"); runAction.setEnabled(false); runAction.addZoomActionListener(new ZoomActionListener() { /** * @param e */ public void actionPerformed(ZoomActionEvent e) { freeze = false; runAction.setEnabled(false); pauseAction.setEnabled(true); } }); addAction(runAction); pauseAction.addZoomActionListener(new ZoomActionListener() { /** * @param e */ public void actionPerformed(ZoomActionEvent e) { freeze = true; runAction.setEnabled(true); pauseAction.setEnabled(false); } }); addAction(pauseAction); final ImageButton scatterPlotAction = new ImageButton( VisualizationShape.class.getResource("/images/24x24/text_code_colored.png"), "Scatter"); final ImageButton thermoPlotAction = new ImageButton( VisualizationShape.class.getResource("/images/24x24/text_rich_colored.png"), "Thermo"); scatterPlotAction.setEnabled(false); scatterPlotAction.addZoomActionListener(new ZoomActionListener() { /** * @param e */ public void actionPerformed(ZoomActionEvent e) { visualizationIndex = 0; scatterPlotAction.setEnabled(false); thermoPlotAction.setEnabled(true); } }); addAction(scatterPlotAction); thermoPlotAction.addZoomActionListener(new ZoomActionListener() { /** * @param e */ public void actionPerformed(ZoomActionEvent e) { visualizationIndex = 1; scatterPlotAction.setEnabled(true); thermoPlotAction.setEnabled(false); } }); addAction(thermoPlotAction); } /** * */ private final void initSemanticImage() { image = new PImage(VisualizationShape.class.getResource("/images/visualization.png")); image.setScale(10); image.setPickable(false); image.setChildrenPickable(false); addChild(image); } /** * */ private final void initTimeSpinner() { PBounds bounds = getBoundsReference(); final JSpinner spinner = new JSpinner(new SpinnerNumberModel(sampleTime, 1000, 10000, 500)); spinner.addChangeListener(new ChangeListener() { /* * (non-Javadoc) * * @see * javax.swing.event.ChangeListener#stateChanged(javax.swing.event * .ChangeEvent) */ public void stateChanged(ChangeEvent e) { sampleTime = ((Double) spinner.getValue()).longValue(); invalidatePaint(); } }); timeSpinner = new JComponentWrapper(spinner); timeSpinner.setOffset((bounds.getWidth() * 0.25) - (timeSpinner.getWidth() / 2), bounds.getHeight() * 0.75 + 0.25); addChild(timeSpinner); } /* * (non-Javadoc) * * @see * edu.umd.cs.piccolo.PNode#pickAfterChildren(edu.umd.cs.piccolo.util.PPickPath * ) */ @Override protected boolean pickAfterChildren(PPickPath pickPath) { PBounds pickBounds = pickPath.getPickBounds(); return getZoomedInShape().intersects(pickBounds); } /* * (non-Javadoc) * * @see org.squidy.designer.zoom.ZoomObject#getShape() */ @Override protected Shape getZoomedInShape() { if (shape == null) { shape = new Ellipse2D.Double(); shape.setFrame(getBoundsReference()); } return shape; } /* * (non-Javadoc) * * @see org.squidy.designer.zoom.ZoomObject#getShapeZoomedOut() */ @Override protected Shape getZoomedOutShape() { return getZoomedInShape(); } /* * (non-Javadoc) * * @see org.squidy.designer.zoom.ZoomShape#layoutSemanticsZoomedIn() */ @Override protected void layoutSemanticsZoomedIn() { super.layoutSemanticsZoomedIn(); active = true; repaintTask = new TimerTask() { /* * (non-Javadoc) * * @see java.util.TimerTask#run() */ @Override public void run() { if (freeze) { return; } if (DATA_QUEUE.size() > 0) { repaint(); } // TODO [RR]: Optimization // PBounds bounds = getBoundsReference(); // bounds = new PBounds(bounds.getWidth() / 4, // bounds.getHeight() / 4, bounds.getWidth() / 2, // bounds.getHeight() / 2); // // repaintFrom(bounds, getParent()); } }; timer.scheduleAtFixedRate(repaintTask, 0, 50); frameCountTask = new TimerTask() { /* * (non-Javadoc) * * @see java.util.TimerTask#run() */ @Override public void run() { currentFPS = frameCount; frameCount = 0; repaint(); } }; timer.schedule(frameCountTask, 0, 1000); ShapeUtils.setApparent(image, false); ShapeUtils.setApparent(timeSpinner, true); } /* * (non-Javadoc) * * @see org.squidy.designer.zoom.ZoomShape#layoutSemanticsZoomedOut() */ @Override protected void layoutSemanticsZoomedOut() { super.layoutSemanticsZoomedOut(); if (repaintTask != null) { repaintTask.cancel(); repaintTask = null; frameCountTask.cancel(); frameCountTask = null; } active = false; DATA_QUEUE.clear(); currentFPS = 0; frameCount = 0; ShapeUtils.setApparent(image, true); ShapeUtils.setApparent(timeSpinner, false); } private static Font fontLables = internalFont.deriveFont(3f); private static Font fontSmall = internalFont.deriveFont(1.8f); private static Font fontFPS = internalFont.deriveFont(25f); private static Font fontHeadline = internalFont.deriveFont(32f); /* * (non-Javadoc) * * @see * org.squidy.designer.zoom.ZoomObject#paintShapeZoomedIn(edu.umd * .cs.piccolo.util.PPaintContext) */ @Override protected void paintShapeZoomedIn(PPaintContext paintContext) { Graphics2D g = paintContext.getGraphics(); PBounds bounds = getBoundsReference(); // PBounds cameraBounds = getBoundsReference();// paintContext.getCamera().getFullBoundsReference(); // double cameraX = cameraBounds.getX() - 20; // double cameraY = cameraBounds.getY(); // double cameraWidth = cameraBounds.getWidth() + 40; // double cameraHeight = cameraBounds.getHeight(); // g.setColor(Color.WHITE); // g.fillRect((int) cameraX, (int) cameraY, (int) cameraWidth, (int) cameraHeight); // // g.setColor(Color.BLACK); // g.fillRect((int) cameraX, (int) (cameraY + cameraHeight / 4), (int) cameraWidth, (int) (cameraHeight / 2)); g.setFont(fontLables); // Paint node labels. paintNodeLabels(paintContext); super.paintShapeZoomedIn(paintContext); // Paint headline. if (!isShowNavigation()) { paintHeadline(paintContext); } g.setFont(fontSmall); // Translate the current graphics position to 1/4 width and 1/4 height // of this shape. TRANSLATION.setToTranslation((int) (bounds.getWidth() / 4), (int) (bounds.getHeight() / 4)); paintContext.pushTransform(TRANSLATION); // Scale up visualization are because of int constraints to avoid // mathematical round failure. int maxWidth = (int) ((bounds.getWidth() / 2) * 10); int maxHeight = (int) ((bounds.getHeight() / 2) * 10); paintContext.pushTransform(SCALE_TRANSFROM); PlotContext plotContext = new PlotContext(paintContext, maxWidth, maxHeight, SCALE_TRANSFORM_SCALING, sampleTime); long currentTime = System.currentTimeMillis(); for (IDataContainer dataContainer : DATA_QUEUE) { if ((currentTime - dataContainer.getTimestamp()) >= sampleTime) { DATA_QUEUE.remove(dataContainer); continue; } // Delegates plot of data to visualization. visualizations[visualizationIndex].plotData(plotContext, dataContainer); } // Paints the grid. visualizations[visualizationIndex].paintContextual(paintContext, plotContext); paintContext.popTransform(SCALE_TRANSFROM); // Pop transform translation. paintContext.popTransform(TRANSLATION); g.setFont(fontFPS); g.drawString("FPS: " + currentFPS, (int) (bounds.getWidth() / 2 - 50), (int) (bounds.getHeight() - 100)); g.setFont(fontHeadline.deriveFont(18f)); g.setColor(Color.RED); g.fillOval((int) (bounds.getWidth() - 115), (int) (bounds.getHeight() / 2 - 20), 10, 10); g.setColor(Color.BLACK); g.drawString("x-value", (int) (bounds.getWidth() - 100), (int) (bounds.getHeight() / 2 - 10)); g.setColor(Color.BLUE); g.fillOval((int) (bounds.getWidth() - 115), (int) (bounds.getHeight() / 2 + 20), 10, 10); g.setColor(Color.BLACK); g.drawString("y-value", (int) (bounds.getWidth() - 100), (int) (bounds.getHeight() / 2 + 30)); } /* * (non-Javadoc) * * @see * org.squidy.designer.zoom.ZoomShape#paintShapeZoomedOut(edu.umd * .cs.piccolo.util.PPaintContext) */ @Override protected void paintShapeZoomedOut(PPaintContext paintContext) { // Graphics2D g = paintContext.getGraphics(); // g.setStroke(StrokeUtils.getBasicStroke(5f)); // g.draw(getBoundsReference()); super.paintShapeZoomedOut(paintContext); } /** * @param paintContext */ protected void paintHeadline(PPaintContext paintContext) { Graphics2D g = paintContext.getGraphics(); g.setFont(fontHeadline); FontMetrics fm = g.getFontMetrics(); PBounds bounds = getBoundsReference(); double x = bounds.getX(); double width = bounds.getWidth(); int titleWidth = FontUtils.getWidthOfText(fm, getTitle()); g.drawString(getTitle(), (int) (x + width / 2) - (titleWidth / 2), 100); int breadcrumbWidth = FontUtils.getWidthOfText(fm, getBreadcrumb()); g.drawString(getBreadcrumb(), (int) (x + width / 2) - (breadcrumbWidth / 2), 100 + fm.getHeight()); } private PAffineTransform rotation270 = new PAffineTransform(); private PAffineTransform rotation90 = new PAffineTransform(); /** * @param paintContext */ protected void paintNodeLabels(PPaintContext paintContext) { Graphics2D g = paintContext.getGraphics(); g.setFont(g.getFont().deriveFont(18f)); FontMetrics fm = g.getFontMetrics(); PBounds bounds = getBoundsReference(); double width = bounds.getWidth(); double height = bounds.getHeight(); String inputName = pipeShape.getSource().getTitle(); rotation270.setToRotation(Math.toRadians(270)); paintContext.pushTransform(rotation270); g.setColor(Color.WHITE); g.drawString(inputName, (int) -((height / 2) + (FontUtils.getWidthOfText(fm, inputName) / 2)), -25); paintContext.popTransform(rotation270); String outputName = pipeShape.getTarget().getTitle(); rotation90.setToRotation(Math.toRadians(90)); paintContext.pushTransform(rotation90); g.setColor(Color.WHITE); g.drawString(outputName, (int) ((height / 2) - (FontUtils.getWidthOfText(fm, outputName) / 2)), (int) (-width - 30)); paintContext.popTransform(rotation90); } // /** // * Paint the grid of the visualization. // * // * @param paintContext Paint context. // */ // private void paintGrid(PPaintContext paintContext) { // // Graphics2D g = paintContext.getGraphics(); // // PBounds bounds = getBoundsReference(); // // g.setColor(Color.BLACK); // // System.out.println(defaultFont); // g.setFont(defaultFont.deriveFont(1.8f)); // // int timeStep = (int) sampleTime / 500; // int gridStepX = (int) ((bounds.getWidth() / 2) / timeStep); // int gridStepY = (int) ((bounds.getHeight() / 2) / 10); // // // Paint axes. // g.setStroke(new BasicStroke(0.5f)); // g.drawLine(0, 0, 0, (int) (bounds.getHeight() / 2)); // g.drawLine(0, (int) (bounds.getHeight() / 2), (int) (bounds.getWidth() / // 2), (int) (bounds.getHeight() / 2)); // // g.setStroke(new BasicStroke(0.1f)); // // FontMetrics fm = g.getFontMetrics(); // // // Properties for vertical and horizontal lines. // int requiredWidth = (int) (bounds.getWidth() / 2); // int requiredHeight = (int) (bounds.getHeight() / 2); // // String xLabel = "Time (ms)"; // g.drawString(xLabel, requiredWidth / 2 - fm.stringWidth(xLabel), // requiredHeight + 5); // // PAffineTransform rotation = new PAffineTransform(); // rotation.rotate(Math.toRadians(-90)); // paintContext.pushTransform(rotation); // String yLabel = "Value"; // g.drawString(yLabel, - (requiredHeight / 2) - (fm.stringWidth(yLabel) / // 2), - 5); // paintContext.popTransform(rotation); // // // Paint vertical lines. // int timeAxes = (int) sampleTime; // for (int xShift = 0; xShift <= requiredWidth; xShift += gridStepX) { // g.drawLine(xShift, 0, xShift, (int) (bounds.getHeight() / 2)); // // if (xShift > 0) { // String xLabels = timeAxes > 0 ? "-" + timeAxes : "" + timeAxes; // g.drawString(xLabels, xShift - (fm.stringWidth(xLabels) / 2), (int) // (bounds.getHeight() / 2) + 2); // } // timeAxes -= 500; // } // // // Paint horizontal lines. // double i = 0; // for (int yShift = requiredHeight; yShift >= 0; yShift -= gridStepY) { // String yLabels = DECIMAL_FORMAT.format(i); // // g.drawLine(0, yShift, requiredWidth, yShift); // g.drawString(yLabels, - fm.stringWidth(yLabels) - 1, yShift); // i += 0.1; // } // } // ############################################################################# // END INTERNAL // ############################################################################# }