kekule
Version:
Open source JavaScript toolkit for chemoinformatics
433 lines (420 loc) • 14.6 kB
JavaScript
/**
* @fileoverview
* Contains utility class and methods to compare node or connectors in chem structure.
* @author Partridge Jiang
* @deprecated
*/
/*
* requires /core/kekule.common.js
* requires /core/kekule.utils.js
* requires /data/kekule.structures.js
* requires /core/kekule.chemUtils.js
* requires /algorithm/kekule.graph.js
*/
(function(){
"use strict";
var K = Kekule;
var AU = Kekule.ArrayUtils;
var BT = Kekule.BondType;
/**
* A comparer to decide which chem structure object is "bigger" or "superior" than another one.
* In the comparer, each structure object is turned to a int value with the fixed format.
*
* For node, the value will be 0xTNAAABBBLLCPHE (14 digitals)
* [Atom]:
* T: 0-F, object major type, always be 1 to a node.
* N: object class, 1: Atom, 2: Pseudoatom, 3: VariableAtom, E: unspecified atom (atom not the in the previous three types), 0: other node.
* AAA: atom major property. Atomic number for usual atom, FFE for pseudoatom, FFF for variable atom, 000 for other types of node.
* BBB: atom mass number, if mass number is not specified, 000 will be used. For other node, this value is always 000.
* LL: Linked conector count. Connector to hydrogen will not be considered.
* C: charge. 7 for a neutral node, 8 for +1, 9 for +2, 6 for -1, 5 for -2 and so on.
* P: parity. Stereo parity, 0 for no stereo, 1 for odd and 2 for even.
* H: Hydrogen count.
* [Fragment]:
* T: 0-F, object major type, always be 1 to a node.
* N: object class, 1: subgroup, 5: molecule, 0: other fragment.
* AAA: atom count in fragment. 000 for unknown. Hydrogen atoms are ignored.
* BBB: bond count in fragment, 000 for unknown. Bond to hydrogens are ignored.
* LL: Linked conector count.
* C: charge. 7 for a neutral node, 8 for +1, 9 for +2, 6 for -1, 5 for -2 and so on.
* P: parity. Stereo parity, 0 for no stereo, 1 for odd and 2 for even.
* H: Hydrogen count, usually 0.
* E: lone pair count, usually 0.
* For connector the value will be 0xTCBPNNAA (8 digitals)
* T: 0-F, object major type, always be 0 to a connector.
* C: connector type. 1 for bond and 0 for other types of connector.
* B: bond type. 1: covalent, 2: ionic, 3: coordinate, 4: metallic, 9: hydrogen, 0: other
* P: parity.
* NN: bond electron count. 02: single, 04: double, 06: triple, 03: aromatic, 00: other
* AA: connected object count.
*
* @deprecated
*/
Kekule.UnivChemStructObjComparer = {
/** @private */
NODE_CLASS_MAP: (new K.MapEx()).set(K.Atom, 0x1).set(K.Pseudoatom, 0x2).set(K.VariableAtom, 0x3).set(K.AbstractAtom, 0xE).set(K.ChemStructureNode, 0),
/** @private */
CHARGE_BASE: 0x7,
/** @private */
BOND_TYPE_MAP: (new K.MapEx(true)).set(BT.COVALENT, 1).set(BT.IONIC, 2).set(BT.COORDINATE, 3).set(BT.METALLIC, 4).set(BT.HYDROGEN, 9).set(BT.TRANSITION, 20),
/** @private */
_P32: Math.pow(2, 32),
/** @private */
_P36: Math.pow(2, 36),
/** @private */
_P44: Math.pow(2, 44),
/** @private */
_P48: Math.pow(2, 48),
/** @private */
_P20: Math.pow(2, 20),
/** @private */
_P24: Math.pow(2, 24),
/**
* Analysis input options and forms a detail option object for other comparation methods.
* @param {Hash} options May include the following fields:
* {
* level: comparation level, value from {@link Kekule.StructureComparationLevel}.
* (for node)
* compareAtom: Bool,
* compareMass: Bool,
* compareLinkedConnectorCount: Bool,
* compareCharge: Bool,
* compareStereo: Bool,
* compareHydrogenCount: Bool,
* compareLonePair: Bool,
* (for connector)
* compareConnectedObjCount: Bool,
* compareBondType: Bool,
* compareBondOrder: Bool
* compareStereo: Bool
* }
* The detailed bool values will override settings in level.
* @returns {Hash}
*/
prepareCompareOptions: function(options)
{
var CL = Kekule.StructureComparationLevel;
var level = (options && options.level) || Kekule.globalOptions.structureComparation.structureComparationLevel; /*CL.DEFAULT*/
var result;
if (level === CL.SKELETAL)
result = {
compareAtom: false, compareMass: false, compareLinkedConnectorCount: true, compareCharge: false,
compareStereo: false, compareHydrogenCount: false,
compareConnectedObjCount: true, compareBondType: false, compareBondOrder: false};
else if (level === CL.CONSTITUTION)
result = {
compareAtom: true, compareMass: false, compareLinkedConnectorCount: true, compareCharge: false,
compareStereo: false, compareHydrogenCount: true,
compareConnectedObjCount: true, compareBondType: true, compareBondOrder: true};
else if (level === CL.CONFIGURATION)
result = {
compareAtom: true, compareMass: false, compareLinkedConnectorCount: true, compareCharge: false,
compareStereo: true, compareHydrogenCount: true,
compareConnectedObjCount: true, compareBondType: true, compareBondOrder: true};
else if (level === CL.EXACT)
result = {
compareAtom: true, compareMass: true, compareLinkedConnectorCount: true, compareCharge: true,
compareStereo: true, compareHydrogenCount: true,
compareConnectedObjCount: true, compareBondType: true, compareBondOrder: true};
// override bool values
result = Object.extend(result, options || {});
return result;
},
/**
* Get a digital value for comparing object.
* @param {Kekule.ChemStructureObject} chemObj
* @param {options} Value getter options. Can including fields:
* {
* (for node)
* compareAtom: Bool,
* compareMass: Bool,
* compareLinkedConnectorCount: Bool,
* compareCharge: Bool,
* compareStereo: Bool,
* compareHydrogenCount: Bool,
* (for connector)
* compareConnectedObjCount: Bool,
* compareBondType: Bool,
* compareBondOrder: Bool
* compareStereo: Bool
* }
* All fields' default value are true.
* @returns {Int}
*/
getCompareValue: function(chemObj, options)
{
/*
var ops = Object.extend({
compareAtom: true,
compareMass: true,
compareLinkedConnectorCount: true,
compareCharge: true,
compareStereo: true,
compareHydrogenCount: true,
compareConnectedObjCount: true,
compareBondType: true,
compareBondOrder: true
}, options);
*/
var ops = Kekule.UnivChemStructObjComparer.prepareCompareOptions(options);
if (!chemObj)
return 0;
else if (chemObj instanceof K.ChemStructureNode)
return K.UnivChemStructObjComparer.getNodeCompareValue(chemObj, ops);
else if (chemObj instanceof K.ChemStructureConnector)
return K.UnivChemStructObjComparer.getConnectorCompareValue(chemObj, ops);
else
return 0;
},
/**
* Compare the priority of two objects. Result = 0 if objects are the same, < 0 if obj1 < obj2 and > 0 if obj1 > obj2.
* Note: the comparation of structure fragments should be taken out after canonicalizing them.
* @param {Kekule.ChemStructureObject} obj1
* @param {Kekule.ChemStructureObject} obj2
* @param {Hash} compareOptions Can including fields:
* {
* (for node)
* compareLinkedConnectorCount: Bool,
* compareCharge: Bool,
* compareStereo: Bool,
* compareHydrogenCount: Bool,
* (for connector)
* compareConnectedObjCount: Bool,
* compareBondType: Bool,
* compareBondOrder: Bool,
* compareStereo: Bool
* }
* All fields' default value are true.
* @returns {Int}
*/
compare: function(obj1, obj2, compareOptions)
{
var U = K.UnivChemStructObjComparer;
var v1 = U.getCompareValue(obj1, compareOptions);
var v2 = U.getCompareValue(obj2, compareOptions);
var result = v1 - v2;
if (obj1 && obj2)
{
if ((result === 0) && (obj1.getNodes && obj2.getNodes)) // structure fragment, if with same node and connector count, compare nodes and connectors
{
var nodes1 = obj1.getNonHydrogenNodes(); // obj1.getNodes();
var nodes2 = obj2.getNonHydrogenNodes(); // obj2.getNodes();
result = nodes1.length - nodes2.length;
if (result === 0)
{
for (var i = 0, l = nodes1.length; i < l; ++i)
{
var result = U.compare(nodes1[i], nodes2[i], compareOptions);
if (result !== 0)
break;
}
}
}
if ((result === 0) && (obj1.getConnectors && obj2.getConnectors))
{
var connectors1 = obj1.getNonHydrogenConnectors(); //obj1.getConnectors();
var connectors2 = obj2.getNonHydrogenConnectors(); //obj2.getConnectors();
result = connectors1.length - connectors2.length;
if (result === 0)
{
for (var i = 0, l = connectors1.length; i < l; ++i)
{
var result = U.compare(connectors1[i], connectors2[i], compareOptions);
if (result !== 0)
break;
}
}
}
}
return result;
},
/**
* Get the object with max priority.
* @param {Variant} obj You can pass multiple {@link Kekule.ChemStructureObject} in the param, or pass an array of object.
* @returns {Kekule.ChemStructureObject}
*/
max: function()
{
var max = -1;
var objs = arguments;
if ((objs.length === 1) && (DataType.isArrayValue(objs)))
objs = objs[0];
var result = null;
for (var i = 0, l = objs.length; i < l; ++i)
{
var obj = objs[i];
var v = Kekule.UnivChemStructObjComparer.getCompareValue(obj);
if (v > max)
result = obj;
}
return result;
},
/**
* Sort an array of chem objects.
* @param {Array} objs
* @param {Bool} ascendOrder
* @param {Hash} compareOptions
* @returns {Array}
*/
sort: function(objs, ascendOrder, compareOptions)
{
objs.sort(
function(obj1, obj2)
{
var r = Kekule.UnivChemStructObjComparer.compare(obj1, obj2, compareOptions);
if (ascendOrder)
r = -r;
return r;
}
);
return objs;
},
/** @private */
getNodeCompareValue: function(node, options)
{
var U = K.UnivChemStructObjComparer;
var result = 0x10000000000000; // node always start with 1
// object class
if (options.compareAtom)
{
var nodeClass = node.getClass();
var vclass = K.UnivChemStructObjComparer.NODE_CLASS_MAP.get(nodeClass);
var detailValue = 0;
if (K.ObjUtils.isUnset(vclass))
{
if (node instanceof K.AbstractAtom)
{
vclass = (node instanceof K.Atom) ? 0x1 :
(node instanceof K.Pseudoatom) ? 0x2 :
(node instanceof K.VariableAtom) ? 0x3 : 0x0;
//detailValue = K.UnivChemStructObjComparer.getAtomDetailCompareValue(node);
}
if (node instanceof K.StructureFragment)
{
//vclass = 0x30;
vclass = (node instanceof K.SubGroup) ? 0x1 :
(node instanceof K.Molecule) ? 0x5 : 0x0;
//detailValue = K.UnivChemStructObjComparer.getAtomDetailCompareValue(node);
}
else
{
vclass = 0;
//detailValue = K.UnivChemStructObjComparer.getAtomDetailCompareValue(node);
}
}
}
else
var vclass = 0;
detailValue = K.UnivChemStructObjComparer.getAtomDetailCompareValue(node, options);
result += vclass * U._P48; // U._P44; //(vclass << (12 * 4));
result += detailValue;
// Linked conector count
if (options.compareLinkedConnectorCount)
{
var vlinkedConnector = node.getLinkedNonHydrogenConnectors().length; //node.getLinkedConnectorCount();
result += (vlinkedConnector << (4 * 4));
}
// charge
if (options.compareCharge)
{
var vcharge = Math.round(node.getCharge() || 0) + K.UnivChemStructObjComparer.CHARGE_BASE; // there may be partial charge, so a round function is used here
result += (vcharge << (3 * 4));
}
// parity
if (options.compareStereo)
{
var parity = node.getParity? (node.getParity() || 0): 0;
result += parity << (2 * 4);
}
// hydrogen count
if (options.compareHydrogenCount)
{
var vhydrogen = node.getHydrogenCount? node.getHydrogenCount(true) || 0: 0;
result += vhydrogen << (1 * 4);
}
// lone pair
if (options.compareLonePair)
{
var vLonePairCount = node.getLonePairCount? node.getLonePairCount() || 0: 0;
result += vLonePairCount;
}
return result;
},
/** @private */
getAtomDetailCompareValue: function(atom, options)
{
var U = K.UnivChemStructObjComparer;
var result = 0;
// atom major property and atom mass number
var vmajorProp, vmass = 0;
if (options.compareAtom)
{
var nodeClass = atom.getClass();
if (nodeClass === K.Atom)
{
vmajorProp = atom.getAtomicNumber() || 0;
if (options.compareMass)
vmass = atom.getMassNumber() || 0;
}
else if (nodeClass === K.Pseudoatom)
vmajorProp = 0xFFE;
else if (nodeClass === K.VariableAtom)
vmajorProp = 0xFFF;
else
vmajorProp = 0;
//result += vmajorProp * U._P32 + vmass * U._P20; //(vmajorProp << (9 * 4)) | (vmass << (6 * 4));
result += vmajorProp * U._P36 + vmass * U._P24; //(vmajorProp << (9 * 4)) | (vmass << (6 * 4));
}
return result;
},
/** @private */
getFragmentDetailCompareValue: function(fragment, options)
{
var U = K.UnivChemStructObjComparer;
var result = 0;
result += (fragment.getNodeCount() || 0) * U._P32 + (fragment.getConnectorCount() || 0) * U._P20;
return result;
},
/** @private */
getConnectorCompareValue: function(connector, options)
{
var U = K.UnivChemStructObjComparer;
var result = 0; // as result alway start with 0, the actual digital is 8, so bit operations can be used
var isBond = connector instanceof Kekule.Bond;
// connector type
result |= isBond? (1 << (6 * 4)): 0;
// bond type
if (options.compareBondType)
{
var vBondType;
if (isBond)
{
vBondType = U.BOND_TYPE_MAP.get(connector.getBondType());
if (K.ObjUtils.isUnset(vBondType))
vBondType = 0;
}
else
vBondType = 0;
result |= (vBondType << (5 * 4));
}
// parity
if (options.compareStereo)
{
var parity = connector.getParity? (connector.getParity() || 0): 0;
result += parity << (4 * 4);
}
// bond electron count
if (options.compareBondOrder)
{
var electronCount = isBond? Math.round(connector.getElectronCount()): 0;
result |= electronCount << (2 * 4);
}
// connected object count
if (options.compareConnectedObjCount)
{
result |= (connector.getConnectedObjCount() || 0);
}
return result;
}
};
})();