From cbdee43422513b98f9e00579d2d5b63943dfdc0e Mon Sep 17 00:00:00 2001 From: tpoisseau <22891227+tpoisseau@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:40:29 +0200 Subject: [PATCH 1/3] fix: double click event handling DOM `pointerdown` and `pointerup` do not contain number of click in `detail` like `click` events. So I tweak `GenericEditorArea.eventHappened` to call `handleDoubleClick` on `GenericMouseEvent.MOUSE_CLICKED` Closes: https://github.com/cheminfo/openchemlib-js/issues/225 --- lib/canvas_editor/events.js | 4 ++-- .../research/gui/editor/GenericEditorArea.java | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/canvas_editor/events.js b/lib/canvas_editor/events.js index e1bf8b27..28a237f6 100644 --- a/lib/canvas_editor/events.js +++ b/lib/canvas_editor/events.js @@ -30,14 +30,14 @@ function addPointerListeners(canvasElement, drawArea, JavaEditorArea) { canvasElement.addEventListener('pointerdown', (ev) => { if (pointerDownId === -1) { pointerDownId = ev.pointerId; - fireMouseEvent(JavaEditorArea.MOUSE_EVENT_PRESSED, ev, ev.detail); + fireMouseEvent(JavaEditorArea.MOUSE_EVENT_PRESSED, ev); } }); function handlePointerUp(ev) { if (pointerDownId === ev.pointerId) { pointerDownId = -1; - fireMouseEvent(JavaEditorArea.MOUSE_EVENT_RELEASED, ev, ev.detail); + fireMouseEvent(JavaEditorArea.MOUSE_EVENT_RELEASED, ev); } } // Listen on document to capture mouse release outside the canvas. diff --git a/src/com/actelion/research/gwt/chemlib/com/actelion/research/gui/editor/GenericEditorArea.java b/src/com/actelion/research/gwt/chemlib/com/actelion/research/gui/editor/GenericEditorArea.java index b7c8bb87..3e81c7d8 100644 --- a/src/com/actelion/research/gwt/chemlib/com/actelion/research/gui/editor/GenericEditorArea.java +++ b/src/com/actelion/research/gwt/chemlib/com/actelion/research/gui/editor/GenericEditorArea.java @@ -943,15 +943,19 @@ private void eventHappened(GenericMouseEvent e) { return; } + if (e.getButton() == 1) { + mMouseIsDown = false; + updateCursor(); + mouseReleasedButton1(); + } + } + + if (e.getWhat() == GenericMouseEvent.MOUSE_CLICKED) { if (e.getButton() == 1) { if (e.getClickCount() == 2) { handleDoubleClick(e.getX(), e.getY()); return; } - - mMouseIsDown = false; - updateCursor(); - mouseReleasedButton1(); } } From cc96039f785081f54d3657c2d08e563012f7b7b7 Mon Sep 17 00:00:00 2001 From: tpoisseau <22891227+tpoisseau@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:51:42 +0200 Subject: [PATCH 2/3] chore: update openchemlib --- openchemlib | 2 +- .../research/chem/AromaticityResolver.java | 8 +- .../research/chem/AtomFunctionAnalyzer.java | 43 ++- .../com/actelion/research/chem/Canonizer.java | 64 +++- .../research/chem/ExtendedMolecule.java | 65 ++-- .../chem/ExtendedMoleculeFunctions.java | 41 ++- ...DCodeParserWithoutCoordinateInvention.java | 13 +- .../com/actelion/research/chem/Molecule.java | 5 +- .../actelion/research/chem/SmilesParser.java | 2 +- .../research/chem/conf/AtomAssembler.java | 3 +- .../chem/descriptor/DescriptorHelper.java | 17 +- .../descriptor/DescriptorWeightsHelper.java | 151 +++++++++ .../forcefield/mmff/ForceFieldMMFF94.java | 43 +-- .../chem/forcefield/mmff/MMFFMolecule.java | 19 +- .../chem/io/CompoundTableConstants.java | 1 + .../research/chem/shredder/Fragment3D.java | 9 +- .../chem/shredder/FragmentGeometry3D.java | 306 ++++++++++++++++++ .../research/chem/shredder/Fragmenter3D.java | 60 ++-- .../chem/shredder/MoleculeShredder.java | 32 +- .../research/gui/JFileChooserOverwrite.java | 2 +- .../editor/BondQueryFeatureDialogBuilder.java | 83 ++--- .../gui/editor/GenericEditorToolbar.java | 4 +- .../actelion/research/util/ArrayUtils.java | 22 ++ 23 files changed, 803 insertions(+), 192 deletions(-) create mode 100644 src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/descriptor/DescriptorWeightsHelper.java create mode 100644 src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/shredder/FragmentGeometry3D.java diff --git a/openchemlib b/openchemlib index 17d264c8..e30c9f88 160000 --- a/openchemlib +++ b/openchemlib @@ -1 +1 @@ -Subproject commit 17d264c81ccea9176ce576daedf959294eb93ff4 +Subproject commit e30c9f88bb90c9b1ee688c121ee8ecfb2cc043c4 diff --git a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/AromaticityResolver.java b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/AromaticityResolver.java index ca0db78c..12775ea6 100644 --- a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/AromaticityResolver.java +++ b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/AromaticityResolver.java @@ -168,7 +168,13 @@ public boolean locateDelocalizedDoubleBonds(boolean[] isAromaticBond, boolean ma } } - if (!bondsPromoted) { + + if (bondsPromoted) { + promoteObviousBonds(); + continue; + } + + if (!bondsPromoted) { // find and promote one aromatic bond // (should never happen, but to prevent an endless loop nonetheless) for (int bond=0; bond 3)) return false; @@ -552,5 +565,23 @@ && getFakeOxoCount(mol, mol.getConnAtom(conn, j)) != 0) } } return false; + } + + public static boolean isAmphiphilic(StereoMolecule mol) { + boolean amphiphilic=true; + + boolean acidic=false; + boolean basic=false; + for (int at = 0; at < mol.getAtoms(); at++) { + if(isAcidicOxygen(mol, at)){ + acidic=true; + } + if(isBasicNitrogen(mol, at)){ + basic=true; + } } + + amphiphilic = basic && acidic; + return amphiphilic; } +} diff --git a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/Canonizer.java b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/Canonizer.java index 0838b226..56f5d038 100644 --- a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/Canonizer.java +++ b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/Canonizer.java @@ -172,7 +172,7 @@ public class Canonizer { private ArrayList mTHParityNormalizationGroupList; private int mMode,mNoOfRanks,mNoOfPseudoGroups; private boolean mIsOddParityRound; - private boolean mZCoordinatesAvailable; + private boolean mZCoordinatesAvailable,mAllHydrogensAreExplicit; private boolean mCIPParityNoDistinctionProblem; private boolean mEncodeAvoid127; @@ -226,6 +226,18 @@ public Canonizer(StereoMolecule mol, int mode) { mZCoordinatesAvailable = ((mode & COORDS_ARE_3D) != 0) || mMol.is3D(); + mAllHydrogensAreExplicit = false; + if (mMol.getAllAtoms() > mMol.getAtoms() + && !mMol.isFragment()) { + mAllHydrogensAreExplicit = true; + for (int i=0; i mMol.getAtoms() - && !mMol.isFragment()) { - includeHydrogenCoordinates = true; - for (int i=0; i + * If this Molecule is a substructure (mFragment=true) and has no 3-dimensional atom coordinates, + * then all explicit hydrogen atoms are converted into query features, unless setHydrogenProtection(true) + * was called before on this Molecule.
* cHelperRings: Aromatic and non-aromatic rings are detected. Atom and bond ring - * properties are set and a ring collection provides a total set of small rings (7 or less atoms). + * properties are set and a ring collection provides a total set of small rings (7 or fewer atoms). * Atoms being in allylic/benzylic or stabilized (neighbor of a carbonyl or similar group) position * are flagged as such.
* cHelperParities: Atom (tetrahedral or axial) and bond (E/Z or atrop) parities are calculated @@ -3501,7 +3507,7 @@ public void ensureHelperArrays(int required) { calculateNeighbours(); mValidHelperArrays |= cHelperBitNeighbours; - if (convertHydrogenToQueryFeatures()) { + if (mIsFragment && !is3D() && convertHydrogenToQueryFeatures()) { handleHydrogens(); calculateNeighbours(); mValidHelperArrays |= cHelperBitNeighbours; @@ -3989,9 +3995,6 @@ else if (atomRingBondCount[atom] > 3) * @return true if hydrogens were deleted and, thus, mConnAtoms are invalid */ private boolean convertHydrogenToQueryFeatures() { - if (!mIsFragment) - return false; - // if an atom has no free valence then cAtomQFNoMoreNeighbours is not necessary // and cAtomQFMoreNeighbours is not possible // unless it is an uncharged N- or O-family atom that could be e.g. methylated @@ -4001,12 +4004,15 @@ private boolean convertHydrogenToQueryFeatures() { mAtomQueryFeatures[atom] &= ~(cAtomQFNoMoreNeighbours | cAtomQFMoreNeighbours); } - // approximate explicit hydrogens by query features - // and remove explicit hydrogens except those with stereo bonds + if (mProtectHydrogen) + return false; + + // approximate explicit hydrogens by query features + // and remove explicit hydrogens except those with stereo bonds boolean deleteHydrogens = false; for (int atom=0; atom 0) { + if (explicitHydrogens > 0) { if ((mAtomQueryFeatures[atom] & cAtomQFNoMoreNeighbours) == 0) { // add query feature hydrogen to explicit hydrogens int queryFeatureHydrogens = @@ -4048,6 +4054,7 @@ private boolean convertHydrogenToQueryFeatures() { } } } + if (deleteHydrogens) compressMolTable(); @@ -4111,24 +4118,32 @@ public void validateBondQueryFeatures() { ensureHelperArrays(cHelperRings); for (int bond=0; bond dh) { - return TAG_SIMILARITY + dh.getInfo().shortName; - } - public static String getTagDescriptorSimilarity(String shortName) { - return TAG_SIMILARITY + shortName; + return shortName + TAG_SIMILARITY; } + public static String getTagDescriptorSimilarity(ISimilarityCalculator dh) { + return getTagDescriptorSimilarity(dh.getInfo().shortName); + } public static String getTagDescriptorSimilarity(SimilarityCalculatorInfo info) { - return TAG_SIMILARITY + info.shortName; + return getTagDescriptorSimilarity(info.shortName); } public static String getTagDescriptorSimilarity(DescriptorInfo dh){ - return TAG_SIMILARITY + dh.shortName; + return getTagDescriptorSimilarity(dh.shortName); } public static T create(DescriptorHandler dh, String idcode){ diff --git a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/descriptor/DescriptorWeightsHelper.java b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/descriptor/DescriptorWeightsHelper.java new file mode 100644 index 00000000..2d8148fb --- /dev/null +++ b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/descriptor/DescriptorWeightsHelper.java @@ -0,0 +1,151 @@ +package com.actelion.research.chem.descriptor; + +import com.actelion.research.chem.Molecule3D; +import com.actelion.research.chem.descriptor.flexophore.ConstantsFlexophore; +import com.actelion.research.chem.descriptor.flexophore.generator.CreatorMolDistHistViz; +import com.actelion.research.chem.descriptor.flexophore.redgraph.SubGraphIndices; +import com.actelion.research.chem.phesa.DescriptorHandlerShape; +import com.actelion.research.chem.phesa.PheSAMolecule; +import com.actelion.research.chem.phesa.pharmacophore.pp.IPharmacophorePoint; +import com.actelion.research.chem.phesa.pharmacophore.pp.PPGaussian; +import com.actelion.research.util.datamodel.IntArray; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/* + + Copyright (c) 2024 Alipheron AG. All rights reserved. + + This file is part of the Alipheron AG software suite. + + Licensed under the Alipheron AG Software License Agreement (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at the company's official website or upon request. + + 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. + + Created by Modest von Korff + 26/07/2024 + + */ +public class DescriptorWeightsHelper { + + public static final String TAG_WEIGHTS_ATOMS_FLEXOPHORE = "Atom Weights Flexophore"; + public static final String TAG_WEIGHTS_ATOMS_PHESA = "Atom Weights PheSA"; + + + public static final int LABEL_WEIGHT_LOW = 0; + public static final int LABEL_WEIGHT_NORMAL = 1; + public static final int LABEL_WEIGHT_MANDATORY = 2; + public static final int LABEL_WEIGHT_HIGH_USER = 3; + + private CreatorMolDistHistViz creatorMolDistHistViz; + + + + public DescriptorWeightsHelper() { + this.creatorMolDistHistViz = new CreatorMolDistHistViz(); + + } + + /** + * The labels are used to calculate the weight values for Flexophore and PheSA. + * The labels can be given by the users in the + * @param molecule3D + * @return + */ + public int [] calcWeightLabelsFlexophore(Molecule3D molecule3D){ + List liSubGraphIndices = creatorMolDistHistViz.getSubGraphIndices(molecule3D); + int [] weights = calcWeightLabels(liSubGraphIndices, molecule3D); + return weights; + } + + /** + * - End standing pp points are set mandatory. + * - Charged pp points are set mandatory. + * @param liSubGraphIndices + * @param molecule3D + * @return + */ + public static int [] calcWeightLabels(List liSubGraphIndices, Molecule3D molecule3D){ + + int [] weights = getBasisWeightLabels(molecule3D); + + for (int i = 0; i < liSubGraphIndices.size(); i++) { + SubGraphIndices sgi = liSubGraphIndices.get(i); + int indexWeight = DescriptorWeightsHelper.LABEL_WEIGHT_NORMAL; + if (SubGraphIndices.isLinker(molecule3D, liSubGraphIndices, i)) { + if(!SubGraphIndices.isOnlyCarbon(molecule3D, sgi)) { + indexWeight = DescriptorWeightsHelper.LABEL_WEIGHT_MANDATORY; + } + if(SubGraphIndices.isCharged(molecule3D, sgi)) { + indexWeight = DescriptorWeightsHelper.LABEL_WEIGHT_MANDATORY; + } + } else { + indexWeight = DescriptorWeightsHelper.LABEL_WEIGHT_MANDATORY; + } + for (int atomIndex : sgi.getAtomIndices()) { + weights[atomIndex] = indexWeight; + } + } + + return weights; + } + + public static int [] getBasisWeightLabels(Molecule3D molecule3D){ + int [] weights = new int[molecule3D.getAtoms()]; + Arrays.fill(weights, DescriptorWeightsHelper.LABEL_WEIGHT_NORMAL); + return weights; + } + + public static int [] mergeWeightLabels(int[] arrWeightLabel, int[] arrWeightLabelUser) { + + if(arrWeightLabel.length!= arrWeightLabelUser.length){ + throw new RuntimeException("Weight labels differ in size!"); + } + int[] arrWeightLabelMerged = new int[arrWeightLabel.length]; + for (int i = 0; i < arrWeightLabel.length; i++) { + int label = arrWeightLabel[i]; + + if(arrWeightLabelUser[i]== LABEL_WEIGHT_LOW){ + label = arrWeightLabelUser[i]; + } else if(arrWeightLabel[i] == LABEL_WEIGHT_MANDATORY && arrWeightLabelUser[i] == LABEL_WEIGHT_MANDATORY){ + label = LABEL_WEIGHT_HIGH_USER; + } else if(arrWeightLabelUser[i] == LABEL_WEIGHT_MANDATORY){ + label = LABEL_WEIGHT_HIGH_USER; + } + arrWeightLabelMerged[i]=label; + } + + return arrWeightLabelMerged; + } + public static String toStringWeightLabels(int [] weightLabels){ + StringBuilder weightBuilder = new StringBuilder(); + for (int weightLabel : weightLabels) { + weightBuilder.append((char)('0'+weightLabel)); + } + return weightBuilder.toString(); + } + + /** + * + * @param s string with single digits '123456789'. + * @return + */ + public static int [] parseSingleDigitString(String s) { + if (s == null) + return null; + int[] arr = new int[s.length()]; + for (int i = 0; i < s.length(); i++) { + int c = Integer.parseInt(Character.toString(s.charAt(i))); + arr[i] = c; + } + return arr; + } +} diff --git a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/forcefield/mmff/ForceFieldMMFF94.java b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/forcefield/mmff/ForceFieldMMFF94.java index 4e75e6d3..1c137dae 100644 --- a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/forcefield/mmff/ForceFieldMMFF94.java +++ b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/forcefield/mmff/ForceFieldMMFF94.java @@ -33,16 +33,12 @@ package com.actelion.research.chem.forcefield.mmff; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.HashMap; - import com.actelion.research.chem.StereoMolecule; import com.actelion.research.chem.forcefield.AbstractForceField; -/** +import java.util.*; + +/* * The MMFF ForceField class is the top level class used to perform * energy calculations/minimisation on a molecule. It accepts an * ExtendedMolecule and the string name of the parameter tables to use. @@ -68,7 +64,7 @@ * (default: 1.0) * - "dielectric model": A string for the dielectric model. "distance" * selects a distance-dependent dielectric model, everything else -* selects the constant dielectric model (default: "constant") + * selects the constant dielectric model (default: "constant") * - "angle bend": A boolean, default True, for whether to include angle * bending energy terms. * - "bond stretch": A boolean, default True, for whether to include bond @@ -76,9 +72,9 @@ * - "electrostatic": A boolean, default True, for whether to include the * nonbonded electrostatic energy terms. * - "out of plane": A boolean, default True, for whether to include out - of plane energy terms. + * of plane energy terms. * - "stretch bend": A boolean, default True, for whether to include - stretch bending energy terms. + * stretch bending energy terms. * - "torsion angle": A boolean, default True, for whether to include * torsional angle energy terms. * - "van der waals": A boolean, default True, for whether to include the @@ -88,20 +84,15 @@ * @author joel * */ -/** - * @author joel - * - */ public final class ForceFieldMMFF94 extends AbstractForceField { public static final String MMFF94 = "MMFF94"; public static final String MMFF94S = "MMFF94s"; public static final String MMFF94SPLUS = "MMFF94s+"; - //protected final ExtendedMolecule mol; private final MMFFMolecule mMMFFMol; - public static Map mTables = new HashMap(); - private List mEnergies = new ArrayList(); + public static Map mTables = new HashMap<>(); + private final List mEnergies = new ArrayList<>(); /** @@ -114,25 +105,21 @@ public final class ForceFieldMMFF94 extends AbstractForceField { * See class description of a list of options. */ - public ForceFieldMMFF94(StereoMolecule m, String tablename, - Map options) { + public ForceFieldMMFF94(StereoMolecule m, String tablename, Map options) + throws BadAtomTypeException,BadRingAromException { super(m); mMMFFMol = new com.actelion.research.chem.forcefield.mmff.MMFFMolecule(m); mMol.ensureHelperArrays(StereoMolecule.cHelperRings); Tables table = mTables.get(tablename); double nonBondedThresh = options.containsKey("nonbonded cutoff") - ? ((Double)options.get("nonbonded cutoff")).doubleValue() + ? (Double)options.get("nonbonded cutoff") : 100.0; double dielConst = options.containsKey("dielectric constant") - ? ((Double)options.get("dielectric constant")).doubleValue() : 1.0; - - boolean dielModel = options.containsKey("dielectric model") - ? ((String)options.get("dielectric model")).equals("distance") - : false; - + ? (Double)options.get("dielectric constant") : 1.0; + boolean dielModel = options.containsKey("dielectric model") && (options.get("dielectric model")).equals("distance"); Separation sep = new Separation(mMMFFMol); @@ -174,8 +161,8 @@ public ForceFieldMMFF94(StereoMolecule m, String tablename, * must be a table with this name that has been loaded with * "loadTable()". */ - public ForceFieldMMFF94(StereoMolecule mol, String tablename) { - this(mol, tablename, new HashMap()); + public ForceFieldMMFF94(StereoMolecule mol, String tablename) throws BadAtomTypeException,BadRingAromException { + this(mol, tablename, new HashMap<>()); } /** diff --git a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/forcefield/mmff/MMFFMolecule.java b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/forcefield/mmff/MMFFMolecule.java index cc4ba25b..539034cf 100644 --- a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/forcefield/mmff/MMFFMolecule.java +++ b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/forcefield/mmff/MMFFMolecule.java @@ -36,25 +36,24 @@ import com.actelion.research.chem.StereoMolecule; import com.actelion.research.chem.RingCollection; +import java.util.Arrays; + /** * MMFF molecule is a wrapper class for the ExtendedMolecule. It holds some * additional data such as a cache of the atom types, whether the molecule is * valid for MMFF and the ring mmff aromaticity property. */ public final class MMFFMolecule extends StereoMolecule { - private RingBoolean[] mRingArom; - private int[] mAtomTypes; - private int[] mHydrogenMap; + private final RingBoolean[] mRingArom; + private final int[] mAtomTypes; + private final int[] mHydrogenMap; - public MMFFMolecule(StereoMolecule mol) throws BadAtomTypeException, - BadRingAromException - { + public MMFFMolecule(StereoMolecule mol) throws BadAtomTypeException,BadRingAromException { super(mol); mHydrogenMap = getHandleHydrogenMap(); RingCollection rings = getRingSet(); mRingArom = new RingBoolean[rings.getSize()]; - for (int i=0; i { - private String mIDCode,mIDCoords; - private TorsionDescriptor mTorsions; - private int[] mExitAtoms; + private final String mIDCode; + private String mIDCoords; + private final TorsionDescriptor mTorsions; + private final int[] mExitAtoms; public Fragment3D(String idcode, String coords, TorsionDescriptor td, int[] exitAtoms) { this.mIDCode = idcode; @@ -38,7 +39,7 @@ public boolean equals(Fragment3D f) { @Override public int compareTo(Fragment3D f) { int comparison = mIDCode.compareTo(f.mIDCode); if (comparison != 0 || mTorsions == null) // different structure or no rotatable bonds - return comparison; + return comparison; return mTorsions.compareTo(f.mTorsions); } diff --git a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/shredder/FragmentGeometry3D.java b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/shredder/FragmentGeometry3D.java new file mode 100644 index 00000000..0a7eb563 --- /dev/null +++ b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/shredder/FragmentGeometry3D.java @@ -0,0 +1,306 @@ +package com.actelion.research.chem.shredder; + +import com.actelion.research.calc.Matrix; +import com.actelion.research.calc.SingularValueDecomposition; +import com.actelion.research.chem.Coordinates; +import com.actelion.research.chem.Molecule; +import com.actelion.research.chem.StereoMolecule; + +import java.util.ArrayList; +import java.util.Arrays; + +public class FragmentGeometry3D { + public static final int MODE_SELECTED_ATOMS = 1; // molecule with fragment atoms selected, exit vector atoms not selected + public static final int MODE_FRAGMENT_WITH_EXIT_VECTORS = 2; // defined as atom custom label "*" + + private final StereoMolecule mMol; + private ExitVector[] mExitVector; + private final String mFootPrint; // canonical String describing atomic numbers or exit vectors + private int[][] mPermutation; + private final Coordinates[] mAlignmentCoords; + private final Coordinates mAlignmentCOG; + + /** + * Creates a FragmentGeometry3D from a StereoMolecule with the mode defining the situation. + * This creates a canonical exit vector footprint. For geometries with the same footprint + * it provides geometry comparisons regarding alignment and exit vector similarity. + * A transformation mask is provided for the best alignment one object to another. + * Helper functions to attach properly aligned substituents are given. + * @param mol + */ + public FragmentGeometry3D(StereoMolecule mol, int mode) { + mMol = mol; + mMol.ensureHelperArrays(Molecule.cHelperNeighbours); + + switch (mode) { + case MODE_SELECTED_ATOMS: + initMoleculeWithSelection(); + break; + case MODE_FRAGMENT_WITH_EXIT_VECTORS: + initFragmentWithExitVectors(); + break; + } + + Arrays.sort(mExitVector); + + StringBuilder footprint = new StringBuilder(); + for (ExitVector ev : mExitVector) + footprint.append(Molecule.cAtomLabel[ev.atomicNo]); + mFootPrint = footprint.toString(); + + // compile coordinates of all root atoms and calculate their center of gravity + mAlignmentCoords = determineAlignmentCoords(); + mAlignmentCOG = centerOfGravity(mAlignmentCoords); + } + + public void initMoleculeWithSelection() { + ArrayList exitVectorList = new ArrayList<>(); + for (int atom=0; atom exitVectorList = new ArrayList<>(); + for (int atom=0; atom maxRMSD ? null : matrix; + } + + public boolean hasMatchingExitVectors(FragmentGeometry3D geometry, Coordinates[] coords, int permutation, double maxAngleDivergence) { + maxAngleDivergence *= Math.PI / 180; + for (int i = 0; i maxAngleDivergence) + return false; + } + return true; + } + + public int getPermutationCount() { + if (mPermutation == null) { + int[] sameCount = new int[mExitVector.length]; + int[] permCount = new int[mExitVector.length]; + int index = 0; + int totalPermCount = 1; + while (index list = new ArrayList<>(); + addToPermutation(new int[1], objectCount, list); + return list.toArray(new int[0][]); + } + + private void addToPermutation(int[] input, int max, ArrayList list) { + int size = input.length + 1; + for (int pos=0; pos + * To actually perform the alignment with any set of coordinates do:
+ * for (Coordinates c : anyCoords) { c.sub(cog2); c.rotate(matrix); c.add(cog1); } + * @param coords1 + * @param coords2 + * @param cog1 + * @param cog2 + * @return + */ + public static double[][] kabschAlign(Coordinates[] coords1, Coordinates[] coords2, + Coordinates cog1, Coordinates cog2) { + double[][] m = new double[3][3]; + double[][] c1 = Arrays.stream(coords1).map(e -> new double[] {e.x-cog1.x,e.y-cog1.y,e.z-cog1.z}).toArray(double[][]::new); + double[][] c2 = Arrays.stream(coords2).map(e -> new double[] {e.x-cog2.x,e.y-cog2.y,e.z-cog2.z}).toArray(double[][]::new); + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + double rij = 0.0; + for(int a=0; a0.0); + rot = rot.getTranspose(); + return rot.getArray(); + } + + private static class ExitVector implements Comparable { + int rootAtom,exitAtom,atomicNo; + + public ExitVector(int rootAtom, int exitAtom, int atomicNo) { + this.rootAtom = rootAtom; + this.exitAtom = exitAtom; + this.atomicNo = atomicNo; + } + + @Override + public int compareTo(ExitVector o) { + return Integer.compare(o.atomicNo, atomicNo); + } + } +} diff --git a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/shredder/Fragmenter3D.java b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/shredder/Fragmenter3D.java index 6340d30f..89d76417 100644 --- a/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/shredder/Fragmenter3D.java +++ b/src/com/actelion/research/gwt/chemlib/com/actelion/research/chem/shredder/Fragmenter3D.java @@ -2,30 +2,28 @@ import com.actelion.research.chem.Canonizer; import com.actelion.research.chem.StereoMolecule; -import com.actelion.research.chem.conf.TorsionDB; -import com.actelion.research.chem.conf.TorsionDescriptor; -import com.actelion.research.chem.conf.TorsionDescriptorHelper; +import com.actelion.research.chem.conf.*; import com.actelion.research.util.IntArrayComparator; import java.util.ArrayList; import java.util.TreeSet; public class Fragmenter3D { - private int mMinAtoms,mMaxAtoms,mMaxBonds,mMinExits,mMaxExits; - private ArrayList mFragmentList; + private final int mMinAtoms,mMaxAtoms,mMaxBondFlexibilitySum,mMinExits,mMaxExits; + private final ArrayList mFragmentList; /** * * @param minAtoms * @param maxAtoms - * @param maxBonds + * @param maxBondFlexibilitySum * @param minExits * @param maxExits */ - public Fragmenter3D(int minAtoms, int maxAtoms, int maxBonds, int minExits, int maxExits) { + public Fragmenter3D(int minAtoms, int maxAtoms, int maxBondFlexibilitySum, int minExits, int maxExits) { mMinAtoms = minAtoms; mMaxAtoms = maxAtoms; - mMaxBonds = maxBonds; + mMaxBondFlexibilitySum = maxBondFlexibilitySum; mMinExits = minExits; mMaxExits = maxExits; mFragmentList = new ArrayList<>(); @@ -37,16 +35,21 @@ public Fragmenter3D(int minAtoms, int maxAtoms, int maxBonds, int minExits, int * The list is re-used by subsequent calls to this nethod. Thus, process/consume the * fragment list before calling this method again. * @param mol - * @return Fragment3D list of passed molecule + * @param withHydrogen whether built fragments shall include hydrogen atoms + * @return Fragment3D list of given molecule */ - public ArrayList getFragments(StereoMolecule mol) { + public ArrayList buildFragments(StereoMolecule mol, boolean withHydrogen) { mFragmentList.clear(); mol.stripSmallFragments(); - mol.removeExplicitHydrogens(false, true); + if (withHydrogen) + new AtomAssembler(mol).addImplicitHydrogens(); + else + mol.removeExplicitHydrogens(false, true); boolean[] isRotatableBond = new boolean[mol.getAllBonds()]; - int count = TorsionDB.findRotatableBonds(mol, true, isRotatableBond); + TorsionDB.findRotatableBonds(mol, true, isRotatableBond); + float[] bondFlexibility = new MolecularFlexibilityCalculator().calculateBondFlexibilities(mol, isRotatableBond); int[] fragmentNo = new int[mol.getAllAtoms()]; int fragmentCount = mol.getFragmentNumbers(fragmentNo, isRotatableBond, true); @@ -55,16 +58,16 @@ public ArrayList getFragments(StereoMolecule mol) { for (int atom=0; atom baseFragmentCombinationSet = new TreeSet<>(new IntArrayComparator()); boolean[] isMemberFragment = new boolean[fragmentCount]; for (int i=0; i getFragments(StereoMolecule mol) { * provided that the size criteria are fullfilled. * @param mol */ - private void addNewFragments(StereoMolecule mol, int[] fragmentNo, + private void addNewFragments(StereoMolecule mol, int[] fragmentNo, float[] bondFlexibility, float bondFlexibilitySum, boolean[] isMemberFragment, int previousBaseFragment, int usedBaseFragmentCount, int atomCount, - TreeSet baseFragmentCombinationSet, Fragment3DData[] fragmentData) { - if (usedBaseFragmentCount - mMaxBonds > 1 || atomCount > mMaxAtoms) + TreeSet baseFragmentCombinationSet, BaseFragmentInfo[] baseFragmentInfo) { + if (atomCount > mMaxAtoms) return; int[] baseFragmentList = new int[usedBaseFragmentCount]; @@ -99,12 +102,17 @@ private void addNewFragments(StereoMolecule mol, int[] fragmentNo, if (atomCount >= mMinAtoms) addFragment(mol, fragmentNo, isMemberFragment); - for (int neighbour:fragmentData[previousBaseFragment].neighbourFragment) { - if (!isMemberFragment[neighbour]) { + BaseFragmentInfo previousBaseFragmentInfo = baseFragmentInfo[previousBaseFragment]; + for (int i=0; i centralCoreList = new ArrayList(); for (int fragment=0; fragment Date: Mon, 14 Oct 2024 09:39:10 +0200 Subject: [PATCH 3/3] chore: add comments --- lib/canvas_editor/events.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/canvas_editor/events.js b/lib/canvas_editor/events.js index 28a237f6..daa4df4a 100644 --- a/lib/canvas_editor/events.js +++ b/lib/canvas_editor/events.js @@ -47,9 +47,11 @@ function addPointerListeners(canvasElement, drawArea, JavaEditorArea) { fireMouseEvent(JavaEditorArea.MOUSE_EVENT_CLICKED, ev, ev.detail); }); canvasElement.addEventListener('pointerenter', (ev) => { + // event.detail on pointerenter doesn't include the click count fireMouseEvent(JavaEditorArea.MOUSE_EVENT_ENTERED, ev); }); canvasElement.addEventListener('pointerleave', (ev) => { + // event.detail on pointerenter doesn't include the click count fireMouseEvent(JavaEditorArea.MOUSE_EVENT_EXITED, ev); }); canvasElement.addEventListener('pointermove', (ev) => {