jsroot
Version:
JavaScript ROOT
175 lines (140 loc) • 6.12 kB
JavaScript
import { clTPaletteAxis, isFunc, create, kNoZoom } from '../core.mjs';
import { getColorPalette } from '../base/colors.mjs';
import { TAttMarkerHandler } from '../base/TAttMarkerHandler.mjs';
import { TGraphPainter } from './TGraphPainter.mjs';
import { HistContour } from './THistPainter.mjs';
import { TH2Painter } from './TH2Painter.mjs';
class TScatterPainter extends TGraphPainter {
constructor(dom, obj) {
super(dom, obj);
this._is_scatter = true;
this._not_adjust_hrange = true;
}
/** @summary Return drawn graph object */
getGraph() { return this.getObject()?.fGraph; }
/** @summary Return margins for histogram ranges */
getHistRangeMargin() { return this.getObject()?.fMargin ?? 0.1; }
/** @summary Draw axis histogram
* @private */
async drawAxisHisto() {
const need_histo = !this.getHistogram(),
histo = this.createHistogram(need_histo, need_histo);
return TH2Painter.draw(this.getDrawDom(), histo, this.options.Axis + ';IGNORE_PALETTE');
}
/** @summary Provide palette, create if necessary
* @private */
getPalette() {
const gr = this.getGraph();
let pal = gr?.fFunctions?.arr?.find(func => (func._typename === clTPaletteAxis));
if (!pal && gr) {
pal = create(clTPaletteAxis);
const fp = this.get_main();
Object.assign(pal, { fX1NDC: fp.fX2NDC + 0.005, fX2NDC: fp.fX2NDC + 0.05, fY1NDC: fp.fY1NDC, fY2NDC: fp.fY2NDC, fInit: 1, $can_move: true });
Object.assign(pal.fAxis, { fChopt: '+', fLineColor: 1, fLineSyle: 1, fLineWidth: 1, fTextAngle: 0, fTextAlign: 11, fNdiv: 510 });
gr.fFunctions.AddFirst(pal, '');
}
return pal;
}
/** @summary Update TScatter members
* @private */
_updateMembers(scatter, obj) {
scatter.fBits = obj.fBits;
scatter.fTitle = obj.fTitle;
scatter.fNpoints = obj.fNpoints;
scatter.fColor = obj.fColor;
scatter.fSize = obj.fSize;
scatter.fMargin = obj.fMargin;
scatter.fMinMarkerSize = obj.fMinMarkerSize;
scatter.fMaxMarkerSize = obj.fMaxMarkerSize;
return super._updateMembers(scatter.fGraph, obj.fGraph);
}
/** @summary Return Z axis used for palette drawing
* @private */
getZaxis() {
return this.getHistogram()?.fZaxis;
}
/** @summary Checks if it makes sense to zoom inside specified axis range */
canZoomInside(axis, min, max) {
if (axis !== 'z')
return super.canZoomInside(axis, min, max);
const levels = this.fContour?.getLevels();
if (!levels)
return false;
// match at least full color level inside
for (let i = 0; i < levels.length - 1; ++i) {
if ((min <= levels[i]) && (max >= levels[i+1]))
return true;
}
return false;
}
/** @summary Actual drawing of TScatter */
async drawGraph() {
const fpainter = this.get_main(),
hpainter = this.getMainPainter(),
scatter = this.getObject(),
hist = this.getHistogram();
let scale = 1, offset = 0;
if (!fpainter || !hpainter || !scatter) return;
if (scatter.fColor) {
const pal = this.getPalette();
if (pal)
pal.$main_painter = this;
const pp = this.getPadPainter();
if (!this._color_palette && isFunc(pp?.getCustomPalette))
this._color_palette = pp.getCustomPalette();
if (!this._color_palette)
this._color_palette = getColorPalette(this.options.Palette, pp?.isGrayscale());
let minc = scatter.fColor[0], maxc = scatter.fColor[0];
for (let i = 1; i < scatter.fColor.length; ++i) {
minc = Math.min(minc, scatter.fColor[i]);
maxc = Math.max(maxc, scatter.fColor[i]);
}
if (maxc <= minc)
maxc = minc < 0 ? 0.9*minc : (minc > 0 ? 1.1*minc : 1);
else if ((minc > 0) && (minc < 0.3*maxc))
minc = 0;
this.fContour = new HistContour(minc, maxc);
this.fContour.createNormal(30);
this.fContour.configIndicies(0, 0);
fpainter.zmin = minc;
fpainter.zmax = maxc;
if (!fpainter.zoomChangedInteractive('z') && hist && hist.fMinimum !== kNoZoom && hist.fMaximum !== kNoZoom) {
fpainter.zoom_zmin = hist.fMinimum;
fpainter.zoom_zmax = hist.fMaximum;
}
}
if (scatter.fSize) {
let mins = scatter.fSize[0], maxs = scatter.fSize[0];
for (let i = 1; i < scatter.fSize.length; ++i) {
mins = Math.min(mins, scatter.fSize[i]);
maxs = Math.max(maxs, scatter.fSize[i]);
}
if (maxs <= mins)
maxs = mins < 0 ? 0.9*mins : (mins > 0 ? 1.1*mins : 1);
scale = (scatter.fMaxMarkerSize - scatter.fMinMarkerSize) / (maxs - mins);
offset = mins;
}
this.createG(!fpainter.pad_layer);
const funcs = fpainter.getGrFuncs(),
is_zoom = (fpainter.zoom_zmin !== fpainter.zoom_zmax) && scatter.fColor;
for (let i = 0; i < this.bins.length; ++i) {
if (is_zoom && ((scatter.fColor[i] < fpainter.zoom_zmin) || (scatter.fColor[i] > fpainter.zoom_zmax)))
continue;
const pnt = this.bins[i],
grx = funcs.grx(pnt.x),
gry = funcs.gry(pnt.y),
size = scatter.fSize ? scatter.fMinMarkerSize + scale * (scatter.fSize[i] - offset) : scatter.fMarkerSize,
color = scatter.fColor ? this.fContour.getPaletteColor(this._color_palette, scatter.fColor[i]) : this.getColor(scatter.fMarkerColor),
handle = new TAttMarkerHandler({ color, size, style: scatter.fMarkerStyle });
this.draw_g.append('svg:path')
.attr('d', handle.create(grx, gry))
.call(handle.func);
}
return this;
}
/** @summary Draw TScatter object */
static async draw(dom, obj, opt) {
return TGraphPainter._drawGraph(new TScatterPainter(dom, obj), opt);
}
} // class TScatterPainter
export { TScatterPainter };