1 /**********************************************************************
2 ophighlight.cpp
3 Copyright (C) 2012 by Noel O'Boyle
4 Based on opisomorph.cpp Copyright (C) 2010 by Chris Morley
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 ***********************************************************************/
15 #include <openbabel/babelconfig.h>
16 #include <openbabel/parsmart.h>
17 #include <openbabel/obconversion.h>
18 #include<openbabel/op.h>
19 #include <openbabel/generic.h>
20 #include <openbabel/bond.h>
21 #include <openbabel/mol.h>
22 #include <algorithm>
23 
24 namespace OpenBabel
25 {
26 
27 using namespace std;
28 
29 
30 
31 
32 //*****************************************************
33 
34 class OpHighlight : public OBOp
35 {
36 public:
OpHighlight(const char * ID)37   OpHighlight(const char* ID) : OBOp(ID, false){};
Description()38   const char* Description(){ return
39     "<param> Highlight substructures in 2D depictions\n"
40     "Usage: --highlight \"SMARTS1 color1 [SMARTS2 color2 ...]\"\n"
41     "\n"
42     "Valid colors are black, gray, white, red, green, blue, yellow,\n"
43     "                 cyan, purple, teal and olive.\n"
44     "Additional colors may be specified as hexadecimal RGB values\n"
45     "preceded by #.\n\n"
46     "The following will color the phenyl group green, and the\n"
47     "carboxyl group orange:\n"
48     "  obabel -:\"c1ccccc1CCC(=O)O\" -O mol.svg\n"
49     "     --highlight \"c1ccccc1 green C(=O)O #FFA500\"";
50   }
51 
WorksWith(OBBase * pOb) const52   virtual bool WorksWith(OBBase* pOb) const { return dynamic_cast<OBMol*>(pOb) != nullptr; }
53   virtual bool Do(OBBase* pOb, const char* OptionText=nullptr, OpMap* pOptions=nullptr, OBConversion* pConv=nullptr);
54 private:
55   bool AddDataToSubstruct(OBMol* pmol,
56         const std::vector<int>& atomIdxs,
57         const std::string& attribute,
58         const std::string& value);
59 };
60 
61 /////////////////////////////////////////////////////////////////
62 OpHighlight theOpHighlight("highlight"); //Global instance
63 
64 //////////////////////////////////////////////////////////////////
Do(OBBase * pOb,const char * OptionText,OpMap * pmap,OBConversion * pConv)65 bool OpHighlight::Do(OBBase* pOb, const char* OptionText, OpMap* pmap, OBConversion* pConv)
66 {
67   OBMol* pmol = dynamic_cast<OBMol*>(pOb);
68   if(!pmol)
69     return false;
70   std::vector<std::string> vec;
71   tokenize(vec, OptionText);
72 
73   // Jump in twos over the parameters!
74   // "SMARTS1 color1 SMARTS2 color2 ..."
75   for(unsigned int vecIdx = 0; vecIdx < vec.size(); vecIdx += 2)
76   {
77 
78     std::string smarts = vec[vecIdx];
79     if(vecIdx + 1 == vec.size())
80     {
81       string msg = "No color specified for SMARTS string: " + smarts;
82       obErrorLog.ThrowError(__FUNCTION__, msg, obError, onceOnly);
83       delete pmol;
84       pmol = nullptr;
85       pConv->SetOneObjectOnly(); //stop conversion
86       return false;
87     }
88     std::string color = vec[vecIdx + 1];
89 
90     bool match = false;
91     //These are a vector of each mapping, each containing atom indxs.
92     vector<vector<int> > vecatomvec;
93     vector<vector<int> >* pMappedAtoms = nullptr;
94     OBSmartsPattern sp;
95 
96     // Explicit H in SMARTS requires explicit H in the molecule.
97     // Calling AddHydrogens() on a copy of the molecule  is done in parsmart.cpp
98     // only when SMARTS contains [H]. Doing more has complications with atom typing,
99     // so AddHydrogens here on the molecule (not a copy) when #1 detected.
100     bool addHydrogens = (smarts.find("#1]")!=string::npos);
101 
102     if(!sp.Init(smarts))
103     {
104       string msg = smarts + " cannot be interpreted as a valid SMARTS ";
105       obErrorLog.ThrowError(__FUNCTION__, msg, obError, onceOnly);
106       delete pmol;
107       pmol = nullptr;
108       pConv->SetOneObjectOnly(); //stop conversion
109       return false;
110     }
111 
112     if(addHydrogens)
113       pmol->AddHydrogens(false,false);
114 
115     if( (match = sp.Match(*pmol)) ) // extra parens to indicate truth value
116       pMappedAtoms = &sp.GetMapList();
117 
118     if(match)
119     {
120       vector<vector<int> >::iterator iter;
121       for(iter=pMappedAtoms->begin();iter!=pMappedAtoms->end();++iter)//each match
122          AddDataToSubstruct(pmol, *iter, "color", color);
123     }
124   }
125 
126   return true;
127 }
128 
129 // Taken from opisomorph.cpp
AddDataToSubstruct(OBMol * pmol,const std::vector<int> & atomIdxs,const std::string & attribute,const std::string & value)130 bool OpHighlight::AddDataToSubstruct(OBMol* pmol,
131         const std::vector<int>& atomIdxs,
132         const std::string& attribute,
133         const std::string& value)
134 {
135   //Add data to atoms
136   for(unsigned int j = 0; j < atomIdxs.size(); ++j)
137   {
138     OBAtom* pAtom = pmol->GetAtom(atomIdxs[j]);
139     if(!pAtom)
140       continue;
141     OBPairData* dp = new OBPairData;
142     dp->SetAttribute(attribute);
143     dp->SetValue(value);
144     pAtom->SetData(dp);
145   }
146 
147   OBBond* pBond;
148   vector<OBBond*>::iterator i;
149   for(pBond = pmol->BeginBond(i); pBond; pBond = pmol->NextBond(i))
150   {
151     //Add data to bond if it joins two atoms in list
152     if(count(atomIdxs.begin(), atomIdxs.end(), pBond->GetBeginAtomIdx())
153         && count(atomIdxs.begin(), atomIdxs.end(), pBond->GetEndAtomIdx()))
154     {
155       OBPairData* dp = new OBPairData;
156       dp->SetAttribute(attribute);
157       dp->SetValue(value);
158       pBond->SetData(dp);
159     }
160   }
161   return true;
162 }
163 
164 }//namespace
165 
166