/**
* @class Ext.draw.sprite.Instancing
* @extends Ext.draw.sprite.Sprite
*
* Sprite that represents multiple instances based on the given template.
*/
Ext.define('Ext.draw.sprite.Instancing', {
extend: 'Ext.draw.sprite.Sprite',
alias: 'sprite.instancing',
type: 'instancing',
isInstancing: true,
config: {
/**
* @cfg {Object} [template] The sprite template used by all instances.
*/
template: null,
/**
* @cfg {Array} [instances]
* The instances of the {@link #template} sprite as configs of attributes.
*/
instances: null
},
instances: null,
applyTemplate: function(template) {
var surface;
//<debug>
if (!Ext.isObject(template)) {
Ext.raise("A template of an instancing sprite must either be " +
"a sprite instance or a valid config object from which a template " +
"sprite will be created.");
}
else if (template.isInstancing || template.isComposite) {
Ext.raise("Can't use an instancing or composite sprite " +
"as a template for an instancing sprite.");
}
//</debug>
if (!template.isSprite) {
if (!template.xclass && !template.type) {
// For compatibility with legacy charts.
template.type = 'circle';
}
template = Ext.create(template.xclass || 'sprite.' + template.type, template);
}
surface = template.getSurface();
if (surface) {
surface.remove(template);
}
template.setParent(this);
return template;
},
updateTemplate: function(template, oldTemplate) {
if (oldTemplate) {
delete oldTemplate.ownAttr;
}
template.setSurface(this.getSurface());
// ownAttr is used to get a reference to the template's attributes
// when one of the instances is rendering, as at that moment the template's
// attributes (template.attr) are the instance's attributes.
template.ownAttr = template.attr;
this.clearAll();
this.setDirty(true);
},
updateInstances: function(instances) {
var i, ln;
this.clearAll();
if (Ext.isArray(instances)) {
for (i = 0, ln = instances.length; i < ln; i++) {
this.add(instances[i]);
}
}
},
updateSurface: function(surface) {
var template = this.getTemplate();
if (template && !template.destroyed) {
template.setSurface(surface);
}
},
get: function(index) {
return this.instances[index];
},
getCount: function() {
return this.instances.length;
},
clearAll: function() {
var template = this.getTemplate();
template.attr.children = this.instances = [];
this.position = 0;
},
/**
* @deprecated 6.2.0
* Deprecated, use the {@link #add} method instead.
*/
createInstance: function(config, bypassNormalization, avoidCopy) {
return this.add(config, bypassNormalization, avoidCopy);
},
/**
* Creates a new sprite instance.
*
* @param {Object} config The configuration of the instance.
* @param {Boolean} [bypassNormalization] 'true' to bypass attribute normalization.
* @param {Boolean} [avoidCopy] 'true' to avoid copying the `config` object.
* @return {Object} The attributes of the instance.
*/
add: function(config, bypassNormalization, avoidCopy) {
var me = this,
template = me.getTemplate(),
originalAttr = template.attr,
attr = Ext.Object.chain(originalAttr);
template.modifiers.target.prepareAttributes(attr);
template.attr = attr;
template.setAttributes(config, bypassNormalization, avoidCopy);
attr.template = template;
me.instances.push(attr);
template.attr = originalAttr;
me.position++;
return attr;
},
/**
* Not supported.
*
* @return {null}
*/
getBBox: function() {
return null;
},
/**
* Returns the bounding box for the instance at the given index.
*
* @param {Number} index The index of the instance.
* @param {Boolean} [isWithoutTransform] 'true' to not apply sprite transforms
* to the bounding box.
* @return {Object} The bounding box for the instance.
*/
getBBoxFor: function(index, isWithoutTransform) {
var template = this.getTemplate(),
originalAttr = template.attr,
bbox;
template.attr = this.instances[index];
bbox = template.getBBox(isWithoutTransform);
template.attr = originalAttr;
return bbox;
},
/**
* @private
* Checks if the instancing sprite can be seen.
* @return {Boolean}
*/
isVisible: function() {
var attr = this.attr,
parent = this.getParent(),
result;
result = parent && parent.isSurface && !attr.hidden && attr.globalAlpha;
return !!result;
},
/**
* @private
* Checks if the instance of an instancing sprite can be seen.
* @param {Number} index The index of the instance.
*/
isInstanceVisible: function(index) {
var me = this,
template = me.getTemplate(),
originalAttr = template.attr,
instances = me.instances,
result = false;
if (!Ext.isNumber(index) || index < 0 || index >= instances.length || !me.isVisible()) {
return result;
}
template.attr = instances[index];
// TODO This is clearly a bug, fix it
// eslint-disable-next-line no-undef
result = template.isVisible(point, options);
template.attr = originalAttr;
return result;
},
render: function(surface, ctx, rect) {
//<debug>
if (!this.getTemplate()) {
Ext.raise('An instancing sprite must have a template.');
}
//</debug>
// eslint-disable-next-line vars-on-top
var me = this,
template = me.getTemplate(),
surfaceRect = surface.getRect(),
mat = me.attr.matrix,
originalAttr = template.attr,
instances = me.instances,
ln = me.position,
i;
mat.toContext(ctx);
template.preRender(surface, ctx, rect);
template.useAttributes(ctx, surfaceRect);
template.isSpriteInstance = true;
for (i = 0; i < ln; i++) {
if (instances[i].hidden) {
continue;
}
ctx.save();
template.attr = instances[i];
template.useAttributes(ctx, surfaceRect);
template.render(surface, ctx, rect);
ctx.restore();
}
template.isSpriteInstance = false;
template.attr = originalAttr;
},
/**
* Sets the attributes for the instance at the given index.
*
* @param {Number} index the index of the instance
* @param {Object} changes the attributes to change
* @param {Boolean} [bypassNormalization] 'true' to avoid attribute normalization
*/
setAttributesFor: function(index, changes, bypassNormalization) {
var template = this.getTemplate(),
originalAttr = template.attr,
attr = this.instances[index];
if (!attr) {
return;
}
template.attr = attr;
if (bypassNormalization) {
changes = Ext.apply({}, changes);
}
else {
changes = template.self.def.normalize(changes);
}
template.modifiers.target.pushDown(attr, changes);
template.attr = originalAttr;
},
destroy: function() {
var me = this,
template = me.getTemplate();
me.instances = null;
if (template) {
template.destroy();
}
me.callParent();
}
});