2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando
5 * Copyright (C) 2012 - 2016 - Scilab Enterprises
7 * This file is hereby licensed under the terms of the GNU GPL v2.0,
8 * pursuant to article 5.3.4 of the CeCILL v.2.1.
9 * This file was originally licensed under the terms of the CeCILL v2.1,
10 * and continues to be available under such terms.
11 * For more information, see the COPYING file which you should have received
12 * along with this program.
15 package org.scilab.modules.renderer.JoGLView;
17 import org.scilab.forge.scirenderer.DrawingTools;
18 import org.scilab.forge.scirenderer.ruler.DefaultRulerModel;
19 import org.scilab.forge.scirenderer.ruler.RulerDrawer;
20 import org.scilab.forge.scirenderer.ruler.RulerSpriteFactory;
21 import org.scilab.forge.scirenderer.ruler.graduations.AbstractGraduations;
22 import org.scilab.forge.scirenderer.ruler.graduations.Graduations;
23 import org.scilab.forge.scirenderer.texture.Texture;
24 import org.scilab.forge.scirenderer.texture.TextureManager;
25 import org.scilab.forge.scirenderer.tranformations.Vector3d;
26 import org.scilab.modules.graphic_objects.axes.Axes;
27 import org.scilab.modules.graphic_objects.axis.Axis;
28 import org.scilab.modules.graphic_objects.textObject.FormattedText;
29 import org.scilab.modules.renderer.JoGLView.util.ColorFactory;
30 import org.scilab.modules.renderer.JoGLView.util.FormattedTextSpriteDrawer;
31 import org.scilab.modules.renderer.JoGLView.util.ScaleUtils;
33 import java.text.DecimalFormat;
34 import java.util.Collections;
35 import java.util.Iterator;
36 import java.util.LinkedList;
37 import java.util.List;
40 * @author Pierre Lando
42 public class AxisDrawer {
44 /** Ticks length in pixels. */
45 private static final int TICKS_LENGTH = 8;
47 /** Sub-ticks length in pixels. */
48 private static final int SUB_TICKS_LENGTH = 5;
50 /**Ticks sprites distance in pixels. */
51 private static final int SPRITE_DISTANCE = 12;
53 private final DrawerVisitor drawerVisitor;
55 public AxisDrawer(DrawerVisitor drawerVisitor) {
56 this.drawerVisitor = drawerVisitor;
59 public void draw(Axes axes, Axis axis) {
63 DefaultRulerModel rulerModel = new DefaultRulerModel();
64 rulerModel.setSpriteDistance(SPRITE_DISTANCE);
65 rulerModel.setSubTicksLength(SUB_TICKS_LENGTH);
66 rulerModel.setTicksLength(TICKS_LENGTH);
67 boolean[] logFlags = new boolean[] {axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
69 Double[] xTicksValues;
70 Double[] yTicksValues;
73 double[][] factors = axes.getScaleTranslateFactors();
75 if (axis.getXTicksCoords().length == 1) {
76 xTicksValues = axis.getXTicksCoords();
77 yTicksValues = decodeValue(axis.getYTicksCoords(), axis.getTicksStyle());
78 xMinAndMax = getMinAndMax(xTicksValues);
79 yMinAndMax = getMinAndMax(yTicksValues);
82 rulerModel.setUserGraduation(new AxisGraduation(axis, yTicksValues, yMinAndMax));
84 xTicksValues = decodeValue(axis.getXTicksCoords(), axis.getTicksStyle());
85 yTicksValues = axis.getYTicksCoords();
86 xMinAndMax = getMinAndMax(xTicksValues);
87 yMinAndMax = getMinAndMax(yTicksValues);
90 rulerModel.setUserGraduation(new AxisGraduation(axis, xTicksValues, xMinAndMax));
93 Vector3d start = new Vector3d(xMinAndMax[0], yMinAndMax[0], 0);
94 Vector3d end = new Vector3d(xMinAndMax[1], yMinAndMax[1], 0);
95 start = ScaleUtils.applyLogScale(start, logFlags);
96 end = ScaleUtils.applyLogScale(end, logFlags);
98 start = new Vector3d(start.getX() * factors[0][0] + factors[1][0], start.getY() * factors[0][1] + factors[1][1], start.getZ() * factors[0][2] + factors[1][2]);
99 end = new Vector3d(end.getX() * factors[0][0] + factors[1][0], end.getY() * factors[0][1] + factors[1][1], end.getZ() * factors[0][2] + factors[1][2]);
101 // TODO : RulerModel should be an interface.
102 rulerModel.setAutoTicks(false);
103 rulerModel.setFirstValue(0);
104 rulerModel.setSecondValue(1);
105 rulerModel.setLineVisible(axis.getTicksSegment());
106 rulerModel.setColor(ColorFactory.createColor(drawerVisitor.getColorMap(), axis.getTicksColor()));
108 rulerModel.setPoints(start, end);
109 rulerModel.setTicksDirection(computeTicksDirection(axis.getTicksDirectionAsEnum()));
111 DrawingTools drawingTools = drawerVisitor.getDrawingTools();
113 RulerDrawer rulerDrawer = new RulerDrawer(drawerVisitor.getCanvas().getTextureManager());
114 rulerDrawer.setSpriteFactory(new AxisSpriteFactory(axis, min, max));
115 rulerDrawer.draw(drawingTools, rulerModel);
118 rulerDrawer.disposeResources();
122 * Return [min, max] the minimum and maximum of given values.
123 * @param values the given value.
124 * @return [min, max] the minimum and maximum of given values.
126 private double[] getMinAndMax(Double[] values) {
127 double min = +Double.MAX_VALUE;
128 double max = -Double.MAX_VALUE;
129 for (double value : values) {
130 min = Math.min(min, value);
131 max = Math.max(max, value);
133 return new double[] {min, max};
137 * Decode ticks coordinate.
138 * If ticksStyle='0' then v gives the tics positions along the axis.
139 * If ticksStyle='1' then v must be of size 3. r=[xmin,xmax,n] and n gives the number of intervals.
140 * If ticksStyle='2' then v must be of size 4, r=[k1,k2,a,n]. then xmin=k1*10^a, xmax=k2*10^a and n gives the number of intervals
141 * @param v the given value.
142 * @param ticksStyle used ticks style.
143 * @return decoded ticks coordinate.
145 private Double[] decodeValue(Double[] v, int ticksStyle) {
146 if ((ticksStyle == 1) && (v.length >= 3)) {
147 double min = v[0], max = v[1];
148 int n = v[2].intValue();
149 Double[] r = new Double[n + 1];
150 double k = (max - min) / n;
151 for (int i = 0 ; i <= n ; i++) {
155 } else if ((ticksStyle == 2) && (v.length >= 4)) {
156 double pow10 = Math.pow(10, v[2]);
157 double min = v[0] * pow10;
158 double max = v[1] * pow10;
159 int n = v[3].intValue();
160 Double[] r = new Double[n + 1];
161 double k = (max - min) / n;
162 for (int i = 0 ; i <= n ; i++) {
172 * Return the ticks direction in scene coordinate.
173 * @param direction the ticks direction as an {@see Axis.TicksDirection}
174 * @return the ticks direction in scene coordinate.
176 private Vector3d computeTicksDirection(Axis.TicksDirection direction) {
179 return new Vector3d(0, +1, 0);
181 return new Vector3d(0, -1, 0);
183 return new Vector3d(-1, 0, 0);
185 return new Vector3d(+1, 0, 0);
189 private class AxisGraduation extends AbstractGraduations {
190 private final List<Double> subTicksValue;
191 private final List<Double> allValues;
192 private final Axis axis;
195 AxisGraduation(Axis axis, Double[] ticksCoordinate, double[] minAndMax) {
198 allValues = computeValue(ticksCoordinate, minAndMax);
199 subTicksValue = computeSubValue(allValues, axis.getSubticks());
204 * Compute the sub-ticks value.
205 * @param allValues the sorted list of ticks value.
206 * @param subTicks the number of sub-division between two ticks.
207 * @return the sub-ticks value.
209 private List<Double> computeSubValue(List<Double> allValues, Integer subTicks) {
210 if ((allValues == null) || (allValues.size() < 2)) {
211 return new LinkedList<Double>();
213 LinkedList<Double> subTicksValue = new LinkedList<Double>();
214 Iterator<Double> iterator = allValues.iterator();
215 double lastValue = iterator.next();
216 while (iterator.hasNext()) {
217 double currentValue = iterator.next();
218 double k = (currentValue - lastValue) / subTicks;
219 for (int i = 1 ; i < subTicks ; i++) {
220 subTicksValue.add(lastValue + i * k);
222 lastValue = currentValue;
224 return subTicksValue;
229 * Return a list of graduation value corresponding to the given coordinate.
230 * Value are in the range [0, 1] and the coordinate are linearly mapped
232 * If the coordinates are empty, empty list is returned.
233 * If the coordinates are all the same, a same-sized list of zero is returned.
235 * @param coordinates given coordinate.
236 * @param minAndMax the min an max of the given coordinate.
237 * @return a list of graduation value corresponding to the given coordinate.
239 private List<Double> computeValue(Double[] coordinates, double[] minAndMax) {
240 if ((coordinates == null) || (coordinates.length == 0)) {
241 return new LinkedList<Double>();
242 } else if (coordinates.length == 1) {
243 LinkedList<Double> allValues = new LinkedList<Double>();
247 LinkedList<Double> allValues = new LinkedList<Double>();
248 double min = minAndMax[0];
249 double max = minAndMax[1];
251 for (Double coordinate : coordinates) {
255 double k = 1 / (max - min);
256 for (double value : coordinates) {
257 allValues.add(k * (value - min));
259 Collections.sort(allValues);
266 public List<Double> getAllValues() {
271 public List<Double> getNewValues() {
276 public Graduations getMore() {
281 public Graduations getAlternative() {
286 public Graduations getSubGraduations() {
287 return new AbstractGraduations(this) {
289 public List<Double> getAllValues() {
290 return subTicksValue;
294 public List<Double> getNewValues() {
295 return getAllValues();
299 public Graduations getMore() {
304 public Graduations getAlternative() {
309 public Graduations getSubGraduations() {
314 public int getSubDensity() {
321 public int getSubDensity() {
322 return axis.getSubticks();
326 private class AxisSpriteFactory implements RulerSpriteFactory {
327 private final Axis axis;
328 private final double min;
329 private final double max;
331 public AxisSpriteFactory(Axis axis, double min, double max) {
338 public Texture create(double value, DecimalFormat adaptedFormat, TextureManager spriteManager) {
339 String label = getLabel(value);
341 FormattedText formattedText = new FormattedText();
342 formattedText.setFont(axis.getFont());
343 formattedText.setText(getLabel(value));
345 FormattedTextSpriteDrawer textureDrawer = new FormattedTextSpriteDrawer(drawerVisitor.getColorMap(), formattedText);
346 Texture texture = spriteManager.createTexture();
347 texture.setMagnificationFilter(Texture.Filter.LINEAR);
348 texture.setMinifyingFilter(Texture.Filter.LINEAR);
349 texture.setDrawer(textureDrawer);
358 * Return the label corresponding to the given value.
359 * @param value the given value.
360 * @return the label corresponding to the given value.
362 private String getLabel(double value) {
364 // Should find right index through given labels.
365 String[] ticksLabel = axis.getTicksLabels();
366 int index = (int) Math.round(value * (ticksLabel.length - 1));
367 if ((index < 0) || (index > ticksLabel.length) || ticksLabel.length == 0) {
370 return ticksLabel[index];