/** * This is a modifier to place labels and callouts by additional attributes. */Ext.define('Ext.chart.modifier.Callout', { extend: 'Ext.draw.modifier.Modifier', alternateClassName: 'Ext.chart.label.Callout', prepareAttributes: function(attr) { if (!attr.hasOwnProperty('calloutOriginal')) { attr.calloutOriginal = Ext.Object.chain(attr); // No __proto__, nor getPrototypeOf in IE8, // so manually saving a reference to 'attr' after chaining. attr.calloutOriginal.prototype = attr; } if (this._lower) { this._lower.prepareAttributes(attr.calloutOriginal); } }, setAttrs: function(attr, changes) { var callout = attr.callout, origin = attr.calloutOriginal, bbox = attr.bbox.plain, width = (bbox.width || 0) + attr.labelOverflowPadding, height = (bbox.height || 0) + attr.labelOverflowPadding, dx, dy, rotationRads, x, y, calloutPlaceX, calloutPlaceY, calloutVertical, temp; if ('callout' in changes) { callout = changes.callout; } if ('callout' in changes || 'calloutPlaceX' in changes || 'calloutPlaceY' in changes || 'x' in changes || 'y' in changes) { rotationRads = 'rotationRads' in changes ? origin.rotationRads = changes.rotationRads : origin.rotationRads; x = 'x' in changes ? (origin.x = changes.x) : origin.x; y = 'y' in changes ? (origin.y = changes.y) : origin.y; calloutPlaceX = 'calloutPlaceX' in changes ? changes.calloutPlaceX : attr.calloutPlaceX; calloutPlaceY = 'calloutPlaceY' in changes ? changes.calloutPlaceY : attr.calloutPlaceY; calloutVertical = 'calloutVertical' in changes ? changes.calloutVertical : attr.calloutVertical; // Normalize Rotations rotationRads %= Math.PI * 2; if (Math.cos(rotationRads) < 0) { rotationRads = (rotationRads + Math.PI) % (Math.PI * 2); } if (rotationRads > Math.PI) { rotationRads -= Math.PI * 2; } if (calloutVertical) { rotationRads = rotationRads * (1 - callout) - Math.PI / 2 * callout; temp = width; width = height; height = temp; } else { rotationRads = rotationRads * (1 - callout); } changes.rotationRads = rotationRads; // Placing a label in the middle of a pie slice (x/y) // if callout doesn't exists (callout=0), // or outside the pie slice (calloutPlaceX/Y) if it does (callout=1). changes.x = x * (1 - callout) + calloutPlaceX * callout; changes.y = y * (1 - callout) + calloutPlaceY * callout; dx = calloutPlaceX - x; dy = calloutPlaceY - y; // Finding where the callout line intersects the bbox of the label // if it were to go to the center of the label, // and make that intersection point the end of the callout line. // Effectively, the end of the callout line traces label's bbox when chart is rotated. if (Math.abs(dy * width) > Math.abs(dx * height)) { // on top/bottom if (dy > 0) { changes.calloutEndX = changes.x - (height / 2) * (dx / dy) * callout; changes.calloutEndY = changes.y - (height / 2) * callout; } else { changes.calloutEndX = changes.x + (height / 2) * (dx / dy) * callout; changes.calloutEndY = changes.y + (height / 2) * callout; } } else { // on left/right if (dx > 0) { changes.calloutEndX = changes.x - width / 2; changes.calloutEndY = changes.y - (width / 2) * (dy / dx) * callout; } else { changes.calloutEndX = changes.x + width / 2; changes.calloutEndY = changes.y + (width / 2) * (dy / dx) * callout; } } // Since the length of the callout line is adjusted depending on the label's position // and dimensions, we hide the callout line if the length becomes negative. if (changes.calloutStartX && changes.calloutStartY) { changes.calloutHasLine = (dx > 0 && changes.calloutStartX < changes.calloutEndX) || (dx <= 0 && changes.calloutStartX > changes.calloutEndX) || (dy > 0 && changes.calloutStartY < changes.calloutEndY) || (dy <= 0 && changes.calloutStartY > changes.calloutEndY); } else { changes.calloutHasLine = true; } } return changes; }, pushDown: function(attr, changes) { changes = this.callParent([attr.calloutOriginal, changes]); return this.setAttrs(attr, changes); }, popUp: function(attr, changes) { attr = attr.prototype; changes = this.setAttrs(attr, changes); if (this._upper) { return this._upper.popUp(attr, changes); } else { return Ext.apply(attr, changes); } }});