/******************************************************************************* * Copyright (c) 2010 Oak Ridge National Laboratory. * 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 ******************************************************************************/ package org.eclipse.nebula.visualization.widgets.figureparts; import org.eclipse.draw2d.Figure; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.nebula.visualization.widgets.util.GraphicsUtil; import org.eclipse.nebula.visualization.xygraph.util.XYGraphMediaFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Pattern; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; /** * A ramp looks like a colorful donut, which is used to indicate the alarm limit, hihi, hi, lo or lolo. * The ramp is based on a round scale which is in the same polar coordinate system as the ramp. * The ramp could be used for any round scale based widgets, such as meter, gauge and knob etc. * @author Xihui Chen * */ public class RoundScaledRamp extends Figure { private static final int OVERLAP_DEGREE = 2; /** * The alarm thereshold for a PV, includs HIHI, HI, LO or LOLO. */ public enum Threshold{ HIHI, HI, LO, LOLO } private RoundScale scale; private ThresholdMarker lolo = new ThresholdMarker(10, XYGraphMediaFactory.COLOR_RED, true); private ThresholdMarker lo = new ThresholdMarker(25, XYGraphMediaFactory.COLOR_ORANGE, true); private ThresholdMarker hi = new ThresholdMarker(75, XYGraphMediaFactory.COLOR_ORANGE, true); private ThresholdMarker hihi = new ThresholdMarker(90, XYGraphMediaFactory.COLOR_RED, true); //the middle value in the normal range, for internal use only private ThresholdMarker normal = new ThresholdMarker(50, XYGraphMediaFactory.COLOR_GREEN, true); private ThresholdMarker min = new ThresholdMarker(0, null, true); private ThresholdMarker max = new ThresholdMarker(100, null, true); private int rampWidth = 20; private boolean gradient = true; private boolean dirty = true; /** * Constructor * @param scale the round scale */ public RoundScaledRamp(RoundScale scale) { this.scale = scale; } @Override public void setBounds(Rectangle rect) { if(!bounds.equals(rect)) setDirty(true); //get the square in the rect rect.width = Math.min(rect.width, rect.height); rect.height = rect.width; super.setBounds(rect); } @Override public Dimension getPreferredSize(int wHint, int hHint) { wHint = Math.min(wHint, hHint); hHint = wHint; Dimension size = new Dimension(wHint, hHint); return size; } /** * update the the position for each threshold, and other parameters related to the positions. */ private void updateThresholdPosition(){ if(dirty){ //get normal value double lowLimit; double upLimit; if(lo.visible) lowLimit = lo.value; else if(lolo.visible) lowLimit = lolo.value; else lowLimit = scale.getRange().getLower(); if(hi.visible) upLimit = hi.value; else if(hihi.visible) upLimit = hihi.value; else upLimit = scale.getRange().getUpper(); //update normal normal.value = (lowLimit + upLimit)/2; normal.absolutePosition = (int) scale.getCoercedValuePosition(normal.value, false); normal.relativePosition = (int) scale.getCoercedValuePosition(normal.value, true); normal.rightPoint = new PolarPoint( bounds.width/2, (normal.absolutePosition - OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); normal.leftPoint = new PolarPoint( bounds.width/2, (normal.absolutePosition + OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); //update min, max if(scale.getRange().isMinBigger()){ min.value = scale.getRange().getUpper(); max.value = scale.getRange().getLower(); } else{ min.value = scale.getRange().getLower(); max.value = scale.getRange().getUpper(); } min.absolutePosition = (int) scale.getCoercedValuePosition(min.value, false); min.relativePosition = (int) scale.getCoercedValuePosition(min.value, true); max.absolutePosition = (int) scale.getCoercedValuePosition(max.value, false); max.relativePosition = (int) scale.getCoercedValuePosition(max.value, true); //update lolo, lo, hi, hihi if(lolo.visible){ lolo.absolutePosition = (int) scale.getCoercedValuePosition(lolo.value, false); lolo.relativePosition = (int) scale.getCoercedValuePosition(lolo.value, true); lolo.rightPoint = new PolarPoint( bounds.width/2, (lolo.absolutePosition - OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); lolo.leftPoint = new PolarPoint( bounds.width/2, (lolo.absolutePosition + OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); } if(lo.visible) { lo.absolutePosition = (int) scale.getCoercedValuePosition(lo.value, false); lo.relativePosition = (int) scale.getCoercedValuePosition(lo.value, true); lo.rightPoint = new PolarPoint( bounds.width/2, (lo.absolutePosition - OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); lo.leftPoint = new PolarPoint( bounds.width/2, (lo.absolutePosition + OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); } if(hi.visible){ hi.absolutePosition = (int) scale.getCoercedValuePosition(hi.value, false); hi.relativePosition = (int) scale.getCoercedValuePosition(hi.value, true); hi.rightPoint = new PolarPoint( bounds.width/2, (hi.absolutePosition - OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); hi.leftPoint = new PolarPoint( bounds.width/2, (hi.absolutePosition + OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); } if(hihi.visible){ hihi.absolutePosition = (int) scale.getCoercedValuePosition(hihi.value, false); hihi.relativePosition = (int) scale.getCoercedValuePosition(hihi.value, true); hihi.rightPoint = new PolarPoint( bounds.width/2, (hihi.absolutePosition - OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); hihi.leftPoint = new PolarPoint( bounds.width/2, (hihi.absolutePosition + OVERLAP_DEGREE)*Math.PI/180).toAbsolutePoint(bounds); } setDirty(false); } } @Override protected void paintClientArea(Graphics graphics) { updateThresholdPosition(); graphics.setAntialias(SWT.ON); graphics.setLineWidth(rampWidth); graphics.pushState(); int overlap = 0; Pattern pattern = null; boolean support3D = GraphicsUtil.testPatternSupported(graphics); //draw lolo part if(lolo.visible){ graphics.setBackgroundColor(lolo.color); graphics.fillArc(bounds, lolo.absolutePosition, min.relativePosition - lolo.relativePosition); } //draw lo part if(lo.visible){ if(support3D && gradient && lolo.visible){ try { pattern = new Pattern(Display.getCurrent(), lolo.leftPoint.x, lolo.leftPoint.y, lo.rightPoint.x, lo.rightPoint.y, lolo.color, lo.color); graphics.setBackgroundPattern(pattern); overlap = OVERLAP_DEGREE/2; } catch (Exception e) { support3D = false; pattern.dispose(); graphics.setBackgroundColor(lo.color); overlap = 0; } } else { graphics.setBackgroundColor(lo.color); overlap = 0; } if(lolo.visible) graphics.fillArc(bounds, lo.absolutePosition, lolo.relativePosition - lo.relativePosition + overlap); else graphics.fillArc(bounds, lo.absolutePosition, min.relativePosition - lo.relativePosition); if(gradient && lolo.visible && support3D) pattern.dispose(); } //draw left normal part //get the left marker boolean leftMarkerVisible = false; ThresholdMarker leftMarker = null; if(lo.visible){ leftMarkerVisible = true; leftMarker = lo; } else if (lolo.visible){ leftMarkerVisible =true; leftMarker = lolo; } else leftMarkerVisible = false; if(gradient && leftMarkerVisible && support3D){ pattern = new Pattern(Display.getCurrent(), leftMarker.leftPoint.x, leftMarker.leftPoint.y, normal.rightPoint.x, normal.rightPoint.y, leftMarker.color, normal.color); graphics.setBackgroundPattern(pattern); overlap = OVERLAP_DEGREE/2; } else { graphics.setBackgroundColor(normal.color); overlap = 0; } if(leftMarkerVisible) graphics.fillArc(bounds, normal.absolutePosition, leftMarker.relativePosition - normal.relativePosition + overlap); else graphics.fillArc(bounds, normal.absolutePosition, min.relativePosition - normal.relativePosition); if(gradient && leftMarkerVisible && support3D) pattern.dispose(); //draw right normal part //get the right marker boolean rightMarkerVisible = false; ThresholdMarker rightMarker = null; if(hi.visible){ rightMarkerVisible = true; rightMarker = hi; } else if (hihi.visible){ rightMarkerVisible =true; rightMarker = hihi; } else rightMarkerVisible = false; if(gradient && rightMarkerVisible && support3D){ pattern = new Pattern(Display.getCurrent(), rightMarker.rightPoint.x, rightMarker.rightPoint.y, normal.leftPoint.x, normal.leftPoint.y, rightMarker.color, normal.color); graphics.setBackgroundPattern(pattern); overlap = OVERLAP_DEGREE/2; } else { graphics.setBackgroundColor(normal.color); overlap = 0; } if(rightMarkerVisible) graphics.fillArc(bounds, rightMarker.absolutePosition, normal.relativePosition - rightMarker.relativePosition + overlap + 1); else graphics.fillArc(bounds, max.absolutePosition, normal.relativePosition - max.relativePosition +1); if(gradient && rightMarkerVisible && support3D) pattern.dispose(); //draw hi part if(hi.visible){ if(hihi.visible){ rightMarkerVisible = true; rightMarker = hihi; } else rightMarkerVisible = false; if(gradient && rightMarkerVisible && support3D){ pattern = new Pattern(Display.getCurrent(), rightMarker.rightPoint.x, rightMarker.rightPoint.y, hi.leftPoint.x, hi.leftPoint.y, rightMarker.color, hi.color); graphics.setBackgroundPattern(pattern); overlap = OVERLAP_DEGREE/2; } else { graphics.setBackgroundColor(hi.color); overlap = 0; } if(rightMarkerVisible) graphics.fillArc(bounds, rightMarker.absolutePosition, hi.relativePosition - rightMarker.relativePosition + overlap); else graphics.fillArc(bounds, max.absolutePosition, hi.relativePosition - max.relativePosition); if(gradient && rightMarkerVisible && support3D) pattern.dispose(); } //draw hihi part if(hihi.visible){ if(gradient && support3D) overlap = OVERLAP_DEGREE/2; else overlap = 0; graphics.setBackgroundColor(hihi.color); graphics.fillArc(bounds, max.absolutePosition, hihi.relativePosition - max.relativePosition + overlap); } graphics.popState(); graphics.fillOval(bounds.x + rampWidth, bounds.y + rampWidth, bounds.width-2*rampWidth,bounds.height - 2*rampWidth); super.paintClientArea(graphics); } /** * @return the round scale for this ramp */ public RoundScale getScale() { return scale; } /** * @param scale the round scale to set */ public void setScale(RoundScale scale) { this.scale = scale; setDirty(true); } /** * @return the rampWidth */ public int getRampWidth() { return rampWidth; } /** * @param rampWidth the rampWidth to set */ public void setRampWidth(int rampWidth) { this.rampWidth = rampWidth; setDirty(true); } /** * If gradient is true, the color will be displayed in gradient style * @param gradient the gradient to set */ public void setGradient(boolean gradient) { this.gradient = gradient; setDirty(true); } /** * Set value of the threshold. * @param thresholdName the threshold name which should be one of {@link Threshold} * @param value the value to set */ public void setThresholdValue(Threshold thresholdName, double value){ switch (thresholdName) { case HIHI: hihi.value = value; break; case HI: hi.value =value; break; case LO: lo.value = value; break; case LOLO: lolo.value = value; default: break; } setDirty(true); } /** * Set color of the threshold. * @param thresholdName the threshold name which should be one of {@link Threshold} * @param color the RGB color to set */ public void setThresholdColor(Threshold thresholdName, RGB color){ switch (thresholdName) { case HIHI: hihi.setColor(color); break; case HI: hi.setColor(color); break; case LO: lo.setColor(color); break; case LOLO: lolo.setColor(color); default: break; } } /** * Set visibility of the threshold. * @param thresholdName the threshold name which should be one of {@link Threshold} * @param visible true if this threshold should be visible */ public void setThresholdVisibility(Threshold thresholdName, boolean visible){ switch (thresholdName) { case HIHI: hihi.visible = visible; break; case HI: hi.visible =visible; break; case LO: lo.visible = visible; break; case LOLO: lolo.visible = visible; default: break; } setDirty(true); } /** * @param dirty the dirty to set */ public void setDirty(boolean dirty) { this.dirty = dirty; } /** * Hold the properties for each threshold. * @author Xihui Chen */ static class ThresholdMarker { private double value; private Color color; private boolean visible; /** Its absolute degree position on the scale */ private int absolutePosition; /** Its relative degree position on the scale */ private int relativePosition; /** The right overlap point. Only used for gradient */ private Point rightPoint; /** The left overlap point. Only used for gradient */ private Point leftPoint; /** * @param value * @param color * @param visible */ public ThresholdMarker(double value, RGB color, boolean visible) { this.value = value; if(color != null) this.color = XYGraphMediaFactory.getInstance().getColor(color); this.visible = visible; } /** * @param color the RGB color to set */ public void setColor(RGB color) { this.color = XYGraphMediaFactory.getInstance().getColor(color); } } }