/* A* ------------------------------------------------------------------- B* This file contains source code for the PyMOL computer program C* Copyright (c) Schrodinger, LLC. D* ------------------------------------------------------------------- E* It is unlawful to modify or remove this copyright notice. F* ------------------------------------------------------------------- G* Please see the accompanying LICENSE file for further information. H* ------------------------------------------------------------------- I* Additional authors of this source file include: -* -* -* Z* ------------------------------------------------------------------- */ #include"os_predef.h" #include"os_std.h" #include"os_gl.h" #include"Base.h" #include"Sphere.h" #include"Vector.h" #include"Err.h" #include"MemoryDebug.h" #define FAST_SPHERE_INIT #ifndef FAST_SPHERE_INIT /* Twelve vertices of icosahedron on unit sphere */ #define tau 0.8506508084F /* t=(1+sqrt(5))/2, tau=t/sqrt(1+t^2) */ #define one 0.5257311121F /* one=1/sqrt(1+t^2) , unit sphere */ static const float start_points[13][3] = { {tau, one, 0}, {-tau, one, 0}, {-tau, -one, 0}, {tau, -one, 0}, {one, 0, tau}, {one, 0, -tau}, {-one, 0, -tau}, {-one, 0, tau}, {0, tau, one}, {0, -tau, one}, {0, -tau, -one}, {0, tau, -one} }; static const int icosahedron[21][3] = { {4, 8, 7}, {4, 7, 9}, {5, 6, 11}, {5, 10, 6}, {0, 4, 3}, {0, 3, 5}, {2, 7, 1}, {2, 1, 6}, {8, 0, 11}, {8, 11, 1}, {9, 10, 3}, {9, 2, 10}, {8, 4, 0}, {11, 0, 5}, {4, 9, 3}, {5, 3, 10}, {7, 8, 1}, {6, 1, 11}, {7, 2, 9}, {6, 10, 2} }; #endif static const int mesh[30][2] = { {0, 3}, {0, 4}, {0, 5}, {0, 8}, {0, 11}, {1, 2}, {1, 6}, {1, 7}, {1, 8}, {1, 11}, {2, 6}, {2, 7}, {2, 9}, {2, 10}, {3, 4}, {3, 5}, {3, 9}, {3, 10}, {4, 7}, {4, 8}, {4, 9}, {5, 6}, {5, 10}, {5, 11}, {6, 10}, {6, 11}, {7, 8}, {7, 9}, {8, 11}, {9, 10} }; #ifdef FAST_SPHERE_INIT #include"SphereData.h" #else static SphereRec *MakeDotSphere(PyMOLGlobals * G, int level); #endif #ifndef FAST_SPHERE_INIT static void SphereDumpAll(CSphere *I) { FILE *f; int i, dot_total, a, c, strip_total, seq_total, tri_total; SphereRec *sp; f = fopen("SphereData.h", "w"); fprintf(f, "static int Sphere_NSpheres = %d;\n", NUMBER_OF_SPHERE_LEVELS); fprintf(f, "static int Sphere_NTri[%d] = {\n", NUMBER_OF_SPHERE_LEVELS); for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ fprintf(f, " %d, ", I->Sphere[i]->NTri); } fprintf(f, "\n};\n"); fprintf(f, "static int Sphere_NStrip[%d] = {\n", NUMBER_OF_SPHERE_LEVELS); for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ fprintf(f, " %d, ", I->Sphere[i]->NStrip); } fprintf(f, "\n};\n"); fprintf(f, "static int Sphere_NVertTot[%d] = {\n", NUMBER_OF_SPHERE_LEVELS); for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ fprintf(f, " %d, ", I->Sphere[i]->NVertTot); } fprintf(f, "\n};\n"); fprintf(f, "static int Sphere_nDot[%d] = {\n", NUMBER_OF_SPHERE_LEVELS); for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ fprintf(f, " %d, ", I->Sphere[i]->nDot); } fprintf(f, "\n};\n"); fprintf(f, "static int Sphere_dot_start[%d] = {\n", NUMBER_OF_SPHERE_LEVELS); dot_total = 0; for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ fprintf(f, " %d, ", dot_total); dot_total += I->Sphere[i]->nDot; } fprintf(f, "\n};\n"); fprintf(f, "static float Sphere_dot[][3] = {\n"); for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ sp = I->Sphere[i]; fprintf(f, "/* dots for Sphere #%d */\n", i); for(a = 0; a < sp->nDot; a++) { fprintf(f, "{ %15.12fF, %15.12fF, %15.12fF },\n", sp->dot[a][0], sp->dot[a][1], sp->dot[a][2]); } } fprintf(f, "};\n"); fprintf(f, "static float Sphere_area[] = {\n"); for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ sp = I->Sphere[i]; fprintf(f, "/* areas for Sphere #%d */\n", i); c = 0; for(a = 0; a < sp->nDot; a++) { fprintf(f, "%15.12fF,", sp->area[a]); c = (c + 1) % 4; if (!c) fprintf(f, "\n"); } if (c) fprintf(f, "\n"); } fprintf(f, "};\n"); fprintf(f, "static int Sphere_StripLen_start[%d] = {\n", NUMBER_OF_SPHERE_LEVELS); strip_total = 0; for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ fprintf(f, " %d, ", strip_total); strip_total += I->Sphere[i]->NStrip; } fprintf(f, "\n};\n"); fprintf(f, "static int Sphere_StripLen[] = {\n"); for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ sp = I->Sphere[i]; fprintf(f, "/* StripLen for Sphere #%d */\n", i); c = 0; for(a = 0; a < sp->NStrip; a++) { fprintf(f, "%6d,", sp->StripLen[a]); c = (c + 1) % 10; if(!c) fprintf(f, "\n"); } if (c) fprintf(f, "\n"); } fprintf(f, "};\n"); fprintf(f, "static int Sphere_Sequence_start[%d] = {\n", NUMBER_OF_SPHERE_LEVELS); seq_total = 0; for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ fprintf(f, " %d, ", seq_total); seq_total += I->Sphere[i]->NVertTot; } fprintf(f, "\n};\n"); fprintf(f, "static int Sphere_Sequence[] = {\n"); for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ sp = I->Sphere[i]; fprintf(f, "/* Sequence for Sphere #%d */\n", i); c = 0; for(a = 0; a < sp->NVertTot; a++) { fprintf(f, "%6d,", sp->Sequence[a]); c = (c + 1) % 10; if(!c) fprintf(f, "\n"); } if (c) fprintf(f, "\n"); } fprintf(f, "};\n"); fprintf(f, "static int Sphere_Tri_start[%d] = {\n", NUMBER_OF_SPHERE_LEVELS); tri_total = 0; for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ fprintf(f, " %d, ", tri_total); tri_total += 3 * I->Sphere[i]->NTri; } fprintf(f, "\n};\n"); fprintf(f, "static int Sphere_Tri[] = {\n"); for (i=0; i < NUMBER_OF_SPHERE_LEVELS; i++){ sp = I->Sphere[i]; fprintf(f, "/* Tri for Sphere #%d */\n", i); c = 0; for(a = 0; a < 3* sp->NTri; a++) { fprintf(f, "%6d,", sp->Tri[a]); c = (c + 1) % 10; if(!c) fprintf(f, "\n"); } if (c) fprintf(f, "\n"); } fprintf(f, "};\n"); fclose(f); } #endif void SphereInit(PyMOLGlobals * G) { CSphere *I = (G->Sphere = pymol::calloc(1)); #ifdef FAST_SPHERE_INIT I->Array = pymol::malloc(Sphere_NSpheres); { int i; for (i=0; iArray[i].area = &Sphere_area[Sphere_dot_start[i]]; I->Array[i].dot = &Sphere_dot[Sphere_dot_start[i]]; I->Array[i].StripLen = &Sphere_StripLen[Sphere_StripLen_start[i]]; I->Array[i].Sequence = &Sphere_Sequence[Sphere_Sequence_start[i]]; I->Array[i].NStrip = Sphere_NStrip[i]; I->Array[i].NVertTot = Sphere_NVertTot[i]; I->Array[i].nDot = Sphere_nDot[i]; I->Array[i].Tri = &Sphere_Tri[Sphere_Tri_start[i]]; I->Array[i].NTri = Sphere_NTri[i]; if (i){ I->Array[i].Mesh = nullptr; I->Array[i].NMesh = 0; } else { I->Array[i].Mesh = (int *) (void *) mesh; I->Array[i].NMesh = 30; } I->Sphere[i] = &I->Array[i]; } } #else { int i; for (i=0; iSphere[i] = MakeDotSphere(G, i); } SphereDumpAll(I); } #endif } #ifndef FAST_SPHERE_INIT static void SpherePurge(SphereRec * I) { /* NOTE: S->Mesh is not currently a pointer */ mfree(I->dot); mfree(I->area); mfree(I->StripLen); mfree(I->Sequence); mfree(I->Tri); FreeP(I); } #endif void SphereFree(PyMOLGlobals * G) { CSphere *I = G->Sphere; #ifndef FAST_SPHERE_INIT SpherePurge(I->Sphere[0]); SpherePurge(I->Sphere[1]); SpherePurge(I->Sphere[2]); SpherePurge(I->Sphere[3]); SpherePurge(I->Sphere[4]); #else FreeP(I->Array); #endif FreeP(I); } /* private stuff */ #ifndef FAST_SPHERE_INIT // MAXDOT : 12, 42, 162, 642, 2562 ... :: 12 + (30 + 120 + 480 + 1920 + ... ) :: 12 + ( 30 + (30*4) + (30*4*4) + (30*4*4*4) + ... ) // MAXTRI : 80, 320, 1280, 5120, ... :: 20*1 + 20*4 + 20*4*4 + 20*4*4*4 + 20*4*4*4*4 :: //For NUMBER_OF_SPHERE_LEVELS=6 //#define MAXDOT 12900 // 12800 //#define MAXTRI 20500 // 20480 //For NUMBER_OF_SPHERE_LEVELS=5 #define MAXDOT 2600 // 2562 #define MAXTRI 5200 // 5120 typedef int EdgeCol[MAXDOT]; /* should move these into dynamic storage to save 3MB mem */ typedef EdgeCol EdgeArray[MAXDOT]; typedef int Triangle[3]; typedef struct { float *Dot; EdgeArray *EdgeRef; Triangle *Tri; int NDot, NTri; } SphereBuilderRec; static void MakeVertex(SphereBuilderRec * S, int d1, int d2) { if((*S->EdgeRef)[d1][d2] < 0) { average3f(S->Dot + (3 * d1), S->Dot + (3 * d2), S->Dot + (3 * S->NDot)); (*S->EdgeRef)[d1][d2] = S->NDot; (*S->EdgeRef)[d2][d1] = S->NDot; normalize3f(S->Dot + (3 * S->NDot)); S->NDot++; } } static float SphericalAngle(SphereBuilderRec * S, int d0, int d1, int d2) { Vector3f v1, v2, s1, s2; /* map vector onto surface of sphere and measure angle */ subtract3f(S->Dot + (3 * d1), S->Dot + (3 * d0), v1); subtract3f(S->Dot + (3 * d2), S->Dot + (3 * d0), v2); remove_component3f(v1, S->Dot + (3 * d0), s1); remove_component3f(v2, S->Dot + (3 * d0), s2); return (get_angle3f(s1, s2)); } static SphereRec *MakeDotSphere(PyMOLGlobals * G, int level) { SphereRec *result; int *TriFlag; int a, b, c, h, k, l, curTri, n, it; float area, sumArea = 0.0; int nStrip, *q, *s; int nVertTot; int flag; float vt1[3], vt2[3], vt[3]; SphereBuilderRec SBuild, *S; S = &SBuild; S->Dot = pymol::malloc(3 * MAXDOT); ErrChkPtr(G, S->Dot); S->EdgeRef = pymol::malloc(1); ErrChkPtr(G, S->EdgeRef); S->Tri = pymol::malloc(MAXTRI); ErrChkPtr(G, S->Tri); TriFlag = pymol::malloc(MAXTRI); ErrChkPtr(G, TriFlag); S->NDot = 12; for(a = 0; a < S->NDot; a++) { for(c = 0; c < 3; c++) S->Dot[3 * a + c] = start_points[a][c]; normalize3f(S->Dot + (3 * a)); } S->NTri = 20; for(a = 0; a < S->NTri; a++) for(c = 0; c < 3; c++) S->Tri[a][c] = icosahedron[a][c]; for(a = 0; a < MAXDOT; a++) for(b = 0; b < MAXDOT; b++) (*S->EdgeRef)[a][b] = -1; if(level > (NUMBER_OF_SPHERE_LEVELS-1)) level = (NUMBER_OF_SPHERE_LEVELS-1); for(c = 0; c < level; c++) { /* create new vertices */ for(a = 0; a < S->NTri; a++) { MakeVertex(S, S->Tri[a][0], S->Tri[a][1]); MakeVertex(S, S->Tri[a][1], S->Tri[a][2]); MakeVertex(S, S->Tri[a][0], S->Tri[a][2]); } /* create new triangles */ curTri = S->NTri; for(a = 0; a < curTri; a++) { h = S->Tri[a][0]; k = S->Tri[a][1]; l = S->Tri[a][2]; S->Tri[a][0] = h; S->Tri[a][1] = (*S->EdgeRef)[h][k]; S->Tri[a][2] = (*S->EdgeRef)[h][l]; S->Tri[S->NTri][0] = k; S->Tri[S->NTri][1] = (*S->EdgeRef)[k][h]; S->Tri[S->NTri][2] = (*S->EdgeRef)[k][l]; S->NTri++; S->Tri[S->NTri][0] = l; S->Tri[S->NTri][1] = (*S->EdgeRef)[l][h]; S->Tri[S->NTri][2] = (*S->EdgeRef)[l][k]; S->NTri++; S->Tri[S->NTri][0] = (*S->EdgeRef)[h][k]; S->Tri[S->NTri][1] = (*S->EdgeRef)[k][l]; S->Tri[S->NTri][2] = (*S->EdgeRef)[l][h]; S->NTri++; } // printf( "MakeDotSphere: Level: %i S->NTri: %i\n",c, S->NTri); } // printf(" MakeDotSphere: NDot %i S->NTri %i\n",S->NDot,S->NTri); result = pymol::malloc(1); ErrChkPtr(G, result); result->dot = pymol::malloc(S->NDot); ErrChkPtr(G, result->dot); result->area = pymol::malloc(S->NDot); ErrChkPtr(G, result->area); result->StripLen = pymol::malloc(S->NTri * 3); ErrChkPtr(G, result->StripLen); result->Sequence = pymol::malloc(S->NTri * 3); ErrChkPtr(G, result->Sequence); for(a = 0; a < S->NDot; a++) { for(c = 0; c < 3; c++) result->dot[a][c] = *(S->Dot + (3 * a + c)); result->area[a] = 0.0; } /* fix normals so that v1-v0 x v2-v0 is the correct normal */ for(a = 0; a < S->NTri; a++) { subtract3f(result->dot[S->Tri[a][1]], result->dot[S->Tri[a][0]], vt1); subtract3f(result->dot[S->Tri[a][2]], result->dot[S->Tri[a][0]], vt2); cross_product3f(vt1, vt2, vt); if(dot_product3f(vt, result->dot[S->Tri[a][0]]) < 0.0) { /* if wrong, then interchange */ it = S->Tri[a][2]; S->Tri[a][2] = S->Tri[a][1]; S->Tri[a][1] = it; } } for(a = 0; a < S->NTri; a++) { area = (float) (SphericalAngle(S, S->Tri[a][0], S->Tri[a][1], S->Tri[a][2]) + SphericalAngle(S, S->Tri[a][1], S->Tri[a][0], S->Tri[a][2]) + SphericalAngle(S, S->Tri[a][2], S->Tri[a][0], S->Tri[a][1]) - cPI); /* multiply by r^2 to get area */ sumArea += area; area /= 3.0; result->area[S->Tri[a][0]] += area; result->area[S->Tri[a][1]] += area; result->area[S->Tri[a][2]] += area; } if(fabs(sumArea - (4 * cPI)) > 0.001) { printf(" MakeDotSphere: sumArea: %8.6f which is %8.6f Pi\n", sumArea, sumArea / cPI); ErrFatal(G, "MakeDotSphere", "Area of sphere does not sum to 4*pi!\n"); } for(a = 0; a < S->NTri; a++) TriFlag[a] = false; nStrip = 0; nVertTot = 0; s = result->StripLen; q = result->Sequence; /* tesselate the sphere in a semi-efficient fashion...this could definitely be improved */ flag = true; while(flag) { flag = false; a = 0; while(a < S->NTri) { if(!TriFlag[a]) { flag = true; TriFlag[a] = true; *(q++) = S->Tri[a][0]; *(q++) = S->Tri[a][1]; *(q++) = S->Tri[a][2]; n = 3; b = 0; while(b < S->NTri) { if(!TriFlag[b]) { if(((S->Tri[b][0] == q[-2]) && (S->Tri[b][1] == q[-1])) || ((S->Tri[b][0] == q[-1]) && (S->Tri[b][1] == q[-2]))) { *(q++) = S->Tri[b][2]; TriFlag[b] = true; b = 0; n++; } else if(((S->Tri[b][0] == q[-2]) && (S->Tri[b][2] == q[-1])) || ((S->Tri[b][0] == q[-1]) && (S->Tri[b][2] == q[-2]))) { *(q++) = S->Tri[b][1]; TriFlag[b] = true; b = 0; n++; } else if(((S->Tri[b][2] == q[-2]) && (S->Tri[b][1] == q[-1])) || ((S->Tri[b][2] == q[-1]) && (S->Tri[b][1] == q[-2]))) { *(q++) = S->Tri[b][0]; TriFlag[b] = true; b = 0; n++; } } b++; } if(n == 3) { q[-3] = S->Tri[a][1]; q[-2] = S->Tri[a][2]; q[-1] = S->Tri[a][0]; b = 0; while(b < S->NTri) { if(!TriFlag[b]) { if(((S->Tri[b][0] == q[-2]) && (S->Tri[b][1] == q[-1])) || ((S->Tri[b][0] == q[-1]) && (S->Tri[b][1] == q[-2]))) { *(q++) = S->Tri[b][2]; TriFlag[b] = true; b = 0; n++; } else if(((S->Tri[b][0] == q[-2]) && (S->Tri[b][2] == q[-1])) || ((S->Tri[b][0] == q[-1]) && (S->Tri[b][2] == q[-2]))) { *(q++) = S->Tri[b][1]; TriFlag[b] = true; b = 0; n++; } else if(((S->Tri[b][2] == q[-2]) && (S->Tri[b][1] == q[-1])) || ((S->Tri[b][2] == q[-1]) && (S->Tri[b][1] == q[-2]))) { *(q++) = S->Tri[b][0]; TriFlag[b] = true; b = 0; n++; } } b++; } } if(n == 3) { q[-3] = S->Tri[a][2]; q[-2] = S->Tri[a][0]; q[-1] = S->Tri[a][1]; b = 0; while(b < S->NTri) { if(!TriFlag[b]) { if(((S->Tri[b][0] == q[-2]) && (S->Tri[b][1] == q[-1])) || ((S->Tri[b][0] == q[-1]) && (S->Tri[b][1] == q[-2]))) { *(q++) = S->Tri[b][2]; TriFlag[b] = true; b = 0; n++; } else if(((S->Tri[b][0] == q[-2]) && (S->Tri[b][2] == q[-1])) || ((S->Tri[b][0] == q[-1]) && (S->Tri[b][2] == q[-2]))) { *(q++) = S->Tri[b][1]; TriFlag[b] = true; b = 0; n++; } else if(((S->Tri[b][2] == q[-2]) && (S->Tri[b][1] == q[-1])) || ((S->Tri[b][2] == q[-1]) && (S->Tri[b][1] == q[-2]))) { *(q++) = S->Tri[b][0]; TriFlag[b] = true; b = 0; n++; } } b++; } } *(s++) = n; nVertTot += n; nStrip++; } a++; } } mfree(S->Dot); mfree(S->EdgeRef); mfree(TriFlag); result->Tri = (int *) S->Tri; result->Tri = pymol::realloc(result->Tri, S->NTri * 3); result->NTri = S->NTri; result->StripLen = pymol::realloc(result->StripLen, nStrip); result->Sequence = pymol::realloc(result->Sequence, nVertTot); result->dot = pymol::realloc(result->dot, S->NDot); result->area = pymol::realloc(result->area, S->NDot); result->nDot = S->NDot; result->NStrip = nStrip; result->NVertTot = nVertTot; result->Mesh = nullptr; result->NMesh = 0; if(!level) { /* provide mesh for S->Sphere[0] only...rest, to do. */ result->Mesh = (int *) mesh; result->NMesh = 30; } /* q=result->Sequence; for(a=0;aNStrip;a++) { printf("%d:",result->StripLen[a]); for(b=0;bStripLen[a];b++) { printf("%d ",*(q++)); } printf("\n"); } */ return (result); } #endif void SphereRender(PyMOLGlobals * G, int level, const float *centroid, const float *color, float alpha, float radius){ #ifndef PURE_OPENGL_ES_2 SphereRec *sp = G->Sphere->Sphere[level]; int a, cc; int *q = sp->Sequence; float pt[3]; if (color) glColor4f(color[0], color[1], color[2], alpha); for(a = 0; a < sp->NStrip; a++) { glBegin(GL_TRIANGLE_STRIP); cc = sp->StripLen[a]; while(cc--) { glNormal3fv(sp->dot[*q]); mult3f(sp->dot[*q], radius, pt); add3f(centroid, pt, pt); glVertex3fv(pt); q++; } glEnd(); } #endif } SphereRec* GetSpheroidSphereRec(PyMOLGlobals* G) { return G->Sphere->Sphere[2]; }