]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.trend/src/org/simantics/trend/impl/VertRuler.java
New trend axis mode SingleAxisShowLegends
[simantics/platform.git] / bundles / org.simantics.trend / src / org / simantics / trend / impl / VertRuler.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
3  * Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.trend.impl;
13
14 import java.awt.Color;
15 import java.awt.Font;
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;
23
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;
29
30 public class VertRuler extends TrendGraphicalNode {
31
32         private static final long serialVersionUID = 3773787909384380074L;
33
34         GridSpacing spacing = GridSpacing.SOME_SPACING;
35         double min = -1, max = 1;
36         double iMin = Double.MAX_VALUE, iMax = -Double.MAX_VALUE;
37         String label = "";
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;
46         
47         static final double TRIANGLE_SIZE = 7;
48     static final Path2D TRIANGLE;
49                         
50         public void layout()
51         {
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));
56                 
57                 double w = 30 + labelWidth;
58                 
59                 // Snap w -> next 20 pixels
60                 double quantization = 10; 
61                 int x = (int) Math.ceil( w / quantization );
62                 if (x<4) x = 4;
63                 w = x * quantization;
64                 w += 5;
65                 bounds.setFrame(0, 0, w, getHeight());          
66                 trend.shapedirty = true;
67         }
68         
69         public void addExtraLabel(String label, Color color) {
70                 extra_labels.add(label);
71                 extra_label_colors.add(color);
72         }
73         
74         public void setHeight(double height) {
75                 if (height==bounds.getHeight()) return;
76                 bounds.setFrame(0, 0, bounds.getWidth(), height);               
77                 getTrend().shapedirty = true;
78         }
79         
80         @Override
81         public double getWidth() { 
82                 if(extra_labels.size() == 0) {
83                         return super.getWidth();
84                 } else {
85                         if(extra_width == 0.0) {
86                                 extra_width = 15.0 * extra_labels.size();
87                                 double w = bounds.getWidth();
88                                 return w + extra_width;
89                         } else {
90                                 double w = bounds.getWidth();
91                                 return w + extra_width;
92                         }
93                 }
94                 
95         }
96         
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);
100                 this.min = min;
101                 this.max = max;
102                 getTrend().shapedirty = true;
103                 return true;
104         }       
105         
106         @Override
107         protected void doRender(Graphics2D g) {
108                 TrendNode trend = (TrendNode) getParent();
109                 
110                 VertRuler master = trend.vertRuler;
111                 VertRuler slave = this;
112
113                 // Draw little "Frozen"
114                 if ( !trend.printing )
115                 {
116                         g.setColor( Color.LIGHT_GRAY );
117                         g.setFont( RULER_FONT );
118                         String txt = !autoscroll ? (manualscale ? "*" : "Auto off") : (manualscale ? "" : "Auto on");
119                         // Draw at bottom
120                         //g.drawString(txt, 2.f, (float)getHeight() + 14.f );
121                         // Draw at top
122                         g.drawString(txt, 5.f, -9.f );
123                 }
124
125                 g.setPaint( color );
126                 g.setStroke( GridUtil.RULER_LINE_STROKE );              
127
128                 ValueFormat vf = trend.valueFormat;
129
130                 if ( master != slave )
131                 {       
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);
136
137                         GridUtil.paintVerticalSlaveRuler(
138                                         master.spacing,
139                                         spacing, 
140                                         g,
141                                         master.min,
142                                         slave.min,
143                                         getHeight(),
144                                         format);
145                 } else {
146                         Format format = vf.format;
147
148                         // Paint normal ruler
149                         GridUtil.paintVerticalRuler(
150                                         spacing, 
151                                         g,
152                                         min,
153                                         getHeight(),
154                                         format);
155                 }
156
157                 // Draw label
158                 {
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;
164                         
165                         Font font = selected ? RULER_FONT_BOLD : RULER_FONT;
166                         FontMetrics fm = g.getFontMetrics( font );
167                         //LineMetrics lm = fm.getLineMetrics(label, g);
168                         
169                         if(extra_labels.size() == 0) {
170                                 double wid = fm.stringWidth(label);
171                                 
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) );
176                                 g.setColor( color );
177                                 g.setFont( font );
178                                 g.drawString( label, (float) 0, (float) 0);
179                                 g.setTransform( at );                   
180         //                      g2d.setClip(oldClip);
181                                 
182                                 // Triangle
183                                 if (selected) {
184                                         at = g.getTransform();
185                                         g.translate( getWidth() - TRIANGLE_SIZE - 5, 0 );
186                                         g.setColor( color );
187                                         g.fill( TRIANGLE );                             
188                                         g.setTransform( at );                   
189                                 }
190                         } else {
191                                 extra_width = 0.0;
192                                 double bounds_width = bounds.getWidth();
193                                 
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
197                                         }
198                                         
199                                         String label = extra_labels.get(label_index);
200                                         Color color = extra_label_colors.get(label_index);
201                                         
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
206                                         
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) );
211                                         g.setColor( color );
212                                         g.setFont( font );
213                                         g.drawString( label, (float) 0, (float) 0);
214                                         g.setTransform( at );   
215                                 }
216                         }
217                 }
218         }
219
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 );
228                 }
229                 
230                 if (iMin == Double.MAX_VALUE && iMax == -Double.MAX_VALUE) {
231                         iMin = 0.;
232                         iMax = 1.;
233                 }
234         }
235         
236         public boolean autoscale() {
237                 if (!autoscroll) return false;
238                 setKnownMinMax();
239                 double nMin = iMin;
240                 double nMax = iMax;
241                 double diff = nMax - nMin;
242                 if (diff==0.0) {
243                         nMin -= 0.5;
244                         nMax += 0.5;
245                         diff = nMax - nMin;
246                 }
247                 double margin = diff*0.02;
248                 
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;
255                         
256                 } else {
257                         nMin = iMin - margin;
258                         nMax = iMax + margin;
259                 }               
260
261                 return setMinMax(nMin, nMax);
262         }
263         
264         public void translate(double dy) {
265                 min += dy;
266                 max += dy;
267                 autoscroll = false;
268         }
269         
270         public void zoomIn(double y, double height) {
271                 autoscroll = false;
272                 
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);
282                         return;
283                 } else {
284                         double newMin  = min + (getHeight() - y-height)*sy;
285                         double newMax  = max - (y)*sy;
286                         setMinMax(newMin, newMax);
287                 }
288         }
289         
290         public void zoomOut() {
291                 autoscroll = true;
292                 autoscale();
293         }       
294         
295         public void zoomTo(double min, double max) {
296                 setMinMax(min, max);
297                 autoscroll = false;
298         }
299         
300         public double unitsPerPixel() {
301                 return (max-min) / getHeight();
302         }
303         
304         static int calcNoOfDecimals(int tickCount, double diff)
305         {
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;
312                 
313                 int decimals = interestingNumbers - ((int)Math.ceil( Math.log10(diff) ));
314                 if (decimals<0) decimals = 0;
315                 
316                 return decimals;
317         }
318         
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));
325                 }
326         }
327         static {
328                 
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);
333
334         }
335
336 }