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

com.googlecode.blaisemath.graphics.core.GraphicComposite Maven / Gradle / Ivy

There is a newer version: 3.0.16
Show newest version
/*
 * GraphicComposite.java
 * Created Jan 16, 2011
 */

package com.googlecode.blaisemath.graphics.core;

/*
 * #%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.checkState;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import static com.googlecode.blaisemath.graphics.core.PrimitiveGraphic.STYLE_PROP;
import com.googlecode.blaisemath.style.AttributeSet;
import com.googlecode.blaisemath.style.Renderer;
import com.googlecode.blaisemath.style.StyleContext;
import com.googlecode.blaisemath.style.StyleHints;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import javax.swing.JPopupMenu;

/**
 * 

* An ordered collection of {@link Graphic}s, where the ordering indicates draw order. * May also have a {@link StyleContext} that graphics can reference when rendering. * The composite is NOT thread safe. Any access and changes should be done from a single * thread. *

* * @param type of graphics canvas to render to * * @author Elisha */ @NotThreadSafe public class GraphicComposite extends Graphic { public static final String BOUNDING_BOX_VISIBLE_PROP = "boundingBoxVisible"; public static final String BOUNDING_BOX_STYLE_PROP = "boundingBoxStyle"; /** Stores the shapes and their styles (in order) */ protected final Set> entries = Sets.newLinkedHashSet(); /** The attributes associated with the composite. These will be inherited by child graphics. */ protected AttributeSet style = new AttributeSet(); /** The associated style provider; overrides the default style for the components in the composite (may be null). */ @Nullable protected StyleContext styleContext; /** Delegate graphic used for drawing the bounding box */ private final PrimitiveGraphic boundingBoxGraphic = new PrimitiveGraphic(); /** Constructs with default settings */ public GraphicComposite() { setTooltipEnabled(true); boundingBoxGraphic.setStyleHint(StyleHints.HIDDEN_HINT, true); } @Override public void initContextMenu(JPopupMenu menu, Graphic src, Point2D point, Object focus, Set selection) { // add children menu options for (Graphic en : visibleEntriesInReverse()) { if ((en instanceof GraphicComposite || en.isContextMenuEnabled()) && en.contains(point)) { en.initContextMenu(menu, en, point, focus, selection); } } // behavior adds inits registered with this class after children if (isContextMenuEnabled()) { super.initContextMenu(menu, this, point, focus, selection); } } /** * Called when a graphic has changed. * @param source the entry changed */ public void graphicChanged(Graphic source) { if (parent != null) { parent.graphicChanged(source); } } // // // PROPERTIES // /** * Get graphic entries in the order they are drawn. * @return iterator over the entries, in draw order */ public Iterable> getGraphics() { return Iterables.unmodifiableIterable(entries); } /** * Explicitly set list of entries. The draw order will correspond to the iteration order. * @param graphics graphics in the composite */ public void setGraphics(Iterable> graphics) { clearGraphics(); addGraphics(graphics); } /** * Return style provider with default styles * @return style provider with default styles * @throws IllegalStateException if the object returned would be null */ @Nonnull public StyleContext getStyleContext() { if (styleContext != null) { return styleContext; } else { checkState(parent != null); return parent.getStyleContext(); } } /** * Sets default style provider for all child entries (may be null) * @param styler the style provider (may be null) * @throws IllegalArgumentException if the styler is null, and the composite cannot * get a non-null context from its parent */ public void setStyleContext(@Nullable StyleContext styler) { if (styler == null) { checkState(parent != null); } if (styleContext != styler) { styleContext = styler; fireGraphicChanged(); } } @Override @Nullable public AttributeSet getStyle() { return style; } public final void setStyle(@Nullable AttributeSet sty) { if (this.style != sty) { Object old = this.style; this.style = sty; fireGraphicChanged(); pcs.firePropertyChange(STYLE_PROP, old, style); } } public boolean isBoundingBoxVisible() { return !GraphicUtils.isInvisible(boundingBoxGraphic); } public void setBoundingBoxVisible(boolean show) { if (isBoundingBoxVisible() != show) { boundingBoxGraphic.setStyleHint(StyleHints.HIDDEN_HINT, !show); fireGraphicChanged(); pcs.firePropertyChange(BOUNDING_BOX_VISIBLE_PROP, !show, show); } } public AttributeSet getBoundingBoxStyle() { return boundingBoxGraphic.getStyle(); } public void setBoundingBoxStyle(AttributeSet style) { Object old = getBoundingBoxStyle(); if (old != style) { boundingBoxGraphic.setStyle(style); fireGraphicChanged(); pcs.firePropertyChange(BOUNDING_BOX_STYLE_PROP, old, style); } } public Renderer getBoundingBoxRenderer() { return boundingBoxGraphic.getRenderer(); } public void setBoundingBoxRenderer(Renderer renderer) { boundingBoxGraphic.setRenderer(renderer); fireGraphicChanged(); } // // // // COMPOSITE METHODS // /** Add w/o events */ private boolean addHelp(Graphic en) { if (entries.add(en)) { GraphicComposite par = en.getParent(); if (par != null) { par.removeGraphic(en); } en.setParent(this); return true; } return false; } /** Remove w/o events */ private boolean removeHelp(Graphic en) { if (entries.remove(en)) { if (en.getParent() == this) { en.setParent(null); } return true; } return false; } /** * Add an entry to the composite. * @param gfc the entry * @return whether composite was changed by add */ public final boolean addGraphic(Graphic gfc) { if (addHelp(gfc)) { fireGraphicChanged(); return true; } return false; } /** * Remove an entry from the composite * @param gfc the entry to remove * @return true if composite was changed */ public boolean removeGraphic(Graphic gfc) { if (removeHelp(gfc)) { fireGraphicChanged(); return true; } return false; } /** * Adds several entries to the composite * @param add the entries to add * @return true if composite was changed */ public final boolean addGraphics(Iterable> add) { boolean change = false; for (Graphic en : add) { change = addHelp(en) || change; } if (change) { fireGraphicChanged(); return true; } else { return false; } } /** * Removes several entries from the composite * @param remove the entries to remove * @return true if composite was changed */ public final boolean removeGraphics(Iterable> remove) { boolean change = false; for (Graphic en : remove) { change = removeHelp(en) || change; } if (change) { fireGraphicChanged(); return true; } else { return false; } } /** * Replaces entries * @param remove entries to remove * @param add entries to add * @return true if composite changed */ public boolean replaceGraphics( Iterable> remove, Iterable> add) { boolean change = false; for (Graphic en : remove) { change = removeHelp(en) || change; } for (Graphic en : add) { change = addHelp(en) || change; } if (change) { fireGraphicChanged(); } return change; } /** * Removes all entries, clearing their parents * @return true if composite was changed */ public boolean clearGraphics() { boolean change = !entries.isEmpty(); for (Graphic en : entries) { if (en.getParent() == this) { en.setParent(null); } } entries.clear(); if (change) { fireGraphicChanged(); return true; } return false; } // // // // Graphic METHODS // @Override public Rectangle2D boundingBox() { return GraphicUtils.boundingBox(entries); } @Override public boolean contains(Point2D point) { return graphicAt(point) != null; } @Override public boolean intersects(Rectangle2D box) { for (Graphic en : entries) { if (en.intersects(box)) { return true; } } return false; } @Override public void renderTo(G canvas) { for (Graphic en : entries) { if (!StyleHints.isInvisible(en.getStyleHints())) { en.renderTo(canvas); } } if (!GraphicUtils.isInvisible(boundingBoxGraphic)) { AttributeSet baseStyle = boundingBoxGraphic.getStyle(); AttributeSet modStyle = getStyleContext().applyModifiers(baseStyle, styleHints); boundingBoxGraphic.setStyle(modStyle); boundingBoxGraphic.setPrimitive(boundingBox()); boundingBoxGraphic.renderTo(canvas); boundingBoxGraphic.setStyle(baseStyle); } } // // // // METHODS that iterate through entries // /** * Iterable over visible entries * @return iterable */ public Iterable> visibleEntries() { return Iterables.filter(entries, GraphicUtils.visibleFilter()); } /** * Iterable over visible entries, in reverse order * @return iterable */ public Iterable> visibleEntriesInReverse() { return Lists.reverse(Lists.newArrayList(visibleEntries())); } /** * Iterable over functional entries * @return iterable */ public Iterable> functionalEntries() { return Iterables.filter(entries, GraphicUtils.functionalFilter()); } /** * Iterable over functional entries, in reverse order * @return iterable */ public Iterable> functionalEntriesInReverse() { return Lists.reverse(Lists.newArrayList(functionalEntries())); } /** * Return the topmost graphic at specified point, or null if there is none. * @param point the window point * @return topmost graphic within the composite, or null if there is none */ public Graphic graphicAt(Point2D point) { for (Graphic en : visibleEntriesInReverse()) { if (en instanceof GraphicComposite) { Graphic s = ((GraphicComposite)en).graphicAt(point); if (s != null) { return s; } } else if (en.contains(point)) { return en; } } if (GraphicUtils.isFunctional(boundingBoxGraphic) && boundingBox().contains(point)) { return this; } return null; } @Override public String getTooltip(Point2D p) { // return the first non-null tooltip, in draw order for (Graphic en : visibleEntriesInReverse()) { if (en.isTooltipEnabled() && en.contains(p)) { String l = en.getTooltip(p); if (l != null) { return l; } } } return defaultTooltip; } /** * Return the topmost graphic at specified point that is interested in mouse events, or null if there is none. * @param point the window point * @return topmost graphic within the composite */ public Graphic mouseGraphicAt(Point2D point) { // return the first graphic containing the point, in draw order for (Graphic en : functionalEntriesInReverse()) { if (!en.isMouseEnabled()) { // ignore } else if (en instanceof GraphicComposite) { Graphic s = ((GraphicComposite)en).mouseGraphicAt(point); if (s != null) { return s; } } else if (en.contains(point)) { return en; } } Rectangle2D rect = boundingBox(); if (GraphicUtils.isFunctional(boundingBoxGraphic) && rect != null && rect.contains(point)) { return this; } return null; } /** * Return selectable graphic at given point * @param point point of interest * @return graphic at point that can be selected */ public Graphic selectableGraphicAt(Point2D point) { for (Graphic en : visibleEntriesInReverse()) { if (en instanceof GraphicComposite) { Graphic s = ((GraphicComposite)en).selectableGraphicAt(point); if (s != null) { return s; } } else if (en.isSelectionEnabled() && en.contains(point)) { return en; } } return isSelectionEnabled() && contains(point) ? this : null; } /** * Return collection of graphics in the composite in specified bounding box * @param box bounding box * @return graphics within bounds */ public Set> selectableGraphicsIn(Rectangle2D box) { Set> result = new HashSet>(); for (Graphic g : visibleEntries()) { if (g instanceof GraphicComposite) { result.addAll(((GraphicComposite)g).selectableGraphicsIn(box)); } // no else belongs here if (g.intersects(box) && g.isSelectionEnabled()) { result.add(g); } } return result; } // }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy