-<!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-chart.series.Line'>/**
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>The source code</title>
+ <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
+ <script type="text/javascript" src="../prettify/prettify.js"></script>
+ <style type="text/css">
+ .highlight { display: block; background-color: #ddd; }
+ </style>
+ <script type="text/javascript">
+ function highlight() {
+ document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
+ }
+ </script>
+</head>
+<body onload="prettyPrint(); highlight();">
+ <pre class="prettyprint lang-js"><span id='Ext-chart-series-Line'>/**
</span> * @class Ext.chart.series.Line
* @extends Ext.chart.series.Cartesian
*
* }]
* });
*
- * In this configuration we're adding two series (or lines), one bound to the `data1` property of the store and the other to `data3`. The type for both configurations is
- * `line`. The `xField` for both series is the same, the name propert of the store. Both line series share the same axis, the left axis. You can set particular marker
- * configuration by adding properties onto the markerConfig object. Both series have an object as highlight so that markers animate smoothly to the properties in highlight
- * when hovered. The second series has `fill=true` which means that the line will also have an area below it of the same color.
+ * In this configuration we're adding two series (or lines), one bound to the `data1`
+ * property of the store and the other to `data3`. The type for both configurations is
+ * `line`. The `xField` for both series is the same, the name propert of the store.
+ * Both line series share the same axis, the left axis. You can set particular marker
+ * configuration by adding properties onto the markerConfig object. Both series have
+ * an object as highlight so that markers animate smoothly to the properties in highlight
+ * when hovered. The second series has `fill=true` which means that the line will also
+ * have an area below it of the same color.
+ *
+ * **Note:** In the series definition remember to explicitly set the axis to bind the
+ * values of the line series to. This can be done by using the `axis` configuration property.
*/
-
Ext.define('Ext.chart.series.Line', {
/* Begin Definitions */
type: 'line',
alias: 'series.line',
+
+<span id='Ext-chart-series-Line-cfg-axis'> /**
+</span> * @cfg {String} axis
+ * The position of the axis to bind the values to. Possible values are 'left', 'bottom', 'top' and 'right'.
+ * You must explicitly set this value to bind the values of the line series to the ones in the axis, otherwise a
+ * relative scale will be used.
+ */
-<span id='Ext-chart.series.Line-cfg-selectionTolerance'> /**
+<span id='Ext-chart-series-Line-cfg-selectionTolerance'> /**
</span> * @cfg {Number} selectionTolerance
* The offset distance from the cursor position to the line series to trigger events (then used for highlighting series, etc).
*/
selectionTolerance: 20,
-<span id='Ext-chart.series.Line-cfg-showMarkers'> /**
+<span id='Ext-chart-series-Line-cfg-showMarkers'> /**
</span> * @cfg {Boolean} showMarkers
* Whether markers should be displayed at the data points along the line. If true,
* then the {@link #markerConfig} config item will determine the markers' styling.
*/
showMarkers: true,
-<span id='Ext-chart.series.Line-cfg-markerConfig'> /**
+<span id='Ext-chart-series-Line-cfg-markerConfig'> /**
</span> * @cfg {Object} markerConfig
* The display style for the markers. Only used if {@link #showMarkers} is true.
* The markerConfig is a configuration object containing the same set of properties defined in
*/
markerConfig: {},
-<span id='Ext-chart.series.Line-cfg-style'> /**
+<span id='Ext-chart-series-Line-cfg-style'> /**
</span> * @cfg {Object} style
* An object containing styles for the visualization lines. These styles will override the theme styles.
* Some options contained within the style object will are described next.
*/
style: {},
-<span id='Ext-chart.series.Line-cfg-smooth'> /**
-</span> * @cfg {Boolean} smooth
- * If true, the line will be smoothed/rounded around its points, otherwise straight line
- * segments will be drawn. Defaults to false.
+<span id='Ext-chart-series-Line-cfg-smooth'> /**
+</span> * @cfg {Boolean/Number} smooth
+ * If set to `true` or a non-zero number, the line will be smoothed/rounded around its points; otherwise
+ * straight line segments will be drawn.
+ *
+ * A numeric value is interpreted as a divisor of the horizontal distance between consecutive points in
+ * the line; larger numbers result in sharper curves while smaller numbers result in smoother curves.
+ *
+ * If set to `true` then a default numeric value of 3 will be used. Defaults to `false`.
*/
smooth: false,
-<span id='Ext-chart.series.Line-cfg-fill'> /**
+<span id='Ext-chart-series-Line-property-defaultSmoothness'> /**
+</span> * @private Default numeric smoothing value to be used when {@link #smooth} = true.
+ */
+ defaultSmoothness: 3,
+
+<span id='Ext-chart-series-Line-cfg-fill'> /**
</span> * @cfg {Boolean} fill
* If true, the area below the line will be filled in using the {@link #style.eefill} and
* {@link #style.opacity} config properties. Defaults to false.
};
},
-<span id='Ext-chart.series.Line-method-drawSeries'> /**
+<span id='Ext-chart-series-Line-method-drawSeries'> /**
</span> * Draws the series for the current chart.
*/
drawSeries: function() {
markerGroup = me.markerGroup,
enableShadows = chart.shadow,
shadowGroups = me.shadowGroups,
- shadowAttributes = this.shadowAttributes,
+ shadowAttributes = me.shadowAttributes,
+ smooth = me.smooth,
lnsh = shadowGroups.length,
dummyPath = ["M"],
path = ["M"],
shadowBarAttr,
xValues = [],
yValues = [],
+ storeIndices = [],
+ numericAxis = true,
+ axisCount = 0,
onbreak = false,
markerStyle = me.markerStyle,
seriesStyle = me.seriesStyle,
seriesLabelStyle = me.seriesLabelStyle,
colorArrayStyle = me.colorArrayStyle,
colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
+ posHash = {
+ 'left': 'right',
+ 'right': 'left',
+ 'top': 'bottom',
+ 'bottom': 'top'
+ },
+ isNumber = Ext.isNumber,
seriesIdx = me.seriesIdx, shadows, shadow, shindex, fromPath, fill, fillPath, rendererAttributes,
x, y, prevX, prevY, firstY, markerCount, i, j, ln, axis, ends, marker, markerAux, item, xValue,
yValue, coords, xScale, yScale, minX, maxX, minY, maxY, line, animation, endMarkerStyle,
- endLineStyle, type, props, firstMarker;
+ endLineStyle, type, props, firstMarker, count, smoothPath, renderPath;
//if store is empty then there's nothing to draw.
if (!store || !store.getCount()) {
me.clipRect = [bbox.x, bbox.y, bbox.width, bbox.height];
- for (i = 0, ln = axes.length; i < ln; i++) {
- axis = chart.axes.get(axes[i]);
- if (axis) {
- ends = axis.calcEnds();
- if (axis.position == 'top' || axis.position == 'bottom') {
- minX = ends.from;
- maxX = ends.to;
+ chart.axes.each(function(axis) {
+ //only apply position calculations to axes that affect this series
+ //this means the axis in the position referred by this series and also
+ //the axis in the other coordinate for this series. For example: (left, top|bottom),
+ //or (top, left|right), etc.
+ if (axis.position == me.axis || axis.position != posHash[me.axis]) {
+ axisCount++;
+ if (axis.type != 'Numeric') {
+ numericAxis = false;
+ return;
}
- else {
- minY = ends.from;
- maxY = ends.to;
+ numericAxis = (numericAxis && axis.type == 'Numeric');
+ if (axis) {
+ ends = axis.calcEnds();
+ if (axis.position == 'top' || axis.position == 'bottom') {
+ minX = ends.from;
+ maxX = ends.to;
+ }
+ else {
+ minY = ends.from;
+ maxY = ends.to;
+ }
}
}
+ });
+
+ //If there's only one axis specified for a series, then we set the default type of the other
+ //axis to a category axis. So in this case numericAxis, which would be true if both axes affecting
+ //the series are numeric should be false.
+ if (numericAxis && axisCount == 1) {
+ numericAxis = false;
}
+
// If a field was specified without a corresponding axis, create one to get bounds
//only do this for the axis where real values are bound (that's why we check for
//me.axis)
- if (me.xField && !Ext.isNumber(minX)
- && (me.axis == 'bottom' || me.axis == 'top')) {
- axis = Ext.create('Ext.chart.axis.Axis', {
- chart: chart,
- fields: [].concat(me.xField)
- }).calcEnds();
- minX = axis.from;
- maxX = axis.to;
- }
- if (me.yField && !Ext.isNumber(minY)
- && (me.axis == 'right' || me.axis == 'left')) {
- axis = Ext.create('Ext.chart.axis.Axis', {
- chart: chart,
- fields: [].concat(me.yField)
- }).calcEnds();
- minY = axis.from;
- maxY = axis.to;
+ if (me.xField && !isNumber(minX)) {
+ if (me.axis == 'bottom' || me.axis == 'top') {
+ axis = Ext.create('Ext.chart.axis.Axis', {
+ chart: chart,
+ fields: [].concat(me.xField)
+ }).calcEnds();
+ minX = axis.from;
+ maxX = axis.to;
+ } else if (numericAxis) {
+ axis = Ext.create('Ext.chart.axis.Axis', {
+ chart: chart,
+ fields: [].concat(me.xField),
+ forceMinMax: true
+ }).calcEnds();
+ minX = axis.from;
+ maxX = axis.to;
+ }
}
-
+
+ if (me.yField && !isNumber(minY)) {
+ if (me.axis == 'right' || me.axis == 'left') {
+ axis = Ext.create('Ext.chart.axis.Axis', {
+ chart: chart,
+ fields: [].concat(me.yField)
+ }).calcEnds();
+ minY = axis.from;
+ maxY = axis.to;
+ } else if (numericAxis) {
+ axis = Ext.create('Ext.chart.axis.Axis', {
+ chart: chart,
+ fields: [].concat(me.yField),
+ forceMinMax: true
+ }).calcEnds();
+ minY = axis.from;
+ maxY = axis.to;
+ }
+ }
+
if (isNaN(minX)) {
minX = 0;
xScale = bbox.width / (store.getCount() - 1);
}
else {
- xScale = bbox.width / (maxX - minX);
+ //In case some person decides to set an axis' minimum and maximum
+ //configuration properties to the same value, then fallback the
+ //denominator to a > 0 value.
+ xScale = bbox.width / ((maxX - minX) || (store.getCount() - 1));
}
if (isNaN(minY)) {
yScale = bbox.height / (store.getCount() - 1);
}
else {
- yScale = bbox.height / (maxY - minY);
+ //In case some person decides to set an axis' minimum and maximum
+ //configuration properties to the same value, then fallback the
+ //denominator to a > 0 value.
+ yScale = bbox.height / ((maxY - minY) || (store.getCount() - 1));
}
-
+
store.each(function(record, i) {
xValue = record.get(me.xField);
yValue = record.get(me.yField);
// Ensure a value
if (typeof xValue == 'string' || typeof xValue == 'object'
//set as uniform distribution if the axis is a category axis.
- || (me.axis != 'top' && me.axis != 'bottom')) {
+ || (me.axis != 'top' && me.axis != 'bottom' && !numericAxis)) {
xValue = i;
}
if (typeof yValue == 'string' || typeof yValue == 'object'
//set as uniform distribution if the axis is a category axis.
- || (me.axis != 'left' && me.axis != 'right')) {
+ || (me.axis != 'left' && me.axis != 'right' && !numericAxis)) {
yValue = i;
}
+ storeIndices.push(i);
xValues.push(xValue);
yValues.push(yValue);
}, me);
me.items = [];
+ count = 0;
ln = xValues.length;
for (i = 0; i < ln; i++) {
xValue = xValues[i];
}
}
if (showMarkers) {
- marker = markerGroup.getAt(i);
+ marker = markerGroup.getAt(count++);
if (!marker) {
marker = Ext.chart.Shape[type](surface, Ext.apply({
group: [group, markerGroup],
};
}
}
+
me.items.push({
series: me,
value: [xValue, yValue],
point: [x, y],
sprite: marker,
- storeItem: store.getAt(i)
+ storeItem: store.getAt(storeIndices[i])
});
prevX = x;
prevY = y;
//nothing to be rendered
return;
}
-
- if (me.smooth) {
- path = Ext.draw.Draw.smooth(path, 6);
+
+ if (smooth) {
+ smoothPath = Ext.draw.Draw.smooth(path, isNumber(smooth) ? smooth : me.defaultSmoothness);
}
+ renderPath = smooth ? smoothPath : path;
+
//Correct path if we're animating timeAxis intervals
if (chart.markerIndex && me.previousPath) {
fromPath = me.previousPath;
- fromPath.splice(1, 2);
+ if (!smooth) {
+ Ext.Array.erase(fromPath, 1, 2);
+ }
} else {
fromPath = path;
}
-
+
// Only create a line if one doesn't exist.
if (!me.line) {
me.line = surface.add(Ext.apply({
}
}
if (me.fill) {
- fillPath = path.concat([
+ fillPath = renderPath.concat([
["L", x, bbox.y + bbox.height],
["L", bbox.x, bbox.y + bbox.height],
["L", bbox.x, firstY]
group: group,
type: 'path',
opacity: endLineStyle.opacity || 0.3,
- fill: colorArrayStyle[seriesIdx % colorArrayLength] || endLineStyle.fill,
+ fill: endLineStyle.fill || colorArrayStyle[seriesIdx % colorArrayLength],
path: dummyPath
});
}
fill = me.fill;
line = me.line;
//Add renderer to line. There is not unique record associated with this.
- rendererAttributes = me.renderer(line, false, { path: path }, i, store);
+ rendererAttributes = me.renderer(line, false, { path: renderPath }, i, store);
Ext.apply(rendererAttributes, endLineStyle || {}, {
stroke: endLineStyle.stroke || endLineStyle.fill
});
for(j = 0; j < lnsh; j++) {
if (chart.markerIndex && me.previousPath) {
me.onAnimate(shadows[j], {
- to: { path: path },
+ to: { path: renderPath },
from: { path: fromPath }
});
} else {
me.onAnimate(shadows[j], {
- to: { path: path }
+ to: { path: renderPath }
});
}
}
me.onAnimate(me.fillPath, {
to: Ext.apply({}, {
path: fillPath,
- fill: colorArrayStyle[seriesIdx % colorArrayLength] || endLineStyle.fill
+ fill: endLineStyle.fill || colorArrayStyle[seriesIdx % colorArrayLength]
}, endLineStyle || {})
});
}
//animate markers
if (showMarkers) {
+ count = 0;
for(i = 0; i < ln; i++) {
- item = markerGroup.getAt(i);
- if (item) {
- if (me.items[i]) {
+ if (me.items[i]) {
+ item = markerGroup.getAt(count++);
+ if (item) {
rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
me.onAnimate(item, {
to: Ext.apply(rendererAttributes, endMarkerStyle || {})
});
- } else {
- item.setAttributes(Ext.apply({
- hidden: true
- }, item._to), true);
}
- }
+ }
}
- for(; i < markerCount; i++) {
- item = markerGroup.getAt(i);
+ for(; count < markerCount; count++) {
+ item = markerGroup.getAt(count);
item.hide(true);
}
-// for(i = 0; i < (chart.markerIndex || 0)-1; i++) {
-// item = markerGroup.getAt(i);
-// item.hide(true);
-// }
}
} else {
- rendererAttributes = me.renderer(me.line, false, { path: path, hidden: false }, i, store);
+ rendererAttributes = me.renderer(me.line, false, { path: renderPath, hidden: false }, i, store);
Ext.apply(rendererAttributes, endLineStyle || {}, {
stroke: endLineStyle.stroke || endLineStyle.fill
});
shadows = me.line.shadows;
for(j = 0; j < lnsh; j++) {
shadows[j].setAttributes({
- path: path
+ path: renderPath
}, true);
}
}
}, true);
}
if (showMarkers) {
+ count = 0;
for(i = 0; i < ln; i++) {
- item = markerGroup.getAt(i);
- if (item) {
- if (me.items[i]) {
+ if (me.items[i]) {
+ item = markerGroup.getAt(count++);
+ if (item) {
rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true);
- } else {
- item.hide(true);
}
- }
+ }
}
- for(; i < markerCount; i++) {
- item = markerGroup.getAt(i);
+ for(; count < markerCount; count++) {
+ item = markerGroup.getAt(count);
item.hide(true);
}
}
}
if (chart.markerIndex) {
- path.splice(1, 0, path[1], path[2]);
+ if (me.smooth) {
+ Ext.Array.erase(path, 1, 2);
+ } else {
+ Ext.Array.splice(path, 1, 0, path[1], path[2]);
+ }
me.previousPath = path;
}
me.renderLabels();
showAll: function() {
this.toggleAll(true);
}
-});</pre></pre></body></html>
\ No newline at end of file
+});</pre>
+</body>
+</html>