1 /*******************************************************************************
2 * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.trend.impl;
14 import java.awt.Color;
16 import java.awt.FontMetrics;
17 import java.awt.Graphics2D;
18 import java.awt.geom.AffineTransform;
19 import java.awt.geom.Path2D;
20 import java.text.Format;
21 import java.util.ArrayList;
22 import java.util.List;
24 import org.simantics.g2d.utils.GridSpacing;
25 import org.simantics.g2d.utils.GridUtil;
26 import org.simantics.trend.configuration.ItemPlacement;
27 import org.simantics.trend.configuration.TrendItem.Renderer;
28 import org.simantics.utils.format.ValueFormat;
30 public class VertRuler extends TrendGraphicalNode {
32 private static final long serialVersionUID = 3773787909384380074L;
34 GridSpacing spacing = GridSpacing.SOME_SPACING;
35 double min = -1, max = 1;
36 double iMin = Double.MAX_VALUE, iMax = -Double.MAX_VALUE;
38 Color color = Color.GRAY;
39 boolean autoscroll = true; // Autoscroll on/off
40 boolean manualscale = false; // Autoscale / manual scale
41 double labelWidth = 7;
42 List<String> extra_labels = new ArrayList<>();
43 List<Color> extra_label_colors = new ArrayList<>();
44 double extra_width = 0.0;
45 int singleAxisShowLegendsMaxLegends = 10;
47 static final double TRIANGLE_SIZE = 7;
48 static final Path2D TRIANGLE;
52 TrendNode trend = getTrend();
53 ValueFormat vf = trend.valueFormat;
54 spacing = GridSpacing.makeGridSpacing(max-min, getHeight(), 15);
55 labelWidth = Math.max(7, GridUtil.calcLabelWidth(min, max, vf.format, spacing));
57 double w = 30 + labelWidth;
59 // Snap w -> next 20 pixels
60 double quantization = 10;
61 int x = (int) Math.ceil( w / quantization );
65 bounds.setFrame(0, 0, w, getHeight());
66 trend.shapedirty = true;
69 public void addExtraLabel(String label, Color color) {
70 extra_labels.add(label);
71 extra_label_colors.add(color);
74 public void setHeight(double height) {
75 if (height==bounds.getHeight()) return;
76 bounds.setFrame(0, 0, bounds.getWidth(), height);
77 getTrend().shapedirty = true;
81 public double getWidth() {
82 if(extra_labels.size() == 0) {
83 return super.getWidth();
85 if(extra_width == 0.0) {
86 extra_width = 15.0 * extra_labels.size();
87 double w = bounds.getWidth();
88 return w + extra_width;
90 double w = bounds.getWidth();
91 return w + extra_width;
97 public boolean setMinMax(double min, double max) {
98 if (min==this.min && max==this.max) return false;
99 spacing = GridSpacing.makeGridSpacing(max-min, getHeight(), 15);
102 getTrend().shapedirty = true;
107 protected void doRender(Graphics2D g) {
108 TrendNode trend = (TrendNode) getParent();
110 VertRuler master = trend.vertRuler;
111 VertRuler slave = this;
113 // Draw little "Frozen"
114 if ( !trend.printing )
116 g.setColor( Color.LIGHT_GRAY );
117 g.setFont( RULER_FONT );
118 String txt = !autoscroll ? (manualscale ? "*" : "Auto off") : (manualscale ? "" : "Auto on");
120 //g.drawString(txt, 2.f, (float)getHeight() + 14.f );
122 g.drawString(txt, 5.f, -9.f );
126 g.setStroke( GridUtil.RULER_LINE_STROKE );
128 ValueFormat vf = trend.valueFormat;
130 if ( master != slave )
132 // Paint "slave" ruler - a ruler with ticks from master and labels from this
133 int tickCount = GridUtil.getTickCount(master.spacing, master.min, master.getHeight());
134 int noOfDecimals = calcNoOfDecimals(tickCount, slave.max-slave.min);
135 Format format = vf.toFormat(noOfDecimals);
137 GridUtil.paintVerticalSlaveRuler(
146 Format format = vf.format;
148 // Paint normal ruler
149 GridUtil.paintVerticalRuler(
159 // Shape oldClip = g2d.getClip();
160 // Rectangle2D.Double newClip = new Rectangle2D.Double(0,-20, getWidth(), 30);
161 // g2d.setClip(newClip);
162 boolean selected = trend.singleAxis ? false : trend.vertRuler==this;
163 selected &= !trend.printing;
165 Font font = selected ? RULER_FONT_BOLD : RULER_FONT;
166 FontMetrics fm = g.getFontMetrics( font );
167 //LineMetrics lm = fm.getLineMetrics(label, g);
169 if(extra_labels.size() == 0) {
170 double wid = fm.stringWidth(label);
172 AffineTransform at = g.getTransform();
173 g.translate( getWidth()-15, (getHeight()-wid)/2);
174 // g2d.translate( 18+labelWidth, (getHeight()-wid)/2);
175 g.transform( AffineTransform.getQuadrantRotateInstance(1) );
178 g.drawString( label, (float) 0, (float) 0);
179 g.setTransform( at );
180 // g2d.setClip(oldClip);
184 at = g.getTransform();
185 g.translate( getWidth() - TRIANGLE_SIZE - 5, 0 );
188 g.setTransform( at );
192 double bounds_width = bounds.getWidth();
194 for(int label_index = 0; label_index < extra_labels.size(); label_index++) {
195 if(label_index >= singleAxisShowLegendsMaxLegends) {
196 break; // Maximum amount of labels that we should display has been reached
199 String label = extra_labels.get(label_index);
200 Color color = extra_label_colors.get(label_index);
202 double wid = fm.stringWidth(label);
203 double font_height = fm.getHeight();
204 extra_width += font_height;
205 //Letters are displayed in 90 degree angle, so font height is the width of the label as seen in X-direction
207 AffineTransform at = g.getTransform();
208 g.translate( bounds_width + label_index*font_height, (getHeight()-wid)/2);
209 // g2d.translate( 18+labelWidth, (getHeight()-wid)/2);
210 g.transform( AffineTransform.getQuadrantRotateInstance(1) );
213 g.drawString( label, (float) 0, (float) 0);
214 g.setTransform( at );
220 public void setKnownMinMax() {
221 TrendNode trend = (TrendNode) getParent();
222 iMax = -Double.MAX_VALUE;
223 iMin = Double.MAX_VALUE;
224 for (ItemNode item : trend.analogItems) {
225 if (item.item.renderer != Renderer.Analog || item.ruler!=this) continue;
226 if ( !Double.isNaN(item.min ) ) iMin = Math.min(iMin, item.min );
227 if ( !Double.isNaN(item.max ) ) iMax = Math.max(iMax, item.max );
230 if (iMin == Double.MAX_VALUE && iMax == -Double.MAX_VALUE) {
236 public boolean autoscale() {
237 if (!autoscroll) return false;
241 double diff = nMax - nMin;
247 double margin = diff*0.02;
249 TrendNode trend = getTrend();
250 if (trend.itemPlacement == ItemPlacement.Stacked) {
251 int c = trend.vertRulers.size();
252 int i = c-trend.vertRulers.indexOf(this)-1;
253 nMin = nMin - (diff)*i - margin;
254 nMax = nMax + (diff)*(c-i-1) + margin;
257 nMin = iMin - margin;
258 nMax = iMax + margin;
261 return setMinMax(nMin, nMax);
264 public void translate(double dy) {
270 public void zoomIn(double y, double height) {
273 double diff = max-min;
274 double sy = diff / getHeight();
275 if ( Math.abs(diff)<GridSpacing.GRID_MIN_USER_SIZE && height<getHeight()) {
276 // System.out.println(y/getHeight()*4+", "+getHeight());
277 // diff = GridSpacing.GRID_MIN_USER_SIZE;
278 // sy = diff / getHeight();
279 // double ry = y/getHeight()*4;
280 // double vy = (max+min)/2-y*sy;
281 // setMinMax(vy-GridSpacing.GRID_MIN_USER_SIZE/2, vy+GridSpacing.GRID_MIN_USER_SIZE/2);
284 double newMin = min + (getHeight() - y-height)*sy;
285 double newMax = max - (y)*sy;
286 setMinMax(newMin, newMax);
290 public void zoomOut() {
295 public void zoomTo(double min, double max) {
300 public double unitsPerPixel() {
301 return (max-min) / getHeight();
304 static int calcNoOfDecimals(int tickCount, double diff)
306 int interestingNumbers;
307 if ( tickCount<=2 ) interestingNumbers = 1; else
308 if ( tickCount>=3 && tickCount<=9 ) interestingNumbers = 2; else
309 if ( tickCount>=10 && tickCount<=99 ) interestingNumbers = 3; else
310 if ( tickCount>=100 && tickCount<=999 ) interestingNumbers = 4; else
311 interestingNumbers = 5;
313 int decimals = interestingNumbers - ((int)Math.ceil( Math.log10(diff) ));
314 if (decimals<0) decimals = 0;
319 public static void main(String[] args) {
320 double diff = 12.4567890;
321 for ( int tickCount=0; tickCount<15; tickCount++) {
322 int noOfDecimals = calcNoOfDecimals(tickCount, diff);
323 Format format = ValueFormat.Currency.toFormat(noOfDecimals);
324 System.out.println("diff="+diff+", tickcount="+tickCount+", #ofDecimals="+noOfDecimals+", formatted diff="+format.format(diff));
329 TRIANGLE = new Path2D.Double();
330 TRIANGLE.moveTo(-TRIANGLE_SIZE/2, 0);
331 TRIANGLE.lineTo(TRIANGLE_SIZE/2, 0);
332 TRIANGLE.lineTo(0, TRIANGLE_SIZE);