/******************************************************************************* * 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 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; 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 setHeight(double height) { if (height==bounds.getHeight()) return; bounds.setFrame(0, 0, bounds.getWidth(), height); getTrend().shapedirty = true; } 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(); // 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; VertRuler master = trend.vertRuler; VertRuler slave = this; 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); 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 ); } } } 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)=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); } }