Files
enviPy-bayer/static/js/ketcher2/script/render/index.js
2025-06-23 20:13:54 +02:00

211 lines
6.9 KiB
JavaScript

/****************************************************************************
* Copyright 2017 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
var Raphael = require('../raphael-ext');
var Box2Abs = require('../util/box2abs');
var Vec2 = require('../util/vec2');
var scale = require('../util/scale');
var Struct = require('../chem/struct');
var ReStruct = require('./restruct');
var defaultOptions = require('./options');
var DEBUG = { debug: false, logcnt: 0, logmouse: false, hl: false };
DEBUG.logMethod = function () { };
// DEBUG.logMethod = function (method) {addionalAtoms("METHOD: " + method);
function Render(clientArea, opt) {
this.userOpts = opt;
this.clientArea = clientArea;
this.paper = new Raphael(clientArea, 0, 0);
this.sz = Vec2.ZERO;
this.ctab = new ReStruct(new Struct(), this);
this.options = defaultOptions(this.userOpts);
}
Render.prototype.view2obj = function (p, isRelative) {
var scroll = this.scrollPos();
if (!this.useOldZoom) {
p = p.scaled(1 / this.options.zoom);
scroll = scroll.scaled(1 / this.options.zoom);
}
p = isRelative ? p : p.add(scroll).sub(this.options.offset);
return scale.scaled2obj(p, this.options);
};
Render.prototype.obj2view = function (v, isRelative) {
var p = scale.obj2scaled(v, this.options);
p = isRelative ? p : p.add(this.options.offset).sub(this.scrollPos().scaled(1 / this.options.zoom));
if (!this.useOldZoom)
p = p.scaled(this.options.zoom);
return p;
};
Render.prototype.scrollPos = function () {
return new Vec2(this.clientArea.scrollLeft, this.clientArea.scrollTop);
};
function cumulativeOffset(el) {
var curtop = 0;
var curleft = 0;
if (el.parentNode) {
do {
curtop += el.offsetTop || 0;
curleft += el.offsetLeft || 0;
el = el.offsetParent;
} while (el);
}
return { left: curleft, top: curtop };
}
Render.prototype.page2obj = function (pagePos) {
var offset = cumulativeOffset(this.clientArea);
var pp = new Vec2(pagePos.pageX - offset.left, pagePos.pageY - offset.top);
return this.view2obj(pp);
};
Render.prototype.setPaperSize = function (sz) {
DEBUG.logMethod('setPaperSize');
this.sz = sz;
this.paper.setSize(sz.x * this.options.zoom, sz.y * this.options.zoom);
this.setViewBox(this.options.zoom);
};
Render.prototype.setOffset = function (newoffset) {
DEBUG.logMethod('setOffset');
var delta = new Vec2(newoffset.x - this.options.offset.x, newoffset.y - this.options.offset.y);
this.clientArea.scrollLeft += delta.x;
this.clientArea.scrollTop += delta.y;
this.options.offset = newoffset;
};
Render.prototype.setZoom = function (zoom) {
// when scaling the canvas down it may happen that the scaled canvas is smaller than the view window
// don't forget to call setScrollOffset after zooming (or use extendCanvas directly)
console.info('set zoom', zoom);
this.options.zoom = zoom;
this.paper.setSize(this.sz.x * zoom, this.sz.y * zoom);
this.setViewBox(zoom);
};
function calcExtend(sSz, x0, y0, x1, y1) { // eslint-disable-line max-params
var ex = (x0 < 0) ? -x0 : 0;
var ey = (y0 < 0) ? -y0 : 0;
if (sSz.x < x1)
ex += x1 - sSz.x;
if (sSz.y < y1)
ey += y1 - sSz.y;
return new Vec2(ex, ey);
}
Render.prototype.setScrollOffset = function (x, y) {
var clientArea = this.clientArea;
var cx = clientArea.clientWidth;
var cy = clientArea.clientHeight;
var e = calcExtend(this.sz.scaled(this.options.zoom), x, y,
cx + x, cy + y).scaled(1 / this.options.zoom);
if (e.x > 0 || e.y > 0) {
this.setPaperSize(this.sz.add(e));
var d = new Vec2((x < 0) ? -x : 0,
(y < 0) ? -y : 0).scaled(1 / this.options.zoom);
if (d.x > 0 || d.y > 0) {
this.ctab.translate(d);
this.setOffset(this.options.offset.add(d));
}
}
clientArea.scrollLeft = x;
clientArea.scrollTop = y;
// TODO: store drag position in scaled systems
// scrollLeft = clientArea.scrollLeft;
// scrollTop = clientArea.scrollTop;
this.update(false);
};
Render.prototype.setScale = function (z) {
if (this.options.offset)
this.options.offset = this.options.offset.scaled(1 / z).scaled(z);
this.userOpts.scale *= z;
this.options = null;
this.update(true);
};
Render.prototype.setViewBox = function (z) {
if (!this.useOldZoom)
this.paper.canvas.setAttribute('viewBox', '0 0 ' + this.sz.x + ' ' + this.sz.y);
else
this.setScale(z);
};
Render.prototype.setMolecule = function (ctab) {
debugger;
DEBUG.logMethod('setMolecule');
this.paper.clear();
this.ctab = new ReStruct(ctab, this);
this.options.offset = new Vec2();
this.update(false);
};
Render.prototype.update = function (force, viewSz) { // eslint-disable-line max-statements
viewSz = viewSz || new Vec2(this.clientArea.clientWidth || 100,
this.clientArea.clientHeight || 100);
var changes = this.ctab.update(force);
this.ctab.setSelection(); // [MK] redraw the selection bits where necessary
if (changes) {
var sf = this.options.scale;
var bb = this.ctab.getVBoxObj().transform(scale.obj2scaled, this.options).translate(this.options.offset || new Vec2());
if (!this.options.autoScale) {
var ext = Vec2.UNIT.scaled(sf);
var eb = bb.sz().length() > 0 ? bb.extend(ext, ext) : bb;
var vb = new Box2Abs(this.scrollPos(), viewSz.scaled(1 / this.options.zoom).sub(Vec2.UNIT.scaled(20)));
var cb = Box2Abs.union(vb, eb);
if (!this.oldCb)
this.oldCb = new Box2Abs();
var sz = cb.sz().floor();
var delta = this.oldCb.p0.sub(cb.p0).ceil();
this.oldBb = bb;
if (!this.sz || sz.x != this.sz.x || sz.y != this.sz.y)
this.setPaperSize(sz);
this.options.offset = this.options.offset || new Vec2();
if (delta.x != 0 || delta.y != 0) {
this.setOffset(this.options.offset.add(delta));
this.ctab.translate(delta);
}
} else {
var sz1 = bb.sz();
var marg = this.options.autoScaleMargin;
var mv = new Vec2(marg, marg);
var csz = viewSz;
if (csz.x < (2 * marg) + 1 || csz.y < (2 * marg) + 1)
throw new Error('View box too small for the given margin');
var rescale = Math.max(sz1.x / (csz.x - (2 * marg)), sz1.y / (csz.y - (2 * marg)));
if (this.options.maxBondLength / rescale > 1.0)
rescale = 1.0;
var sz2 = sz1.add(mv.scaled(2 * rescale));
/* eslint-disable no-mixed-operators*/
this.paper.setViewBox(bb.pos().x - marg * rescale - (csz.x * rescale - sz2.x) / 2, bb.pos().y - marg * rescale - (csz.y * rescale - sz2.y) / 2, csz.x * rescale, csz.y * rescale);
/* eslint-enable no-mixed-operators*/
}
}
};
module.exports = Render;