PHP Classes

File: web/bundles/extjs/src/chart/series/Scatter.js

Recommend this page to a friend!
  Classes of william amed   Raptor 2   web/bundles/extjs/src/chart/series/Scatter.js   Download  
File: web/bundles/extjs/src/chart/series/Scatter.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Raptor 2
Framework that takes routes from annotations
Author: By
Last change:
Date: 8 years ago
Size: 23,716 bytes
 

Contents

Class file image Download
/* This file is part of Ext JS 4.2 Copyright (c) 2011-2013 Sencha Inc Contact: http://www.sencha.com/contact GNU General Public License Usage This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html. If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact. Build date: 2013-05-16 14:36:50 (f9be68accb407158ba2b1be2c226a6ce1f649314) */ /** * @class Ext.chart.series.Scatter * @extends Ext.chart.series.Cartesian * * Creates a Scatter Chart. The scatter plot is useful when trying to display more than two variables in the same visualization. * These variables can be mapped into x, y coordinates and also to an element's radius/size, color, etc. * As with all other series, the Scatter Series must be appended in the *series* Chart array configuration. See the Chart * documentation for more information on creating charts. A typical configuration object for the scatter could be: * * @example * var store = Ext.create('Ext.data.JsonStore', { * fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'], * data: [ * { 'name': 'metric one', 'data1': 10, 'data2': 12, 'data3': 14, 'data4': 8, 'data5': 13 }, * { 'name': 'metric two', 'data1': 7, 'data2': 8, 'data3': 16, 'data4': 10, 'data5': 3 }, * { 'name': 'metric three', 'data1': 5, 'data2': 2, 'data3': 14, 'data4': 12, 'data5': 7 }, * { 'name': 'metric four', 'data1': 2, 'data2': 14, 'data3': 6, 'data4': 1, 'data5': 23 }, * { 'name': 'metric five', 'data1': 27, 'data2': 38, 'data3': 36, 'data4': 13, 'data5': 33 } * ] * }); * * Ext.create('Ext.chart.Chart', { * renderTo: Ext.getBody(), * width: 500, * height: 300, * animate: true, * theme:'Category2', * store: store, * axes: [{ * type: 'Numeric', * position: 'left', * fields: ['data2', 'data3'], * title: 'Sample Values', * grid: true, * minimum: 0 * }, { * type: 'Category', * position: 'bottom', * fields: ['name'], * title: 'Sample Metrics' * }], * series: [{ * type: 'scatter', * markerConfig: { * radius: 5, * size: 5 * }, * axis: 'left', * xField: 'name', * yField: 'data2' * }, { * type: 'scatter', * markerConfig: { * radius: 5, * size: 5 * }, * axis: 'left', * xField: 'name', * yField: 'data3' * }] * }); * * In this configuration we add three different categories of scatter series. Each of them is bound to a different field of the same data store, * `data1`, `data2` and `data3` respectively. All x-fields for the series must be the same field, in this case `name`. * Each scatter series has a different styling configuration for markers, specified by the `markerConfig` object. Finally we set the left axis as * axis to show the current values of the elements. */ Ext.define('Ext.chart.series.Scatter', { /* Begin Definitions */ extend: 'Ext.chart.series.Cartesian', requires: ['Ext.chart.axis.Axis', 'Ext.chart.Shape', 'Ext.fx.Anim'], /* End Definitions */ type: 'scatter', alias: 'series.scatter', /** * @cfg {Object} markerConfig * The display style for the scatter series markers. */ /** * @cfg {Object} style * Append styling properties to this object for it to override theme properties. */ constructor: function(config) { this.callParent(arguments); var me = this, shadow = me.chart.shadow, surface = me.chart.surface, i, l; Ext.apply(me, config, { style: {}, markerConfig: {}, shadowAttributes: [{ "stroke-width": 6, "stroke-opacity": 0.05, stroke: 'rgb(0, 0, 0)' }, { "stroke-width": 4, "stroke-opacity": 0.1, stroke: 'rgb(0, 0, 0)' }, { "stroke-width": 2, "stroke-opacity": 0.15, stroke: 'rgb(0, 0, 0)' }] }); me.group = surface.getGroup(me.seriesId); if (shadow) { for (i = 0, l = me.shadowAttributes.length; i < l; i++) { me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i)); } } }, // @private Get chart and data boundaries getBounds: function() { var me = this, chart = me.chart, store = chart.getChartStore(), chartAxes = chart.axes, boundAxes = me.getAxesForXAndYFields(), boundXAxis = boundAxes.xAxis, boundYAxis = boundAxes.yAxis, bbox, xScale, yScale, ln, minX, minY, maxX, maxY, i, axis, ends; me.setBBox(); bbox = me.bbox; if (axis = chartAxes.get(boundXAxis)) { ends = axis.applyData(); minX = ends.from; maxX = ends.to; } if (axis = chartAxes.get(boundYAxis)) { ends = axis.applyData(); minY = ends.from; maxY = ends.to; } // If a field was specified without a corresponding axis, create one to get bounds if (me.xField && !Ext.isNumber(minX)) { axis = me.getMinMaxXValues(); minX = axis[0]; maxX = axis[1]; } if (me.yField && !Ext.isNumber(minY)) { axis = me.getMinMaxYValues(); minY = axis[0]; maxY = axis[1]; } if (isNaN(minX)) { minX = 0; maxX = store.getCount() - 1; xScale = bbox.width / (store.getCount() - 1); } else { xScale = bbox.width / (maxX - minX); } if (isNaN(minY)) { minY = 0; maxY = store.getCount() - 1; yScale = bbox.height / (store.getCount() - 1); } else { yScale = bbox.height / (maxY - minY); } return { bbox: bbox, minX: minX, minY: minY, xScale: xScale, yScale: yScale }; }, // @private Build an array of paths for the chart getPaths: function() { var me = this, chart = me.chart, enableShadows = chart.shadow, store = chart.getChartStore(), data = store.data.items, i, ln, record, group = me.group, bounds = me.bounds = me.getBounds(), bbox = me.bbox, xScale = bounds.xScale, yScale = bounds.yScale, minX = bounds.minX, minY = bounds.minY, boxX = bbox.x, boxY = bbox.y, boxHeight = bbox.height, items = me.items = [], attrs = [], x, y, xValue, yValue, sprite; for (i = 0, ln = data.length; i < ln; i++) { record = data[i]; xValue = record.get(me.xField); yValue = record.get(me.yField); //skip undefined or null values if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue) || xValue == null || yValue == null) { //<debug warn> if (Ext.isDefined(Ext.global.console)) { Ext.global.console.warn("[Ext.chart.series.Scatter] Skipping a store element with a value which is either undefined or null at ", record, xValue, yValue); } //</debug> continue; } // Ensure a value if (typeof xValue == 'string' || typeof xValue == 'object' && !Ext.isDate(xValue)) { xValue = i; } if (typeof yValue == 'string' || typeof yValue == 'object' && !Ext.isDate(yValue)) { yValue = i; } x = boxX + (xValue - minX) * xScale; y = boxY + boxHeight - (yValue - minY) * yScale; attrs.push({ x: x, y: y }); me.items.push({ series: me, value: [xValue, yValue], point: [x, y], storeItem: record }); // When resizing, reset before animating if (chart.animate && chart.resizing) { sprite = group.getAt(i); if (sprite) { me.resetPoint(sprite); if (enableShadows) { me.resetShadow(sprite); } } } } return attrs; }, // @private translate point to the center resetPoint: function(sprite) { var bbox = this.bbox; sprite.setAttributes({ translate: { x: (bbox.x + bbox.width) / 2, y: (bbox.y + bbox.height) / 2 } }, true); }, // @private translate shadows of a sprite to the center resetShadow: function(sprite) { var me = this, shadows = sprite.shadows, shadowAttributes = me.shadowAttributes, ln = me.shadowGroups.length, bbox = me.bbox, i, attr; for (i = 0; i < ln; i++) { attr = Ext.apply({}, shadowAttributes[i]); // TODO: fix this with setAttributes if (attr.translate) { attr.translate.x += (bbox.x + bbox.width) / 2; attr.translate.y += (bbox.y + bbox.height) / 2; } else { attr.translate = { x: (bbox.x + bbox.width) / 2, y: (bbox.y + bbox.height) / 2 }; } shadows[i].setAttributes(attr, true); } }, // @private create a new point createPoint: function(attr, type) { var me = this, chart = me.chart, group = me.group, bbox = me.bbox; return Ext.chart.Shape[type](chart.surface, Ext.apply({}, { x: 0, y: 0, group: group, translate: { x: (bbox.x + bbox.width) / 2, y: (bbox.y + bbox.height) / 2 } }, attr)); }, // @private create a new set of shadows for a sprite createShadow: function(sprite, endMarkerStyle, type) { var me = this, chart = me.chart, shadowGroups = me.shadowGroups, shadowAttributes = me.shadowAttributes, lnsh = shadowGroups.length, bbox = me.bbox, i, shadow, shadows, attr; sprite.shadows = shadows = []; for (i = 0; i < lnsh; i++) { attr = Ext.apply({}, shadowAttributes[i]); if (attr.translate) { attr.translate.x += (bbox.x + bbox.width) / 2; attr.translate.y += (bbox.y + bbox.height) / 2; } else { Ext.apply(attr, { translate: { x: (bbox.x + bbox.width) / 2, y: (bbox.y + bbox.height) / 2 } }); } Ext.apply(attr, endMarkerStyle); shadow = Ext.chart.Shape[type](chart.surface, Ext.apply({}, { x: 0, y: 0, group: shadowGroups[i] }, attr)); shadows.push(shadow); } }, /** * Draws the series for the current chart. */ drawSeries: function() { var me = this, chart = me.chart, store = chart.getChartStore(), group = me.group, enableShadows = chart.shadow, shadowGroups = me.shadowGroups, shadowAttributes = me.shadowAttributes, lnsh = shadowGroups.length, sprite, attrs, attr, ln, i, endMarkerStyle, shindex, type, shadows, rendererAttributes, shadowAttribute; endMarkerStyle = Ext.apply(me.markerStyle, me.markerConfig); type = endMarkerStyle.type || 'circle'; delete endMarkerStyle.type; //if the store is empty then there's nothing to be rendered if (!store || !store.getCount()) { me.hide(); me.items = []; return; } me.unHighlightItem(); me.cleanHighlights(); attrs = me.getPaths(); ln = attrs.length; for (i = 0; i < ln; i++) { attr = attrs[i]; sprite = group.getAt(i); Ext.apply(attr, endMarkerStyle); // Create a new sprite if needed (no height) if (!sprite) { sprite = me.createPoint(attr, type); if (enableShadows) { me.createShadow(sprite, endMarkerStyle, type); } } shadows = sprite.shadows; if (chart.animate) { rendererAttributes = me.renderer(sprite, store.getAt(i), { translate: attr }, i, store); sprite._to = rendererAttributes; me.onAnimate(sprite, { to: rendererAttributes }); //animate shadows for (shindex = 0; shindex < lnsh; shindex++) { shadowAttribute = Ext.apply({}, shadowAttributes[shindex]); rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({}, { hidden: false, translate: { x: attr.x + (shadowAttribute.translate? shadowAttribute.translate.x : 0), y: attr.y + (shadowAttribute.translate? shadowAttribute.translate.y : 0) } }, shadowAttribute), i, store); me.onAnimate(shadows[shindex], { to: rendererAttributes }); } } else { rendererAttributes = me.renderer(sprite, store.getAt(i), { translate: attr }, i, store); sprite._to = rendererAttributes; sprite.setAttributes(rendererAttributes, true); //animate shadows for (shindex = 0; shindex < lnsh; shindex++) { shadowAttribute = Ext.apply({}, shadowAttributes[shindex]); rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({}, { hidden: false, translate: { x: attr.x + (shadowAttribute.translate? shadowAttribute.translate.x : 0), y: attr.y + (shadowAttribute.translate? shadowAttribute.translate.y : 0) } }, shadowAttribute), i, store); shadows[shindex].setAttributes(rendererAttributes, true); } } me.items[i].sprite = sprite; } // Hide unused sprites ln = group.getCount(); for (i = attrs.length; i < ln; i++) { group.getAt(i).hide(true); } me.renderLabels(); me.renderCallouts(); }, // @private callback for when creating a label sprite. onCreateLabel: function(storeItem, item, i, display) { var me = this, group = me.labelsGroup, config = me.label, endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle), bbox = me.bbox; return me.chart.surface.add(Ext.apply({ type: 'text', 'text-anchor': 'middle', group: group, x: Number(item.point[0]), y: bbox.y + bbox.height / 2 }, endLabelStyle)); }, // @private callback for when placing a label sprite. onPlaceLabel: function(label, storeItem, item, i, display, animate, index) { var me = this, chart = me.chart, resizing = chart.resizing, config = me.label, format = config.renderer, field = config.field, bbox = me.bbox, x = Number(item.point[0]), y = Number(item.point[1]), radius = item.sprite.attr.radius, labelBox, markerBox, width, height, xOffset, yOffset, anim; label.setAttributes({ text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index), hidden: true }, true); //TODO(nicolas): find out why width/height values in circle bounding boxes are undefined. markerBox = item.sprite.getBBox(); markerBox.width = markerBox.width || (radius * 2); markerBox.height = markerBox.height || (radius * 2); labelBox = label.getBBox(); width = labelBox.width/2; height = labelBox.height/2; if (display == 'rotate') { //correct label position to fit into the box xOffset = markerBox.width/2 + width + height/2; if (x + xOffset + width > bbox.x + bbox.width) { x -= xOffset; } else { x += xOffset; } label.setAttributes({ 'rotation': { x: x, y: y, degrees: -45 } }, true); } else if (display == 'under' || display == 'over') { label.setAttributes({ 'rotation': { degrees: 0 } }, true); //correct label position to fit into the box if (x < bbox.x + width) { x = bbox.x + width; } else if (x + width > bbox.x + bbox.width) { x = bbox.x + bbox.width - width; } yOffset = markerBox.height/2 + height; y = y + (display == 'over' ? -yOffset : yOffset); if (y < bbox.y + height) { y += 2 * yOffset; } else if (y + height > bbox.y + bbox.height) { y -= 2 * yOffset; } } if (!chart.animate) { label.setAttributes({ x: x, y: y }, true); label.show(true); } else { if (resizing) { anim = item.sprite.getActiveAnimation(); if (anim) { anim.on('afteranimate', function() { label.setAttributes({ x: x, y: y }, true); label.show(true); }); } else { label.show(true); } } else { me.onAnimate(label, { to: { x: x, y: y } }); } } }, // @private callback for when placing a callout sprite. onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) { var me = this, chart = me.chart, surface = chart.surface, resizing = chart.resizing, config = me.callouts, items = me.items, cur = item.point, normal, bbox = callout.label.getBBox(), offsetFromViz = 30, offsetToSide = 10, offsetBox = 3, boxx, boxy, boxw, boxh, p, clipRect = me.bbox, x, y; //position normal = [Math.cos(Math.PI /4), -Math.sin(Math.PI /4)]; x = cur[0] + normal[0] * offsetFromViz; y = cur[1] + normal[1] * offsetFromViz; //box position and dimensions boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox)); boxy = y - bbox.height /2 - offsetBox; boxw = bbox.width + 2 * offsetBox; boxh = bbox.height + 2 * offsetBox; //now check if we're out of bounds and invert the normal vector correspondingly //this may add new overlaps between labels (but labels won't be out of bounds). if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) { normal[0] *= -1; } if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) { normal[1] *= -1; } //update positions x = cur[0] + normal[0] * offsetFromViz; y = cur[1] + normal[1] * offsetFromViz; //update box position and dimensions boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox)); boxy = y - bbox.height /2 - offsetBox; boxw = bbox.width + 2 * offsetBox; boxh = bbox.height + 2 * offsetBox; if (chart.animate) { //set the line from the middle of the pie to the box. me.onAnimate(callout.lines, { to: { path: ["M", cur[0], cur[1], "L", x, y, "Z"] } }, true); //set box position me.onAnimate(callout.box, { to: { x: boxx, y: boxy, width: boxw, height: boxh } }, true); //set text position me.onAnimate(callout.label, { to: { x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)), y: y } }, true); } else { //set the line from the middle of the pie to the box. callout.lines.setAttributes({ path: ["M", cur[0], cur[1], "L", x, y, "Z"] }, true); //set box position callout.box.setAttributes({ x: boxx, y: boxy, width: boxw, height: boxh }, true); //set text position callout.label.setAttributes({ x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)), y: y }, true); } for (p in callout) { callout[p].show(true); } }, // @private handles sprite animation for the series. onAnimate: function(sprite, attr) { sprite.show(); return this.callParent(arguments); }, isItemInPoint: function(x, y, item) { var point, tolerance = 10, abs = Math.abs; function dist(point) { var dx = abs(point[0] - x), dy = abs(point[1] - y); return Math.sqrt(dx * dx + dy * dy); } point = item.point; return (point[0] - tolerance <= x && point[0] + tolerance >= x && point[1] - tolerance <= y && point[1] + tolerance >= y); } });