This is part of a tutorial series on creating extension components for Design Studio.
In the last instalment, we investigated adding a two component indicator needle to our gauge; at least in the sandbox html file. Now we're going to update the component. As usual, the process follows the following steps:
We added large number of variables in Part 9a. Of these, a dozen make sense as properties. We also won't want to leave any of these properties uninitialized.
In the contribution.xml file, we're adding the "Indicator Needle" group, the properties in the table above and their initialization values.
Property | Type | Default Value |
---|---|---|
enableIndicatorNeedle | boolean | false |
enableIndicatorNeedleTail | boolean | false |
fillNeedle | boolean | false |
needleColorCode | string | 'black' |
needleWidth | int | 10 |
needleHeadLength | int | 100 |
needleTailLength | int | 10 |
needleLineThickness | int | 2 |
enableIndicatorNeedleBase | boolean | false |
fullBasePinRing | boolean | false |
fillNeedlaBasePin | boolean | false |
needleBaseRadius | boolean | false |
The new group declaration:
<group
id="SCNGaugeNeedleSettings"
title="Indicator Needle"
tooltip="Gauge Indicator Needle Settings"/>
The property declarations:
<property id="enableIndicatorNeedle" title="Enable Indicator Needle" type="boolean" group="SCNGaugeNeedleSettings"/>
<property id="enableIndicatorNeedleTail"
title="Enable Indicator Needle Tail"
type="boolean"
tooltip="Enable the tail on the the indicator needle and make it a diamond"
group="SCNGaugeNeedleSettings"/>
<property id="fillNeedle"
title="Fill Indicator Needle"
type="boolean"
tooltip="Enable color fill on the indicator needle"
group="SCNGaugeNeedleSettings"/>
<property id="needleColorCode"
title="Needle Color"
type="Color"
tooltip="Needle Color (outline and fill of indicator needle and base pin)"
group="SCNGaugeNeedleSettings"/>
<property id="needleWidth"
title="Indicator Needle Width"
type="int"
tooltip="Base width of the indicator needle, as a percentage of the gauge radius"
group="SCNGaugeNeedleSettings"/>
<property id="needleHeadLength"
title="Indicator Needle Length"
type="int"
tooltip="Length of the indicator needle, as a percentage of the gauge radius"
group="SCNGaugeNeedleSettings">
<!-- <possibleValue>1</possibleValue> -->
</property>
<property id="needleTailLength"
title="Indicator Needle Tail Length"
type="int"
tooltip="Tail Length of the indicator needle, as a percentage of the gauge radius"
group="SCNGaugeNeedleSettings"/>
<property id="needleLineThickness"
title="Indicator Line Thickness"
type="int"
tooltip="Thickness of the lines used to draw the indicator needle and base pin"
group="SCNGaugeNeedleSettings"/>
<property id="enableIndicatorNeedleBase"
title="Enable Base Pin"
type="boolean"
tooltip="Enable the base pin (circle) on the indicator needle"
group="SCNGaugeNeedleSettings"/>
<property id="fullBasePinRing"
title="360° Base Pin"
type="boolean"
tooltip="Enable a full 360 degree base pin circle. Disabling this results in a 180 degree arc on the needle tail"
group="SCNGaugeNeedleSettings"/>
<property id="fillNeedlaBasePin"
title="Fill Base Pin"
type="boolean"
tooltip="Fill the base pin, with the indicator needle fill color"
group="SCNGaugeNeedleSettings"/>
<property id="needleBaseWidth"
title="Base Pin Width"
type="int"
tooltip="Diameter (as a % of main arc radius) of the base pin"
group="SCNGaugeNeedleSettings"/>
The new initialization default values:
<initialization>
…
<defaultValue property="enableIndicatorNeedle">false</defaultValue>
<defaultValue property="enableIndicatorNeedleTail">false</defaultValue>
<defaultValue property="fillNeedle">false</defaultValue>
<defaultValue property="needleColorCode">black</defaultValue>
<defaultValue property="needleWidth">10</defaultValue>
<defaultValue property="needleHeadLength">100</defaultValue>
<defaultValue property="needleTailLength">10</defaultValue>
<defaultValue property="needleLineThickness">2</defaultValue>
<defaultValue property="enableIndicatorNeedleBase">false</defaultValue>
<defaultValue property="fullBasePinRing">false</defaultValue>
<defaultValue property="fillNeedlaBasePin">false</defaultValue>
<defaultValue property="needleBaseWidth">20</defaultValue>
</initialization>
As usual, our property proxy values ( the "me._" variables) get declared at the head of the component.js file.
me._enableIndicatorNeedle = false;
me._enableIndicatorNeedleTail = false;
me._fillNeedle = false;
me._needleColorCode = 'black';
me._needleWidth = 10;
me._needleHeadLength = 100;
me._needleTailLength = 10;
me._needleLineThickness = 2;
me._enableIndicatorNeedleBase = false;
me._fullBasePinRing = false;
me._fillNeedlaBasePin = false;
me._needleBaseRadius = false;
And each of these will need a getter/setter for property synchronization to work. Don't forget that all of these properties affect display and if any is changed, we need to trigger a redraw:
//Step 9
me.enableIndicatorNeedle = function(value) {
if (value === undefined) {
return me._enableIndicatorNeedle;
} else {
me._enableIndicatorNeedle = value;
me.redraw();
return me;
}
};
me.enableIndicatorNeedleTail = function(value) {
if (value === undefined) {
return me._enableIndicatorNeedleTail;
} else {
me._enableIndicatorNeedleTail = value;
me.redraw();
return me;
}
};
me.fillNeedle = function(value) {
if (value === undefined) {
return me._fillNeedle;
} else {
me._fillNeedle = value;
me.redraw();
return me;
}
};
me.needleColorCode = function(value) {
if (value === undefined) {
return me._needleColorCode;
} else {
me._needleColorCode = value;
me.redraw();
return me;
}
};
me.needleWidth = function(value) {
if (value === undefined) {
return me._needleWidth;
} else {
me._needleWidth = value;
me.redraw();
return me;
}
};
me.needleHeadLength = function(value) {
if (value === undefined) {
return me._needleHeadLength;
} else {
me._needleHeadLength = value;
me.redraw();
return me;
}
};
me.needleTailLength = function(value) {
if (value === undefined) {
return me._needleTailLength;
} else {
me._needleTailLength = value;
me.redraw();
return me;
}
};
me.needleLineThickness = function(value) {
if (value === undefined) {
return me._needleLineThickness;
} else {
me._needleLineThickness = value;
me.redraw();
return me;
}
};
me.enableIndicatorNeedleBase = function(value) {
if (value === undefined) {
return me._enableIndicatorNeedleBase;
} else {
me._enableIndicatorNeedleBase = value;
me.redraw();
return me;
}
};
me.fullBasePinRing = function(value) {
if (value === undefined) {
return me._fullBasePinRing;
} else {
me._fullBasePinRing = value;
me.redraw();
return me;
}
};
me.fillNeedlaBasePin = function(value) {
if (value === undefined) {
return me._fillNeedlaBasePin;
} else {
me._fillNeedlaBasePin = value;
me.redraw();
return me;
}
};
me.needleBaseRadius = function(value) {
if (value === undefined) {
return me._needleBaseRadius;
} else {
me._needleBaseRadius = value;
me.redraw();
return me;
}
};
Once we have everything else in place, we can copy/paste the 110 lines of drawing code to componet.js's redraw() function and refactor the variables that are properties to use the proxy values.
///////////////////////////////////////////
//Lets add the indicator needle
///////////////////////////////////////////
if (me._enableIndicatorNeedle == true){
var needleWaypointOffset = me._needleWidth/2;
//needleWaypoints is defined with positive y axis being up
//The initial definition of needleWaypoints is for a full diamond, but if me._enableIndicatorNeedleTail is false, we'll abbreviate to a chevron
var needleWaypoints = [{x: 0,y: me._needleHeadLength}, {x: needleWaypointOffset,y: 0}, {x: 0,y: (-1*me._needleTailLength)}, {x: (-1*needleWaypointOffset),y: 0}, {x: 0,y: me._needleHeadLength}]
if (me._enableIndicatorNeedleTail == false){
if (me._fillNeedle == false){
//If we have no tail and no fill then there is no need to close the shape.
//Leave it as an open chevron
needleWaypoints = [{x: needleWaypointOffset,y: 0}, {x: 0,y: me._needleHeadLength}, {x: (-1*needleWaypointOffset),y: 0}];
}
else {
//There is no tail, but we are filling the needle.
//In this case, draw it as a triangle
needleWaypoints = [{x: 0,y: me._needleHeadLength}, {x: needleWaypointOffset,y: 0}, {x: (-1*needleWaypointOffset),y: 0}, {x: 0,y: me._needleHeadLength}]
}
}
//we need to invert the y-axis and scale the indicator to the gauge.
// If Y = 100, then that is 100% of outer radius. So of Y = 100 and outerRad = 70, then the scaled Y will be 70.
var needleFunction = d3.svg.line()
.x(function(d) { return (d.x)*(outerRad/100); })
.y(function(d) { return -1*(d.y)*(outerRad/100); })
.interpolate("linear");
//Draw the needle, either filling it in, or not
var needleFillColorCode = me._needleColorCode;
if (me._fillNeedle == false){
needleFillColorCode = "none";
}
//Draw the needle
var needle = vis
.append("g")
.attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")")
.append("path")
.attr("class", "tri")
.attr("d", needleFunction(needleWaypoints))
.attr("stroke", me._needleColorCode)
.attr("stroke-width", me._needleLineThickness)
.attr("fill", needleFillColorCode);
//Arcs are in radians, but rotation transformations are in degrees. Kudos to D3 for consistency
needle.attr("transform", "rotate(" + endAngleDeg + ")");
}
///////////////////////////////////////////
//Lets add a needle base pin
///////////////////////////////////////////
var me._enableIndicatorNeedleBase = true;
var me._fullBasePinRing = true;
var me._fillNeedlaBasePin = true;
var me._needleBaseRadius = 20;
if (me._enableIndicatorNeedleBase == true){
// Like the rest of the needle, the size of the pin is defined relative to the main arc, as a % value
var needleIBasennerRadius = (me._needleBaseRadius/2)*(outerRad/100) - (me._needleLineThickness/2);
var needleBaseOuterRadius = needleIBasennerRadius + me._needleLineThickness;
if (me._fillNeedlaBasePin == true){
needleIBasennerRadius = 0.0;
}
// The pin will either be a 180 degree arc, or a 360 degree ring; starting from the 9 O'clock position.
var needleBaseStartAngle = 90.0;
var needleBaseEndAngle = 270.0;
if (me._fullBasePinRing == true){
needleBaseEndAngle = 450.0;
}
//Don't let the arc have a negative length
if (needleBaseEndAngle < needleBaseStartAngle){
needleBaseEndAngle = needleBaseStartAngle;
alert("End angle of outer ring may not be less than start angle!");
}
//Transform the pin ring
var nbTransformedStartAngle = needleBaseStartAngle + endAngleDeg;
var nbTransformedEndAngle = needleBaseEndAngle + endAngleDeg;
var pinArcDefinition = d3.svg.arc()
.innerRadius(needleIBasennerRadius)
.outerRadius(needleBaseOuterRadius)
.startAngle(nbTransformedStartAngle * (pi/180)) //converting from degs to radians
.endAngle(nbTransformedEndAngle * (pi/180)) //converting from degs to radians
var pinArc = vis
.append("path")
.attr("d", pinArcDefinition)
.attr("fill", me._needleColorCode)
.attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")");
}
Once these go into place, you can use the properties pane to enable the indicator needle and configure it.
As usual, the current state of the component and a test app are in a Github repository.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
26 | |
22 | |
19 | |
13 | |
10 | |
9 | |
9 | |
8 | |
7 | |
7 |