001/* ===========================================================
002 * Orson Charts : a 3D chart library for the Java(tm) platform
003 * ===========================================================
004 * 
005 * (C)opyright 2013-2022, by David Gilbert.  All rights reserved.
006 * 
007 * https://github.com/jfree/orson-charts
008 * 
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as published by
011 * the Free Software Foundation, either version 3 of the License, or
012 * (at your option) any later version.
013 *
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 *
019 * You should have received a copy of the GNU General Public License
020 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
021 * 
022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
023 * Other names may be trademarks of their respective owners.]
024 * 
025 * If you do not wish to be bound by the terms of the GPL, an alternative
026 * commercial license can be purchased.  For details, please see visit the
027 * Orson Charts home page:
028 * 
029 * http://www.object-refinery.com/orsoncharts/index.html
030 * 
031 */
032
033package org.jfree.chart3d.table;
034
035import java.awt.Color;
036import java.awt.Graphics2D;
037import java.awt.Insets;
038import java.awt.Shape;
039import java.awt.geom.AffineTransform;
040import java.awt.geom.Dimension2D;
041import java.awt.geom.Rectangle2D;
042import java.util.ArrayList;
043import java.util.List;
044import java.util.Map;
045
046import org.jfree.chart3d.internal.Args;
047
048/**
049 * A table element that displays a shape.
050 * <br><br>
051 * NOTE: This class is serializable, but the serialization format is subject 
052 * to change in future releases and should not be relied upon for persisting 
053 * instances of this class.
054 */
055@SuppressWarnings("serial")
056public class ShapeElement extends AbstractTableElement 
057        implements TableElement {
058
059    /** 
060     * The shape (by convention, the shape should be centered on the point
061     * (0, 0)). 
062     */
063    private final Shape shape;
064    
065    private Color fillColor;
066    
067    /**
068     * Creates a new shape element.
069     * 
070     * @param shape  the shape ({@code null} not permitted).
071     * @param fillColor  the fill color ({@code null} not permitted).
072     */
073    public ShapeElement(Shape shape, Color fillColor) {
074        super();
075        Args.nullNotPermitted(shape, "shape");
076        Args.nullNotPermitted(fillColor, "fillColor");
077        this.shape = shape;
078        this.fillColor = fillColor;
079    }
080
081    /**
082     * Returns the fill color.
083     * 
084     * @return The fill color.
085     * 
086     * @since 1.2
087     */
088    public Color getFillColor() {
089        return this.fillColor;
090    }
091    
092    /**
093     * Sets the fill color.
094     * 
095     * @param color  the fill color ({@code null} not permitted).
096     * 
097     * @since 1.2
098     */
099    public void setFillColor(Color color) {
100        Args.nullNotPermitted(color, "color");
101        this.fillColor = color;
102    }
103    
104    @Override
105    public Dimension2D preferredSize(Graphics2D g2, Rectangle2D bounds, 
106            Map<String, Object> constraints) {
107        Insets insets = getInsets();
108        Rectangle2D shapeBounds = shape.getBounds2D();
109        return new ElementDimension(Math.min(shapeBounds.getWidth() 
110                + insets.left + insets.right, bounds.getWidth()), 
111                Math.min(shapeBounds.getHeight() + insets.top + insets.bottom, 
112                bounds.getHeight()));
113    }
114
115    @Override
116    public List<Rectangle2D> layoutElements(Graphics2D g2, Rectangle2D bounds, 
117            Map<String, Object> constraints) {
118        List<Rectangle2D> result = new ArrayList<>(1);
119        Insets insets = getInsets();
120        Rectangle2D shapeBounds = this.shape.getBounds2D();
121        double w = Math.min(shapeBounds.getWidth() + insets.left + insets.right,
122                bounds.getWidth());
123        double h = Math.min(shapeBounds.getHeight() + insets.top 
124                + insets.bottom, bounds.getHeight());
125        Rectangle2D pos = new Rectangle2D.Double(bounds.getCenterX() - w / 2.0,
126                bounds.getCenterY() - h / 2.0, w, h);
127        result.add(pos);
128        return result;
129    }
130
131    /**
132     * Draws the shape element within the specified bounds.
133     * 
134     * @param g2  the graphics target ({@code null} not permitted).
135     * @param bounds  the bounds ({@code null} not permitted).
136     */
137    @Override
138    public void draw(Graphics2D g2, Rectangle2D bounds) {
139        draw(g2, bounds, null);
140    }
141    
142    /**
143     * Draws the element within the specified bounds.  If the 
144     * {@code recordBounds} flag is set, this element and each of its
145     * children will have their {@code BOUNDS_2D} property updated with 
146     * the current bounds.
147     * 
148     * @param g2  the graphics target ({@code null} not permitted).
149     * @param bounds  the bounds ({@code null} not permitted).
150     * @param onDrawHandler  an object that will receive notification before 
151     *     and after the element is drawn ({@code null} permitted).
152     * 
153     * @since 1.3
154     */
155    @Override
156    public void draw(Graphics2D g2, Rectangle2D bounds, 
157            TableElementOnDraw onDrawHandler) {
158        if (onDrawHandler != null) {
159            onDrawHandler.beforeDraw(this, g2, bounds);
160        }
161        AffineTransform saved = g2.getTransform();
162        RectanglePainter background = getBackground();
163        if (background != null) {
164            background.fill(g2, bounds);
165        }
166        g2.translate(bounds.getCenterX(), bounds.getCenterY());
167        g2.setPaint(this.fillColor);
168        g2.fill(shape);
169        g2.setTransform(saved);
170        if (onDrawHandler != null) {
171            onDrawHandler.afterDraw(this, g2, bounds);
172        }
173    }
174    
175    /**
176     * Receives a visitor.
177     * 
178     * @param visitor  the visitor ({@code null} not permitted).
179     * 
180     * @since 1.2
181     */
182    @Override
183    public void receive(TableElementVisitor visitor) {
184        visitor.visit(this);
185    }
186    
187    /**
188     * Returns a string representation of this element, primarily for
189     * debugging purposes.
190     * 
191     * @return A string representation of this element. 
192     */
193    @Override
194    public String toString() {
195        return "ShapeElement[shape=" + this.shape + "]";
196    }
197    
198}