bool PainterFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv) { OBMol* pmol = dynamic_cast<OBMol*>(pOb); if(pmol==NULL) return false; ostream& ofs = *pConv->GetOutStream(); OBMol workingmol(*pmol); // Copy the molecule //*** Coordinate generation *** //Generate coordinates only if no existing 2D coordinates if(!workingmol.Has2D(true)) { OBOp* pOp = OBOp::FindType("gen2D"); if(!pOp) { obErrorLog.ThrowError("PainterFormat", "gen2D not found", obError, onceOnly); return false; } if(!pOp->Do(&workingmol)) { obErrorLog.ThrowError("PainterFormat", string(workingmol.GetTitle()) + "- Coordinate generation unsuccessful", obError); return false; } } if(!workingmol.Has2D() && workingmol.NumAtoms()>1) { string mes("Molecule "); mes += workingmol.GetTitle(); mes += " needs 2D coordinates to display in PNG2format"; obErrorLog.ThrowError("PainterFormat", mes, obError); return false; } CommandPainter painter(*pConv->GetOutStream()); OBDepict depictor(&painter); if(pConv->IsOption("M")) depictor.SetOption(OBDepict::noMargin); depictor.DrawMolecule(&workingmol); return true; //or false to stop converting }
bool FastSearchFormat::ReadChemObject(OBConversion* pConv) { //Searches index file for structural matches //This function is called only once per search std::string auditMsg = "OpenBabel::Read fastsearch index "; std::string description(Description()); auditMsg += description.substr(0,description.find('\n')); obErrorLog.ThrowError(__FUNCTION__, auditMsg, obAuditMsg); //Derive index name string indexname = pConv->GetInFilename(); string::size_type pos=indexname.find_last_of('.'); if(pos!=string::npos) { indexname.erase(pos); indexname += ".fs"; } //Have to open input stream again because needs to be in binary mode ifstream ifs; stringstream errorMsg; if(!indexname.empty()) ifs.open(indexname.c_str(),ios::binary); if(!ifs) { errorMsg << "Couldn't open " << indexname << endl; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError); return false; } string datafilename = fs.ReadIndex(&ifs); if(datafilename.empty()) { errorMsg << "Difficulty reading from index " << indexname << endl; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError); return false; } vector<OBMol> patternMols; if(!ObtainTarget(pConv, patternMols, indexname)) return false; bool exactmatch = pConv->IsOption("e",OBConversion::INOPTIONS)!=NULL;// -ae option //Open the datafile and put it in pConv //datafile name derived from index file probably won't have a file path //but indexname may. Derive a full datafile name string path; pos = indexname.find_last_of("/\\"); if(pos==string::npos) path = datafilename; else path = indexname.substr(0,pos+1) + datafilename; ifstream datastream(path.c_str()); if(!datastream) { errorMsg << "Difficulty opening " << path << endl; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError); return false; } pConv->SetInStream(&datastream); //Input format is currently fs; set it appropriately if(!pConv->SetInAndOutFormats(pConv->FormatFromExt(datafilename.c_str()),pConv->GetOutFormat())) return false; // If target has dative bonds like -[N+](=O)[O-] convert it to the uncharged form // (-N(=O)=O and add uncharged form to vector of mols which are sent to // the -s (SMARTS)filter. // Also check whether the target has dative bonds in the uncharged form and supply // the charged form to the -s filter. // Together with the automatic conversion to the uncharged form when the fs index is made, // this ensures that both forms are found however they occur in the datafile or the taget. vector<OBBase*> extraSMARTSMols; vector<OBMol>extraUnchargedMols; for(unsigned i=0;i<patternMols.size();++i) { if(patternMols[i].ConvertDativeBonds()) extraSMARTSMols.push_back(&patternMols[i]); else { // If target has uncharged dative bonds, still use it for fastsearching, // but add the charged form for -s filter. extraUnchargedMols.push_back(patternMols[i]); if(extraUnchargedMols.back().MakeDativeBonds()) extraSMARTSMols.push_back(&extraUnchargedMols.back()); } } OBOp* sFilter = OBOp::FindType("s"); if(sFilter) sFilter->ProcessVec(extraSMARTSMols); //Now do searching const char* p = pConv->IsOption("t",OBConversion::INOPTIONS); if(p) { //Do a similarity search multimap<double, unsigned int> SeekposMap; string txt=p; if(txt.find('.')==string::npos) { //Finds n molecules with largest Tanimoto int n = atoi(p); fs.FindSimilar(&patternMols[0], SeekposMap, n); } else { //Finds molecules with Tanimoto > MinTani double MaxTani = 1.1; size_t pos = txt.find(','); if( pos != string::npos ) { MaxTani = atof( txt.substr( pos + 1 ).c_str() ); } double MinTani = atof( txt.substr( 0, pos ).c_str() ); fs.FindSimilar(&patternMols[0], SeekposMap, MinTani, MaxTani); } //Don't want to filter through SMARTS filter pConv->RemoveOption("s", OBConversion::GENOPTIONS); //also because op names are case independent pConv->RemoveOption("S", OBConversion::GENOPTIONS); multimap<double, unsigned int>::reverse_iterator itr; for(itr=SeekposMap.rbegin();itr!=SeekposMap.rend();++itr) { datastream.seekg(itr->second); if(pConv->IsOption("a", OBConversion::INOPTIONS)) { //Adds Tanimoto coeff to title //First remove any previous value pConv->RemoveOption("addtotitle", OBConversion::GENOPTIONS); stringstream ss; ss << " " << itr->first; pConv->AddOption("addtotitle",OBConversion::GENOPTIONS, ss.str().c_str()); } pConv->SetOneObjectOnly(); if(itr != --SeekposMap.rend()) pConv->SetMoreFilesToCome();//so that not seen as last on output pConv->Convert(NULL,NULL); } } else { //Structure search int MaxCandidates = 4000; p = pConv->IsOption("l",OBConversion::INOPTIONS); if(p && atoi(p)) MaxCandidates = atoi(p); vector<unsigned int> SeekPositions; if(exactmatch) { //Find mols where all fingerprint bits are the same as the target fs.FindMatch(&patternMols[0], SeekPositions, MaxCandidates); // ensure that SMARTS filter in transform.cpp looks only for an exact match // by setting an option with the number of heavy atoms in the pattern mol included. stringstream ss; ss << patternMols[0].NumHvyAtoms(); pConv->AddOption("exactmatch", OBConversion::GENOPTIONS, ss.str().c_str()); } else { //Do a substructure search for each target vector<OBMol>::iterator iter; for(iter=patternMols.begin();iter!=patternMols.end();++iter) fs.Find(&*iter, SeekPositions, MaxCandidates); clog << SeekPositions.size() << " candidates from fingerprint search phase" << endl; } vector<unsigned int>::iterator seekitr, begin = SeekPositions.begin(), end = SeekPositions.end(); if(patternMols.size()>1)//only sort and eliminate duplicates if necessary { sort(begin, end); end = unique(begin, end); //removed duplicates are after new end } //Output the candidate molecules, filtering through s filter, unless it was not requested if(pConv->IsOption("n", OBConversion::INOPTIONS) ) pConv->RemoveOption("s",OBConversion::GENOPTIONS); pConv->SetLast(false); for(seekitr=begin; seekitr!=end; ++seekitr) { datastream.seekg(*seekitr); if(!pConv->GetInFormat()->ReadChemObject(pConv)) return false; pConv->SetFirstInput(false); //needed for OpSort } } return false; //To finish }
bool OpAlign::Do(OBBase* pOb, const char* OptionText, OpMap* pmap, OBConversion* pConv) { OBMol* pmol = dynamic_cast<OBMol*>(pOb); if(!pmol) return false; map<string,string>::const_iterator itr; // Is there an -s option? if(pConv->IsFirstInput()) { _pOpIsoM = NULL; //assume no -s option itr = pmap->find("s"); if(itr!=pmap->end()) { //There is an -s option; check it is ok _pOpIsoM = static_cast<OpNewS*>(OBOp::FindType("s")); _stext = itr->second; //get its parameter(s) if(!_pOpIsoM || _stext.empty()) { obErrorLog.ThrowError(__FUNCTION__, "No parameter on -s option, or its OBOp version is not loaded", obError); pConv->SetOneObjectOnly(); //to finish return false; } } } // If the output format is a 2D depiction format, then we should align // on the 2D coordinates and not the 3D coordinates (if present). This //means we need to generate the 2D coordinates at this point. if(pmol->GetDimension()==3 && (pConv->GetOutFormat()->Flags() & DEPICTION2D)) { OBOp* pgen = OBOp::FindType("gen2D"); if(pgen) pgen->Do(pmol); } // All molecules must have coordinates, so add them if 0D // They may be added again later when gen2D or gen3D is called, but they will be the same. // It would be better if this op was called after them, which would happen // if its name was alphabetically after "gen" (and before "s"). if(pmol->GetDimension()==0) { //Will the coordinates be 2D or 3D? itr = pmap->find("gen3D"); OBOp* pgen = (itr==pmap->end()) ? OBOp::FindType("gen2D") : OBOp::FindType("gen3D"); if(pgen) pgen->Do(pmol); } //Do the alignment in 2D if the output format is svg, png etc. and there is no -xn option if(pmol->GetDimension()==3 && pConv && !pConv->IsOption("n")) { OBFormat* pOutFormat = pConv->GetOutFormat(); if(pOutFormat->Flags() & DEPICTION2D) { OBOp* pgen = OBOp::FindType("gen2D"); if(pgen) pgen->Do(pmol); } } if(pConv->IsFirstInput() || _refMol.NumAtoms()==0) { _refvec.clear(); // Reference molecule is basically the first molecule _refMol = *pmol; if(!_pOpIsoM) //no -s option. Use a molecule reference. _align.SetRefMol(_refMol); else { //If there is a -s option, reference molecule has only those atoms that are matched //Call the -s option from here bool ret = _pOpIsoM->Do(pmol, _stext.c_str(), pmap, pConv); // Get the atoms that were matched vector<int> ats = _pOpIsoM->GetMatchAtoms(); if(!ats.empty()) { // Make a vector of the matching atom coordinates... for(vector<int>::iterator iter=ats.begin(); iter!=ats.end(); ++iter) _refvec.push_back((pmol->GetAtom(*iter))->GetVector()); // ...and use a vector reference _align.SetRef(_refvec); } // Stop -s option being called normally, although it will still be called once // in the DoOps loop already started for the current (first) molecule. pConv->RemoveOption("s",OBConversion::GENOPTIONS); if(!ret) { // the first molecule did not match the -s option so a reference molecule // could not be made. Keep trying. _refMol.Clear(); //obErrorLog.ThrowError(__FUNCTION__, "The first molecule did not match the -s option\n" // "so the reference structure was not derived from it", obWarning, onceOnly); return false; //not matched } } } //All molecules if(pmol->GetDimension()!= _refMol.GetDimension()) { stringstream ss; ss << "The molecule" << pmol->GetTitle() << " does not have the same dimensions as the reference molecule " << _refMol.GetTitle() << " and is ignored."; obErrorLog.ThrowError(__FUNCTION__, ss.str().c_str(), obError); return false; } if(_pOpIsoM) //Using -s option { //Ignore mol if it does not pass -s option if(!_pOpIsoM->Do(pmol, "", pmap, pConv)) // "" means will use existing parameters return false; // Get the atoms equivalent to those in ref molecule vector<int> ats = _pOpIsoM->GetMatchAtoms(); // Make a vector of their coordinates and get the centroid vector<vector3> vec; vector3 centroid; for(vector<int>::iterator iter=ats.begin(); iter!=ats.end(); ++iter) { vector3 v = pmol->GetAtom(*iter)->GetVector(); centroid += v; vec.push_back(v); } centroid /= vec.size(); // Do the alignment _align.SetTarget(vec); if(!_align.Align()) return false; // Get the centroid of the reference atoms vector3 ref_centroid; for(vector<vector3>::iterator iter=_refvec.begin(); iter!=_refvec.end(); ++iter) ref_centroid += *iter; ref_centroid /= _refvec.size(); //subtract the centroid, rotate the target molecule, then add the centroid matrix3x3 rotmatrix = _align.GetRotMatrix(); for (unsigned int i = 1; i <= pmol->NumAtoms(); ++i) { vector3 tmpvec = pmol->GetAtom(i)->GetVector(); tmpvec -= centroid; tmpvec *= rotmatrix; //apply the rotation tmpvec += ref_centroid; pmol->GetAtom(i)->SetVector(tmpvec); } } else //Not using -s option) { _align.SetTargetMol(*pmol); if(!_align.Align()) return false; _align.UpdateCoords(pmol); } //Save rmsd as a property OBPairData* dp = new OBPairData; dp->SetAttribute("rmsd"); double val = _align.GetRMSD(); if(val<1e-12) val = 0.0; dp->SetValue(toString(val)); dp->SetOrigin(local); pmol->SetData(dp); return true; }
bool SVGFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv) { OBMol* pmol = dynamic_cast<OBMol*>(pOb); if(!pmol) return false; ostream &ofs = *pConv->GetOutStream(); //Check for option for single mol in fixed size image const char* fixedpx = pConv->IsOption("P"); if(!fixedpx) fixedpx= pConv->IsOption("px", OBConversion::GENOPTIONS); //If WriteMolecule called directly, e.g. from OBConversion::Write() //the default mode is a fixed image size of 200px square if(!fixedpx && !pConv->IsOption("svgwritechemobject")) fixedpx = "200"; if(fixedpx) { _nmax = _nrows = _ncols = 1; pConv->AddOption("j"); pConv->SetLast(true); pConv->SetOutputIndex(1); } //*** Coordinate generation *** //Generate coordinates only if no existing 2D coordinates if( (pConv->IsOption("y") || !pmol->Has2D(true)) && !pConv->IsOption("n") ) { OBOp* pOp = OBOp::FindType("gen2D"); if(!pOp) { obErrorLog.ThrowError("SVGFormat", "gen2D not found", obError, onceOnly); return false; } if(!pOp->Do(pmol)) { obErrorLog.ThrowError("SVGFormat", string(pmol->GetTitle()) + "- Coordinate generation unsuccessful", obError); return false; } } if(!pmol->Has2D() && pmol->NumAtoms()>1)//allows 3D coordinates (if passed by -xn above) { string mes("Molecule "); mes += pmol->GetTitle(); mes += " needs 2D coordinates to display in SVGformat"; obErrorLog.ThrowError("SVGFormat", mes, obError); return false; } bool hasTable = (_nrows || _ncols); bool transparent=false; string background, bondcolor; const char* bg = pConv->IsOption("b"); background = bg ? "black" : "white"; bondcolor = bg ? "white" : "black"; if(bg && (!strcmp(bg, "none") || bg[0]=='0')) { transparent = true; bondcolor = "gray"; } const char* bcol = pConv->IsOption("B"); if(bcol && *bcol) bondcolor = bcol; if(bg && *bg) background = bg; if(pConv->GetOutputIndex()==1 || fixedpx) { //For the first molecule... if(hasTable) { //multiple molecules - use a table //Outer svg has viewbox for 0 0 100 100 or adjusted for table shape, //and no width or height - it uses the whole of its containing element. //Inner svg with width, height, x, y of table cell, //and viewbox to match molecule min and max x and y if(!pConv->IsOption("x")) ofs << "<?xml version=\"1.0\"?>\n"; ofs << "<svg version=\"1.1\" id=\"topsvg\"\n" "xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" "xmlns:cml=\"http://www.xml-cml.org/schema\" "; //*** Outer viewbox *** double vbwidth=100, vbheight=100; if (_nrows>_ncols) vbwidth = (100*_ncols)/_nrows; else if(_ncols>_nrows) vbheight = (100*_nrows)/_ncols; if(fixedpx)//fixed size image ofs << "x=\"0\" y=\"0\" width=\"" << fixedpx << "px\" height=\"" << fixedpx <<"px\" "; else ofs << "x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" "; ofs << "viewBox=\"0 0 " << vbwidth << ' ' << vbheight << "\">\n"; ofs << "<title>OBDepict</title>\n"; // Draw the background unless transparent if(!transparent) ofs << "<rect x=\"0\" y=\"0\" width=\"" << vbwidth << "\" height=\"" << vbheight << "\" fill=\"" << background << "\"/>\n"; } } //All mols double cellsize; if(hasTable) { //*** Parameter for inner svg *** int nc = _ncols ? _ncols : 1; int nr = (_nrows ? _nrows : 1); cellsize = 100. / std::max(nc, nr); int indx = pConv->GetOutputIndex() - 1; double innerX = (indx % nc) * cellsize; double innerY = (indx / nc) * cellsize; // Change the background in this cell if the condition in the first // parameter of the -xh option is met. Use a default color if // the highlight color is not specified in the second parameter. const char* htxt = pConv->IsOption("h"); if(htxt) { vector<string> vec; tokenize(vec, htxt); string highlight(vec.size()>1 ? vec[1] : "#f4f0ff"); std::istringstream conditionText(vec[0]); if(OBDescriptor::FilterCompare(pOb, conditionText, false)) //Still in outer <svg>, unfortunately ofs << "<rect x=\"" << innerX << "\" y=\"" << innerY << "\" width=\"" << cellsize << "\" height=\"" << cellsize << "\" fill=\"" << highlight << "\"/>\n"; } //*** Write molecule name *** if(!pConv->IsOption("d")) ofs << "<text text-anchor=\"middle\" font-size=\"" << 0.06*cellsize << "\"" << " fill =\"" << bondcolor << "\" font-family=\"sans-serif\"\n" << "x=\"" << innerX + cellsize * 0.5 << "\" y=\"" << innerY + cellsize - 2.0/nr << "\" >" << pmol->GetTitle() << "</text>\n"; SVGPainter painter(*pConv->GetOutStream(), true, cellsize,cellsize,innerX,innerY); OBDepict depictor(&painter); if(!pConv->IsOption("C")) depictor.SetOption(OBDepict::drawTermC);// on by default if(pConv->IsOption("a")) depictor.SetOption(OBDepict::drawAllC); if(pConv->IsOption("A")) { AliasData::RevertToAliasForm(*pmol); depictor.SetAliasMode(); } painter.SetFontFamily("sans-serif"); painter.SetPenColor(OBColor(bondcolor)); depictor.SetBondColor(bondcolor); if(pConv->IsOption("t")) painter.SetPenWidth(4); else painter.SetPenWidth(2); //No element-specific atom coloring if requested if(pConv->IsOption("u")) depictor.SetOption(OBDepict::bwAtoms); if(!pConv->IsOption("U")) depictor.SetOption(OBDepict::internalColor); if(pConv->IsOption("s")) depictor.SetOption(OBDepict::asymmetricDoubleBond); depictor.DrawMolecule(pmol); //Draw atom indices if requested if(pConv->IsOption("i")) depictor.AddAtomLabels(OBDepict::AtomIndex); //Embed CML of molecule if requested if(pConv->IsOption("e")) EmbedCML(pmol,pConv); } else //single molecule { //Nothing written until DrawMolecule call //Final </svg> written at the end of this block (painter destructor) //This leads to some code duplication. double factor = 1.0; SVGPainter painter(*pConv->GetOutStream(), false); OBDepict depictor(&painter); //Scale image by specifying the average bond length in pixels. const char* ppx = pConv->IsOption("p"); if(!ppx) ppx= pConv->IsOption("px", OBConversion::GENOPTIONS); if(ppx) { double oldblen = depictor.GetBondLength(); double newblen = atof(ppx); depictor.SetBondLength(newblen); factor = newblen / oldblen; //Scale bondspacing and font size by same factor depictor.SetBondSpacing(depictor.GetBondSpacing() * factor); depictor.SetFontSize((int)(depictor.GetFontSize() * factor)); } if(pConv->IsOption("W")) depictor.SetOption(OBDepict::noWedgeHashGen); if(!pConv->IsOption("C")) depictor.SetOption(OBDepict::drawTermC);// on by default if(pConv->IsOption("a")) depictor.SetOption(OBDepict::drawAllC); if(pConv->IsOption("A")) { AliasData::RevertToAliasForm(*pmol); depictor.SetAliasMode(); } painter.SetFontFamily("sans-serif"); painter.SetPenColor(OBColor(bondcolor)); depictor.SetBondColor(bondcolor); painter.SetFillColor(OBColor(background)); if(pConv->IsOption("t")) painter.SetPenWidth(4); else painter.SetPenWidth(1); //No element-specific atom coloring if requested if(pConv->IsOption("u")) depictor.SetOption(OBDepict::bwAtoms); if(!pConv->IsOption("U")) depictor.SetOption(OBDepict::internalColor); if(pConv->IsOption("s")) depictor.SetOption(OBDepict::asymmetricDoubleBond); depictor.DrawMolecule(pmol); //Draw atom indices if requested if(pConv->IsOption("i")) depictor.AddAtomLabels(OBDepict::AtomIndex); //*** Write molecule name *** if(!pConv->IsOption("d")) ofs << "<text font-size=\"" << 18 * factor << "\"" << " fill =\"" << bondcolor << "\" font-family=\"sans-serif\"\n" << "x=\"" << 10 * factor << "\" y=\"" << 20 * factor << "\" >" << pmol->GetTitle() << "</text>\n"; //*** Write page title name *** ofs << "<title>" << pmol->GetTitle() << " - OBDepict</title>\n"; //Embed CML of molecule if requested if(pConv->IsOption("e")) EmbedCML(pmol,pConv); } if(hasTable && pConv->IsLast()) { //Draw grid lines if(_nrows && _ncols && pConv->IsOption("l")) { for(int i=1; i<_nrows; ++i) ofs << " <line stroke=\"gray\" stroke-width=\"0.1\" x1=\"0\" x2=\"100\"" << " y1=\"" << i*cellsize << "\" y2=\"" << i*cellsize << "\"/>\n"; for(int i=1; i<_ncols; ++i) ofs << " <line stroke=\"gray\" stroke-width=\"0.1\" y1=\"0\" y2=\"100\"" << " x1=\"" << i*cellsize << "\" x2=\"" << i*cellsize << "\"/>\n"; } //Insert javascript for zooming and panning if(!pConv->IsOption("j")) EmbedScript(ofs); ofs << "</svg>\n" << endl;//Outer svg } return !fixedpx; // return false with fixed size image because only 1 mol }
bool SVGFormat::WriteSVG(OBConversion* pConv, vector<OBBase*>& molecules) { bool ret=true; //Check for option for single mol in fixed size image const char* fixedpx = pConv->IsOption("P"); if(!fixedpx) fixedpx= pConv->IsOption("px", OBConversion::GENOPTIONS); //If WriteMolecule called directly, e.g. from OBConversion::Write() //the default mode is a fixed image size of 200px square if(!fixedpx && molecules.size()==1) fixedpx = "200"; if(fixedpx) { _nmax = _nrows = _ncols = 1; pConv->AddOption("j"); } ostream &ofs = *pConv->GetOutStream(); bool hasTable = (_nrows>1) || (_ncols>1); bool transparent=false; string background, bondcolor; const char* bg = pConv->IsOption("b"); background = bg ? "black" : "white"; bondcolor = bg ? "white" : "black"; if(bg && (!strcmp(bg, "none") || bg[0]=='0')) { transparent = true; bondcolor = "gray"; } const char* bcol = pConv->IsOption("B"); if(bcol && *bcol) bondcolor = bcol; if(bg && *bg) background = bg; if(!pConv->IsOption("x")) ofs << "<?xml version=\"1.0\"?>\n"; ofs << "<svg version=\"1.1\" id=\"topsvg\"\n" "xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" "xmlns:cml=\"http://www.xml-cml.org/schema\" "; double vbwidth=100, vbheight=100; if (_nrows>_ncols) vbwidth = (100*_ncols)/_nrows; else if(_ncols>_nrows) vbheight = (100*_nrows)/_ncols; if(fixedpx)//fixed size image ofs << "x=\"0\" y=\"0\" width=\"" << fixedpx << "px\" height=\"" << fixedpx <<"px\" "; else ofs << "x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" "; ofs << "viewBox=\"0 0 " << vbwidth << ' ' << vbheight << "\">\n"; if (hasTable) ofs << "<title>Multiple Molecules - Open Babel Depiction</title>\n"; else if(molecules.size() == 1) ofs << "<title>" << molecules[0]->GetTitle() << " - Open Babel Depiction</title>\n"; // Draw the background unless transparent if(!transparent) ofs << "<rect x=\"0\" y=\"0\" width=\"" << vbwidth << "\" height=\"" << vbheight << "\" fill=\"" << background << "\"/>\n"; unsigned opts = 0; if(pConv->IsOption("u")) opts |= OBDepict::bwAtoms; if(!pConv->IsOption("U")) opts |= OBDepict::internalColor; if(!pConv->IsOption("C")) opts |= OBDepict::drawTermC;// on by default if(pConv->IsOption("a")) opts |= OBDepict::drawAllC; if(pConv->IsOption("W")) opts |= OBDepict::noWedgeHashGen; if(pConv->IsOption("s")) opts |= OBDepict::asymmetricDoubleBond; if(pConv->IsOption("X")) opts |= OBDepict::allExplicit; bool balldepict = false; if(pConv->IsOption("S")) balldepict = true; double factor = 1.0; int nc = _ncols ? _ncols : 1; int nr = (_nrows ? _nrows : 1); double cellsize = 100. / std::max(nc, nr); stringstream molfs; std::set<ColorGradient> gradients; OBOp* pOp = OBOp::FindType("gen2D"); if(!pOp) { obErrorLog.ThrowError("SVGFormat", "gen2D not found", obError, onceOnly); return false; } vector<OBBase*>::iterator iter; int indx = 0; for(iter=_objects.begin(); ret && iter!=_objects.end(); ++iter,++indx) { OBMol* pmol = dynamic_cast<OBMol*>(*iter); if (!pmol) continue; //*** Coordinate generation *** //Generate coordinates only if no existing 2D coordinates if( (pConv->IsOption("y") || !pmol->Has2D(true)) && !pConv->IsOption("n") ) { if(!pOp->Do(pmol)) { obErrorLog.ThrowError("SVGFormat", string(pmol->GetTitle()) + "- Coordinate generation unsuccessful", obError); return false; } } if(!pmol->Has2D() && pmol->NumAtoms()>1)//allows 3D coordinates (if passed by -xn above) { string mes("Molecule "); mes += pmol->GetTitle(); mes += " needs 2D coordinates to display in SVGformat"; obErrorLog.ThrowError("SVGFormat", mes, obError); return false; } double innerX = 0.0; double innerY = 0.0; if(hasTable) { //*** Parameter for inner svg *** innerX = (indx % nc) * cellsize; innerY = (indx / nc) * cellsize; // Change the background in this cell if the condition in the first // parameter of the -xh option is met. Use a default color if // the highlight color is not specified in the second parameter. const char* htxt = pConv->IsOption("h"); if(htxt) { vector<string> vec; tokenize(vec, htxt); string highlight(vec.size()>1 ? vec[1] : "#f4f0ff"); std::istringstream conditionText(vec[0]); if(OBDescriptor::FilterCompare(*iter, conditionText, false)) //Still in outer <svg>, unfortunately molfs << "<rect x=\"" << innerX << "\" y=\"" << innerY << "\" width=\"" << cellsize << "\" height=\"" << cellsize << "\" fill=\"" << highlight << "\"/>\n"; } } SVGPainter painter(molfs, &gradients, true, cellsize, cellsize); OBDepict depictor(&painter, balldepict); depictor.SetOption(opts); if(pConv->IsOption("A")) { AliasData::RevertToAliasForm(*pmol); depictor.SetAliasMode(); } painter.SetFontFamily("sans-serif"); painter.SetPenColor(OBColor(bondcolor)); depictor.SetBondColor(bondcolor); if(pConv->IsOption("t")) painter.SetPenWidth(4); else painter.SetPenWidth(2); molfs << "<g transform=\"translate(" << innerX << "," << innerY << ")\">\n"; ret = depictor.DrawMolecule(pmol); //Draw atom indices if requested if(pConv->IsOption("i")) depictor.AddAtomLabels(OBDepict::AtomIndex); painter.EndCanvas(); //Embed CML of molecule if requested if(pConv->IsOption("e")) EmbedCML(pmol, pConv, &molfs); molfs <<"</g>\n"; //*** Write molecule name *** if(!pConv->IsOption("d")) if(hasTable) molfs << "<text text-anchor=\"middle\" font-size=\"" << 0.06*cellsize << "\"" << " fill =\"" << bondcolor << "\" font-family=\"sans-serif\"\n" << "x=\"" << innerX + cellsize * 0.5 << "\" y=\"" << innerY + cellsize - 2.0/nr << "\" >" << pmol->GetTitle() << "</text>\n"; else molfs << "<text font-size=\"" << 18 * factor << "\"" << " fill =\"" << bondcolor << "\" font-family=\"sans-serif\"\n" << "x=\"" << 10 * factor << "\" y=\"" << 20 * factor << "\" >" << pmol->GetTitle() << "</text>\n"; } // finally write svg defs SVGPainter painter(ofs, &gradients, true, cellsize,cellsize); painter.WriteDefs(); // and stream back all molecule data ofs << molfs.str(); //Draw grid lines if(hasTable && pConv->IsOption("l")) { for(int i=1; i<_nrows; ++i) ofs << " <line stroke=\"gray\" stroke-width=\"0.1\" x1=\"0\" x2=\"100\"" << " y1=\"" << i*cellsize << "\" y2=\"" << i*cellsize << "\"/>\n"; for(int i=1; i<_ncols; ++i) ofs << " <line stroke=\"gray\" stroke-width=\"0.1\" y1=\"0\" y2=\"100\"" << " x1=\"" << i*cellsize << "\" x2=\"" << i*cellsize << "\"/>\n"; } //Insert javascript for zooming and panning if(!pConv->IsOption("j")) EmbedScript(ofs); ofs << "</svg>\n"; return ret; }
bool SVGFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv) { OBMol* pmol = dynamic_cast<OBMol*>(pOb); if(!pmol) return false; ostream &ofs = *pConv->GetOutStream(); //*** Coordinate generation *** //Generate coordinates only if no existing 2D coordinates if( (pConv->IsOption("y") || !pmol->Has2D(true)) && !pConv->IsOption("n") ) { OBOp* pOp = OBOp::FindType("gen2D"); if(!pOp) { obErrorLog.ThrowError("SVGFormat", "gen2D not found", obError, onceOnly); return false; } if(!pOp->Do(pmol)) { obErrorLog.ThrowError("SVGFormat", string(pmol->GetTitle()) + "- Coordinate generation unsuccessful", obError); return false; } } if(!pmol->Has2D() && pmol->NumAtoms()>1)//allows 3D coordinates (if passed by -xn above) { string mes("Molecule "); mes += pmol->GetTitle(); mes += " needs 2D coordinates to display in SVGformat"; obErrorLog.ThrowError("SVGFormat", mes, obError); return false; } bool hasTable = (_nrows || _ncols); string background = pConv->IsOption("b") ? "black" : "white"; string bondcolor = pConv->IsOption("b") ? "white" : "black"; if(pConv->GetOutputIndex()==1) { //For the first molecule... if(hasTable) { //multiple molecules - use a table //Outer svg has viewbox for 0 0 100 100 or adjusted for table shape, //and no width or height - it uses the whole of its containing element. //Inner svg with width, height, x, y of table cell, //and viewbox to match molecule min and max x and y if(!pConv->IsOption("x")) ofs << "<?xml version=\"1.0\"?>\n"; ofs << "<svg version=\"1.1\" id=\"topsvg\"\n" "xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" "xmlns:cml=\"http://www.xml-cml.org/schema\" "; //*** Outer viewbox *** double vbwidth=100, vbheight=100; if (_nrows>_ncols) vbwidth = (100*_ncols)/_nrows; else if(_ncols>_nrows) vbheight = (100*_nrows)/_ncols; ofs << "viewBox=\"0 0 " << vbwidth << ' ' << vbheight << "\">\n"; ofs << "<title>OBDepict</title>\n"; // Draw the background ofs << "<rect x=\"0\" y=\"0\" width=\"" << vbwidth << "\" height=\"" << vbheight << "\" fill=\"" << background << "\"/>\n"; } } //All mols double cellsize; if(hasTable) { //*** Parameter for inner svg *** int nc = _ncols ? _ncols : 1; int nr = (_nrows ? _nrows : 1); cellsize = 100. / std::max(nc, nr); int indx = pConv->GetOutputIndex() - 1; double innerX = (indx % nc) * cellsize; double innerY = (indx / nc) * cellsize; //*** Write molecule name *** if(!pConv->IsOption("d")) ofs << "<text text-anchor=\"middle\" font-size=\"" << 0.06*cellsize << "\"" << " fill =\"" << bondcolor << "\" font-family=\"sans-serif\"\n" << "x=\"" << innerX + cellsize * 0.5 << "\" y=\"" << innerY + cellsize - 2.0/nr << "\" >" << pmol->GetTitle() << "</text>\n"; SVGPainter painter(*pConv->GetOutStream(), true, cellsize,cellsize,innerX,innerY); OBDepict depictor(&painter); if(pConv->IsOption("w")) depictor.SetOption(OBDepict::genWedgeHash); if(!pConv->IsOption("C")) depictor.SetOption(OBDepict::drawTermC);// on by default if(pConv->IsOption("a")) depictor.SetOption(OBDepict::drawAllC); if(pConv->IsOption("A")) { AliasData::RevertToAliasForm(*pmol); depictor.SetAliasMode(); } painter.SetFontFamily("sans-serif"); painter.SetPenColor(OBColor(bondcolor)); depictor.SetBondColor(bondcolor); painter.SetPenWidth(2); //No element-specific atom coloring if requested if(pConv->IsOption("u")) depictor.SetOption(OBDepict::bwAtoms); if(!pConv->IsOption("U")) depictor.SetOption(OBDepict::internalColor); depictor.DrawMolecule(pmol); //Draw atom indices if requested if(pConv->IsOption("i")) depictor.AddAtomLabels(OBDepict::AtomIndex); //Embed CML of molecule if requested if(pConv->IsOption("e")) EmbedCML(pmol,pConv); } else //single molecule { //Nothing written until DrawMolecule call //Final </svg> written at the end of this block (painter destructor) //This leads to some code duplication. double factor = 1.0; SVGPainter painter(*pConv->GetOutStream()); OBDepict depictor(&painter); //Scale image by specifying the average bond length in pixels. if(pConv->IsOption("p")) { double oldblen = depictor.GetBondLength(); double newblen = atof(pConv->IsOption("p")); depictor.SetBondLength(newblen); factor = newblen / oldblen; //Scale bondspacing and font size by same factor depictor.SetBondSpacing(depictor.GetBondSpacing() * factor); depictor.SetFontSize((int)(depictor.GetFontSize() * factor)); } if(!pConv->IsOption("w")) depictor.SetOption(OBDepict::genWedgeHash); if(!pConv->IsOption("C")) depictor.SetOption(OBDepict::drawTermC);// on by default if(pConv->IsOption("a")) depictor.SetOption(OBDepict::drawAllC); if(pConv->IsOption("A")) { AliasData::RevertToAliasForm(*pmol); depictor.SetAliasMode(); } painter.SetFontFamily("sans-serif"); painter.SetPenColor(OBColor(bondcolor)); depictor.SetBondColor(bondcolor); painter.SetFillColor(OBColor(background)); painter.SetPenWidth(1); //No element-specific atom coloring if requested if(pConv->IsOption("u")) depictor.SetOption(OBDepict::bwAtoms); if(!pConv->IsOption("U")) depictor.SetOption(OBDepict::internalColor); depictor.DrawMolecule(pmol); //Draw atom indices if requested if(pConv->IsOption("i")) depictor.AddAtomLabels(OBDepict::AtomIndex); //*** Write molecule name *** if(!pConv->IsOption("d")) ofs << "<text font-size=\"" << 18 * factor << "\"" << " fill =\"" << bondcolor << "\"\n" << "x=\"" << 140 * factor << "\" y=\"" << 20 * factor << "\" >" << pmol->GetTitle() << "</text>\n"; //*** Write page title name *** ofs << "<title>" << pmol->GetTitle() << " - OBDepict</title>\n"; //Embed CML of molecule if requested if(pConv->IsOption("e")) EmbedCML(pmol,pConv); } if(hasTable && pConv->IsLast()) { //Draw grid lines if(_nrows && _ncols && pConv->IsOption("l")) { for(int i=1; i<_nrows; ++i) ofs << " <line stroke=\"gray\" stroke-width=\"0.1\" x1=\"0\" x2=\"100\"" << " y1=\"" << i*cellsize << "\" y2=\"" << i*cellsize << "\"/>\n"; for(int i=1; i<_ncols; ++i) ofs << " <line stroke=\"gray\" stroke-width=\"0.1\" y1=\"0\" y2=\"100\"" << " x1=\"" << i*cellsize << "\" x2=\"" << i*cellsize << "\"/>\n"; } //Insert javascript for zooming and panning if(!pConv->IsOption("j")) EmbedScript(ofs); ofs << "</svg>\n" << endl;//Outer svg } return true; }