#include #include #include #include #include #include #include #include using namespace std; typedef vector vd; typedef vector vi; typedef vector< pair > vpairs; class Point2d { public: double x,y; Point2d (double X, double Y) { x=X; y=Y; } }; class Point3d { public: double x,y,z; Point3d () {} Point3d (double X, double Y, double Z) { x=X; y=Y; z=Z; } }; class Crossing { public: size_t arc1, arc2; Point2d point = Point2d(0,0), height = Point2d(0,0); bool over; // true if arc1 over arc2 int index; Crossing (size_t a1, size_t a2, Point2d p, Point2d h, bool o, int i) { arc1=a1; arc2=a2; point=p; height=h; over=o; index=i; } }; // declarations of functions void Gauss_to_presentation(vi const& GaussCode, vector& Wirtinger); bool sequence_to_Gauss (vector& Atoms, bool r1move, vi& GaussCode); void shorten_sequence (vector& Atoms); double volume (Point3d const& a, Point3d const& b, Point3d const& c, Point3d const& d); double Det (Point3d const& A, Point3d const& B, Point3d const& C); bool compare_dist (pair a, pair b); void add_crossing (Point2d const& v, Crossing const& c, vector< vector >& vGaussCode); void IntersectLines (Point2d const& v1, Point2d const& v2, Point2d const& v3, Point2d const& v4, bool& flag, Point2d& v); void read_pdb (string const& file_name, vector& Atoms); void read_txt (string const& file_name, vector& Atoms); void read_random_points (size_t const& nAtoms, vector& Atoms); bool BoxesOverlap (vectorconst& FixPoly, vectorconst& VarPoly); pair BoundingBox (vectorconst& Poly); void Det (Point2d const& u, Point2d const& v, double& det); double min_coord (vd const& v); double max_coord (vd const& v); void Det (Point2d const& u, Point2d const& v, double& det); double norm(Point2d v); double sqdist(Point3d const& a,Point3d const& b); void close_sequence(vd& xAtoms, vd& yAtoms, vd& zAtoms); void PrintAtoms(vectorconst& Atoms); void PrintWirtinger (vectorconst& Wirtinger); void PrintWirtinger (vectorconst& Wirtinger, string& output); void Distances ( vectorconst& Atoms, vector< pair >& dAtoms ); int main() { string folder = "/Users/kurlin/C++/KnottedProteins/pdbfiles/"; string file_name, output; ofstream file; size_t nAtoms, nCrossings; vector Atoms; vector< pair > dAtoms; string typeinput; bool r1move; vi GaussCode; vector Wirtinger; // set of relations with generators 1,...,m (full arcs) cout<<"Thank you for trying this KGG program computing the Knotted Graph Group.\n"; cout<<"In line 1 of main(), please change the path to the folder on your computer.\n"; cout<<"Input: ordered list of 3D coordinates of vertices of a polygonal chain K.\n"; cout<<"Output: the group pi_1(R^3-K'), where K' is a closed simplification of K.\n"; cout<<"Details are at http://kurlin.org/projects/invariants-knotted-graphs.html.\n"; cout<<"If you have any questions, please e-mail Vitaliy, vitaliy.kurlin@gmail.com.\n"; string welcome = "\nPlease type one of the following options for different types of input.\n"; welcome += " demo: open polygonal trefoil\n"; welcome += " pdb: protein backbone from PDB\n"; welcome += " file: chain from your text file\n"; welcome += " rand: random polygonal chain\n"; welcome += " exit: finish the program \nInput: "; while (true) { cout<>typeinput; if (typeinput == "exit") break; if (typeinput == "demo") { Atoms.clear(); Atoms.push_back( Point3d(-2,-2, 1) ); Atoms.push_back( Point3d( 2, 2,-1) ); Atoms.push_back( Point3d( 2,-1, 0) ); Atoms.push_back( Point3d(-2,-1, 0) ); Atoms.push_back( Point3d(-2, 2, 2) ); Atoms.push_back( Point3d( 2,-2,-1) ); cout<<"Vertices of the polygonal trefoil chain are\n"; PrintAtoms(Atoms); } if (typeinput == "rand") { cout<<"\nA polygonal chain whose vertices are uniformly random in the unit cube."; cout<<"\nSuch a chain on more than 100 vertices is rarely simplifiable."; cout<<"\nPlease input the number of vertices.\nInput: "; cin>>nAtoms; srand( (int) time(0) ); file_name = "random"+to_string(nAtoms); read_random_points (nAtoms, Atoms); } else if (typeinput == "pdb") { cout<<"\nPlease download a pdb file from http://www.rcsb.org/pdb to the same folder as main.cpp."; cout<<"\nExamples: 1V2X, 3OIL, 3OYS, 2RH3, 3NOU, 3NOT, 3NOP, 3ZQ5."; // 2LEN, 4JKJ, 1XD3, 1NS5, 1JS1, 1QMG, 1YRL, 4LRV, 3BJX, 4N2X, 3WJ8, 2EFV, 4UWE cout<<"\nThen please type the PDB id: with capital letters, but without .pdb, e.g. 1V2X.\nFile name: "; cin>>file_name; read_pdb( folder+file_name, Atoms); } else if (typeinput == "file") { cout<<"\nPlease put your input file with the extension .txt in the same folder as main.cpp."; cout<<"\nWrite down 3D coordinates of ordered vertices, in each line: x y z with spaces."; cout<<"\nThen please type the name of your file without extension, e.g. trefoil.\nFile name: "; cin>>file_name; read_txt( folder+file_name, Atoms); } else { cout<<"\nSorry, we couldn't understand your input\n"; continue; } // Stage 0: find initial crossings nAtoms = Atoms.size(); if (nAtoms<3) { cout<<"Too few points: "<9) cout<<"too many to display"; else cout<const& Wirtinger) { for (size_t i=1; iconst& Wirtinger, string& output) { output = ""; for (size_t i=1; i& Wirtinger) { size_t m = size_t ( GaussCode.size() / 2 ); //cout << "\nRelations: " << m << endl; // build generators for overcrossing arcs between negative crossings vector generators(2*m); size_t nGen=1; for (size_t k=0; k<2*m; k++) { if (GaussCode[k]<0) nGen++; generators[k] = nGen; } // find generator for each overcrossing arc vector overcrossgen(m+1); for (size_t k=0; k<2*m; k++) if (GaussCode[k]>0) overcrossgen[GaussCode[k]] = generators[k]; // write relation for each negative crossing Wirtinger.clear(); for (size_t k=0; k<2*m; k++) if (GaussCode[k]<0) { size_t after = generators[k], before = after-1, over = overcrossgen[-GaussCode[k]]; Wirtinger.push_back( { make_pair(over,1), make_pair(before,1), make_pair(over,-1), make_pair(after,-1)} ); //cout << "x_" << over << " x_" << before << " x_" << over << "^{-1}" << " x_" << after << "^{-1} = 1 \n"; } for (size_t i=0; i& Atoms, bool r1move, vi& GaussCode) { bool flag; size_t n=0; size_t nR1moves=0; size_t nAtoms = Atoms.size(); vector< vector > vGaussCode(nAtoms-1); for (size_t i=0; i v12 = { v1, v2 }, v34 = { v3, v4 }; if ( !BoxesOverlap(v12,v34) ) continue; // IntersectLines(v1,v2,v3,v4,flag,v); if (!flag) continue; n++; // new intersection double t12 = (v.x-v1.x)/(v2.x-v1.x); double t34 = (v.x-v3.x)/(v4.x-v3.x); double z12 = (1-t12)*Atoms[i].z+t12*Atoms[i+1].z, z34 = (1-t34)*Atoms[j].z+t34*Atoms[j+1].z; bool over = true; if (z12 < z34) over = false; add_crossing ( v1, Crossing(i,j,v,Point2d(z12,z34),over,0), vGaussCode ); add_crossing ( v3, Crossing(j,i,v,Point2d(z34,z12),!over,0), vGaussCode ); } GaussCode.clear(); int ind = 0; // abs index of crossing in Gauss code for (size_t i=0; iconst& Atoms, vector< pair >& dAtoms ) { dAtoms.resize( Atoms.size()-2 ); for (size_t i=1; i& Atoms) { if (Atoms.size()<=2) return; vector< pair > dAtoms; Distances( Atoms, dAtoms ); //for (int k=0; k=i) && (j<=i+1) ) continue; // too close segments double vol_up = volume(Atoms[i-1],Atoms[i],Atoms[i+1],Atoms[j]); double vol_down = volume(Atoms[i-1],Atoms[i],Atoms[i+1],Atoms[j+1]); if ( vol_up * vol_down >=0 ) continue; // segment j on one side of the i-th plane double vol1 = volume(Atoms[i-1],Atoms[i],Atoms[j],Atoms[j+1]); double vol2 = volume(Atoms[i+1],Atoms[i],Atoms[j],Atoms[j+1]); double vol3 = volume(Atoms[i-1],Atoms[i+1],Atoms[j],Atoms[j+1]); if ( fabs( fabs(vol1)+fabs(vol2)+fabs(vol3) - fabs(vol_up)-fabs(vol_down) ) > 1e-10 ) continue; // intersection outside triangle flag = true; // intersection inside triangle break; } if (flag) continue; // we can't shorten triangle with intersection // now remove i-th vertex Atoms.erase( Atoms.begin() + i ); if (Atoms.size()<=2) break; dAtoms.resize(Atoms.size()-2); for (size_t i=1; i= 1)) // no internal intersection, this case should be considered earlier { cout << " t<=0 or t=>1 " << endl; return; // mistake } // intersection point of line segments (v1,v2) and (v3,v4) v.x = v1.x + t*v21.x; v.y = v1.y + t*v21.y; } void read_pdb(string const& file_name, vector& Atoms) { Atoms.clear(); ifstream file; file.open(file_name+".pdb"); if (file.is_open()) cout << "File ./" << file_name << " is open.\n"; else { cout << "Error opening " << file_name << ".\n"; return; } string line, col1, col2, col3, col4, col5, col6; double x,y,z; while(std::getline(file, line)) // read one line from ifs { std::istringstream iss(line); // access line as a stream iss >> col1; if (col1!="ATOM") continue; // no ATOM yet iss >> col2 >> col3; if (col3!="C") continue; // non-carbon atom iss >> col4 >> col5 >> col6; // irrelevant columns iss >> x >> y >> z; Atoms.push_back(Point3d(x,y,z)); } } void read_txt(string const& file_name, vector& Atoms) { Atoms.clear(); ifstream file; file.open( file_name+".txt" ); if (file.is_open()) cout << "File ./" << file_name << " is open.\n"; else { cout << "Error opening " << file_name << ".\n"; return; } string line; double x,y,z; while(std::getline(file, line)) // read one line from ifs { std::istringstream iss(line); // access line as a stream iss >> x >> y >> z; Atoms.push_back(Point3d(x,y,z)); } } void read_random_points ( size_t const& nAtoms, vector& Atoms) { Atoms.clear(); Atoms.push_back ( Point3d (-1,-1,-1) ); for (size_t i=2; iconst& FixPoly, vectorconst& VarPoly) { pair boxF = BoundingBox(FixPoly); pair boxV = BoundingBox(VarPoly); //printPoint(boxF.first); printPoint(boxF.second); if (boxF.first.x >= boxV.second.x) return false; if (boxF.second.x <= boxV.first.x) return false; if (boxF.first.y >= boxV.second.y) return false; if (boxF.second.y <= boxV.first.y) return false; //std::cout << "overlap true"< BoundingBox(vectorconst& Poly) { double xmin = Poly[0].x; double xmax = Poly[0].x; double ymin = Poly[0].y; double ymax = Poly[0].y; for (auto it = Poly.begin(); it != Poly.end(); ++it) { double x = it->x; if (xmin > x) xmin = x; if (xmax < x) xmax = x; double y = it->y; if (ymin > y) ymin = y; if (ymax < y) ymax = y; } //printPoint(Point2d(xmin, ymin)); //printPoint(Point2d(xmax, ymax)); return make_pair(Point2d(xmin, ymin), Point2d(xmax, ymax)); } double min_coord (vd const& v) { size_t n = v.size(); double m = 1e+64; for (size_t i=0;im) m=v[i]; return m; } void Det(Point2d const& u, Point2d const& v, double& det) { det = u.x * v.y - u.y * v.x; } double norm(Point2d v) { return sqrt(v.x*v.x+v.y*v.y); } double sqdist(Point3d const& a, Point3d const& b) { return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z); } void close_sequence(vd& xAtoms, vd& yAtoms, vd& zAtoms) { size_t nAtoms = xAtoms.size(); if (nAtoms<=1) return; double Xmin = min_coord(xAtoms); double Xmax = max_coord(xAtoms); double Ymin = min_coord(yAtoms); double Ymax = max_coord(yAtoms); double Zmin = min_coord(zAtoms); double Zmax = max_coord(zAtoms); // move the first point away from the bounding box double vx = xAtoms[0]-xAtoms[1]; double vy = yAtoms[0]-yAtoms[1]; double vz = zAtoms[0]-zAtoms[1]; double vl = sqrt(vx*vx+vy*vy+vz*vz); if (vl==0) { cout << "vl=0" << endl; return; } vx/=vl; vy/=vl; vz/=vl; xAtoms[0] += vx * (Xmax-Xmin); yAtoms[0] += vy * (Ymax-Ymin); zAtoms[0] += vz * (Zmax-Zmin); // move the start and end points away from the bounding box } void PrintAtoms (vectorconst& Atoms) { for (size_t i=0; i