class Mark {
constructor(data = [], encodingOptions = {}) {
this.defaultWidth = 640;
this.defaultHeight = 400;
this.defaultMargin = 10;
this.minHeight = 50;
this.encodingOptions = encodingOptions;
this.data = data;
this.update();
}
update(width, height, margin, scaleOptions, context) {
this.scaleOptions = scaleOptions || {};
this.margin = margin || this.margin || this.defaultMargin;
this.width = width || this.width || this.defaultWidth;
this.height = height || this.height || this.getDefaultHeight();
this.context = context;
this.createScales();
syncScales([this]);
this.composeScales();
}
createScales() {
this.nameScale = {};
for (const [name, channel] of Object.entries(this.channels())) {
const attribute = this.encodingOptions[name];
const encoding = this.createEncoding(attribute);
const scale = this.createScale(encoding, channel, name);
this.nameScale[name] = {
...scale,
encoding,
channelType: channel.options.type
};
}
}
composeScales() {
for (const name of Object.keys(this.nameScale)) {
const { type, options, encoding } = this.nameScale[name];
const Scale = getScaleByType(type);
const scale = Scale(options);
const transform = encoding ? v => scale(encoding(v)) : v => scale(v);
Object.assign(transform, scale);
this[`$${name}`] = transform;
}
}
createEncoding(attribute) {
if (attribute === undefined || attribute === null) return undefined;
if (typeof attribute === 'function') return attribute;
const value = this.data[0] && this.data[0][attribute];
if (value === undefined) {
const encoding = () => attribute;
encoding.identity = true;
return encoding;
}
return d => d[attribute];
}
createScale(encoding, channel, name) {
if (!encoding)
return { type: 'constant', options: { range: [channel.getValue(this)] } };
if (encoding.identity) return { type: 'identity' };
const options = this.scaleOptions[name] || {};
const range = this.chooseRange(encoding, options, channel);
const domain = this.chooseDomain(encoding, { ...options, range });
const [type, defaultOptions = {}] = this.chooseType(
encoding,
{ ...options, range, domain },
channel
);
const newOptions = { ...defaultOptions, ...options, range, domain };
return { type, options: newOptions };
}
chooseType(encoding, options, channel) {
if (options.type) return [options.type];
const { type, range, domain } = options;
return channel.getScaleByTypes(domain[0], range[0]);
}
chooseRange(encoding, options, channel) {
if (options.range) return options.range;
return channel.getRange(this);
}
chooseDomain(encoding, options) {
if (options.domain) return options.domain;
const { domain, range, type } = options;
return getDomain(this.data, encoding, type);
}
getSize() {
return [this.width + this.margin * 2, this.height + this.margin * 2];
}
createCanvas() {
return DOM.context2d(...this.getSize());
}
plot() {
if (!this.context) this.context = this.createCanvas();
for (const [index, d] of this.data.entries()) {
this.drawEach(this.context, d, index, this.data);
}
return this.context.canvas;
}
getDefaultHeight() {
return this.defaultHeight;
}
channels() {
return {
x: new X(),
y: new Y(),
fill: new Color({ value: undefined }),
stroke: new Color({ value: '#00000' }),
lineWidth: new Channel({ value: 1.5, range: [1.5, 1.5] })
};
}
drawEach() {}
}