bool OBDepict::AddAtomLabels(AtomLabelType type) { d->painter->SetPenColor(OBColor("red")); d->painter->SetFillColor(OBColor("red")); d->painter->SetFontSize((int)(GetFontSize() * 0.8));// smaller text OBAtomIterator i; for (OBAtom *atom = d->mol->BeginAtom(i); atom; atom = d->mol->NextAtom(i)) { vector3 pos(atom->GetVector()); std::stringstream ss; switch (type) { case AtomId: ss << atom->GetId(); d->painter->DrawText(pos.x(), pos.y(), ss.str()); break; case AtomSymmetryClass: ss << GetAtomSymClass(atom); d->painter->DrawText(pos.x(), pos.y(), ss.str()); break; case AtomIndex: ss << atom->GetIdx(); d->painter->DrawText(pos.x(), pos.y(), ss.str()); break; default: break; } } return true; }
void CairoPainter::NewCanvas(double width, double height) { double titleheight = m_title.empty() ? 0.0 : 16.0; if (m_index == 1) { // create new surface to paint on if(m_cropping) { double ratio = width / height; if(ratio > 1.0) m_height = m_height / ratio; else m_width = m_width * ratio; } m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, static_cast<int> (m_width), static_cast<int> (m_height)); m_cairo = cairo_create(m_surface); if(m_transparent) cairo_set_source_rgba (m_cairo, 0.0, 0.0, 0.0, 0.0); else { OBColor bg = OBColor(m_fillcolor); cairo_set_source_rgb (m_cairo, bg.red, bg.green, bg.blue); } cairo_paint (m_cairo); cairo_set_line_width(m_cairo, m_pen_width); } else { // reset transformation matrix cairo_identity_matrix(m_cairo); } // Work out some things! double cellwidth = m_width/m_ncols; double cellheight = m_height/m_nrows; int row = (m_index - 1)/m_ncols + 1; int col = m_index - ((row-1)*m_ncols); // Work out the scaling factor double scale_x = cellwidth / (double) width; double scale_y = (cellheight-titleheight) / (double) height; // Leave some extra space for the title if present double scale = std::min(scale_x, scale_y); // Add the title if (!m_title.empty()) { this->SetPenColor(OBColor(m_bondcolor)); this->SetFontSize(static_cast<int>(16.0)); OBFontMetrics fm = this->GetFontMetrics(m_title); this->DrawText(cellwidth/2.0 - fm.width/2.0 + cellwidth*(col-1), cellheight - fm.height * 0.25 + cellheight*(row-1), m_title); } // Translate the over-scaled dimension into the centre if (scale < scale_y) cairo_translate(m_cairo, 0 + cellwidth*(col-1), cellheight/2.0 - scale*height/2.0 + cellheight*(row-1)); else cairo_translate(m_cairo, cellwidth/2.0 - scale*width/2.0 + cellwidth*(col-1), 0 + cellheight*(row-1)); cairo_scale(m_cairo, scale, scale); // Set a scaling transformation }
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 OBDepict::DrawMolecule(OBMol *mol) { if (!d->painter) return false; d->mol = mol; double width=0.0, height=0.0; OBAtom *atom; OBBondIterator j; OBAtomIterator i; if(mol->NumAtoms()>0) { // scale bond lengths double bondLengthSum = 0.0; for (OBBond *bond = mol->BeginBond(j); bond; bond = mol->NextBond(j)) bondLengthSum += bond->GetLength(); const double averageBondLength = bondLengthSum / mol->NumBonds(); const double f = mol->NumBonds() ? d->bondLength / averageBondLength : 1.0; for (atom = mol->BeginAtom(i); atom; atom = mol->NextAtom(i)) atom->SetVector(atom->GetX() * f, atom->GetY() * f, 0.0); // find min/max values double min_x, max_x; double min_y, max_y; atom = mol->BeginAtom(i); min_x = max_x = atom->GetX(); min_y = max_y = atom->GetY(); for (atom = mol->NextAtom(i); atom; atom = mol->NextAtom(i)) { min_x = std::min(min_x, atom->GetX()); max_x = std::max(max_x, atom->GetX()); min_y = std::min(min_y, atom->GetY()); max_y = std::max(max_y, atom->GetY()); } const double margin = 40.0; // translate all atoms so the bottom-left atom is at margin,margin for (atom = mol->BeginAtom(i); atom; atom = mol->NextAtom(i)) atom->SetVector(atom->GetX() - min_x + margin, atom->GetY() - min_y + margin, 0.0); width = max_x - min_x + 2*margin; height = max_y - min_y + 2*margin; //d->painter->SetPenWidth(d->penWidth); //d->painter->SetPenColor(d->pen)); //d->painter->SetFillColor(OBColor("black")); } d->painter->NewCanvas(width, height); // draw bonds if(d->options & genWedgeHash) d->SetWedgeAndHash(mol); for (OBBond *bond = mol->BeginBond(j); bond; bond = mol->NextBond(j)) { OBAtom *begin = bond->GetBeginAtom(); OBAtom *end = bond->GetEndAtom(); if((d->options & internalColor) && bond->HasData("color")) d->painter->SetPenColor(OBColor(bond->GetData("color")->GetValue())); else d->painter->SetPenColor(d->bondColor); if (bond->IsWedge()) { d->DrawWedge(begin, end); } else if (bond->IsHash()) { d->DrawHash(begin, end); } else if (!bond->IsInRing()) { d->DrawSimpleBond(begin, end, bond->GetBO()); } } // draw ring bonds std::vector<OBRing*> rings(mol->GetSSSR()); OBBitVec drawnBonds; for (std::vector<OBRing*>::iterator k = rings.begin(); k != rings.end(); ++k) { OBRing *ring = *k; std::vector<int> indexes = ring->_path; vector3 center(VZero); for (std::vector<int>::iterator l = indexes.begin(); l != indexes.end(); ++l) { center += mol->GetAtom(*l)->GetVector(); } center /= indexes.size(); for (unsigned int l = 0; l < indexes.size(); ++l) { OBAtom *begin = mol->GetAtom(indexes[l]); OBAtom *end; if (l+1 < indexes.size()) end = mol->GetAtom(indexes[l+1]); else end = mol->GetAtom(indexes[0]); OBBond *ringBond = mol->GetBond(begin, end); if (drawnBonds.BitIsSet(ringBond->GetId())) continue; if((d->options & internalColor) && ringBond->HasData("color")) d->painter->SetPenColor(OBColor(ringBond->GetData("color")->GetValue())); else d->painter->SetPenColor(d->bondColor); d->DrawRingBond(begin, end, center, ringBond->GetBO()); drawnBonds.SetBitOn(ringBond->GetId()); } } // draw atom labels for (atom = mol->BeginAtom(i); atom; atom = mol->NextAtom(i)) { double x = atom->GetX(); double y = atom->GetY(); int alignment = GetLabelAlignment(atom); bool rightAligned = false; switch (alignment) { case TopRight: case CenterRight: case BottomRight: rightAligned = true; default: break; } if((d->options & internalColor) && atom->HasData("color")) d->painter->SetPenColor(OBColor(atom->GetData("color")->GetValue())); else if(d->options & bwAtoms) d->painter->SetPenColor(d->bondColor); else d->painter->SetPenColor(OBColor(etab.GetRGB(atom->GetAtomicNum()))); //charge and radical int charge = atom->GetFormalCharge(); int spin = atom->GetSpinMultiplicity(); if(charge || spin) { OBFontMetrics metrics = d->painter->GetFontMetrics("N"); double yoffset = d->HasLabel(atom) ? 0.4 * metrics.height : 0.0; switch (GetLabelAlignment(atom)) { case TopCenter: case TopRight: case TopLeft: case CenterLeft: case CenterRight: yoffset = - 1.2 * metrics.height; } stringstream ss; if(charge) { if(abs(charge)!=1) ss << abs(charge); ss << (charge>0 ? "+" : "-") ; } if(spin) { ss << (spin==2 ? "." : ".."); yoffset += 0.5 * metrics.height; } if(spin || charge<0) d->painter->SetFontSize(2 * metrics.fontSize); d->painter->DrawText(x-0.4*metrics.width, y-yoffset, ss.str()); d->painter->SetFontSize(metrics.fontSize);//restore } if (atom->IsCarbon()) { if(!(d->options & drawAllC)) { if (atom->GetValence() > 1) continue; if ((atom->GetValence() == 1) && !(d->options & drawTermC))//!d->drawTerminalC) continue; } } stringstream ss; AliasData* ad = NULL; if(d->aliasMode && atom->HasData(AliasDataType)) ad = static_cast<AliasData*>(atom->GetData(AliasDataType)); //For unexpanded aliases use appropriate form of alias instead of element symbol, Hs, etc if(ad && !ad->IsExpanded()) { ss <<ad->GetAlias(rightAligned); OBColor aliasColor = !ad->GetColor().empty() ? ad->GetColor() : d->bondColor; d->painter->SetPenColor(aliasColor); } else { const char* atomSymbol; if(atom->IsHydrogen() && atom->GetIsotope()>1) atomSymbol = atom->GetIsotope()==2 ? "D" : "T"; else atomSymbol = etab.GetSymbol(atom->GetAtomicNum()); unsigned int hCount = atom->ImplicitHydrogenCount(); // rightAligned: // false CH3 // true H3C if (hCount && rightAligned) ss << "H"; if ((hCount > 1) && rightAligned) ss << hCount; ss << atomSymbol; if (hCount && !rightAligned) ss << "H"; if ((hCount > 1) && !rightAligned) ss << hCount; } d->DrawAtomLabel(ss.str(), alignment, vector3(x, y, 0.0)); } return true; }
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; }
SVGPainter::SVGPainter(ostream& ofs, std::set<ColorGradient>* gradients, bool withViewBox, double width, double height) : m_ofs(ofs), m_withViewBox(withViewBox), m_width(width), m_height(height), m_Pencolor("black"), m_Fillcolor("white"), m_Gradientcolor(make_pair(OBColor("white"),OBColor("white"))), m_PenWidth(1), m_fontPointSize(16), m_isFillcolor(true), m_Gradients(gradients) {}