All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.blaisemath.graphics.swing.JGraphicComponent Maven / Gradle / Ivy

There is a newer version: 3.0.16
Show newest version
/**
 * JGraphicComponent.java
 * Created on Jul 30, 2009
 */

package com.googlecode.blaisemath.graphics.swing;

/*
 * #%L
 * BlaiseGraphics
 * --
 * Copyright (C) 2009 - 2019 Elisha Peterson
 * --
 * 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.
 * #L%
 */

import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.Lists;
import com.googlecode.blaisemath.graphics.core.GMouseEvent;
import com.googlecode.blaisemath.graphics.core.Graphic;
import com.googlecode.blaisemath.graphics.core.GraphicUtils;
import com.googlecode.blaisemath.style.StyleContext;
import com.googlecode.blaisemath.util.CanvasPainter;
import com.googlecode.blaisemath.util.SetSelectionModel;
import com.googlecode.blaisemath.util.TransformedCoordinateSpace;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import javax.annotation.Nullable;

/**
 * 

* Swing component that collects and draws shapes on a screen. * The shapes and their styles are enclosed within a {@link JGraphicRoot} class, * which also sets up the requisite mouse handling and manages the drawing. *

*

* This class is not designed for serialization. *

* * @see JGraphicRoot * * @author Elisha Peterson */ public class JGraphicComponent extends javax.swing.JComponent implements TransformedCoordinateSpace { /** The visible shapes. */ protected final transient JGraphicRoot root; /** Affine transform applied to graphics canvas before drawing (enables pan and zoom). */ @Nullable protected AffineTransform transform = null; /** Store inverse transform */ @Nullable protected AffineTransform inverseTransform = null; /** Underlay painters */ protected final transient List underlays = Lists.newArrayList(); /** Overlay painters */ protected final transient List overlays = Lists.newArrayList(); /** Used for selecting graphics */ protected final transient JGraphicSelectionHandler selector = new JGraphicSelectionHandler(this); /** Whether antialias is enabled */ protected boolean antialias = true; // CONSTRUCTOR /** * Construction of a generic graphics view component. */ public JGraphicComponent() { root = new JGraphicRoot(this); selector.setSelectionEnabled(false); addMouseListener(selector); addMouseMotionListener(selector); overlays.add(selector); setDoubleBuffered(true); setBackground(Color.WHITE); setOpaque(true); setPreferredSize(new Dimension(300, 200)); // this line enables tooltips setToolTipText(""); } /** * Return the tooltip associated with the mouse event's point. * This will look for the topmost {@link Graphic} beneath the mouse and return that. * @param event the event with the point for the tooltip * @return tooltip for the point */ @Override public String getToolTipText(MouseEvent event) { String ct = root.getTooltip(toGraphicCoordinate(event.getPoint())); return ct != null ? ct : "".equals(super.getToolTipText()) ? null : super.getToolTipText(); } // // // PROPERTIES // /** * Return graphic root managing the shapes to be rendered * @return shapes root */ public JGraphicRoot getGraphicRoot() { return root; } /** * Return the default render factory used to draw shapes * @return current style provider used to draw shapes in the component */ public StyleContext getStyleContext() { return root.getStyleContext(); } /** * Sets the default render factory used to draw shapes * @param factory render factory * @throws IllegalArgumentException if the factory is null */ public void setStyleContext(StyleContext factory) { root.setStyleContext(factory); } /** * If the mouse control allowing for selection of graphic objects is currently active * @return true if enabled, false if not */ public boolean isSelectionEnabled() { return selector.isSelectionEnabled(); } /** * Enable/disable the mouse control allowing for selection of graphic objects. * @param b true to enable, false to disable */ public void setSelectionEnabled(boolean b) { selector.setSelectionEnabled(b); } public SetSelectionModel> getSelectionModel() { return selector.getSelectionModel(); } /** * Return true if antialias is enabled * @return antialias setting */ public boolean isAntialiasOn() { return antialias; } /** * Sets antialias status * @param aa antialias status */ public void setAntialiasOn(boolean aa) { antialias = aa; repaint(); } // // // // DELEGATES // /** * Add graphics to the component * @param add graphics to add */ public final void addGraphics(Iterable> add) { root.addGraphics(add); } /** * Add a single graphic to the component * @param gfc graphic to add */ public final void addGraphic(Graphic gfc) { root.addGraphic(gfc); } /** * Remove graphics from the component * @param remove graphics to remove */ public final void removeGraphics(Iterable> remove) { root.removeGraphics(remove); } /** * Remove a single graphic from the component * @param gfc graphic to remove */ public void removeGraphic(Graphic gfc) { root.removeGraphic(gfc); } /** * Remove all graphics from the component. */ public void clearGraphics() { root.clearGraphics(); } // // @Nullable @Override public AffineTransform getTransform() { return transform; } @Nullable @Override public AffineTransform getInverseTransform() { return inverseTransform; } /** * Set thee transform used for drawing objects on the canvas. * @param at the transform * @throws IllegalArgumentException if the transform is non-null but not invertible */ @Override public void setTransform(@Nullable AffineTransform at) { checkArgument(at == null || at.getDeterminant() != 0); AffineTransform old = transform; if (old != at) { transform = at; try { inverseTransform = at == null ? null : at.createInverse(); } catch (NoninvertibleTransformException ex) { throw new IllegalStateException("Already checked that the argument is invertible...", ex); } firePropertyChange("transform", old, at); repaint(); } } /** * Reset transform to the default. */ public void resetTransform() { setTransform(null); } // /** * Set transform to include all components in the graphic tree. Does nothing * if there are no graphics. Animates zoom operation. */ public void zoomToAll() { zoomToAll(new Insets(0, 0, 0, 0), true); } /** * Set transform to include all components in the graphic tree inside display * area plus insets. The insets are expressed in local coordinates, not window * coordinates. Positive insets result in extra space around the graphics. * Animates zoom operation. * * @param outsets additional space to leave around the graphics */ public void zoomToAll(Insets outsets) { zoomToAll(outsets, true); } /** * Set transform to include all components in the graphic tree inside display * area plus insets. The insets are expressed in local coordinates, not window * coordinates. Positive insets result in extra space around the graphics. * * @param outsets additional space to leave around the graphics * @param animate if true, zoom operation will animate */ public void zoomToAll(Insets outsets, boolean animate) { Rectangle2D bounds = getGraphicRoot().boundingBox(); if (bounds != null && animate) { animatedZoomWithOutsets(bounds, outsets); } else if (bounds != null) { instantZoomWithOutsets(bounds, outsets); } } /** * Zooms in in to the graphics canvas (animated). */ public void zoomIn() { PanAndZoomHandler.zoomIn(this, true); } /** * Zooms in in to the graphics canvas. * @param animate if true, zoom operation will animate */ public void zoomIn(boolean animate) { PanAndZoomHandler.zoomIn(this, animate); } /** * Zooms out of the graphics canvas (animated). */ public void zoomOut() { PanAndZoomHandler.zoomOut(this, true); } /** * Zooms out of the graphics canvas. * @param animate if true, zoom operation will animate */ public void zoomOut(boolean animate) { PanAndZoomHandler.zoomOut(this, animate); } /** * Set transform to include all selected components. Does nothing if nothing * is selected. Zoom is animated. */ public void zoomToSelected() { zoomToSelected(new Insets(0, 0, 0, 0), true); } /** * Set transform to include all components in the graphic tree inside display * area plus insets. The outsets are expressed in local coordinates, not window * coordinates. Positive insets result in extra space around the graphics. * Zoom is animated. * * @param locCoordOutsets additional space to leave around the graphics (in local coordinate space) */ public void zoomToSelected(Insets locCoordOutsets) { zoomToSelected(locCoordOutsets, true); } /** * Set transform to include all components in the graphic tree inside display * area plus insets. The outsets are expressed in local coordinates, not window * coordinates. Positive insets result in extra space around the graphics. * Zoom is anmiated. * * @param locCoordOutsets additional space to leave around the graphics (in local coordinate space) * @param animate if true, zoom operation will animate */ public void zoomToSelected(Insets locCoordOutsets, boolean animate) { Rectangle2D bounds = GraphicUtils.boundingBox(getSelectionModel().getSelection()); if (bounds != null && animate) { animatedZoomWithOutsets(bounds, locCoordOutsets); } else if (bounds != null) { instantZoomWithOutsets(bounds, locCoordOutsets); } } /** * Utility method to animate the zoom operation to the target local bounds. * @param bounds local bounds * @param locCoordOutsets outsets beyond the local bounds */ private void animatedZoomWithOutsets(Rectangle2D bounds, Insets locCoordOutsets) { double minX = bounds.getMinX() - locCoordOutsets.left; double maxX = Math.max(minX, bounds.getMaxX() + locCoordOutsets.right); double minY = bounds.getMinY() - locCoordOutsets.top; double maxY = Math.max(minY, bounds.getMaxY() + locCoordOutsets.bottom); PanAndZoomHandler.zoomCoordBoxAnimated(this, new Point2D.Double(minX, minY), new Point2D.Double(maxX, maxY)); } /** * Utility method to instantly change the zoom to the target local bounds. * @param bounds local bounds * @param locCoordOutsets outsets beyond the local bounds */ private void instantZoomWithOutsets(Rectangle2D bounds, Insets locCoordOutsets) { double minX = bounds.getMinX() - locCoordOutsets.left; double maxX = Math.max(minX, bounds.getMaxX() + locCoordOutsets.right); double minY = bounds.getMinY() - locCoordOutsets.top; double maxY = Math.max(minY, bounds.getMaxY() + locCoordOutsets.bottom); PanAndZoomHandler.setDesiredLocalBounds(this, new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY)); } // /** * Convert window point location to graphic root location * @param winLoc window location * @return graphic coordinate system location */ @Override public Point2D toGraphicCoordinate(Point2D winLoc) { return inverseTransform == null ? winLoc : inverseTransform.transform(winLoc, null); } /** * Convert mouse event to local coordinate space * @param winEvent event in windows coordinate space * @return event w/ location in local coordinate space */ public GMouseEvent toGraphicCoordinateSpace(MouseEvent winEvent) { Point2D loc = toGraphicCoordinate(winEvent.getPoint()); return new GMouseEvent(winEvent, loc, null); } /** * Return the graphic at the given window location * @param winLoc window location * @return graphic */ public Graphic graphicAt(Point winLoc) { return root.graphicAt(toGraphicCoordinate(winLoc)); } /** * Return the functional graphic at the given window location * @param winLoc window location * @return graphic */ public Graphic functionalGraphicAt(Point winLoc) { return root.mouseGraphicAt(toGraphicCoordinate(winLoc)); } /** * Return the selectable graphic at the given window location * @param winLoc window location * @return graphic */ public Graphic selectableGraphicAt(Point winLoc) { return root.selectableGraphicAt(toGraphicCoordinate(winLoc)); } // // // PAINT METHODS // /** * Return modifiable list of overlay painters * @return list */ public List getOverlays() { return overlays; } /** * Return modifiable list of underlay painters * @return list */ public List getUnderlays() { return underlays; } /** * Paints the graphics to the specified canvas. * @param g graphics object */ @Override protected void paintChildren(Graphics g) { renderTo((Graphics2D) g); super.paintChildren(g); } /** * Renders all shapes in root to specified graphics object. * @param canvas graphics canvas to render to */ public void renderTo(Graphics2D canvas) { canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialias ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF ); if (isOpaque()) { canvas.setColor(getBackground()); canvas.fillRect(0, 0, getWidth(), getHeight()); } renderUnderlay(canvas); if (transform == null) { root.renderTo(canvas); } else { AffineTransform priorTransform = canvas.getTransform(); canvas.transform(transform); root.renderTo(canvas); canvas.setTransform(priorTransform); } renderOverlay(canvas); } /** * Hook to render underlay elements. Called after the background is drawn, * but before anything else. * @param canvas the canvas to render to */ protected void renderUnderlay(Graphics2D canvas) { for (CanvasPainter p : underlays) { p.paint(this, canvas); } } /** * Hook to render overlay elements. Called after everything else is drawn. * @param canvas the canvas to render to */ protected void renderOverlay(Graphics2D canvas) { for (CanvasPainter p : overlays) { p.paint(this, canvas); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy