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