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