1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.trend.impl;
\r
14 import java.awt.Color;
\r
15 import java.awt.Font;
\r
16 import java.awt.FontMetrics;
\r
17 import java.awt.Graphics2D;
\r
18 import java.awt.geom.AffineTransform;
\r
19 import java.awt.geom.Path2D;
\r
20 import java.text.Format;
\r
22 import org.simantics.g2d.utils.GridSpacing;
\r
23 import org.simantics.g2d.utils.GridUtil;
\r
24 import org.simantics.trend.configuration.ItemPlacement;
\r
25 import org.simantics.trend.configuration.TrendItem.Renderer;
\r
26 import org.simantics.utils.format.ValueFormat;
\r
28 public class VertRuler extends TrendGraphicalNode {
\r
30 private static final long serialVersionUID = 3773787909384380074L;
\r
32 GridSpacing spacing = GridSpacing.SOME_SPACING;
\r
33 double min = -1, max = 1;
\r
34 double iMin = Double.MAX_VALUE, iMax = -Double.MAX_VALUE;
\r
36 Color color = Color.GRAY;
\r
37 boolean autoscroll = true; // Autoscroll on/off
\r
38 boolean manualscale = false; // Autoscale / manual scale
\r
39 double labelWidth = 7;
\r
41 static final double TRIANGLE_SIZE = 7;
\r
42 static final Path2D TRIANGLE;
\r
44 public void layout()
\r
46 TrendNode trend = getTrend();
\r
47 ValueFormat vf = trend.valueFormat;
\r
48 spacing = GridSpacing.makeGridSpacing(max-min, getHeight(), 15);
\r
49 labelWidth = Math.max(7, GridUtil.calcLabelWidth(min, max, vf.format, spacing));
\r
50 double w = 30 + labelWidth;
\r
51 // Snap w -> next 20 pixels
\r
52 double quantization = 10;
\r
53 int x = (int) Math.ceil( w / quantization );
\r
55 w = x * quantization;
\r
57 bounds.setFrame(0, 0, w, getHeight());
\r
58 trend.shapedirty = true;
\r
61 public void setHeight(double height) {
\r
62 if (height==bounds.getHeight()) return;
\r
63 bounds.setFrame(0, 0, bounds.getWidth(), height);
\r
64 getTrend().shapedirty = true;
\r
67 public boolean setMinMax(double min, double max) {
\r
68 if (min==this.min && max==this.max) return false;
\r
69 spacing = GridSpacing.makeGridSpacing(max-min, getHeight(), 15);
\r
72 getTrend().shapedirty = true;
\r
77 protected void doRender(Graphics2D g) {
\r
78 TrendNode trend = (TrendNode) getParent();
\r
80 // Draw little "Frozen"
\r
81 if ( !trend.printing )
\r
83 g.setColor( Color.LIGHT_GRAY );
\r
84 g.setFont( RULER_FONT );
\r
85 String txt = !autoscroll ? (manualscale ? "*" : "Auto off") : (manualscale ? "" : "Auto on");
\r
87 //g.drawString(txt, 2.f, (float)getHeight() + 14.f );
\r
89 g.drawString(txt, 5.f, -9.f );
\r
92 g.setPaint( color );
\r
93 g.setStroke( GridUtil.RULER_LINE_STROKE );
\r
95 ValueFormat vf = trend.valueFormat;
\r
96 VertRuler master = trend.vertRuler;
\r
97 VertRuler slave = this;
\r
98 if ( master != slave )
\r
100 // Paint "slave" ruler - a ruler with ticks from master and labels from this
\r
101 int tickCount = GridUtil.getTickCount(master.spacing, master.min, master.getHeight());
\r
102 int noOfDecimals = calcNoOfDecimals(tickCount, slave.max-slave.min);
\r
103 Format format = vf.toFormat(noOfDecimals);
\r
105 GridUtil.paintVerticalSlaveRuler(
\r
114 Format format = vf.format;
\r
116 // Paint normal ruler
\r
117 GridUtil.paintVerticalRuler(
\r
127 // Shape oldClip = g2d.getClip();
\r
128 // Rectangle2D.Double newClip = new Rectangle2D.Double(0,-20, getWidth(), 30);
\r
129 // g2d.setClip(newClip);
\r
130 boolean selected = trend.singleAxis ? false : trend.vertRuler==this;
\r
131 selected &= !trend.printing;
\r
133 Font font = selected ? RULER_FONT_BOLD : RULER_FONT;
\r
134 FontMetrics fm = g.getFontMetrics( font );
\r
135 //LineMetrics lm = fm.getLineMetrics(label, g);
\r
136 double wid = fm.stringWidth(label);
\r
138 AffineTransform at = g.getTransform();
\r
139 g.translate( getWidth()-15, (getHeight()-wid)/2);
\r
140 // g2d.translate( 18+labelWidth, (getHeight()-wid)/2);
\r
141 g.transform( AffineTransform.getQuadrantRotateInstance(1) );
\r
142 g.setColor( color );
\r
144 g.drawString( label, (float) 0, (float) 0);
\r
145 g.setTransform( at );
\r
146 // g2d.setClip(oldClip);
\r
150 at = g.getTransform();
\r
151 g.translate( getWidth() - TRIANGLE_SIZE - 5, 0 );
\r
152 g.setColor( color );
\r
153 g.fill( TRIANGLE );
\r
154 g.setTransform( at );
\r
159 public void setKnownMinMax() {
\r
160 TrendNode trend = (TrendNode) getParent();
\r
161 iMax = -Double.MAX_VALUE;
\r
162 iMin = Double.MAX_VALUE;
\r
163 for (ItemNode item : trend.analogItems) {
\r
164 if (item.item.renderer != Renderer.Analog || item.ruler!=this) continue;
\r
165 if ( !Double.isNaN(item.min ) ) iMin = Math.min(iMin, item.min );
\r
166 if ( !Double.isNaN(item.max ) ) iMax = Math.max(iMax, item.max );
\r
169 if (iMin == Double.MAX_VALUE && iMax == -Double.MAX_VALUE) {
\r
175 public boolean autoscale() {
\r
176 if (!autoscroll) return false;
\r
178 double nMin = iMin;
\r
179 double nMax = iMax;
\r
180 double diff = nMax - nMin;
\r
184 diff = nMax - nMin;
\r
186 double margin = diff*0.02;
\r
188 TrendNode trend = getTrend();
\r
189 if (trend.itemPlacement == ItemPlacement.Stacked) {
\r
190 int c = trend.vertRulers.size();
\r
191 int i = c-trend.vertRulers.indexOf(this)-1;
\r
192 nMin = nMin - (diff)*i - margin;
\r
193 nMax = nMax + (diff)*(c-i-1) + margin;
\r
196 nMin = iMin - margin;
\r
197 nMax = iMax + margin;
\r
200 return setMinMax(nMin, nMax);
\r
203 public void translate(double dy) {
\r
206 autoscroll = false;
\r
209 public void zoomIn(double y, double height) {
\r
210 autoscroll = false;
\r
212 double diff = max-min;
\r
213 double sy = diff / getHeight();
\r
214 if ( Math.abs(diff)<GridSpacing.GRID_MIN_USER_SIZE && height<getHeight()) {
\r
215 // System.out.println(y/getHeight()*4+", "+getHeight());
\r
216 // diff = GridSpacing.GRID_MIN_USER_SIZE;
\r
217 // sy = diff / getHeight();
\r
218 // double ry = y/getHeight()*4;
\r
219 // double vy = (max+min)/2-y*sy;
\r
220 // setMinMax(vy-GridSpacing.GRID_MIN_USER_SIZE/2, vy+GridSpacing.GRID_MIN_USER_SIZE/2);
\r
223 double newMin = min + (getHeight() - y-height)*sy;
\r
224 double newMax = max - (y)*sy;
\r
225 setMinMax(newMin, newMax);
\r
229 public void zoomOut() {
\r
234 public void zoomTo(double min, double max) {
\r
235 setMinMax(min, max);
\r
236 autoscroll = false;
\r
239 public double unitsPerPixel() {
\r
240 return (max-min) / getHeight();
\r
243 static int calcNoOfDecimals(int tickCount, double diff)
\r
245 int interestingNumbers;
\r
246 if ( tickCount<=2 ) interestingNumbers = 1; else
\r
247 if ( tickCount>=3 && tickCount<=9 ) interestingNumbers = 2; else
\r
248 if ( tickCount>=10 && tickCount<=99 ) interestingNumbers = 3; else
\r
249 if ( tickCount>=100 && tickCount<=999 ) interestingNumbers = 4; else
\r
250 interestingNumbers = 5;
\r
252 int decimals = interestingNumbers - ((int)Math.ceil( Math.log10(diff) ));
\r
253 if (decimals<0) decimals = 0;
\r
258 public static void main(String[] args) {
\r
259 double diff = 12.4567890;
\r
260 for ( int tickCount=0; tickCount<15; tickCount++) {
\r
261 int noOfDecimals = calcNoOfDecimals(tickCount, diff);
\r
262 Format format = ValueFormat.Currency.toFormat(noOfDecimals);
\r
263 System.out.println("diff="+diff+", tickcount="+tickCount+", #ofDecimals="+noOfDecimals+", formatted diff="+format.format(diff));
\r
268 TRIANGLE = new Path2D.Double();
\r
269 TRIANGLE.moveTo(-TRIANGLE_SIZE/2, 0);
\r
270 TRIANGLE.lineTo(TRIANGLE_SIZE/2, 0);
\r
271 TRIANGLE.lineTo(0, TRIANGLE_SIZE);
\r