-/*******************************************************************************\r
- * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.trend.impl;\r
-\r
-import java.awt.Color;\r
-import java.awt.Font;\r
-import java.awt.FontMetrics;\r
-import java.awt.Graphics2D;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Path2D;\r
-import java.text.Format;\r
-\r
-import org.simantics.g2d.utils.GridSpacing;\r
-import org.simantics.g2d.utils.GridUtil;\r
-import org.simantics.trend.configuration.ItemPlacement;\r
-import org.simantics.trend.configuration.TrendItem.Renderer;\r
-import org.simantics.utils.format.ValueFormat;\r
-\r
-public class VertRuler extends TrendGraphicalNode {\r
-\r
- private static final long serialVersionUID = 3773787909384380074L;\r
-\r
- GridSpacing spacing = GridSpacing.SOME_SPACING;\r
- double min = -1, max = 1;\r
- double iMin = Double.MAX_VALUE, iMax = -Double.MAX_VALUE;\r
- String label = "";\r
- Color color = Color.GRAY;\r
- boolean autoscroll = true; // Autoscroll on/off\r
- boolean manualscale = false; // Autoscale / manual scale\r
- double labelWidth = 7;\r
-\r
- static final double TRIANGLE_SIZE = 7;\r
- static final Path2D TRIANGLE;\r
- \r
- public void layout()\r
- {\r
- TrendNode trend = getTrend();\r
- ValueFormat vf = trend.valueFormat;\r
- spacing = GridSpacing.makeGridSpacing(max-min, getHeight(), 15);\r
- labelWidth = Math.max(7, GridUtil.calcLabelWidth(min, max, vf.format, spacing));\r
- double w = 30 + labelWidth;\r
- // Snap w -> next 20 pixels\r
- double quantization = 10; \r
- int x = (int) Math.ceil( w / quantization );\r
- if (x<4) x = 4;\r
- w = x * quantization;\r
- w += 5;\r
- bounds.setFrame(0, 0, w, getHeight()); \r
- trend.shapedirty = true;\r
- }\r
-\r
- public void setHeight(double height) {\r
- if (height==bounds.getHeight()) return;\r
- bounds.setFrame(0, 0, bounds.getWidth(), height); \r
- getTrend().shapedirty = true;\r
- }\r
- \r
- public boolean setMinMax(double min, double max) {\r
- if (min==this.min && max==this.max) return false;\r
- spacing = GridSpacing.makeGridSpacing(max-min, getHeight(), 15);\r
- this.min = min;\r
- this.max = max;\r
- getTrend().shapedirty = true;\r
- return true;\r
- } \r
- \r
- @Override\r
- protected void doRender(Graphics2D g) {\r
- TrendNode trend = (TrendNode) getParent();\r
- \r
- // Draw little "Frozen"\r
- if ( !trend.printing )\r
- {\r
- g.setColor( Color.LIGHT_GRAY );\r
- g.setFont( RULER_FONT );\r
- String txt = !autoscroll ? (manualscale ? "*" : "Auto off") : (manualscale ? "" : "Auto on");\r
- // Draw at bottom\r
- //g.drawString(txt, 2.f, (float)getHeight() + 14.f );\r
- // Draw at top\r
- g.drawString(txt, 5.f, -9.f );\r
- }\r
- \r
- g.setPaint( color );\r
- g.setStroke( GridUtil.RULER_LINE_STROKE ); \r
- \r
- ValueFormat vf = trend.valueFormat;\r
- VertRuler master = trend.vertRuler;\r
- VertRuler slave = this;\r
- if ( master != slave )\r
- { \r
- // Paint "slave" ruler - a ruler with ticks from master and labels from this\r
- int tickCount = GridUtil.getTickCount(master.spacing, master.min, master.getHeight());\r
- int noOfDecimals = calcNoOfDecimals(tickCount, slave.max-slave.min);\r
- Format format = vf.toFormat(noOfDecimals);\r
- \r
- GridUtil.paintVerticalSlaveRuler(\r
- master.spacing,\r
- spacing, \r
- g,\r
- master.min,\r
- slave.min,\r
- getHeight(),\r
- format);\r
- } else {\r
- Format format = vf.format;\r
- \r
- // Paint normal ruler\r
- GridUtil.paintVerticalRuler(\r
- spacing, \r
- g,\r
- min,\r
- getHeight(),\r
- format);\r
- }\r
- \r
- // Draw label\r
- {\r
-// Shape oldClip = g2d.getClip();\r
-// Rectangle2D.Double newClip = new Rectangle2D.Double(0,-20, getWidth(), 30);\r
-// g2d.setClip(newClip);\r
- boolean selected = trend.singleAxis ? false : trend.vertRuler==this;\r
- selected &= !trend.printing;\r
- \r
- Font font = selected ? RULER_FONT_BOLD : RULER_FONT;\r
- FontMetrics fm = g.getFontMetrics( font );\r
- //LineMetrics lm = fm.getLineMetrics(label, g);\r
- double wid = fm.stringWidth(label);\r
- \r
- AffineTransform at = g.getTransform();\r
- g.translate( getWidth()-15, (getHeight()-wid)/2);\r
-// g2d.translate( 18+labelWidth, (getHeight()-wid)/2);\r
- g.transform( AffineTransform.getQuadrantRotateInstance(1) );\r
- g.setColor( color );\r
- g.setFont( font );\r
- g.drawString( label, (float) 0, (float) 0);\r
- g.setTransform( at ); \r
-// g2d.setClip(oldClip);\r
- \r
- // Triangle\r
- if (selected) {\r
- at = g.getTransform();\r
- g.translate( getWidth() - TRIANGLE_SIZE - 5, 0 );\r
- g.setColor( color );\r
- g.fill( TRIANGLE ); \r
- g.setTransform( at ); \r
- }\r
- }\r
- }\r
-\r
- public void setKnownMinMax() {\r
- TrendNode trend = (TrendNode) getParent();\r
- iMax = -Double.MAX_VALUE;\r
- iMin = Double.MAX_VALUE;\r
- for (ItemNode item : trend.analogItems) {\r
- if (item.item.renderer != Renderer.Analog || item.ruler!=this) continue;\r
- if ( !Double.isNaN(item.min ) ) iMin = Math.min(iMin, item.min ); \r
- if ( !Double.isNaN(item.max ) ) iMax = Math.max(iMax, item.max );\r
- }\r
- \r
- if (iMin == Double.MAX_VALUE && iMax == -Double.MAX_VALUE) {\r
- iMin = 0.;\r
- iMax = 1.;\r
- }\r
- }\r
- \r
- public boolean autoscale() {\r
- if (!autoscroll) return false;\r
- setKnownMinMax();\r
- double nMin = iMin;\r
- double nMax = iMax;\r
- double diff = nMax - nMin;\r
- if (diff==0.0) {\r
- nMin -= 0.5;\r
- nMax += 0.5;\r
- diff = nMax - nMin;\r
- }\r
- double margin = diff*0.02;\r
- \r
- TrendNode trend = getTrend();\r
- if (trend.itemPlacement == ItemPlacement.Stacked) {\r
- int c = trend.vertRulers.size();\r
- int i = c-trend.vertRulers.indexOf(this)-1;\r
- nMin = nMin - (diff)*i - margin;\r
- nMax = nMax + (diff)*(c-i-1) + margin;\r
- \r
- } else {\r
- nMin = iMin - margin;\r
- nMax = iMax + margin;\r
- } \r
-\r
- return setMinMax(nMin, nMax);\r
- }\r
- \r
- public void translate(double dy) {\r
- min += dy;\r
- max += dy;\r
- autoscroll = false;\r
- }\r
- \r
- public void zoomIn(double y, double height) {\r
- autoscroll = false;\r
- \r
- double diff = max-min;\r
- double sy = diff / getHeight();\r
- if ( Math.abs(diff)<GridSpacing.GRID_MIN_USER_SIZE && height<getHeight()) {\r
-// System.out.println(y/getHeight()*4+", "+getHeight());\r
-// diff = GridSpacing.GRID_MIN_USER_SIZE;\r
-// sy = diff / getHeight();\r
-// double ry = y/getHeight()*4;\r
-// double vy = (max+min)/2-y*sy; \r
-// setMinMax(vy-GridSpacing.GRID_MIN_USER_SIZE/2, vy+GridSpacing.GRID_MIN_USER_SIZE/2);\r
- return;\r
- } else {\r
- double newMin = min + (getHeight() - y-height)*sy;\r
- double newMax = max - (y)*sy;\r
- setMinMax(newMin, newMax);\r
- }\r
- }\r
- \r
- public void zoomOut() {\r
- autoscroll = true;\r
- autoscale();\r
- } \r
- \r
- public void zoomTo(double min, double max) {\r
- setMinMax(min, max);\r
- autoscroll = false;\r
- }\r
- \r
- public double unitsPerPixel() {\r
- return (max-min) / getHeight();\r
- }\r
- \r
- static int calcNoOfDecimals(int tickCount, double diff)\r
- {\r
- int interestingNumbers;\r
- if ( tickCount<=2 ) interestingNumbers = 1; else\r
- if ( tickCount>=3 && tickCount<=9 ) interestingNumbers = 2; else\r
- if ( tickCount>=10 && tickCount<=99 ) interestingNumbers = 3; else\r
- if ( tickCount>=100 && tickCount<=999 ) interestingNumbers = 4; else\r
- interestingNumbers = 5;\r
- \r
- int decimals = interestingNumbers - ((int)Math.ceil( Math.log10(diff) ));\r
- if (decimals<0) decimals = 0;\r
- \r
- return decimals;\r
- }\r
- \r
- public static void main(String[] args) {\r
- double diff = 12.4567890;\r
- for ( int tickCount=0; tickCount<15; tickCount++) {\r
- int noOfDecimals = calcNoOfDecimals(tickCount, diff);\r
- Format format = ValueFormat.Currency.toFormat(noOfDecimals);\r
- System.out.println("diff="+diff+", tickcount="+tickCount+", #ofDecimals="+noOfDecimals+", formatted diff="+format.format(diff));\r
- }\r
- }\r
- static {\r
- \r
- TRIANGLE = new Path2D.Double();\r
- TRIANGLE.moveTo(-TRIANGLE_SIZE/2, 0);\r
- TRIANGLE.lineTo(TRIANGLE_SIZE/2, 0);\r
- TRIANGLE.lineTo(0, TRIANGLE_SIZE);\r
-\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.trend.impl;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Path2D;
+import java.text.Format;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.simantics.g2d.utils.GridSpacing;
+import org.simantics.g2d.utils.GridUtil;
+import org.simantics.trend.configuration.ItemPlacement;
+import org.simantics.trend.configuration.TrendItem.Renderer;
+import org.simantics.utils.format.ValueFormat;
+
+public class VertRuler extends TrendGraphicalNode {
+
+ private static final long serialVersionUID = 3773787909384380074L;
+
+ GridSpacing spacing = GridSpacing.SOME_SPACING;
+ double min = -1, max = 1;
+ double iMin = Double.MAX_VALUE, iMax = -Double.MAX_VALUE;
+ String label = "";
+ Color color = Color.GRAY;
+ boolean autoscroll = true; // Autoscroll on/off
+ boolean manualscale = false; // Autoscale / manual scale
+ double labelWidth = 7;
+ List<String> extra_labels = new ArrayList<>();
+ List<Color> extra_label_colors = new ArrayList<>();
+ double extra_width = 0.0;
+ int singleAxisShowLegendsMaxLegends = 10;
+
+ static final double TRIANGLE_SIZE = 7;
+ static final Path2D TRIANGLE;
+
+ public void layout()
+ {
+ TrendNode trend = getTrend();
+ ValueFormat vf = trend.valueFormat;
+ spacing = GridSpacing.makeGridSpacing(max-min, getHeight(), 15);
+ labelWidth = Math.max(7, GridUtil.calcLabelWidth(min, max, vf.format, spacing));
+
+ double w = 30 + labelWidth;
+
+ // Snap w -> next 20 pixels
+ double quantization = 10;
+ int x = (int) Math.ceil( w / quantization );
+ if (x<4) x = 4;
+ w = x * quantization;
+ w += 5;
+ bounds.setFrame(0, 0, w, getHeight());
+ trend.shapedirty = true;
+ }
+
+ public void addExtraLabel(String label, Color color) {
+ extra_labels.add(label);
+ extra_label_colors.add(color);
+ }
+
+ public void setHeight(double height) {
+ if (height==bounds.getHeight()) return;
+ bounds.setFrame(0, 0, bounds.getWidth(), height);
+ getTrend().shapedirty = true;
+ }
+
+ @Override
+ public double getWidth() {
+ if(extra_labels.size() == 0) {
+ return super.getWidth();
+ } else {
+ if(extra_width == 0.0) {
+ extra_width = 15.0 * extra_labels.size();
+ double w = bounds.getWidth();
+ return w + extra_width;
+ } else {
+ double w = bounds.getWidth();
+ return w + extra_width;
+ }
+ }
+
+ }
+
+ public boolean setMinMax(double min, double max) {
+ if (min==this.min && max==this.max) return false;
+ spacing = GridSpacing.makeGridSpacing(max-min, getHeight(), 15);
+ this.min = min;
+ this.max = max;
+ getTrend().shapedirty = true;
+ return true;
+ }
+
+ @Override
+ protected void doRender(Graphics2D g) {
+ TrendNode trend = (TrendNode) getParent();
+
+ VertRuler master = trend.vertRuler;
+ VertRuler slave = this;
+
+ // Draw little "Frozen"
+ if ( !trend.printing )
+ {
+ g.setColor( Color.LIGHT_GRAY );
+ g.setFont( RULER_FONT );
+ String txt = !autoscroll ? (manualscale ? "*" : "Auto off") : (manualscale ? "" : "Auto on");
+ // Draw at bottom
+ //g.drawString(txt, 2.f, (float)getHeight() + 14.f );
+ // Draw at top
+ g.drawString(txt, 5.f, -9.f );
+ }
+
+ g.setPaint( color );
+ g.setStroke( GridUtil.RULER_LINE_STROKE );
+
+ ValueFormat vf = trend.valueFormat;
+
+ if ( master != slave )
+ {
+ // Paint "slave" ruler - a ruler with ticks from master and labels from this
+ int tickCount = GridUtil.getTickCount(master.spacing, master.min, master.getHeight());
+ int noOfDecimals = calcNoOfDecimals(tickCount, slave.max-slave.min);
+ Format format = vf.toFormat(noOfDecimals);
+
+ GridUtil.paintVerticalSlaveRuler(
+ master.spacing,
+ spacing,
+ g,
+ master.min,
+ slave.min,
+ getHeight(),
+ format);
+ } else {
+ Format format = vf.format;
+
+ // Paint normal ruler
+ GridUtil.paintVerticalRuler(
+ spacing,
+ g,
+ min,
+ getHeight(),
+ format);
+ }
+
+ // Draw label
+ {
+// Shape oldClip = g2d.getClip();
+// Rectangle2D.Double newClip = new Rectangle2D.Double(0,-20, getWidth(), 30);
+// g2d.setClip(newClip);
+ boolean selected = trend.singleAxis ? false : trend.vertRuler==this;
+ selected &= !trend.printing;
+
+ Font font = selected ? RULER_FONT_BOLD : RULER_FONT;
+ FontMetrics fm = g.getFontMetrics( font );
+ //LineMetrics lm = fm.getLineMetrics(label, g);
+
+ if(extra_labels.size() == 0) {
+ double wid = fm.stringWidth(label);
+
+ AffineTransform at = g.getTransform();
+ g.translate( getWidth()-15, (getHeight()-wid)/2);
+ // g2d.translate( 18+labelWidth, (getHeight()-wid)/2);
+ g.transform( AffineTransform.getQuadrantRotateInstance(1) );
+ g.setColor( color );
+ g.setFont( font );
+ g.drawString( label, (float) 0, (float) 0);
+ g.setTransform( at );
+ // g2d.setClip(oldClip);
+
+ // Triangle
+ if (selected) {
+ at = g.getTransform();
+ g.translate( getWidth() - TRIANGLE_SIZE - 5, 0 );
+ g.setColor( color );
+ g.fill( TRIANGLE );
+ g.setTransform( at );
+ }
+ } else {
+ extra_width = 0.0;
+ double bounds_width = bounds.getWidth();
+
+ for(int label_index = 0; label_index < extra_labels.size(); label_index++) {
+ if(label_index >= singleAxisShowLegendsMaxLegends) {
+ break; // Maximum amount of labels that we should display has been reached
+ }
+
+ String label = extra_labels.get(label_index);
+ Color color = extra_label_colors.get(label_index);
+
+ double wid = fm.stringWidth(label);
+ double font_height = fm.getHeight();
+ extra_width += font_height;
+ //Letters are displayed in 90 degree angle, so font height is the width of the label as seen in X-direction
+
+ AffineTransform at = g.getTransform();
+ g.translate( bounds_width + label_index*font_height, (getHeight()-wid)/2);
+ // g2d.translate( 18+labelWidth, (getHeight()-wid)/2);
+ g.transform( AffineTransform.getQuadrantRotateInstance(1) );
+ g.setColor( color );
+ g.setFont( font );
+ g.drawString( label, (float) 0, (float) 0);
+ g.setTransform( at );
+ }
+ }
+ }
+ }
+
+ public void setKnownMinMax() {
+ TrendNode trend = (TrendNode) getParent();
+ iMax = -Double.MAX_VALUE;
+ iMin = Double.MAX_VALUE;
+ for (ItemNode item : trend.analogItems) {
+ if (item.item.renderer != Renderer.Analog || item.ruler!=this) continue;
+ if ( !Double.isNaN(item.min ) ) iMin = Math.min(iMin, item.min );
+ if ( !Double.isNaN(item.max ) ) iMax = Math.max(iMax, item.max );
+ }
+
+ if (iMin == Double.MAX_VALUE && iMax == -Double.MAX_VALUE) {
+ iMin = 0.;
+ iMax = 1.;
+ }
+ }
+
+ public boolean autoscale() {
+ if (!autoscroll) return false;
+ setKnownMinMax();
+ double nMin = iMin;
+ double nMax = iMax;
+ double diff = nMax - nMin;
+ if (diff==0.0) {
+ nMin -= 0.5;
+ nMax += 0.5;
+ diff = nMax - nMin;
+ }
+ double margin = diff*0.02;
+
+ TrendNode trend = getTrend();
+ if (trend.itemPlacement == ItemPlacement.Stacked) {
+ int c = trend.vertRulers.size();
+ int i = c-trend.vertRulers.indexOf(this)-1;
+ nMin = nMin - (diff)*i - margin;
+ nMax = nMax + (diff)*(c-i-1) + margin;
+
+ } else {
+ nMin = iMin - margin;
+ nMax = iMax + margin;
+ }
+
+ return setMinMax(nMin, nMax);
+ }
+
+ public void translate(double dy) {
+ min += dy;
+ max += dy;
+ autoscroll = false;
+ }
+
+ public void zoomIn(double y, double height) {
+ autoscroll = false;
+
+ double diff = max-min;
+ double sy = diff / getHeight();
+ if ( Math.abs(diff)<GridSpacing.GRID_MIN_USER_SIZE && height<getHeight()) {
+// System.out.println(y/getHeight()*4+", "+getHeight());
+// diff = GridSpacing.GRID_MIN_USER_SIZE;
+// sy = diff / getHeight();
+// double ry = y/getHeight()*4;
+// double vy = (max+min)/2-y*sy;
+// setMinMax(vy-GridSpacing.GRID_MIN_USER_SIZE/2, vy+GridSpacing.GRID_MIN_USER_SIZE/2);
+ return;
+ } else {
+ double newMin = min + (getHeight() - y-height)*sy;
+ double newMax = max - (y)*sy;
+ setMinMax(newMin, newMax);
+ }
+ }
+
+ public void zoomOut() {
+ autoscroll = true;
+ autoscale();
+ }
+
+ public void zoomTo(double min, double max) {
+ setMinMax(min, max);
+ autoscroll = false;
+ }
+
+ public double unitsPerPixel() {
+ return (max-min) / getHeight();
+ }
+
+ static int calcNoOfDecimals(int tickCount, double diff)
+ {
+ int interestingNumbers;
+ if ( tickCount<=2 ) interestingNumbers = 1; else
+ if ( tickCount>=3 && tickCount<=9 ) interestingNumbers = 2; else
+ if ( tickCount>=10 && tickCount<=99 ) interestingNumbers = 3; else
+ if ( tickCount>=100 && tickCount<=999 ) interestingNumbers = 4; else
+ interestingNumbers = 5;
+
+ int decimals = interestingNumbers - ((int)Math.ceil( Math.log10(diff) ));
+ if (decimals<0) decimals = 0;
+
+ return decimals;
+ }
+
+ public static void main(String[] args) {
+ double diff = 12.4567890;
+ for ( int tickCount=0; tickCount<15; tickCount++) {
+ int noOfDecimals = calcNoOfDecimals(tickCount, diff);
+ Format format = ValueFormat.Currency.toFormat(noOfDecimals);
+ System.out.println("diff="+diff+", tickcount="+tickCount+", #ofDecimals="+noOfDecimals+", formatted diff="+format.format(diff));
+ }
+ }
+ static {
+
+ TRIANGLE = new Path2D.Double();
+ TRIANGLE.moveTo(-TRIANGLE_SIZE/2, 0);
+ TRIANGLE.lineTo(TRIANGLE_SIZE/2, 0);
+ TRIANGLE.lineTo(0, TRIANGLE_SIZE);
+
+ }
+
+}