#include "MoleculeFunctionLibrary.h" #include "Misc/Paths.h" #include "Misc/FileHelper.h" bool UMoleculeFunctionLibrary::ParsePDBFromString(const FString& PDBContent, TArray& OutAtoms, TArray& OutBonds) { OutAtoms.Empty(); OutBonds.Empty(); if (PDBContent.IsEmpty()) { UE_LOG(LogTemp, Warning, TEXT("ParsePDBFromString: Input string is empty")); return false; } TArray Lines; PDBContent.ParseIntoArrayLines(Lines, true); for (const FString& Line : Lines) { if (!Line.StartsWith(TEXT("ATOM")) && !Line.StartsWith(TEXT("HETATM"))) continue; if (Line.Len() < 54) // At least enough for X, Y, Z continue; FAtom Atom; // Coordinates (columns are 1-indexed in spec) Atom.Position.X = FCString::Atof(*Line.Mid(30, 8)); Atom.Position.Y = FCString::Atof(*Line.Mid(38, 8)); Atom.Position.Z = FCString::Atof(*Line.Mid(46, 8)); // Element symbol (columns 77-78), fallback to atom name (columns 13-16) if (Line.Len() >= 78) Atom.Element = Line.Mid(76, 2).TrimStartAndEnd(); if (Atom.Element.IsEmpty()) Atom.Element = Line.Mid(12, 4).TrimStartAndEnd().Left(1); // fallback OutAtoms.Add(Atom); } // Bond inference: simple distance-based const float MaxBondDistance = 1.6f; // can tweak for different atom types for (int32 i = 0; i < OutAtoms.Num(); i++) { for (int32 j = i + 1; j < OutAtoms.Num(); j++) { if (FVector::Distance(OutAtoms[i].Position, OutAtoms[j].Position) <= MaxBondDistance) { FBond Bond; Bond.AtomIndex1 = i; Bond.AtomIndex2 = j; OutBonds.Add(Bond); } } } UE_LOG(LogTemp, Log, TEXT("Parsed %d atoms and %d bonds from string"), OutAtoms.Num(), OutBonds.Num()); return true; }