001/* =========================================================== 002 * Orson Charts : a 3D chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C)opyright 2013-2016, by Object Refinery Limited. All rights reserved. 006 * 007 * http://www.object-refinery.com/orsoncharts/index.html 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 com.orsoncharts.renderer.category; 034 035import com.orsoncharts.Range; 036import com.orsoncharts.data.category.CategoryDataset3D; 037import com.orsoncharts.data.DataUtils; 038import com.orsoncharts.data.Values3D; 039import com.orsoncharts.graphics3d.Dimension3D; 040import com.orsoncharts.graphics3d.World; 041import com.orsoncharts.plot.CategoryPlot3D; 042import com.orsoncharts.Chart3DFactory; 043import com.orsoncharts.data.KeyedValues3DItemKey; 044import com.orsoncharts.graphics3d.Object3D; 045import com.orsoncharts.graphics3d.Offset3D; 046import com.orsoncharts.label.ItemLabelPositioning; 047 048/** 049 * A renderer that can be used with the {@link CategoryPlot3D} class to create 050 * 3D stacked bar charts from data in a {@link CategoryDataset3D}. The 051 * {@code createStackedBarChart()} method in the {@link Chart3DFactory} 052 * class will construct a chart that uses this renderer. Here is a sample: 053 * <div> 054 * <object id="ABC" data="../../../../doc-files/StackedBarChart3DDemo1.svg" 055 * type="image/svg+xml" width="500" height="359"></object> 056 * </div> 057 * (refer to {@code StackedBarChart3DDemo1.java} for the code to generate 058 * the above chart). 059 * <br><br> 060 * There is a factory method to create a chart using this renderer - see 061 * {@link Chart3DFactory#createStackedBarChart(String, String, CategoryDataset3D, String, String, String)}. 062 * <br><br> 063 * NOTE: This class is serializable, but the serialization format is subject 064 * to change in future releases and should not be relied upon for persisting 065 * instances of this class. 066 */ 067@SuppressWarnings("serial") 068public class StackedBarRenderer3D extends BarRenderer3D { 069 070 /** 071 * Creates a default constructor. 072 */ 073 public StackedBarRenderer3D() { 074 super(); 075 setItemLabelPositioning(ItemLabelPositioning.FRONT_AND_BACK); 076 setItemLabelOffsets(new Offset3D(0.0, 0.0, -1.0)); 077 } 078 079 /** 080 * Returns the range of values that will be required on the value axis 081 * to see all the data from the dataset. We override the method to 082 * account for the bars from each series being stacked on top of one 083 * another. 084 * 085 * @param data the data ({@code null} not permitted). 086 * 087 * @return The range (possibly {@code null}) 088 */ 089 @Override 090 public Range findValueRange(Values3D<? extends Number> data) { 091 return DataUtils.findStackedValueRange(data); 092 } 093 094 /** 095 * Constructs and places one item from the specified dataset into the given 096 * world. This method will be called by the {@link CategoryPlot3D} class 097 * while iterating over the items in the dataset. 098 * 099 * @param dataset the dataset ({@code null} not permitted). 100 * @param series the series index. 101 * @param row the row index. 102 * @param column the column index. 103 * @param world the world ({@code null} not permitted). 104 * @param dimensions the plot dimensions ({@code null} not permitted). 105 * @param xOffset the x-offset. 106 * @param yOffset the y-offset. 107 * @param zOffset the z-offset. 108 */ 109 @Override 110 public void composeItem(CategoryDataset3D dataset, int series, int row, 111 int column, World world, Dimension3D dimensions, 112 double xOffset, double yOffset, double zOffset) { 113 114 double value = dataset.getDoubleValue(series, row, column); 115 if (Double.isNaN(value)) { 116 return; 117 } 118 double[] stack = DataUtils.stackSubTotal(dataset, getBase(), series, 119 row, column); 120 double lower = stack[1]; 121 if (value < 0.0) { 122 lower = stack[0]; 123 } 124 double upper = lower + value; 125 composeItem(upper, lower, dataset, series, row, column, world, 126 dimensions, xOffset, yOffset, zOffset); 127 128 } 129 130 @Override 131 protected void drawItemLabels(World world, CategoryDataset3D dataset, 132 KeyedValues3DItemKey itemKey, double xw, double yw, double zw, 133 double basew, boolean inverted) { 134 ItemLabelPositioning positioning = getItemLabelPositioning(); 135 if (getItemLabelGenerator() != null) { 136 String label = getItemLabelGenerator().generateItemLabel(dataset, 137 itemKey.getSeriesKey(), itemKey.getRowKey(), 138 itemKey.getColumnKey()); 139 if (label != null) { 140 Dimension3D dimensions = getPlot().getDimensions(); 141 double dx = getItemLabelOffsets().getDX(); 142 double dy = getItemLabelOffsets().getDY() 143 * dimensions.getHeight(); 144 double dz = getItemLabelOffsets().getDZ() * getBarZWidth(); 145 if (positioning.equals(ItemLabelPositioning.CENTRAL)) { 146 double yy = yw; 147 if (inverted) { 148 yy = basew; 149 dy = -dy; 150 } 151 Object3D labelObj = Object3D.createLabelObject(label, 152 getItemLabelFont(), getItemLabelColor(), 153 getItemLabelBackgroundColor(), xw + dx, 154 yy + dy, zw, false, true); 155 labelObj.setProperty(Object3D.ITEM_KEY, itemKey); 156 world.add(labelObj); 157 } else if (positioning.equals( 158 ItemLabelPositioning.FRONT_AND_BACK)) { 159 double yy = (yw + basew) / 2.0; 160 Object3D labelObj1 = Object3D.createLabelObject(label, 161 getItemLabelFont(), getItemLabelColor(), 162 getItemLabelBackgroundColor(), xw + dx, 163 yy + dy, zw + dz, false, false); 164 labelObj1.setProperty(Object3D.ITEM_KEY, itemKey); 165 world.add(labelObj1); 166 Object3D labelObj2 = Object3D.createLabelObject(label, 167 getItemLabelFont(), getItemLabelColor(), 168 getItemLabelBackgroundColor(), xw + dx, 169 yy + dy, zw - dz, true, false); 170 labelObj2.setProperty(Object3D.ITEM_KEY, itemKey); 171 world.add(labelObj2); 172 } 173 } 174 } 175 } 176 177 /** 178 * Tests this renderer for equality with an arbitrary object. 179 * 180 * @param obj the object ({@code null} permitted). 181 * 182 * @return A boolean. 183 */ 184 @Override 185 public boolean equals(Object obj) { 186 if (obj == this) { 187 return true; 188 } 189 if (!(obj instanceof StackedBarRenderer3D)) { 190 return false; 191 } 192 return super.equals(obj); 193 } 194 195}