//! Sets atom->IsChiral() to true for chiral atoms void OBMol::FindChiralCenters() { if (HasChiralityPerceived()) return; SetChiralityPerceived(); obErrorLog.ThrowError(__FUNCTION__, "Ran OpenBabel::FindChiralCenters", obAuditMsg); //do quick test to see if there are any possible chiral centers bool mayHaveChiralCenter=false; OBAtom *atom,*nbr; vector<OBAtom*>::iterator i; for (atom = BeginAtom(i);atom;atom = NextAtom(i)) if (atom->GetHyb() == 3 && atom->GetHvyValence() >= 3) { mayHaveChiralCenter=true; break; } if (!mayHaveChiralCenter) return; OBBond *bond; vector<OBBond*>::iterator j; for (bond = BeginBond(j);bond;bond = NextBond(j)) if (bond->IsWedge() || bond->IsHash()) (bond->GetBeginAtom())->SetChiral(); vector<unsigned int> vgid; GetGIDVector(vgid); vector<unsigned int> tlist; vector<unsigned int>::iterator k; bool ischiral; for (atom = BeginAtom(i);atom;atom = NextAtom(i)) if (atom->GetHyb() == 3 && atom->GetHvyValence() >= 3 && !atom->IsChiral()) { tlist.clear(); ischiral = true; for (nbr = atom->BeginNbrAtom(j);nbr;nbr = atom->NextNbrAtom(j)) { for (k = tlist.begin();k != tlist.end();++k) if (vgid[nbr->GetIdx()-1] == *k) ischiral = false; if (ischiral) tlist.push_back(vgid[nbr->GetIdx()-1]); else break; } if (ischiral) atom->SetChiral(); } }
bool ChemDrawXMLFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv) { static const xmlChar C_MOLECULE[] = "fragment"; static const xmlChar C_CDXML[] = "CDXML"; static const xmlChar C_BONDLENGTH[] = "BondLength"; static const xmlChar C_PAGE[] = "page"; static const xmlChar C_ATOM[] = "n"; static const xmlChar C_BOND[] = "b"; static const xmlChar C_ID[] = "id"; static const xmlChar C_CHARGE[] = "Charge"; static const xmlChar C_COORDS[] = "p"; static const xmlChar C_ELEMENT[] = "Element"; static const xmlChar C_ORDER[] = "Order"; static const xmlChar C_BEGIN[] = "B"; static const xmlChar C_END[] = "E"; static const xmlChar C_DISPLAY[] = "Display"; _pxmlConv = XMLConversion::GetDerived(pConv,false); if(!_pxmlConv) return false; OBMol* pmol = dynamic_cast<OBMol*>(pOb); if(pmol==NULL) return false; OBMol &mol = *pmol; OBBond *pbond; vector<OBBond*>::iterator j; if(_pxmlConv->GetOutputIndex() == 1) { xmlTextWriterStartDocument(writer(), NULL, NULL, NULL); xmlTextWriterWriteDTD(writer(), BAD_CAST "CDXML", NULL, BAD_CAST "http://www.camsoft.com/xml/cdxml.dtd", NULL); xmlTextWriterStartElement(writer(), C_CDXML); xmlTextWriterWriteFormatAttribute(writer(), C_BONDLENGTH , "30"); xmlTextWriterStartElement(writer(), C_PAGE); // put everything on one page // now guess the average bond size for the first molecule and scale to 30. _scale = 0.; if (mol.NumBonds()) { for (pbond = mol.BeginBond(j); pbond; pbond = mol.NextBond(j)) _scale += pbond->GetLength(); _scale /= mol.NumBonds(); } else _scale = 1.; // FIXME: what happens if the molecule has no bond? _scale = 30. / _scale; _offset = 0; } xmlTextWriterStartElement(writer(), C_MOLECULE); OBAtom *patom; vector<OBAtom*>::iterator i; int n; for (patom = mol.BeginAtom(i); patom; patom = mol.NextAtom(i)) { xmlTextWriterStartElement(writer(), C_ATOM); xmlTextWriterWriteFormatAttribute(writer(), C_ID , "%d", patom->GetIdx() + _offset); xmlTextWriterWriteFormatAttribute(writer(), C_COORDS , "%f %f", patom->GetX() * _scale, patom->GetY() * _scale); n = patom->GetAtomicNum(); if (n != 6) { xmlTextWriterWriteFormatAttribute(writer(), C_ELEMENT , "%d", n); } n = patom->GetFormalCharge(); if (n != 0) { xmlTextWriterWriteFormatAttribute(writer(), C_CHARGE , "%d", n); } xmlTextWriterEndElement(writer()); } for (pbond = mol.BeginBond(j); pbond; pbond = mol.NextBond(j)) { xmlTextWriterStartElement(writer(), C_BOND); patom = pbond->GetBeginAtom(); xmlTextWriterWriteFormatAttribute(writer(), C_BEGIN , "%d", patom->GetIdx() + _offset); patom = pbond->GetEndAtom(); xmlTextWriterWriteFormatAttribute(writer(), C_END , "%d", patom->GetIdx() + _offset); n = pbond->GetBO(); if (n != 1) { xmlTextWriterWriteFormatAttribute(writer(), C_ORDER , "%d", n); } if (pbond->IsHash()) xmlTextWriterWriteFormatAttribute(writer(), C_DISPLAY , "WedgeBegin"); else if (pbond->IsWedge()) xmlTextWriterWriteFormatAttribute(writer(), C_DISPLAY , "WedgedHashEnd"); xmlTextWriterEndElement(writer()); } _offset += mol.NumAtoms (); xmlTextWriterEndElement(writer());//molecule //TODO: Writing property block if(_pxmlConv->IsLast()) { xmlTextWriterEndDocument(writer()); // page xmlTextWriterEndDocument(writer()); //document OutputToStream(); } return true; }
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; }
//! Calculate the signed volume for an atom. If the atom has a valence of 3 //! the coordinates of an attached hydrogen are calculated //! Puts attached Hydrogen last at the moment, like mol V3000 format. //! If ReZero=false (the default is true) always make pseudo z coords and leave them in mol double CalcSignedVolume(OBMol &mol,OBAtom *atm, bool ReZeroZ) { vector3 tmp_crd; vector<unsigned int> nbr_atms; vector<vector3> nbr_crds; bool use_central_atom = false,is2D=false; // double hbrad = etab.CorrectedBondRad(1,0); if (!ReZeroZ || !mol.Has3D()) //give pseudo Z coords if mol is 2D { vector3 v,vz(0.0,0.0,1.0); is2D = true; OBAtom *nbr; OBBond *bond; vector<OBBond*>::iterator i; for (bond = atm->BeginBond(i);bond;bond = atm->NextBond(i)) { nbr = bond->GetEndAtom(); if (nbr != atm) { v = nbr->GetVector(); if (bond->IsWedge()) v += vz; else if (bond->IsHash()) v -= vz; nbr->SetVector(v); } else { nbr = bond->GetBeginAtom(); v = nbr->GetVector(); if (bond->IsWedge()) v -= vz; else if (bond->IsHash()) v += vz; nbr->SetVector(v); } } } if (atm->GetHvyValence() < 3) { stringstream errorMsg; errorMsg << "Cannot calculate a signed volume for an atom with a heavy atom valence of " << atm->GetHvyValence() << endl; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obInfo); return(0.0); } // Create a vector with the coordinates of the neighbor atoms // Also make a vector with Atom IDs OBAtom *nbr; vector<OBBond*>::iterator bint; for (nbr = atm->BeginNbrAtom(bint);nbr;nbr = atm->NextNbrAtom(bint)) { nbr_atms.push_back(nbr->GetIdx()); } // sort the neighbor atoms to insure a consistent ordering sort(nbr_atms.begin(),nbr_atms.end()); for (unsigned int i = 0; i < nbr_atms.size(); ++i) { OBAtom *tmp_atm = mol.GetAtom(nbr_atms[i]); nbr_crds.push_back(tmp_atm->GetVector()); } /* // If we have three heavy atoms we need to calculate the position of the fourth if (atm->GetHvyValence() == 3) { double bondlen = hbrad+etab.CorrectedBondRad(atm->GetAtomicNum(),atm->GetHyb()); atm->GetNewBondVector(tmp_crd,bondlen); nbr_crds.push_back(tmp_crd); } */ for(unsigned int j=0;j < nbr_crds.size();++j) // Checks for a neighbour having 0 co-ords (added hydrogen etc) { // are the coordinates zero to 6 or more significant figures if (nbr_crds[j].IsApprox(VZero, 1.0e-6) && use_central_atom==false) use_central_atom=true; else if (nbr_crds[j].IsApprox(VZero, 1.0e-6)) { obErrorLog.ThrowError(__FUNCTION__, "More than 2 neighbours have 0 co-ords when attempting 3D chiral calculation", obInfo); } } // If we have three heavy atoms we can use the chiral center atom itself for the fourth // will always give same sign (for tetrahedron), magnitude will be smaller. if(nbr_atms.size()==3 || use_central_atom==true) { nbr_crds.push_back(atm->GetVector()); nbr_atms.push_back(mol.NumAtoms()+1); // meed to add largest number on end to work } OBChiralData* cd=(OBChiralData*)atm->GetData(OBGenericDataType::ChiralData); //Set the output atom4refs to the ones used if(cd==NULL) { cd = new OBChiralData; cd->SetOrigin(perceived); atm->SetData(cd); } cd->SetAtom4Refs(nbr_atms,calcvolume); //re-zero psuedo-coords if (is2D && ReZeroZ) { vector3 v; OBAtom *atom; vector<OBAtom*>::iterator k; for (atom = mol.BeginAtom(k);atom;atom = mol.NextAtom(k)) { v = atom->GetVector(); v.SetZ(0.0); atom->SetVector(v); } } return(signed_volume(nbr_crds[0],nbr_crds[1],nbr_crds[2],nbr_crds[3])); }