UNPKG

jsroot

Version:
228 lines (188 loc) 7.44 kB
import { getColor } from '../base/colors.mjs'; import { ObjectPainter } from '../base/ObjectPainter.mjs'; import { pointer as d3_pointer } from '../d3.mjs'; import { urlClassPrefix } from '../core.mjs'; import { assignContextMenu } from '../gui/menu.mjs'; /** * @summary Painter for TWebPainting classes. * * @private */ class TWebPaintingPainter extends ObjectPainter { /** @summary Update TWebPainting object */ updateObject(obj) { if (!this.matchObjectType(obj)) return false; this.assignObject(obj); return true; } /** @summary Provides menu header */ getMenuHeader() { return this.getObject()?.fClassName || 'TWebPainting'; } /** @summary Fill context menu * @desc Create only header, items will be requested from server */ fillContextMenu(menu) { const cl = this.getMenuHeader(); menu.header(cl, `${urlClassPrefix}${cl}.html`); return true; } /** @summary Mouse click handler * @desc Redirect mouse click events to the ROOT application * @private */ handleMouseClick(evnt) { const pp = this.getPadPainter(), rect = pp?.getPadRect(); if (pp && rect && this.getSnapId()) { const pos = d3_pointer(evnt, this.getG().node()); pp.selectObjectPainter(this, { x: pos[0] + rect.x, y: pos[1] + rect.y }); } } /** @summary draw TWebPainting object */ async redraw() { const obj = this.getObject(), func = this.getAxisToSvgFunc(); if (!obj?.fOper || !func) return this; let indx = 0, attr = {}, lastpath = null, lastkind = 'none', d = '', oper, npoints, n; /* eslint-disable one-var */ const g = this.createG(), arr = obj.fOper.split(';'); const check_attributes = kind => { if (kind === lastkind) return; if (lastpath) { lastpath.attr('d', d); // flush previous d = ''; lastpath = null; lastkind = 'none'; } if (!kind) return; lastkind = kind; lastpath = g.append('svg:path').attr('d', ''); // placeholder for 'd' to have it always in front switch (kind) { case 'f': lastpath.call(this.fillatt.func); break; case 'l': lastpath.call(this.lineatt.func).style('fill', 'none'); break; case 'm': lastpath.call(this.markeratt.func); break; } }; const read_attr = (str, names) => { let lastp = 0; const obj2 = { _typename: 'any' }; for (let k = 0; k < names.length; ++k) { const p = str.indexOf(':', lastp + 1); obj2[names[k]] = parseInt(str.slice(lastp + 1, p > lastp ? p : undefined)); lastp = p; } return obj2; }; const process = k => { while (++k < arr.length) { oper = arr[k][0]; switch (oper) { case 'z': this.createAttLine({ attr: read_attr(arr[k], ['fLineColor', 'fLineStyle', 'fLineWidth']), force: true }); check_attributes(); continue; case 'y': this.createAttFill({ attr: read_attr(arr[k], ['fFillColor', 'fFillStyle']), force: true }); check_attributes(); continue; case 'x': this.createAttMarker({ attr: read_attr(arr[k], ['fMarkerColor', 'fMarkerStyle', 'fMarkerSize']), force: true }); check_attributes(); continue; case 'o': attr = read_attr(arr[k], ['fTextColor', 'fTextFont', 'fTextSize', 'fTextAlign', 'fTextAngle']); if (attr.fTextSize < 0) attr.fTextSize *= -0.001; check_attributes(); continue; case 'r': case 'b': { check_attributes((oper === 'b') ? 'f' : 'l'); const x1 = func.x(obj.fBuf[indx++]), y1 = func.y(obj.fBuf[indx++]), x2 = func.x(obj.fBuf[indx++]), y2 = func.y(obj.fBuf[indx++]); d += `M${x1},${y1}h${x2 - x1}v${y2 - y1}h${x1 - x2}z`; continue; } case 'l': case 'f': { check_attributes(oper); npoints = parseInt(arr[k].slice(1)); for (n = 0; n < npoints; ++n) d += `${(n > 0) ? 'L' : 'M'}${func.x(obj.fBuf[indx++])},${func.y(obj.fBuf[indx++])}`; if (oper === 'f') d += 'Z'; continue; } case 'm': { check_attributes(oper); npoints = parseInt(arr[k].slice(1)); this.markeratt.resetPos(); for (n = 0; n < npoints; ++n) d += this.markeratt.create(func.x(obj.fBuf[indx++]), func.y(obj.fBuf[indx++])); continue; } case 'h': case 't': { if (attr.fTextSize) { check_attributes(); const height = (attr.fTextSize > 1) ? attr.fTextSize : this.getPadPainter().getPadHeight() * attr.fTextSize, group = g.append('svg:g'); return this.startTextDrawingAsync(attr.fTextFont, height, group).then(() => { let text = arr[k].slice(1), angle = attr.fTextAngle; if (angle >= 360) angle -= Math.floor(angle / 360) * 360; if (oper === 'h') { let res = ''; for (n = 0; n < text.length; n += 2) res += String.fromCharCode(parseInt(text.slice(n, n + 2), 16)); text = res; } // todo - correct support of angle this.drawText({ align: attr.fTextAlign, x: func.x(obj.fBuf[indx++]), y: func.y(obj.fBuf[indx++]), rotate: -angle, text, color: getColor(attr.fTextColor), latex: 0, draw_g: group }); return this.finishTextDrawing(group); }).then(() => process(k)); } continue; } default: console.log(`unsupported operation ${oper}`); } } return Promise.resolve(true); }; return process(-1).then(() => { check_attributes(); assignContextMenu(this); if (!this.isBatchMode()) g.on('click', evnt => this.handleMouseClick(evnt)); return this; }); } static async draw(dom, obj) { const painter = new TWebPaintingPainter(dom, obj); painter.addToPadPrimitives(); return painter.redraw(); } } // class TWebPaintingPainter export { TWebPaintingPainter };