Light Ramp Gauges
In general, ILOG Elixir gauges are displaying the current value of the gauge using a pointer pattern such as a needle or a marker. However in some cases you might want to display the current value using a different pattern than pointing the value. A typical example is a light ramp gauge that is displaying the current value using color lighting on a ramp such as in the following example:
Fortunately ILOG Elixir is not just providing you with predefined gauges, but with a gauges framework that allows you to do that pretty easily. There are several options on how to implement this behavior using the gauges framework. The one that sounds the more natural would be to extend RectangularValueRenderer (for horizontal and vertical gauges) and CircularValueRenderer (for circular gauges) in order to draw the ramp and light it. This is natural but not very easy because you have to code the drawing of the ramp while the code that does that is already there! Indeed RectangularScaleRenderer (for horizontal and vertical gauges) and CircularScaleRenderer (for circular gauges) are already drawing a kind of ramp, the only missing thing being that the ticks of the ramp are not lighted according to the current value. So a less natural but easier way to achieve what we are trying to do is to configure the scale renderers to draw ticks that light or unlight instead of subclassing value renderers. Let’s try to proceed this way using the rectangular gauge use-case.
The first step is to create a lighting tick that will replace the regular ticks on the scale. For that we inherit from ProgrammaticSkin and implement the IDataRenderer interface. We code the updateDisplayList() method such that a tick is drawn with the correct lighting:
public class LightRectangularTickRenderer extends ProgrammaticSkin implements IDataRenderer
{
public function LightRectangularTickRenderer() {
super();
}
// [...]
protected override function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void {
var value:Number = NaN;
// we get the scale we are rendering
var scale:IRectangularNumericScale = TickItem(data).scale as IRectangularNumericScale;
// we get the value renderer of the scale using a utility method
var rvr:RectangularValueRenderer = valueRenderer;
// we get the value we currently render from the value renderer
// this is either a transitionPosition if we are animating or the actual value if the transitionPosition
// is not available.
if (rvr != null) {
if (!isNaN(rvr.transitionPosition)) {
value = scale.valueForPosition(rvr.transitionPosition) as Number;
} else {
value = rvr.value as Number;
}
}
graphics.clear();
// we choose base color depending on the value of the tick. We can get it because we
// are a IDataRenderer
var color:uint = getColor(Number(TickItem(data).value) / (scale.maximum - scale.minimum));
// apply some brightness depending on whether the tick lighted or not
// i.e. whether the ticks is after or before the current value
if (!isNaN(value) && value != 0 && TickItem(data).value <= value) {
graphics.beginFill(lightColor(color));
graphics.lineStyle(1, color);
} else {
graphics.beginFill(color);
}
graphics.drawRoundRect(0, 0, unscaledWidth, unscaledHeight, 5, 5);
graphics.endFill();
}
}
Now that we have done that, we have a tick that we can use in a scale and that will renderer with a color based on its value and a lighting depending on whether the tick is before or after the current value of the gauge. However the scale is not supposed to render the value of the gauge, so the gauges framework is doing some optimization and not invalidating it when the gauge value is changing. That means we have to add an invalidation somewhere for this to happen. The simplest way of doing this is to subclass RectangularValueRenderer and invalidate the scale renderer each time the value renderer is updated. This way we will benefit from invalidation on the value and redrawing during the animation of the value. This can be done easily as in the following piece of code:
public class LightRectangularValueRenderer
extends RectangularValueRenderer
{
public function LightRectangularValueRenderer() {
}
protected override function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void {
NumericRectangularGauge(gauge).scaleRenderer.invalidateDisplayList();
}
}
The third step is to use our specialized tick and value renderers in a subclass of NumericRectangularGauge to have a light ramp gauge:
<?xml version="1.0" encoding="utf-8"?>
<ilog:NumericRectangularGauge
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:ilog="http://www.ilog.com/2007/ilog/flex"
xmlns:local="*" showTrack="false"
showMinorTicks="false" showLabels="false">
<ilog:scaleRenderer>
<ilog:RectangularScaleRenderer majorTickWidth="20">
<ilog:majorTickRenderer>
<mx:Component>
<local:LightRectangularTickRenderer/>
</mx:Component>
</ilog:majorTickRenderer>
</ilog:RectangularScaleRenderer>
</ilog:scaleRenderer>
<ilog:valueRenderer>
<local:LightRectangularValueRenderer/>
</ilog:valueRenderer>
</ilog:NumericRectangularGauge>
Finally we use our new gauge just as any other gauge in our Flex or AIR application:
<local:LightRampRectangularGauge width="400" height="100"
animationDuration="250"/>
You can proceed in a similar manner for circular gauges. In case you are interested in getting a similar experience in your Flex application, you will find the full source code for horizontal, vertical and circular light ramp gauges and a sample by downloading the Flex Builder project here.
In ILOG Elixir 2.0 we will add a new property to the RectangularValueRenderer and CircularValueRenderer class that will allow to redirect the rendering of the value renderer to another gauge element. Typically the scale renderer in this case. This means you will no longer have to subclass value renderer and you’ll be able to change the code of the light ramp to the following:
<ilog:NumericRectangularGauge
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:ilog="http://www.ilog.com/2007/ilog/flex"
xmlns:local="*" showTrack="false"
showMinorTicks="false" showLabels="false">
<ilog:scaleRenderer>
<ilog:RectangularScaleRenderer majorTickWidth="20" id="sr">
<ilog:majorTickRenderer>
<mx:Component>
<local:LightRectangularTickRenderer/>
</mx:Component>
</ilog:majorTickRenderer>
</ilog:RectangularScaleRenderer>
</ilog:scaleRenderer>
<ilog:valueRenderer>
<ilog:RectangularValueRenderer delegate="{sr}"/>
</ilog:valueRenderer>
</ilog:NumericRectangularGauge>
Leveraging this new feature and using an opaticy masking on the tick renderers to progressively light the scale renderer depending on the value of the gauge will allow us to introduce in ILOG Elixir 2.0 horizontal and vertical discrete gauges that you can see below:
If there are other features that you are missing in ILOG Elixir 1.0 gauges don’t hesitate to comment this post or fill in enhancement requests on our Jira so that we can make sure we look at them for next release!
Tags: custom gauge, Flex








July 19th, 2008 at 10:54 am
[…] the blinking circular gauge, the partition gauge and light ramp gauges, here is another example of a gauge built with ILOG Elixir Gauges framework. […]