MPBond.prototype.validate = function()

in gsoc2022/seagrid-rich-client/molview/src/js/molpad/MPBond_calc.js [19:280]


MPBond.prototype.validate = function () {
	if (this.valid) return;
	this.valid = true;

	if (this.mp.mol.atoms[this.from].center.distanceTo(
		this.mp.mol.atoms[this.to].center) <=
		((this.mp.mol.atoms[this.from].isVisible() ? 1 : 0) +
			(this.mp.mol.atoms[this.to].isVisible() ? 1 : 0)) *
		this.mp.s.atom.radius) {
		this.hidden = true;
	}
	else {
		this.hidden = false;

		var scale = this.mp.s.bond.scale;

		this.cache = {};
		this.line = {
			from: this.mp.mol.atoms[this.from].calculateBondVertices(this.to, [0])[0],
			to: this.mp.mol.atoms[this.to].calculateBondVertices(this.from, [0])[0]
		};
		this.line.center = new MPPFO({
			x: (this.line.from.x + this.line.to.x) / 2,
			y: (this.line.from.y + this.line.to.y) / 2
		});

		if (this.mp.s.bond.colored) {
			var f = this.mp.mol.atoms[this.from];
			var t = this.mp.mol.atoms[this.to];

			if (this.stereo === MP_STEREO_UP) {
				this.cache.bondColor = JmolAtomColorsHashHex["C"];
			}
			else if (f.element === t.element) {
				this.cache.bondColor = JmolAtomColorsHashHex[f.element] || JmolAtomColorsHashHex["C"];
			}
			else {
				this.cache.bondColor = this.mp.ctx.createLinearGradient(f.getX(), f.getY(), t.getX(), t.getY());
				this.cache.bondColor.addColorStop(this.mp.s.bond.gradient.from, JmolAtomColorsHashHex[f.element] || JmolAtomColorsHashHex["C"]);
				this.cache.bondColor.addColorStop(this.mp.s.bond.gradient.to, JmolAtomColorsHashHex[t.element] || JmolAtomColorsHashHex["C"]);
			}
		}
		else//fallback, this color is actually not used
		{
			this.cache.bondColor = this.mp.s.bond.color;
		}

		if (this.mp.getScale() >= this.mp.s.bond.singleOnlyScale) {
			if (this.stereo === MP_STEREO_CIS_TRANS && this.type === MP_BOND_DOUBLE) {
				var ends = transformArrayMult(this.mp.s.bond.delta[MP_BOND_CIS],
					-this.mp.s.bond.deltaScale);//flip ends because of flipped y-axis
				this.cache.ctd = {
					from: this.mp.mol.atoms[this.from].calculateBondVertices(this.to, ends),
					to: this.mp.mol.atoms[this.to].calculateBondVertices(this.from, ends)
				};
			}
			else if (this.stereo === MP_STEREO_UP)//wedge bond
			{
				this.cache.wedge = {
					far: this.mp.mol.atoms[this.from].calculateBondVertices(this.to, [0]),
					near: this.mp.mol.atoms[this.to].calculateBondVertices(this.from,
						transformArrayMult(this.mp.s.bond.delta[MP_BOND_WEDGEHASH],
							-this.mp.s.bond.deltaScale))//flip ends because of flipped y-axis
				};

				if (!this.mp.mol.atoms[this.to].isVisible()) {
					var bonds = this.mp.mol.atoms[this.to].calculateClosestBonds(this.index);

					if (!bonds.none) {
						if (this.mp.mol.bonds[bonds.lower].type == MP_BOND_SINGLE) {
							var i1 = this.mp.mol.bonds[bonds.lower].getLine().intersection(
								new MPLine({
									from: this.cache.wedge.far[0],
									to: this.cache.wedge.near[0]
								}));
							if (i1.p !== undefined && this.line.to.distanceTo(i1.p) < this.mp.s.bond.wedgeFitMaxD) {
								this.cache.wedge.near[0] = i1.p || this.cache.wedge.near[0];
							}
						}
						if (this.mp.mol.bonds[bonds.upper].type == MP_BOND_SINGLE) {
							var i2 = this.mp.mol.bonds[bonds.upper].getLine().intersection(
								new MPLine({
									from: this.cache.wedge.far[0],
									to: this.cache.wedge.near[1]
								}));
							if (i2.p !== undefined && this.line.to.distanceTo(i2.p) < this.mp.s.bond.wedgeFitMaxD) {
								this.cache.wedge.near[1] = i2.p || this.cache.wedge.near[1];
							}
						}
					}
				}
			}
			else if (this.stereo === MP_STEREO_DOWN)//hash bond
			{
				var far = this.mp.mol.atoms[this.from].calculateBondVertices(this.to, [0]);
				var near = this.mp.mol.atoms[this.to].calculateBondVertices(this.from,
					transformArrayMult(this.mp.s.bond.delta[MP_BOND_WEDGEHASH],
						-this.mp.s.bond.deltaScale));//flip ends because of flipped y-axis

				var dx1 = near[0].x - far[0].x;
				var dy1 = near[0].y - far[0].y;
				var dx2 = near[1].x - far[0].x;
				var dy2 = near[1].y - far[0].y;
				var d1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
				var w = this.mp.s.bond.width * scale;
				var s = this.mp.s.bond.hashLineSpace * scale;

				this.cache.hashLines = [];
				while (d1 - s - w > 0) {
					var mult = (d1 - s - w) / d1;
					d1 *= mult;
					dx1 *= mult; dy1 *= mult;
					dx2 *= mult; dy2 *= mult;

					this.cache.hashLines.push({
						from: { x: far[0].x + dx1, y: far[0].y + dy1 },
						to: { x: far[0].x + dx2, y: far[0].y + dy2 }
					});
				}
			}
			else if (this.type === MP_BOND_DOUBLE || this.type === MP_BOND_TRIPLE) {
				var ends = [];
				var doubleSide = 1;
				var fromBonds = this.mp.mol.atoms[this.from].calculateClosestBonds(this.index);
				var toBonds = this.mp.mol.atoms[this.to].calculateClosestBonds(this.index);
				var refineUpperSkeletal = false, refineLowerSkeletal = false;


				//check if this bond will be displayed using all skeletal display rules
				var skeletal = (this.mp.s.skeletalDisplay &&//skeleton display is enabled
					(!this.mp.mol.atoms[this.from].isVisible() || !this.mp.mol.atoms[this.to].isVisible()) &&//and at least one atom is visible
					//and the bond is not a bond which connects:
					!(this.mp.mol.atoms[this.from].bonds.length === 1 &&//an atom with no other bonds
						((this.mp.mol.atoms[this.from].isVisible() &&//which is visible
							this.mp.mol.atoms[this.to].bonds.length > 2) ||//to any atom that is connected to 2+ other atoms
							this.mp.mol.atoms[this.to].bonds.length === 1)) &&//or to another atom with no other bonds
					!(this.mp.mol.atoms[this.to].bonds.length === 1 &&
						((this.mp.mol.atoms[this.to].isVisible() &&
							this.mp.mol.atoms[this.from].bonds.length > 2) ||//to any atom that is connected to 2+ other atoms
							this.mp.mol.atoms[this.from].bonds.length === 1)));

				if (skeletal) {
					/*
					Calculate best doubleSide
					=========================
					1. The sum of the deviation of the bisect angle relative to
					the bestBisect angle of the upper and the lower side are
					calculated. The side with the least bestBisect deviation
					is used for the double bond
					2. The approximated double bond length is calcualted
					using a horizontal bond with length this.line distance
					and bond delta = 8. Using this length, the bond refinement
					is skipped or the bond is force flipped to a different side
					*/
					var length = this.line.from.distanceTo(this.line.to);
					var upperLength = length, upperBisectD = 0;

					if (fromBonds.upperSectionAngle < Math.PI) {
						if (!this.mp.mol.atoms[this.from].isVisible())
							upperLength -= 8 / Math.tan(fromBonds.upperSectionAngle / 2);
						upperBisectD += Math.abs(this.mp.s.bond.bestBisect - fromBonds.upperSectionAngle / 2);
					}
					else upperBisectD += Math.abs(this.mp.s.bond.bestBisect - Math.PI / 2);

					if (toBonds.lowerSectionAngle < Math.PI) {
						if (!this.mp.mol.atoms[this.to].isVisible())
							upperLength -= 8 / Math.tan(toBonds.lowerSectionAngle / 2);
						upperBisectD += Math.abs(this.mp.s.bond.bestBisect - toBonds.lowerSectionAngle / 2);
					}
					else upperBisectD += Math.abs(this.mp.s.bond.bestBisect - Math.PI / 2);

					var lowerLength = length, lowerBisectD = 0;
					if (fromBonds.lowerSectionAngle < Math.PI) {
						if (!this.mp.mol.atoms[this.from].isVisible())
							lowerLength -= 8 / Math.tan(fromBonds.lowerSectionAngle / 2);
						lowerBisectD += Math.abs(this.mp.s.bond.bestBisect - fromBonds.lowerSectionAngle / 2);
					}
					else lowerBisectD += Math.abs(this.mp.s.bond.bestBisect - Math.PI / 2);

					if (toBonds.upperSectionAngle < Math.PI) {
						if (!this.mp.mol.atoms[this.to].isVisible())
							lowerLength -= 8 / Math.tan(toBonds.upperSectionAngle / 2);
						lowerBisectD += Math.abs(this.mp.s.bond.bestBisect - toBonds.upperSectionAngle / 2);
					}
					else lowerBisectD += Math.abs(this.mp.s.bond.bestBisect - Math.PI / 2);

					//check if the opposite sections are almost the same (like in a chain)
					//if so, a fallback rule is applied (in order to prevent from
					//inconsistent double bond sides in carbon chains)
					if (Math.abs(fromBonds.upperSectionAngle - toBonds.upperSectionAngle) +
						Math.abs(fromBonds.lowerSectionAngle - toBonds.lowerSectionAngle) < this.mp.s.bond.angleDev) {
						//fallback rule: double bond to the visual upper side
						var a = this.mp.mol.atoms[this.from].center.angleTo(this.mp.mol.atoms[this.to].center);
						doubleSide = a > -Math.PI / 2 + this.mp.s.bond.angleDev
							&& a <= Math.PI / 2 + this.mp.s.bond.angleDev ? 1 : -1;
					}
					else if (lowerBisectD < upperBisectD ||//the lower side has a smaller bestBisect deviation
						//or the lower side can apply bond refinement while the upper side cannot
						(upperLength < this.mp.s.atom.radius && lowerLength > this.mp.s.atom.radius)) {
						doubleSide = -1;
					}

					refineUpperSkeletal = (this.type === MP_BOND_TRIPLE || doubleSide === 1)
						&& upperLength > this.mp.s.atom.radius;
					refineLowerSkeletal = (this.type === MP_BOND_TRIPLE || doubleSide === -1)
						&& lowerLength > this.mp.s.atom.radius;
				}

				if (this.type === MP_BOND_DOUBLE) {
					ends = this.mp.s.bond.delta[MP_BOND_DOUBLE];
					if (skeletal) ends = transformArrayAdd(ends, -doubleSide * ends[0]);
				}
				else if (this.type === MP_BOND_TRIPLE) {
					ends = this.mp.s.bond.delta[MP_BOND_TRIPLE];
				}

				ends = transformArrayMult(ends, -this.mp.s.bond.deltaScale);//flip ends because of flipped y-axis
				var toEnds = transformArrayMult(ends, -1);//reversed upper/lower side relate to from
				this.cache.bond = {
					from: this.mp.mol.atoms[this.from].calculateBondVertices(this.to, ends),
					to: this.mp.mol.atoms[this.to].calculateBondVertices(this.from, toEnds)
				};

				if (!this.mp.mol.atoms[this.from].isVisible()) {
					if (!fromBonds.none) {
						if ((!skeletal || refineLowerSkeletal)
							&& fromBonds.lowerSectionAngle < Math.PI) {
							this.cache.bond.from[0] = this.refineBondVertex(
								skeletal, fromBonds.lowerBisectAngle,
								this.line.from, this.cache.bond.from[0], this.cache.bond.to[0],
								this.mp.mol.bonds[fromBonds.lower].getLine());
						}
						if ((!skeletal || refineUpperSkeletal)
							&& fromBonds.upperSectionAngle < Math.PI) {
							this.cache.bond.from[1] = this.refineBondVertex(
								skeletal, fromBonds.upperBisectAngle,
								this.line.from, this.cache.bond.from[1], this.cache.bond.to[1],
								this.mp.mol.bonds[fromBonds.upper].getLine());
						}
					}
				}

				if (!this.mp.mol.atoms[this.to].isVisible()) {
					if (!toBonds.none) {
						if ((!skeletal || refineLowerSkeletal)
							&& toBonds.upperSectionAngle < Math.PI) {
							this.cache.bond.to[0] = this.refineBondVertex(skeletal, toBonds.upperBisectAngle,
								this.line.to, this.cache.bond.to[0], this.cache.bond.from[0],
								this.mp.mol.bonds[toBonds.upper].getLine());
						}
						if ((!skeletal || refineUpperSkeletal)
							&& toBonds.lowerSectionAngle < Math.PI) {
							this.cache.bond.to[1] = this.refineBondVertex(skeletal, toBonds.lowerBisectAngle,
								this.line.to, this.cache.bond.to[1], this.cache.bond.from[1],
								this.mp.mol.bonds[toBonds.lower].getLine());
						}
					}
				}
			}
		}
	}
}