import { $ } from './jsmind.dom.js' import { logger } from './jsmind.common.js' class SvgGraph { constructor(view) { this.view = view this.opts = view.opts this.e_svg = SvgGraph.c('svg') this.e_svg.setAttribute('class', 'jsmind') this.size = { w: 0, h: 0 } this.lines = [] this.line_drawing = { straight: this._line_to, curved: this._bezier_to, } this.init_line_render() } static c(tag) { return $.d.createElementNS('http://www.w3.org/2000/svg', tag) } init_line_render() { if (typeof this.opts.custom_line_render === 'function') { this.drawing = (path, x1, y1, x2, y2) => { try { this.opts.custom_line_render.call(this, { ctx: path, start_point: { x: x1, y: y1 }, end_point: { x: x2, y: y2 }, }) } catch (e) { logger.error('custom line renderer error: ', e) } } } else { this.drawing = this.line_drawing[this.opts.line_style] || this.line_drawing.curved } } element() { return this.e_svg } set_size(w, h) { this.size.w = w this.size.h = h this.e_svg.setAttribute('width', w) this.e_svg.setAttribute('height', h) } clear() { var len = this.lines.length while (len--) { this.e_svg.removeChild(this.lines[len]) } this.lines.length = 0 } draw_line(pout, pin, offset, color) { var line = SvgGraph.c('path') line.setAttribute('stroke', color || this.opts.line_color) line.setAttribute('stroke-width', this.opts.line_width) line.setAttribute('fill', 'transparent') this.lines.push(line) this.e_svg.appendChild(line) this.drawing(line, pin.x + offset.x, pin.y + offset.y, pout.x + offset.x, pout.y + offset.y) } copy_to(dest_canvas_ctx, callback) { var img = new Image() img.onload = function () { dest_canvas_ctx.drawImage(img, 0, 0) !!callback && callback() } img.src = 'data:image/svg+xml;base64,' + btoa(new XMLSerializer().serializeToString(this.e_svg)) } _bezier_to(path, x1, y1, x2, y2) { path.setAttribute( 'd', 'M ' + x1 + ' ' + y1 + ' C ' + (x1 + ((x2 - x1) * 2) / 3) + ' ' + y1 + ', ' + x1 + ' ' + y2 + ', ' + x2 + ' ' + y2 ) } _line_to(path, x1, y1, x2, y2) { path.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2) } } class CanvasGraph { constructor(view) { this.opts = view.opts this.e_canvas = $.c('canvas') this.e_canvas.className = 'jsmind' this.canvas_ctx = this.e_canvas.getContext('2d') this.size = { w: 0, h: 0 } this.line_drawing = { straight: this._line_to, curved: this._bezier_to, } this.dpr = view.device_pixel_ratio this.init_line_render() } init_line_render() { if (typeof this.opts.custom_line_render === 'function') { this.drawing = (ctx, x1, y1, x2, y2) => { try { this.opts.custom_line_render.call(this, { ctx, start_point: { x: x1, y: y1 }, end_point: { x: x2, y: y2 }, }) } catch (e) { logger.error('custom line render error: ', e) } } } else { this.drawing = this.line_drawing[this.opts.line_style] || this.line_drawing.curved } } element() { return this.e_canvas } set_size(w, h) { this.size.w = w this.size.h = h if (this.e_canvas.width && this.e_canvas.height && this.canvas_ctx.scale) { this.e_canvas.width = w * this.dpr this.e_canvas.height = h * this.dpr this.e_canvas.style.width = w + 'px' this.e_canvas.style.height = h + 'px' this.canvas_ctx.scale(this.dpr, this.dpr) } else { this.e_canvas.width = w this.e_canvas.height = h } } clear() { this.canvas_ctx.clearRect(0, 0, this.size.w, this.size.h) } draw_line(pout, pin, offset, color) { var ctx = this.canvas_ctx ctx.strokeStyle = color || this.opts.line_color ctx.lineWidth = this.opts.line_width ctx.lineCap = 'round' this.drawing(ctx, pin.x + offset.x, pin.y + offset.y, pout.x + offset.x, pout.y + offset.y) } copy_to(dest_canvas_ctx, callback) { dest_canvas_ctx.drawImage(this.e_canvas, 0, 0, this.size.w, this.size.h) !!callback && callback() } _bezier_to(ctx, x1, y1, x2, y2) { ctx.beginPath() ctx.moveTo(x1, y1) ctx.bezierCurveTo(x1 + ((x2 - x1) * 2) / 3, y1, x1, y2, x2, y2) ctx.stroke() } _line_to(ctx, x1, y1, x2, y2) { ctx.beginPath() ctx.moveTo(x1, y1) ctx.lineTo(x2, y2) ctx.stroke() } } export function init_graph(view, engine) { return engine.toLowerCase() === 'svg' ? new SvgGraph(view) : new CanvasGraph(view) }