com.googlecode.blaisemath.svg.swing.SvgRootGraphic Maven / Gradle / Ivy
package com.googlecode.blaisemath.svg.swing;
/*
* #%L
* BlaiseSVG
* --
* Copyright (C) 2014 - 2020 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 com.googlecode.blaisemath.graphics.Graphic;
import com.googlecode.blaisemath.graphics.GraphicUtils;
import com.googlecode.blaisemath.graphics.svg.SvgGraphic;
import com.googlecode.blaisemath.graphics.swing.JGraphicComponent;
import com.googlecode.blaisemath.svg.internal.SvgUtils;
import com.googlecode.blaisemath.svg.reader.SvgGroupReader;
import com.googlecode.blaisemath.svg.reader.SvgReadException;
import com.googlecode.blaisemath.svg.xml.SvgIo;
import com.googlecode.blaisemath.svg.xml.SvgRoot;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Uses an {@link SvgRoot} as a primitive to be rendered on a {@link JGraphicComponent}. The root element is used to set
* the viewbox (coordinates for the source graphic) fits within the viewport (coordinates on the target canvas).
* Otherwise, the elements will be rendered without transformations or clipping.
*
* @author Elisha Peterson
*/
public class SvgRootGraphic extends SvgGraphic {
private static final Logger LOG = Logger.getLogger(SvgRootGraphic.class.getName());
/** Source SVG element to be drawn */
private SvgRoot element;
/** The graphic object that will be rendered */
private Graphic primitiveElement;
/** Whether viewport should be rendered */
private boolean renderViewport = false;
/** Whether background should be rendered (will be limited to the viewport) */
private boolean renderBackground = true;
/** Whether viewbox should be rendered */
private boolean renderViewBox = false;
/**
* Create a new instance for the provided SVG.
* @param element SVG to draw
* @return graphic
*/
public static SvgRootGraphic create(SvgRoot element) {
checkNotNull(element);
SvgRootGraphic res = new SvgRootGraphic();
res.setElement(element);
return res;
}
/**
* Create a new instance for the provided SVG.
* @param svg svg as a string
* @return graphic
*/
public static SvgRootGraphic create(String svg) throws IOException{
return create(SvgIo.read(svg));
}
//region PROPERTIES
public SvgRoot getElement() {
return element;
}
public void setElement(SvgRoot el) {
Object old = this.element;
if (old != el) {
this.element = el;
updateGraphics();
}
}
public boolean isRenderBackground() {
return renderBackground;
}
public void setRenderBackground(boolean renderBackground) {
this.renderBackground = renderBackground;
fireGraphicChanged();
}
public boolean isRenderViewport() {
return renderViewport;
}
public void setRenderViewport(boolean renderViewport) {
this.renderViewport = renderViewport;
fireGraphicChanged();
}
public boolean isRenderViewBox() {
return renderViewBox;
}
public void setRenderViewBox(boolean renderViewBox) {
this.renderViewBox = renderViewBox;
fireGraphicChanged();
}
//endregion
/** Update the graphic, and the viewport/viewBox parameters if the root element is {@link SvgRoot}. */
private void updateGraphics() {
Graphic nue;
try {
nue = SvgGroupReader.readGraphic(element);
if (primitiveElement == null) {
addGraphic(nue);
} else {
replaceGraphics(Collections.singleton(primitiveElement), Collections.singleton(nue));
}
primitiveElement = nue;
viewport = new Rectangle2D.Double(0, 0, element.getWidth(), element.getHeight());
viewBox = element.getViewBoxAsRectangle();
} catch (SvgReadException e) {
LOG.log(Level.SEVERE, "Unable to read SVG", e);
primitiveElement = null;
clearGraphics();
}
}
//region GRAPHICS API LOCATIONS
@Override
public Rectangle2D boundingBox(Graphics2D canvas) {
AffineTransform tx = transform();
Rectangle2D norm = GraphicUtils.boundingBox(entries, canvas);
return norm == null ? null : tx.createTransformedShape(norm).getBounds2D();
}
@Override
public boolean contains(Point2D point, Graphics2D canvas) {
Point2D tp = transform(point);
return viewBox.contains(tp) && super.contains(tp, canvas);
}
@Override
public boolean intersects(Rectangle2D box, Graphics2D canvas) {
Rectangle2D clippedBox = transform(box).createIntersection(viewBox);
return clippedBox.getWidth() >= 0 && clippedBox.getHeight() >= 0 && super.intersects(clippedBox, canvas);
}
@Override
public Graphic graphicAt(Point2D point, Graphics2D canvas) {
Point2D tp = transform(point);
return !viewBox.contains(tp) ? null : super.graphicAt(tp, canvas);
}
@Override
public Graphic selectableGraphicAt(Point2D point, Graphics2D canvas) {
Point2D tp = transform(point);
return !viewBox.contains(tp) ? null : super.selectableGraphicAt(tp, canvas);
}
@Override
public Set> selectableGraphicsIn(Rectangle2D box, Graphics2D canvas) {
Rectangle2D clippedBox = transform(box).createIntersection(viewBox);
return clippedBox.getWidth() <= 0 || clippedBox.getHeight() <= 0 ? Collections.emptySet()
: super.selectableGraphicsIn(clippedBox, canvas);
}
@Override
public Graphic mouseGraphicAt(Point2D point, Graphics2D canvas) {
Point2D tp = transform(point);
return !viewBox.contains(tp) ? null : super.mouseGraphicAt(tp, canvas);
}
@Override
public String getTooltip(Point2D p, Graphics2D canvas) {
Point2D tp = transform(p);
return !viewBox.contains(tp) ? null : super.getTooltip(tp, canvas);
}
@Override
public void initContextMenu(JPopupMenu menu, Graphic src, Point2D point, Object focus, Set> selection, Graphics2D canvas) {
Point2D tp = transform(point);
if (viewBox.contains(tp)) {
super.initContextMenu(menu, src, tp, focus, selection, canvas);
}
}
private Point2D transform(Point2D pt) {
AffineTransform tx = inverseTransform();
return tx == null ? pt : tx.transform(pt, null);
}
private Rectangle2D transform(Rectangle2D box) {
AffineTransform tx = inverseTransform();
return tx == null ? box : tx.createTransformedShape(box).getBounds2D();
}
//endregion
@Override
public void renderTo(Graphics2D canvas) {
AffineTransform tx = transform();
maybeRenderViewport(canvas);
if (element == null) {
return;
}
maybeRenderBackground(canvas);
AffineTransform original = canvas.getTransform();
Shape oldClip = canvas.getClip();
canvas.transform(tx);
if (oldClip != null) {
canvas.setClip(viewBox.createIntersection(transform(oldClip.getBounds2D())));
}
maybeRenderViewBox(canvas);
super.renderTo(canvas);
canvas.setTransform(original);
canvas.setClip(oldClip);
}
private void maybeRenderViewport(Graphics2D canvas) {
if (renderViewport) {
canvas.setStroke(new BasicStroke(1f));
canvas.setColor(Color.red);
canvas.draw(viewport);
}
}
private void maybeRenderBackground(Graphics2D canvas) {
if (renderBackground) {
SvgUtils.backgroundColor(element).ifPresent(bg -> {
canvas.setColor(bg);
canvas.fill(viewport);
});
}
}
private void maybeRenderViewBox(Graphics2D canvas) {
if (renderViewBox) {
canvas.setStroke(new BasicStroke(1f));
canvas.setColor(Color.blue);
canvas.draw(viewBox);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy