layer1/Scene.cpp (6,159 lines of code) (raw):

/* A* ------------------------------------------------------------------- B* This file contains source code for the PyMOL computer program C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. 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: -* sc -* -* Z* ------------------------------------------------------------------- */ #include"os_std.h" #include"os_gl.h" #include"os_python.h" #include"os_numpy.h" #include"Util.h" #include"LangUtil.h" #include"Word.h" #include"main.h" #include"Base.h" #include"MemoryDebug.h" #include"Err.h" #include"Matrix.h" #include"ListMacros.h" #include"PyMOLObject.h" #include"Scene.h" #include"SceneRay.h" #include"ScenePicking.h" #include"Ortho.h" #include"Vector.h" #include"ButMode.h" #include"Control.h" #include"Selector.h" #include"Setting.h" #include"Movie.h" #include"MyPNG.h" #include"P.h" #include"Editor.h" #include"Executive.h" #include"Wizard.h" #include"CGO.h" #include"ObjectGadget.h" #include"Seq.h" #include"Menu.h" #include"View.h" #include"ObjectSlice.h" #include"Text.h" #include"PyMOLOptions.h" #include"PyMOL.h" #include"PConv.h" #include"ScrollBar.h" #include "ShaderMgr.h" #include "PopUp.h" #include "MacPyMOL.h" #include <string> #include <vector> #include <algorithm> static void glReadBufferError(PyMOLGlobals *G, GLenum b, GLenum e){ PRINTFB(G, FB_OpenGL, FB_Warnings) " WARNING: glReadBuffer caused GL error 0x%04x\n", e ENDFB(G); } // TH 2013-11-01: glReadBuffer fails in JyMOL when picking, OSX 10.9, Intel Graphics // for minor cases (i.e., png is called) this might get called outside of the main // thread (from ExecutiveDrawNow()) in this case, just don't call glReadBuffer for // now, it should be ok because i believe it is used to figure out the size of the // 3D window (using SceneImagePrepareImpl) in situations where the size gets changed. #define glReadBuffer(b) { int e; if (PIsGlutThread()) glReadBuffer(b); \ if((e = glGetError())) glReadBufferError(G, b, e); } #ifdef _PYMOL_SHARP3D #define cSliceMin 0.1F #else #define cSliceMin 1.0F #endif #define SceneLineHeight 127 #define SceneTopMargin 0 #define SceneBottomMargin 3 #define SceneLeftMargin 3 /* Shared with ShaderMgr */ /** Coefficients from: http://3dtv.at/Knowhow/AnaglyphComparison_en.aspx */ /** Optimize the look and feel of anaglyph 3D */ /* the last mode is the 3x3 identity */ // matrices are column major float anaglyphL_constants[6][9] = { { 0.299, 0.000, 0.000, 0.587, 0.000, 0.000, 0.114, 0.000, 0.000 }, { 0.299, 0.000, 0.000, 0.587, 0.000, 0.000, 0.114, 0.000, 0.000 }, { 1.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000 }, { 0.299, 0.000, 0.000, 0.587, 0.000, 0.000, 0.114, 0.000, 0.000 }, { 0.000, 0.000, 0.000, 0.700, 0.000, 0.000, 0.300, 0.000, 0.000 }, { 1.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 } }; float anaglyphR_constants[6][9] = { { 0.000, 0.000, 0.299, 0.000, 0.000, 0.587, 0.000, 0.000, 0.114 }, { 0.000, 0.299, 0.299, 0.000, 0.587, 0.587, 0.000, 0.114, 0.114 }, { 0.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 }, { 0.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 }, { 0.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 }, { 1.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 } }; #define F2UI(a) (unsigned int) ((a) * 255.0) /* allow up to 10 seconds at 30 FPS */ /* EXPERIMENTAL VOLUME RAYTRACING DATA */ extern float *rayDepthPixels; extern int rayVolume, rayWidth, rayHeight; static void SceneDoRoving(PyMOLGlobals * G, float old_front, float old_back, float old_origin, int adjust_flag, int zoom_flag); #define SceneGetExactScreenVertexScale SceneGetScreenVertexScale static void SceneRestartPerfTimer(PyMOLGlobals * G); static void SceneRotateWithDirty(PyMOLGlobals * G, float angle, float x, float y, float z, int dirty); static void SceneClipSetWithDirty(PyMOLGlobals * G, float front, float back, int dirty); int SceneViewEqual(SceneViewType left, SceneViewType right) { int i; for(i = 0; i < cSceneViewSize; i++) { if(fabs(left[i] - right[i]) > R_SMALL4) return false; } return true; } void GridSetRayViewport(GridInfo * I, int slot, int *x, int *y, int *width, int *height) { if(slot) I->slot = slot + I->first_slot - 1; else I->slot = slot; /* if we are in grid mode, then prepare the grid slot viewport */ if(slot < 0) { *x = I->cur_view[0]; *y = I->cur_view[1]; *width = I->cur_view[2]; *height = I->cur_view[3]; } else if(!slot) { int vx = 0; int vw = I->cur_view[2] / I->n_col; int vy = 0; int vh = I->cur_view[3] / I->n_row; if(I->n_col < I->n_row) { vw *= I->n_col; vh *= I->n_col; } else { vw *= I->n_row; vh *= I->n_row; } vx += I->cur_view[0] + (I->cur_view[2] - vw) / 2; vy += I->cur_view[1]; *x = vx; *y = vy; *width = vw; *height = vh; } else { int abs_grid_slot = slot - I->first_slot; int grid_col = abs_grid_slot % I->n_col; int grid_row = (abs_grid_slot / I->n_col); int vx = (grid_col * I->cur_view[2]) / I->n_col; int vw = ((grid_col + 1) * I->cur_view[2]) / I->n_col - vx; int vy = I->cur_view[3] - ((grid_row + 1) * I->cur_view[3]) / I->n_row; int vh = (I->cur_view[3] - ((grid_row) * I->cur_view[3]) / I->n_row) - vy; vx += I->cur_view[0]; vy += I->cur_view[1]; *x = vx; *y = vy; *width = vw; *height = vh; } } void GridGetRayViewport(GridInfo * I, int width, int height) { I->cur_view[0] = 0; I->cur_view[1] = 0; I->cur_view[2] = width; I->cur_view[3] = height; } void GridUpdate(GridInfo * I, float asp_ratio, int mode, int size) { if(mode) { I->size = size; I->mode = mode; { int n_row = 1; int n_col = 1; int r_size = size; while((n_row * n_col) < r_size) { float asp1 = asp_ratio * (n_row + 1.0) / n_col; float asp2 = asp_ratio * (n_row) / (n_col + 1.0); if(asp1 < 1.0F) asp1 = 1.0 / asp1; if(asp2 < 1.0F) asp2 = 1.0 / asp2; if(fabs(asp1) > fabs(asp2)) n_col++; else n_row++; } I->n_row = n_row; I->n_col = n_col; } if(I->size > 1) { I->active = true; I->asp_adjust = (float) I->n_row / I->n_col; I->first_slot = 1; I->last_slot = I->size; } else { I->active = false; } } else { I->active = false; } } void SceneInvalidateStencil(PyMOLGlobals * G) { CScene *I = G->Scene; I->StencilValid = false; } int SceneGetGridSize(PyMOLGlobals * G, int grid_mode) { CScene *I = G->Scene; int slot; int size = 0; switch (grid_mode) { case 1: if(!I->SlotVLA) I->SlotVLA = VLACalloc(int, 1); else { UtilZeroMem(I->SlotVLA, sizeof(int) * VLAGetSize(I->SlotVLA)); } { int max_slot = 0; for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) { if((slot = (*it)->grid_slot)) { if(max_slot < slot) max_slot = slot; if(slot > 0) { VLACheck(I->SlotVLA, int, slot); I->SlotVLA[slot] = 1; } } } for(slot = 0; slot <= max_slot; slot++) { if(I->SlotVLA[slot]) I->SlotVLA[slot] = ++size; } } break; case 2: case 3: if(I->SlotVLA) { VLAFreeP(I->SlotVLA); I->SlotVLA = NULL; } { int max_slot = 0; for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) { if((*it)->fGetNFrame) { slot = (*it)->fGetNFrame(*it); if(grid_mode == 3) { (*it)->grid_slot = max_slot; // slot offset for 1st state max_slot += slot; } else if(max_slot < slot) { max_slot = slot; } } } size = max_slot; } break; } { int grid_max = SettingGetGlobal_i(G, cSetting_grid_max); if(grid_max >= 0) if(size > grid_max) size = grid_max; } return size; } int SceneHasImage(PyMOLGlobals * G) { CScene *I = G->Scene; return (I->Image && I->Image->data); } int SceneMustDrawBoth(PyMOLGlobals * G) { CScene *I = G->Scene; return (G->StereoCapable && ((I->StereoMode == 1) || SettingGetGlobal_b(G, cSetting_stereo_double_pump_mono))); } static int SceneDeferClickWhen(Block * block, int button, int x, int y, double when, int mod); static int stereo_via_adjacent_array(int stereo_mode) { switch (stereo_mode) { case cStereo_crosseye: case cStereo_walleye: case cStereo_sidebyside: return true; } return false; } int StereoIsAdjacent(PyMOLGlobals * G){ CScene *I = G->Scene; return stereo_via_adjacent_array(I->StereoMode); } static int get_stereo_x(int x, int *last_x, int width, int *click_side) { int width_2 = width / 2; int width_3 = width / 3; if(!last_x) { if(x > width_2) { x -= width_2; if(click_side) *click_side = 1; /* right */ } else { if(click_side) *click_side = -1; /* left */ } } else { if((x - (*last_x)) > width_3) { x -= width_2; if(click_side) *click_side = 1; /* right */ } else if(((*last_x) - x) > width_3) { x += width_2; if(click_side) *click_side = 1; /* right */ } else { if(click_side) *click_side = -1; /* left */ } } return x; } int SceneLoopDrag(Block * block, int x, int y, int mod); int SceneLoopRelease(Block * block, int button, int x, int y, int mod); int SceneLoopClick(Block * block, int button, int x, int y, int mod); void SceneAbortAnimation(PyMOLGlobals * G) { CScene *I = G->Scene; if(I->cur_ani_elem < I->n_ani_elem) { /* allow user to override animation */ I->cur_ani_elem = I->n_ani_elem; } } void ScenePrimeAnimation(PyMOLGlobals * G) { if(G->HaveGUI) { CScene *I = G->Scene; UtilZeroMem(I->ani_elem, sizeof(CViewElem)); SceneToViewElem(G, I->ani_elem, NULL); I->ani_elem[0].specification_level = 2; I->n_ani_elem = 0; } } static float SceneGetFPS(PyMOLGlobals * G) { float fps = SettingGetGlobal_f(G, cSetting_movie_fps); float minTime; if(fps <= 0.0F) { if(fps < 0.0) minTime = 0.0; /* negative fps means full speed */ else /* 0 fps means use movie_delay instead */ minTime = SettingGetGlobal_f(G, cSetting_movie_delay) / 1000.0; if(minTime >= 0.0F) fps = 1.0F / minTime; else fps = 1000.0F; } return fps; } void ScenePurgeImageImpl(PyMOLGlobals * G, int noinvalid); static void ScenePurgeImage(PyMOLGlobals * G) { ScenePurgeImageImpl(G, 0); } void ScenePurgeImageImpl(PyMOLGlobals * G, int noinvalid) { CScene *I = G->Scene; if(I->MovieOwnsImageFlag) { I->MovieOwnsImageFlag = false; I->Image = NULL; } else { if(I->Image) { FreeP(I->Image->data); } FreeP(I->Image); } I->CopyType = false; if (!noinvalid) OrthoInvalidateDoDraw(G); // right now, need to invalidate since text could be shown } void SceneInvalidateCopy(PyMOLGlobals * G, int free_buffer) { CScene *I = G->Scene; if(I) { if(I->MovieOwnsImageFlag) { I->MovieOwnsImageFlag = false; I->Image = NULL; } else if(free_buffer) { ScenePurgeImage(G); } if (I->CopyType) OrthoInvalidateDoDraw(G); // right now, need to invalidate since text could be shown I->CopyType = false; } } void SceneInvalidate(PyMOLGlobals * G) { SceneInvalidateCopy(G, false); SceneDirty(G); PyMOL_NeedRedisplay(G->PyMOL); } void SceneLoadAnimation(PyMOLGlobals * G, double duration, int hand) { if(G->HaveGUI) { double now; int target = (int) (duration * 30); CScene *I = G->Scene; if(target < 1) target = 1; if(target > MAX_ANI_ELEM) target = MAX_ANI_ELEM; UtilZeroMem(I->ani_elem + 1, sizeof(CViewElem) * target); SceneToViewElem(G, I->ani_elem + target, NULL); I->ani_elem[target].specification_level = 2; now = UtilGetSeconds(G); I->ani_elem[0].timing_flag = true; I->ani_elem[0].timing = now + 0.01; I->ani_elem[target].timing_flag = true; I->ani_elem[target].timing = now + duration; ViewElemInterpolate(G, I->ani_elem, I->ani_elem + target, 2.0F, 1.0F, true, 0.0F, hand, 0.0F); SceneFromViewElem(G, I->ani_elem, true); I->cur_ani_elem = 0; I->n_ani_elem = target; I->AnimationStartTime = UtilGetSeconds(G); I->AnimationStartFlag = true; I->AnimationStartFrame = SceneGetFrame(G); I->AnimationLagTime = 0.0; } } int SceneLoopClick(Block * block, int button, int x, int y, int mod) { PyMOLGlobals *G = block->m_G; CScene *I = G->Scene; I->LoopRect.left = x; I->LoopRect.top = y; I->LoopRect.right = x; I->LoopRect.bottom = y; I->LoopFlag = true; I->LoopMod = mod; OrthoSetLoopRect(G, true, &I->LoopRect); OrthoGrab(G, block); return 1; } int SceneLoopDrag(Block * block, int x, int y, int mod) { PyMOLGlobals *G = block->m_G; CScene *I = G->Scene; I->LoopRect.right = x; I->LoopRect.bottom = y; OrthoSetLoopRect(G, true, &I->LoopRect); return 1; } int SceneLoopRelease(Block * block, int button, int x, int y, int mod) { PyMOLGlobals *G = block->m_G; CScene *I = G->Scene; int tmp; int mode; mode = ButModeTranslate(G, button, I->LoopMod); if(I->LoopRect.top < I->LoopRect.bottom) { tmp = I->LoopRect.top; I->LoopRect.top = I->LoopRect.bottom; I->LoopRect.bottom = tmp; } if(I->LoopRect.right < I->LoopRect.left) { tmp = I->LoopRect.right; I->LoopRect.right = I->LoopRect.left; I->LoopRect.left = tmp; } OrthoSetLoopRect(G, false, &I->LoopRect); ExecutiveSelectRect(G, &I->LoopRect, mode); I->LoopFlag = false; OrthoUngrab(G); OrthoDirty(G); return 1; } /* * Set FrontSafe and BackSafe */ static void UpdateFrontBackSafe(CScene *I) { float front = I->Front; float back = I->Back; // minimum slab if(back - front < cSliceMin) { float avg = (back + front) / 2.0; back = avg + cSliceMin / 2.0; front = avg - cSliceMin / 2.0; } // minimum front if (front < cSliceMin) { front = cSliceMin; // minimum slab if (back < front + cSliceMin) back = front + cSliceMin; } I->FrontSafe = front; I->BackSafe = back; } #define SELE_MODE_MAX 7 static const char SelModeKW[][20] = { "", "byresi", "bychain", "bysegi", "byobject", "bymol", "bca.", }; static void SceneUpdateInvMatrix(PyMOLGlobals * G) { CScene *I = G->Scene; float *rm = I->RotMatrix; float *im = I->InvMatrix; im[0] = rm[0]; im[1] = rm[4]; im[2] = rm[8]; im[3] = 0.0F; im[4] = rm[1]; im[5] = rm[5]; im[6] = rm[9]; im[7] = 0.0F; im[8] = rm[2]; im[9] = rm[6]; im[10] = rm[10]; im[11] = 0.0F; im[12] = 0.0F; im[13] = 0.0F; im[14] = 0.0F; im[15] = 1.0F; } void SceneUpdateStereo(PyMOLGlobals * G) { SceneSetStereo(G, SettingGetGlobal_b(G, cSetting_stereo)); PyMOL_NeedRedisplay(G->PyMOL); } char *SceneGetSeleModeKeyword(PyMOLGlobals * G) { int sel_mode = SettingGetGlobal_i(G, cSetting_mouse_selection_mode); if((sel_mode >= 0) && (sel_mode < SELE_MODE_MAX)) return (char *) SelModeKW[sel_mode]; return (char *) SelModeKW[0]; } void SceneToViewElem(PyMOLGlobals * G, CViewElem * elem, const char *scene_name) { float *fp; double *dp; CScene *I = G->Scene; /* copy rotation matrix */ elem->matrix_flag = true; dp = elem->matrix; fp = I->RotMatrix; *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = 0.0F; *(dp++) = 0.0F; *(dp++) = 0.0F; *(dp++) = 1.0F; /* copy position */ elem->pre_flag = true; dp = elem->pre; fp = I->Pos; *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); *(dp++) = (double) *(fp++); /* copy origin (negative) */ elem->post_flag = true; dp = elem->post; fp = I->Origin; *(dp++) = (double) (-*(fp++)); *(dp++) = (double) (-*(fp++)); *(dp++) = (double) (-*(fp++)); elem->clip_flag = true; elem->front = I->Front; elem->back = I->Back; elem->ortho_flag = true; elem->ortho = SettingGetGlobal_b(G, cSetting_ortho) ? SettingGetGlobal_f(G, cSetting_field_of_view) : -SettingGetGlobal_f(G, cSetting_field_of_view); { if(elem->scene_flag && elem->scene_name) { OVLexicon_DecRef(G->Lexicon, elem->scene_name); elem->scene_name = 0; elem->scene_flag = 0; } } { if(!scene_name) scene_name = SettingGetGlobal_s(G, cSetting_scene_current_name); if(scene_name && scene_name[0]) { OVreturn_word result = OVLexicon_GetFromCString(G->Lexicon, scene_name); if(OVreturn_IS_OK(result)) { elem->scene_name = result.word; elem->scene_flag = true; } } } } void SceneFromViewElem(PyMOLGlobals * G, CViewElem * elem, int dirty) { CScene *I = G->Scene; float *fp; double *dp; int changed_flag = false; if(elem->matrix_flag) { dp = elem->matrix; fp = I->RotMatrix; *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); changed_flag = true; SceneUpdateInvMatrix(G); } if(elem->pre_flag) { dp = elem->pre; fp = I->Pos; *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); *(fp++) = (float) *(dp++); changed_flag = true; } if(elem->post_flag) { dp = elem->post; fp = I->Origin; *(fp++) = (float) (-*(dp++)); *(fp++) = (float) (-*(dp++)); *(fp++) = (float) (-*(dp++)); changed_flag = true; } if(elem->clip_flag) { SceneClipSetWithDirty(G, elem->front, elem->back, dirty); } if(elem->ortho_flag) { if(elem->ortho < 0.0F) { SettingSetGlobal_b(G, cSetting_ortho, 0); if(elem->ortho < -(1.0F - R_SMALL4)) { SettingSetGlobal_f(G, cSetting_field_of_view, -elem->ortho); } } else { SettingSetGlobal_b(G, cSetting_ortho, (elem->ortho > 0.5F)); if(elem->ortho > (1.0F + R_SMALL4)) { SettingSetGlobal_f(G, cSetting_field_of_view, elem->ortho); } } } if(elem->state_flag&&!MovieDefined(G)) { SettingSetGlobal_i(G, cSetting_state, (elem->state)+1); } if(changed_flag) { SceneRestartSweepTimer(G); I->RockFrame = 0; SceneRovingDirty(G); } } void SceneCleanupStereo(PyMOLGlobals * G) { #ifndef _PYMOL_NOPY CScene *I = G->Scene; if(I->StereoMode == 1) PSGIStereo(G, 0); #endif } void ScenePrepareUnitContext(SceneUnitContext * context, int width, int height) { float tw = 1.0F; float th = 1.0F; float aspRat; if(height) { aspRat = width / (float) height; } else { aspRat = 1.0F; } if(aspRat > 1.0F) { tw = aspRat; } else { th = 1.0F / aspRat; } context->unit_left = (1.0F - tw) / 2; context->unit_right = (tw + 1.0F) / 2; context->unit_top = (1.0F - th) / 2; context->unit_bottom = (th + 1.0F) / 2; context->unit_front = -0.5F; context->unit_back = 0.5F; /* printf( "ScenePrepareUnitContext:%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f\n", context->unit_left, context->unit_right, context->unit_top, context->unit_bottom, context->unit_front, context->unit_back); */ } /* * cmd.get_viewport() */ void SceneGetWidthHeight(PyMOLGlobals * G, int *width, int *height) { CScene *I = G->Scene; *width = I->Width; *height = I->Height; } /* * Get the actual current (sub-)viewport size, considering grid mode and * side-by-side stereo */ void SceneGetWidthHeightStereo(PyMOLGlobals * G, int *width, int *height) { CScene *I = G->Scene; if (I->grid.active) { // TODO: this considers "draw W, H" (PYMOL-2775) *width = I->grid.cur_viewport_size[0]; *height = I->grid.cur_viewport_size[1]; return; } // TODO: this does NOT consider "draw W, H" (PYMOL-2775) *width = I->Width; *height = I->Height; if (stereo_via_adjacent_array(I->StereoMode)) *width /= 2.f; } void SceneSetCardInfo(PyMOLGlobals * G, const char *vendor, const char *renderer, const char *version) { CScene *I = G->Scene; if (!vendor) vendor = "(null)"; if (!renderer) renderer = "(null)"; if (!version) version = "(null)"; UtilNCopy(I->vendor, vendor, sizeof(OrthoLineType) - 1); UtilNCopy(I->renderer, renderer, sizeof(OrthoLineType) - 1); UtilNCopy(I->version, version, sizeof(OrthoLineType) - 1); } int SceneGetStereo(PyMOLGlobals * G) { CScene *I = G->Scene; return (I->StereoMode); } void SceneGetCardInfo(PyMOLGlobals * G, char **vendor, char **renderer, char **version) { CScene *I = G->Scene; (*vendor) = I->vendor; (*renderer) = I->renderer; (*version) = I->version; } void SceneSuppressMovieFrame(PyMOLGlobals * G) { CScene *I = G->Scene; I->MovieFrameFlag = false; } /* * Get center of screen in world coordinates */ void SceneGetCenter(PyMOLGlobals * G, float *pos) { CScene *I = G->Scene; MatrixTransformC44fAs33f3f(I->RotMatrix, I->Origin, pos); pos[0] -= I->Pos[0]; pos[1] -= I->Pos[1]; MatrixInvTransformC44fAs33f3f(I->RotMatrix, pos, pos); } /*========================================================================*/ int SceneGetNFrame(PyMOLGlobals * G, int *has_movie) { CScene *I = G->Scene; if(has_movie) *has_movie = I->HasMovie; return (I->NFrame); } /*========================================================================*/ /* SceneGetView: all information required to define the geometry of a particular view, for shipping to and from python as a list of floats 0-15 = 4x4 rotation matrix 16-18 = position 19-21 = origin 22 = front plane 23 = rear plane 24 = orthoscopic flag */ void SceneGetView(PyMOLGlobals * G, SceneViewType view) { float *p; int a; CScene *I = G->Scene; p = view; for(a = 0; a < 16; a++) *(p++) = I->RotMatrix[a]; *(p++) = I->Pos[0]; *(p++) = I->Pos[1]; *(p++) = I->Pos[2]; *(p++) = I->Origin[0]; *(p++) = I->Origin[1]; *(p++) = I->Origin[2]; *(p++) = I->Front; *(p++) = I->Back; *(p++) = SettingGetGlobal_b(G, cSetting_ortho) ? SettingGetGlobal_f(G, cSetting_field_of_view) : -SettingGetGlobal_f(G, cSetting_field_of_view); } /*========================================================================*/ void SceneSetView(PyMOLGlobals * G, SceneViewType view, int quiet, float animate, int hand) { float *p; int a; CScene *I = G->Scene; if(animate < 0.0F) { if(SettingGetGlobal_b(G, cSetting_animation)) animate = SettingGetGlobal_f(G, cSetting_animation_duration); else animate = 0.0F; } if(animate != 0.0F) ScenePrimeAnimation(G); else { SceneAbortAnimation(G); } p = view; for(a = 0; a < 16; a++) I->RotMatrix[a] = *(p++); SceneUpdateInvMatrix(G); I->Pos[0] = *(p++); I->Pos[1] = *(p++); I->Pos[2] = *(p++); I->Origin[0] = *(p++); I->Origin[1] = *(p++); I->Origin[2] = *(p++); I->LastSweep = 0.0F; I->LastSweepX = 0.0F; I->LastSweepY = 0.0F; I->SweepTime = 0.0; I->RockFrame = 0; SceneClipSet(G, p[0], p[1]); p += 2; if(p[0] < 0.0F) { SettingSetGlobal_b(G, cSetting_ortho, 0); if(p[0] < -(1.0F - R_SMALL4)) { SettingSetGlobal_f(G, cSetting_field_of_view, -p[0]); } } else { SettingSetGlobal_b(G, cSetting_ortho, (p[0] > 0.5F)); if(p[0] > (1.0F + R_SMALL4)) { SettingSetGlobal_f(G, cSetting_field_of_view, p[0]); } } if(!quiet) { PRINTFB(G, FB_Scene, FB_Actions) " Scene: view updated.\n" ENDFB(G); } if(animate != 0.0F) SceneLoadAnimation(G, animate, hand); SceneRovingDirty(G); } /*========================================================================*/ void SceneDontCopyNext(PyMOLGlobals * G) /* disables automatic copying of the image for the next rendering run */ { CScene *I = G->Scene; I->CopyNextFlag = false; } /*========================================================================*/ void SceneUpdateStereoMode(PyMOLGlobals * G) { if(G->Scene->StereoMode) { SceneSetStereo(G, true); } } /*========================================================================*/ void SceneSetStereo(PyMOLGlobals * G, int flag) { CScene *I = G->Scene; int cur_stereo, cur_stereo_mode = I->StereoMode, reload = 0; cur_stereo = SettingGetGlobal_b(G, cSetting_stereo); if(flag) { I->StereoMode = SettingGetGlobal_i(G, cSetting_stereo_mode); } else { I->StereoMode = 0; } SettingSetGlobal_b(G, cSetting_stereo, flag); if((cur_stereo_mode != I->StereoMode) && (cur_stereo_mode == cStereo_geowall || I->StereoMode == cStereo_geowall)) { reload = 1; OrthoReshape(G, G->Option->winX, G->Option->winY, true); #ifndef _PYMOL_NOPY if(cur_stereo_mode == cStereo_geowall) { PParse(G, "viewport"); } #endif } SceneInvalidateStencil(G); SceneInvalidate(G); if (cur_stereo != flag || (flag && reload)){ G->ShaderMgr->Set_Reload_Bits(RELOAD_VARIABLES); } } /*========================================================================*/ void SceneTranslate(PyMOLGlobals * G, float x, float y, float z) { CScene *I = G->Scene; I->Pos[0] += x; I->Pos[1] += y; I->Pos[2] += z; SceneClipSet(G, I->Front - z, I->Back - z); } void SceneTranslateScaled(PyMOLGlobals * G, float x, float y, float z, int sdof_mode) { CScene *I = G->Scene; int invalidate = false; switch (sdof_mode) { case SDOF_NORMAL_MODE: if((x != 0.0F) || (y != 0.0F)) { float vScale = SceneGetExactScreenVertexScale(G, NULL); float factor = vScale * (I->Height + I->Width) / 2; I->Pos[0] += x * factor; I->Pos[1] += y * factor; invalidate = true; } if(z != 0.0F) { float factor = ((I->FrontSafe + I->BackSafe) / 2); /* average distance within visible space */ if(factor > 0.0F) { factor *= z; I->Pos[2] += factor; I->Front -= factor; I->Back -= factor; UpdateFrontBackSafe(I); } invalidate = true; } break; case SDOF_CLIP_MODE: if((x != 0.0F) || (y != 0.0F)) { float vScale = SceneGetExactScreenVertexScale(G, NULL); float factor = vScale * (I->Height + I->Width) / 2; I->Pos[0] += x * factor; I->Pos[1] += y * factor; invalidate = true; } if(z != 0.0F) { float factor = ((I->FrontSafe + I->BackSafe) / 2); /* average distance within visible space */ if(factor > 0.0F) { factor *= z; { float old_front = I->Front; float old_back = I->Back; float old_origin = -I->Pos[2]; SceneClip(G, 7, factor, NULL, 0); SceneDoRoving(G, old_front, old_back, old_origin, true, true); } invalidate = true; } } break; case SDOF_DRAG_MODE: { float v2[3]; float scale = SettingGetGlobal_f(G, cSetting_sdof_drag_scale); { /* when dragging, we treat all axes proportionately */ float vScale = SceneGetExactScreenVertexScale(G, NULL); float factor = vScale * (I->Height + I->Width) / 2; x *= factor; y *= factor; z *= factor; } v2[0] = x * scale; v2[1] = y * scale; v2[2] = z * scale; /* transform into model coodinate space */ MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2); EditorDrag(G, NULL, -1, cButModeMovDrag, SettingGetGlobal_i(G, cSetting_state) - 1, NULL, v2, NULL); } break; } if(invalidate) { SceneInvalidate(G); if(SettingGetGlobal_b(G, cSetting_roving_origin)) { float v2[3]; SceneGetCenter(G, v2); /* gets position of center of screen */ SceneOriginSet(G, v2, true); } if(SettingGetGlobal_b(G, cSetting_roving_detail)) { SceneRovingPostpone(G); } } } void SceneRotateScaled(PyMOLGlobals * G, float rx, float ry, float rz, int sdof_mode) { CScene *I = G->Scene; int invalidate = false; float axis[3]; switch (sdof_mode) { case SDOF_NORMAL_MODE: axis[0] = rx; axis[1] = ry; axis[2] = rz; { float angle = length3f(axis); normalize3f(axis); SceneRotate(G, 60 * angle, axis[0], axis[1], axis[2]); } break; case SDOF_CLIP_MODE: if((fabs(rz) > fabs(rx)) || (fabs(rz) > fabs(rx))) { rx = 0.0; ry = 0.0; } else { rz = 0.0; } axis[0] = rx; axis[1] = ry; axis[2] = 0.0; { float angle = length3f(axis); normalize3f(axis); SceneRotate(G, 60 * angle, axis[0], axis[1], axis[2]); } if(axis[2] != rz) { SceneClip(G, 5, 1.0F + rz, NULL, 0); } break; case SDOF_DRAG_MODE: { float scale = SettingGetGlobal_f(G, cSetting_sdof_drag_scale); float v1[3], v2[3]; axis[0] = rx; axis[1] = ry; axis[2] = rz; EditorReadyDrag(G, SettingGetGlobal_i(G, cSetting_state) - 1); { float angle = length3f(axis); normalize3f(axis); v1[0] = cPI * (60 * angle / 180.0F) * scale; /* transform into model coodinate space */ MatrixInvTransformC44fAs33f3f(I->RotMatrix, axis, v2); EditorDrag(G, NULL, -1, cButModeRotDrag, SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, NULL); invalidate = true; } } break; } if(invalidate) { SceneInvalidate(G); } } /*========================================================================*/ static void SceneClipSetWithDirty(PyMOLGlobals * G, float front, float back, int dirty) { CScene *I = G->Scene; // minimum slab if(back - front < cSliceMin) { float avg = (back + front) / 2.0; back = avg + cSliceMin / 2.0; front = avg - cSliceMin / 2.0; } I->Front = front; I->Back = back; UpdateFrontBackSafe(I); if(dirty) SceneInvalidate(G); else SceneInvalidateCopy(G, false); } /*========================================================================*/ void SceneClipSet(PyMOLGlobals * G, float front, float back) { SceneClipSetWithDirty(G, front, back, true); } /*========================================================================*/ void SceneClip(PyMOLGlobals * G, int plane, float movement, const char *sele, int state) { /* 0=front, 1=back */ CScene *I = G->Scene; float avg; float mn[3], mx[3], cent[3], v0[3], offset[3], origin[3]; switch (plane) { case 0: /* near */ SceneClipSet(G, I->Front - movement, I->Back); break; case 1: /* far */ SceneClipSet(G, I->Front, I->Back - movement); break; case 2: /* move */ SceneClipSet(G, I->Front - movement, I->Back - movement); break; case 3: /* slab */ if(sele[0]) { if(!ExecutiveGetExtent(G, sele, mn, mx, true, state, false)) sele = NULL; else { average3f(mn, mx, cent); /* get center of selection */ subtract3f(cent, I->Origin, v0); /* how far from origin? */ MatrixTransformC44fAs33f3f(I->RotMatrix, v0, offset); /* convert to view-space */ } } else { sele = NULL; } avg = (I->Front + I->Back) / 2.0F; movement /= 2.0F; if(sele) { avg = -I->Pos[2] - offset[2]; } SceneClipSet(G, avg - movement, avg + movement); break; case 4: /* atoms */ if(!sele) sele = cKeywordAll; else if(!sele[0]) { sele = cKeywordAll; } if(WordMatchExact(G, sele, cKeywordCenter, true)) { MatrixTransformC44fAs33f3f(I->RotMatrix, I->Origin, origin); /* convert to view-space */ SceneClipSet(G, origin[2] - movement, origin[2] + movement); } else if(WordMatchExact(G, sele, cKeywordOrigin, true)) { SceneClipSet(G, -I->Pos[2] - movement, -I->Pos[2] + movement); } else { if(!ExecutiveGetCameraExtent(G, sele, mn, mx, true, state)) sele = NULL; if(sele) { if(sele[0]) { average3f(mn, mx, cent); /* get center of selection */ MatrixTransformC44fAs33f3f(I->RotMatrix, I->Origin, origin); /* convert to view-space */ subtract3f(mx, origin, mx); /* how far from origin? */ subtract3f(mn, origin, mn); /* how far from origin? */ SceneClipSet(G, -I->Pos[2] - mx[2] - movement, -I->Pos[2] - mn[2] + movement); } else { sele = NULL; } } } break; case 5: /* scaling */ { double avg = (I->Front / 2.0) + (I->Back / 2.0); double width_half = I->Back - avg; double new_w_half = std::min(movement * width_half, width_half + 1000.0); // prevent exploding of clipping planes SceneClipSet(G, avg - new_w_half, avg + new_w_half); } break; case 6: /* proportional movement */ { float shift = (I->Front - I->Back) * movement; SceneClipSet(G, I->Front + shift, I->Back + shift); } break; case 7: /* linear movement */ { SceneClipSet(G, I->Front + movement, I->Back + movement); } break; } } /*========================================================================*/ void SceneSetMatrix(PyMOLGlobals * G, float *m) { CScene *I = G->Scene; int a; for(a = 0; a < 16; a++) I->RotMatrix[a] = m[a]; SceneUpdateInvMatrix(G); } /*========================================================================*/ void SceneGetViewNormal(PyMOLGlobals * G, float *v) { CScene *I = G->Scene; copy3f(I->ViewNormal, v); } /*========================================================================*/ int SceneGetState(PyMOLGlobals * G) { return (SettingGetGlobal_i(G, cSetting_state) - 1); } /*========================================================================*/ float *SceneGetMatrix(PyMOLGlobals * G) { CScene *I = G->Scene; return (I->RotMatrix); } float *SceneGetPmvMatrix(PyMOLGlobals * G) { CScene *I = G->Scene; multiply44f44f44f(I->ModelViewMatrix, I->ProjectionMatrix, I->PmvMatrix); return (I->PmvMatrix); } /*========================================================================*/ int SceneCaptureWindow(PyMOLGlobals * G) { CScene *I = G->Scene; int ok = true; /* check assumptions */ if(ok && G->HaveGUI && G->ValidContext) { int draw_both = SceneMustDrawBoth(G); ScenePurgeImage(G); if(draw_both) { SceneCopy(G, GL_BACK_LEFT, true, true); } else { SceneCopy(G, GL_BACK, true, true); } if(!I->Image) ok = false; if(ok && I->Image) { I->DirtyFlag = false; I->CopyType = 2; /* suppresses display of copied image */ if(SettingGetGlobal_b(G, cSetting_opaque_background)) I->Image->needs_alpha_reset = true; I->MovieOwnsImageFlag = false; } } else { ok = false; } return ok; } static int SceneMakeSizedImage(PyMOLGlobals * G, int width, int height, int antialias) { CScene *I = G->Scene; int ok = true; int save_flag = false; int save_width = 0, save_height = 0; /* check assumptions */ if((width && height && I->Width && I->Height) && fabs(((float) (height - (width * I->Height) / (I->Width))) / height) > 0.01F) { save_width = I->Width; save_height = I->Height; save_flag = true; /* squish the dimensions as needed to maintain aspect ratio within the current rectangle */ if(I->Width > ((width * I->Height) / height)) I->Width = (width * I->Height) / height; else if(I->Height > ((height * I->Width) / width)) I->Height = (height * I->Width) / width; } if((!width) && (!height)) { width = I->Width; height = I->Height; } if(width && !height) { height = (I->Height * width) / I->Width; } if(height && !width) { width = (I->Width * height) / I->Height; } if(!((width > 0) && (height > 0) && (I->Width > 0) && (I->Height > 0))) { PRINTFB(G, FB_Scene, FB_Errors) "SceneMakeSizedImage-Error: invalid image dimensions\n" ENDFB(G); ok = false; } if(ok && G->HaveGUI && G->ValidContext) { int factor = 0; int shift = 0; int max_dim[2]; glGetIntegerv(GL_MAX_VIEWPORT_DIMS, (GLint *) (void *) max_dim); /* clamp to what this OpenGL implementation can do */ if(width > max_dim[0]) { height = (max_dim[0] * height) / width; width = max_dim[0]; PRINTFB(G, FB_Scene, FB_Warnings) "Scene-Warning: Maximum OpenGL viewport dimension exceeded." ENDFB(G) } if(height > max_dim[0]) { width = (max_dim[1] * width) / height; height = max_dim[1]; PRINTFB(G, FB_Scene, FB_Warnings) "Scene-Warning: Maximum OpenGL viewport dimension exceeded." ENDFB(G) } if(antialias == 1) { factor = 2; shift = 2; } if(antialias >= 2) { factor = 4; shift = 4; } while(factor > 1) { if(((width * factor) < max_dim[0]) && ((height * factor) < max_dim[1])) { width = width * factor; height = height * factor; break; } else { factor = (factor >> 1); shift = shift - 2; if(factor < 2) { PRINTFB(G, FB_Scene, FB_Blather) "Scene-Warning: Maximum OpenGL viewport exceeded. Antialiasing disabled." ENDFB(G); break; } } } if(factor < 2) factor = 0; { unsigned int final_buffer_size = width * height; unsigned int *final_image = NULL; int nXStep = (width / (I->Width + 1)) + 1; int nYStep = (height / (I->Height + 1)) + 1; int x, y; int draw_both = SceneMustDrawBoth(G); /* note here we're treating the buffer as 32-bit unsigned ints, not chars */ final_image = Alloc(unsigned int, final_buffer_size); if(!final_image) { ok = false; } ScenePurgeImage(G); if(draw_both) { SceneCopy(G, GL_BACK_LEFT, true, false); } else { SceneCopy(G, GL_BACK, true, false); } if(!I->Image) ok = false; if(ok) { int total_steps = nXStep * nYStep; OrthoBusyPrime(G); /* so the trick here is that we need to move the camera around so that we get a pixel-perfect mosaic */ for(y = 0; y < nYStep; y++) { int y_offset = -(I->Height * y); for(x = 0; x < nXStep; x++) { int x_offset = -(I->Width * x); int a, b; unsigned int *p, *q, *qq, *pp; OrthoBusyFast(G, y * nXStep + x, total_steps); if(draw_both) { OrthoDrawBuffer(G, GL_BACK_LEFT); } else { OrthoDrawBuffer(G, GL_BACK); } glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); SceneInvalidateCopy(G, false); SceneRender(G, NULL, x_offset, y_offset, NULL, width, height, 0, 0); SceneGLClearColor(0.0, 0.0, 0.0, 1.0); if(draw_both) { SceneCopy(G, GL_BACK_LEFT, true, false); } else { SceneCopy(G, GL_BACK, true, false); } if(I->Image) { /* the image into place */ p = (unsigned int *) I->Image->data; q = final_image + (x * I->Width) + (y * I->Height) * width; { int y_limit; int x_limit; if(((y + 1) * I->Height) > height) y_limit = height - (y * I->Height); else y_limit = I->Height; if(((x + 1) * I->Width) > width) x_limit = width - (x * I->Width); else x_limit = I->Width; for(a = 0; a < y_limit; a++) { qq = q; pp = p; for(b = 0; b < x_limit; b++) { *(qq++) = *(pp++); } q += width; p += I->Width; } } } } } if(!OrthoDeferredWaiting(G)) { if(SettingGetGlobal_i(G, cSetting_draw_mode) == -2) { ExecutiveSetSettingFromString(G, cSetting_draw_mode, "-1", "", -1, true, true); SceneUpdate(G, false); } } SceneInvalidateCopy(G, true); if(factor) { /* are we oversampling? */ unsigned int src_row_bytes = width * 4; width = width / factor; height = height / factor; { unsigned char *p = (unsigned char *) final_image; unsigned char *buffer = Alloc(unsigned char, 4 * width * height); unsigned char *q = buffer; unsigned char *pp, *ppp, *pppp; int a, b, c, d; unsigned int c1, c2, c3, c4, alpha; unsigned int factor_col_bytes = factor * 4; unsigned int factor_row_bytes = factor * src_row_bytes; for(b = 0; b < height; b++) { /* rows */ pp = p; for(a = 0; a < width; a++) { /* cols */ c1 = c2 = c3 = c4 = 0; ppp = pp; for(d = 0; d < factor; d++) { /* box rows */ pppp = ppp; for(c = 0; c < factor; c++) { /* box cols */ c4 += (alpha = pppp[3]); c1 += *(pppp++) * alpha; c2 += *(pppp++) * alpha; c3 += *(pppp++) * alpha; pppp++; } ppp += src_row_bytes; } if(c4) { /* divide out alpha channel & average */ c1 = c1 / c4; c2 = c2 / c4; c3 = c3 / c4; } else { /* alpha zero! so compute average RGB */ c1 = c2 = c3 = 0; ppp = pp; for(d = 0; d < factor; d++) { /* box rows */ pppp = ppp; for(c = 0; c < factor; c++) { /* box cols */ c1 += *(pppp++); c2 += *(pppp++); c3 += *(pppp++); pppp++; } ppp += src_row_bytes; } c1 = c1 >> shift; c2 = c2 >> shift; c3 = c3 >> shift; } *(q++) = c1; *(q++) = c2; *(q++) = c3; *(q++) = c4 >> shift; pp += factor_col_bytes; } p += factor_row_bytes; } FreeP(final_image); final_image = (unsigned int *) buffer; } } ScenePurgeImage(G); I->Image = Calloc(ImageType, 1); I->Image->data = (unsigned char *) final_image; final_image = NULL; I->Image->size = final_buffer_size * 4; /* in bytes, not 32-bit words */ I->Image->width = width; I->Image->height = height; I->Image->stereo = false; I->DirtyFlag = false; I->CopyType = true; I->CopyForced = true; if(SettingGetGlobal_b(G, cSetting_opaque_background)) I->Image->needs_alpha_reset = true; I->MovieOwnsImageFlag = false; } FreeP(final_image); } } else { ok = false; } if(save_flag) { I->Width = save_width; I->Height = save_height; } return ok; } #define SceneImagePrepareImpl SceneImagePrepare unsigned char *SceneImagePrepareImpl(PyMOLGlobals * G, int prior_only, int noinvalid) { CScene *I = G->Scene; unsigned char *image = NULL; int save_stereo = (I->StereoMode == 1); int ok = true; if(!noinvalid && !(I->CopyType || prior_only)) { if(G->HaveGUI && G->ValidContext) { unsigned int buffer_size; buffer_size = 4 * I->Width * I->Height; if(save_stereo) image = Alloc(unsigned char, buffer_size * 2); else image = Alloc(unsigned char, buffer_size); CHECKOK(ok, image); if (!ok) return NULL; if(SceneMustDrawBoth(G) || save_stereo) { glReadBuffer(GL_BACK_LEFT); } else { glReadBuffer(GL_BACK); } PyMOLReadPixels(I->rect.left, I->rect.bottom, I->Width, I->Height, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *) (image)); if(save_stereo) { glReadBuffer(GL_BACK_RIGHT); PyMOLReadPixels(I->rect.left, I->rect.bottom, I->Width, I->Height, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *) (image + buffer_size)); } ScenePurgeImageImpl(G, noinvalid); I->Image = Calloc(ImageType, 1); I->Image->needs_alpha_reset = true; I->Image->data = image; I->Image->height = I->Height; I->Image->width = I->Width; I->Image->size = buffer_size; if(save_stereo) I->Image->stereo = 1; } } else if(I->Image) { image = I->Image->data; } if(image) { int opaque_back = SettingGetGlobal_b(G, cSetting_opaque_background); if(opaque_back && I->Image->needs_alpha_reset) { int i, s = 4 * I->Image->width * I->Image->height; for(i = 3; i < s; i += 4) image[i] = 0xFF; I->Image->needs_alpha_reset = false; } } return (unsigned char *) image; } void SceneImageFinish(PyMOLGlobals * G, GLvoid *image) { CScene *I = G->Scene; if(I->Image) { if(I->Image->data != (unsigned char *) image) /* purge the image if this isn't the active copy */ FreeP(image); } else { FreeP(image); } } /* * Get the size of the rendered image. This is either identical to * cmd.get_viewport(), or the dimensions which were last passed to * cmd.draw(), cmd.ray() or cmd.png(). */ void SceneGetImageSize(PyMOLGlobals * G, int *width, int *height) { CScene *I = G->Scene; // TODO: calling ImagePrepare looks like a heavy side effect. Need to // clarify if checking (CopyType && Image && Image->data) here would // be sufficient. GLvoid *image = SceneImagePrepareImpl(G, false, false); if(image && I->Image) { *width = I->Image->width; *height = I->Image->height; } else { *width = I->Width; *height = I->Height; } SceneImageFinish(G, image); /* don't leak if(image != I->Image) */ } float SceneGetGridAspectRatio(PyMOLGlobals * G){ CScene *I = G->Scene; return (I->Width / (float)I->Height) / (float)(I->grid.cur_viewport_size[0] / (float)I->grid.cur_viewport_size[1]); } int SceneCopyExternal(PyMOLGlobals * G, int width, int height, int rowbytes, unsigned char *dest, int mode) { GLvoid *image = SceneImagePrepare(G, false); CScene *I = G->Scene; int result = false; int i, j; int premultiply_alpha = true; int red_index = 0, blue_index = 1, green_index = 2, alpha_index = 3; int no_alpha = (SettingGetGlobal_b(G, cSetting_opaque_background) && SettingGetGlobal_b(G, cSetting_ray_opaque_background)); if(mode & 0x1) { int index = 0; while(index < 4) { if(dest[index] == 'R') red_index = index; if(dest[index] == 'G') green_index = index; if(dest[index] == 'B') blue_index = index; if(dest[index] == 'A') alpha_index = index; index++; } } if(mode & 0x2) { premultiply_alpha = false; } /* printf("image %p I->image %p\n"); if(I->Image) { printf("%d %d %d %d\n",I->Image->width,width,I->Image->height,height); } */ if(image && I->Image && (I->Image->width == width) && (I->Image->height == height)) { for(i = 0; i < height; i++) { unsigned char *src = ((unsigned char *) image) + ((height - 1) - i) * width * 4; unsigned char *dst; if(mode & 0x4) { dst = dest + (height - (i + 1)) * (rowbytes); } else { dst = dest + i * (rowbytes); } for(j = 0; j < width; j++) { if(no_alpha) { dst[red_index] = src[0]; /* no alpha */ dst[green_index] = src[1]; dst[blue_index] = src[2]; dst[alpha_index] = 0xFF; /* if(!(i||j)) { printf("no alpha\n"); } */ } else if(premultiply_alpha) { dst[red_index] = (((unsigned int) src[0]) * src[3]) / 255; /* premultiply alpha */ dst[green_index] = (((unsigned int) src[1]) * src[3]) / 255; dst[blue_index] = (((unsigned int) src[2]) * src[3]) / 255; dst[alpha_index] = src[3]; /* if(!(i||j)) { printf("premult alpha\n"); } */ } else { dst[red_index] = src[0]; /* standard alpha */ dst[green_index] = src[1]; dst[blue_index] = src[2]; dst[alpha_index] = src[3]; /* if(!(i||j)) { printf("standard alpha\n"); } */ } dst += 4; src += 4; } } result = true; } else { printf("image or size mismatch\n"); } SceneImageFinish(G, image); return (result); } static void interlace(unsigned int *dst, unsigned int *src, int width, int height) { int a, b; unsigned int *p0 = src, *p1 = src + (height * width); unsigned int *q = dst; for(a = 0; a < height; a++) { for(b = 0; b < width; b++) { *(q++) = *(p0++); } for(b = 0; b < width; b++) { *(q++) = *(p1++); } } } static void deinterlace(unsigned int *dst, unsigned int *src, int width, int height, int swap) { int a, b; unsigned int *p = src; unsigned int *q0 = dst, *q1 = dst + (height * width); if(swap) { q0 = dst + (height * width); q1 = dst; } for(a = 0; a < height; a++) { for(b = 0; b < width; b++) { *(q0++) = *(p++); } for(b = 0; b < width; b++) { *(q1++) = *(p++); } } } int ScenePNG(PyMOLGlobals * G, const char *png, float dpi, int quiet, int prior_only, int format) { CScene *I = G->Scene; GLvoid *image = SceneImagePrepare(G, prior_only); if(image && I->Image) { int width = I->Image->width; int height = I->Image->height; unsigned char *save_image = (unsigned char*) image; if((image == I->Image->data) && I->Image->stereo) { width = I->Image->width; save_image = Alloc(unsigned char, I->Image->size * 2); interlace((unsigned int *) save_image, (unsigned int *) I->Image->data, width, height); width *= 2; } if(dpi < 0.0F) dpi = SettingGetGlobal_f(G, cSetting_image_dots_per_inch); if(MyPNGWrite(G, png, save_image, width, height, dpi, format, quiet)) { if(!quiet) { PRINTFB(G, FB_Scene, FB_Actions) " ScenePNG: wrote %dx%d pixel image to file \"%s\".\n", width, I->Image->height, png ENDFB(G); } } else { PRINTFB(G, FB_Scene, FB_Errors) " ScenePNG-Error: error writing \"%s\"! Please check directory...\n", png ENDFB(G); } if(save_image && (save_image != image)) FreeP(save_image); } SceneImageFinish(G, image); return (image != NULL); } /*========================================================================*/ int SceneGetFrame(PyMOLGlobals * G) { if(MovieDefined(G)) return (SettingGetGlobal_i(G, cSetting_frame) - 1); else return (SettingGetGlobal_i(G, cSetting_state) - 1); } /*========================================================================*/ /* GM: * Returns the number of movie frames, or the number of states if no movie * is defined. */ int SceneCountFrames(PyMOLGlobals * G) { CScene *I = G->Scene; int n; int mov_len; I->NFrame = 0; for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) { n = (*it)->getNFrame(); if(n > I->NFrame) I->NFrame = n; } mov_len = MovieGetLength(G); I->HasMovie = (mov_len != 0); if(mov_len > 0) { I->NFrame = mov_len; } else if(mov_len < 0) { mov_len = -mov_len; if(I->NFrame < mov_len) /* allows you to see cached movie even w/o object */ I->NFrame = mov_len; } PRINTFD(G, FB_Scene)" SceneCountFrames: leaving... I->NFrame %d\n", I->NFrame ENDFD return I->NFrame; } /*========================================================================*/ void SceneSetFrame(PyMOLGlobals * G, int mode, int frame) { CScene *I = G->Scene; int newFrame; int newState = 0; int movieCommand = false; int suppress = false; newFrame = SettingGetGlobal_i(G, cSetting_frame) - 1; PRINTFD(G, FB_Scene) " SceneSetFrame: entered.\n" ENDFD; switch (mode) { case -1: /* movie/frame override - go to this state absolutely! */ newState = frame; break; case 0: /* absolute frame */ newFrame = frame; break; case 1: /* relative frame */ newFrame += frame; break; case 2: /* end */ newFrame = I->NFrame - 1; break; case 3: /* middle with automatic movie command */ newFrame = I->NFrame / 2; movieCommand = true; break; case 4: /* absolute with automatic movie command */ newFrame = frame; movieCommand = true; break; case 5: /* relative with automatic movie command */ newFrame += frame; movieCommand = true; break; case 6: /* end with automatic movie command */ newFrame = I->NFrame - 1; movieCommand = true; break; case 7: /* absolute with forced movie command */ newFrame = frame; movieCommand = true; break; case 8: /* relative with forced movie command */ newFrame += frame; movieCommand = true; break; case 9: /* end with forced movie command */ newFrame = I->NFrame - 1; movieCommand = true; break; case 10: /* seek forward to current scene (if present) */ { frame = MovieSeekScene(G,true); if(frame>=0) { newFrame = frame; movieCommand = true; } else { suppress = true; } } break; } if(!suppress) { SceneCountFrames(G); if(mode >= 0) { if(newFrame >= I->NFrame) newFrame = I->NFrame - 1; if(newFrame < 0) newFrame = 0; newState = MovieFrameToIndex(G, newFrame); if(newFrame == 0) { if(MovieMatrix(G, cMovieMatrixRecall)) { SceneAbortAnimation(G); /* if we have a programmed initial orientation, don't allow animation to override it */ } } SettingSetGlobal_i(G, cSetting_frame, newFrame + 1); SettingSetGlobal_i(G, cSetting_state, newState + 1); ExecutiveInvalidateSelectionIndicatorsCGO(G); SceneInvalidatePicking(G); if(movieCommand) { #ifndef _PYMOL_NO_UNDO int suspend_undo = SettingGetGlobal_b(G, cSetting_suspend_undo); if (!suspend_undo){ SettingSetGlobal_i(G, cSetting_suspend_undo, 1); } #endif MovieDoFrameCommand(G, newFrame); MovieFlushCommands(G); #ifndef _PYMOL_NO_UNDO SettingSetGlobal_i(G, cSetting_suspend_undo, suspend_undo); #endif } if(SettingGetGlobal_b(G, cSetting_cache_frames)) I->MovieFrameFlag = true; } else { SettingSetGlobal_i(G, cSetting_frame, newFrame + 1); SettingSetGlobal_i(G, cSetting_state, newState + 1); ExecutiveInvalidateSelectionIndicatorsCGO(G); SceneInvalidatePicking(G); } MovieSetScrollBarFrame(G, newFrame); SeqChanged(G); // SceneInvalidate(G); } PRINTFD(G, FB_Scene) " SceneSetFrame: leaving...\n" ENDFD; OrthoInvalidateDoDraw(G); } /*========================================================================*/ void SceneDirty(PyMOLGlobals * G) /* This means that the current image on the screen (and/or in the buffer) needs to be updated */ { CScene *I = G->Scene; PRINTFD(G, FB_Scene) " SceneDirty: called.\n" ENDFD; if(I) { if(!I->DirtyFlag) { I->DirtyFlag = true; /* SceneInvalidateCopy(G,false); */ OrthoDirty(G); } } } void SceneRovingPostpone(PyMOLGlobals * G) { CScene *I = G->Scene; float delay; if(SettingGetGlobal_b(G, cSetting_roving_detail)) { delay = SettingGetGlobal_f(G, cSetting_roving_delay); if(delay < 0.0F) { I->RovingLastUpdate = UtilGetSeconds(G); /* put off delay */ } } } void SceneRovingDirty(PyMOLGlobals * G) { CScene *I = G->Scene; if(SettingGetGlobal_b(G, cSetting_roving_detail)) { SceneRovingPostpone(G); I->RovingDirtyFlag = true; } } /*========================================================================*/ void SceneChanged(PyMOLGlobals * G) { CScene *I = G->Scene; I->ChangedFlag = true; SceneInvalidateCopy(G, false); SceneDirty(G); SeqChanged(G); PyMOL_NeedRedisplay(G->PyMOL); } /*========================================================================*/ Block *SceneGetBlock(PyMOLGlobals * G) { CScene *I = G->Scene; return (I); } /*========================================================================*/ int SceneValidateImageMode(PyMOLGlobals * G, int mode, bool defaultdraw) { switch (mode) { case cSceneImage_Normal: case cSceneImage_Draw: case cSceneImage_Ray: return mode; } if (mode != cSceneImage_Default) { PRINTFB(G, FB_Scene, FB_Warnings) " %s-Warning: invalid mode %d\n", __FUNCTION__, mode ENDFB(G); } if(!G->HaveGUI || SettingGetGlobal_b(G, cSetting_ray_trace_frames)) { return cSceneImage_Ray; } if(defaultdraw || SettingGetGlobal_b(G, cSetting_draw_frames)) { return cSceneImage_Draw; } return cSceneImage_Normal; } /*========================================================================*/ int SceneMakeMovieImage(PyMOLGlobals * G, int show_timing, int validate, int mode, int width, int height) { CScene *I = G->Scene; // float *v; int valid = true; PRINTFB(G, FB_Scene, FB_Blather) " Scene: Making movie image.\n" ENDFB(G); mode = SceneValidateImageMode(G, mode, width || height); I->DirtyFlag = false; switch (mode) { case cSceneImage_Ray: SceneRay(G, width, height, SettingGetGlobal_i(G, cSetting_ray_default_renderer), NULL, NULL, 0.0F, 0.0F, false, NULL, show_timing, -1); break; case cSceneImage_Draw: SceneMakeSizedImage(G, width, height, SettingGetGlobal_i(G, cSetting_antialias)); break; case cSceneImage_Normal: { int draw_both = SceneMustDrawBoth(G); if(G->HaveGUI && G->ValidContext) { if(draw_both) { OrthoDrawBuffer(G, GL_BACK_LEFT); } else { OrthoDrawBuffer(G, GL_BACK); } glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); /* insert OpenGL context validation code here? */ SceneRender(G, NULL, 0, 0, NULL, 0, 0, 0, 0); SceneGLClearColor(0.0, 0.0, 0.0, 1.0); if(draw_both) { SceneCopy(G, GL_BACK_LEFT, true, false); } else { SceneCopy(G, GL_BACK, true, false); } /* insert OpenGL context validation code here? */ } } break; } if(I->Image) { MovieSetImage(G, MovieFrameToImage(G, SettingGetGlobal_i(G, cSetting_frame) - 1), I->Image); I->MovieOwnsImageFlag = true; } else { I->MovieOwnsImageFlag = false; } if(I->Image) I->CopyType = true; return valid; } /*========================================================================*/ static void SceneUpdateCameraRock(PyMOLGlobals * G, int dirty) { CScene *I = G->Scene; float ang_cur, disp, diff; float sweep_angle = SettingGetGlobal_f(G, cSetting_sweep_angle); float sweep_speed = SettingGetGlobal_f(G, cSetting_sweep_speed); float sweep_phase = SettingGetGlobal_f(G, cSetting_sweep_phase); int sweep_mode = SettingGetGlobal_i(G, cSetting_sweep_mode); float shift = (float) (PI / 2.0F); I->SweepTime += I->RenderTime; I->LastSweepTime = UtilGetSeconds(G); switch (sweep_mode) { case 0: case 1: case 2: if(sweep_angle <= 0.0F) { diff = (float) ((PI / 180.0F) * I->RenderTime * 10 * sweep_speed / 0.75F); } else { ang_cur = (float) (I->SweepTime * sweep_speed) + sweep_phase; disp = (float) (sweep_angle * (PI / 180.0F) * sin(ang_cur) / 2); diff = (float) (disp - I->LastSweep); I->LastSweep = disp; } switch (sweep_mode) { case 0: SceneRotateWithDirty(G, (float) (180 * diff / PI), 0.0F, 1.0F, 0.0F, dirty); break; case 1: SceneRotateWithDirty(G, (float) (180 * diff / PI), 1.0F, 0.0F, 0.0F, dirty); break; case 2: /* z-rotation...useless! */ SceneRotateWithDirty(G, (float) (180 * diff / PI), 0.0F, 0.0F, 1.0F, dirty); break; } break; case 3: /* nutate */ SceneRotateWithDirty(G, (float) (-I->LastSweepY), 0.0F, 1.0F, 0.0F, dirty); SceneRotateWithDirty(G, (float) (-I->LastSweepX), 1.0F, 0.0F, 0.0F, dirty); ang_cur = (float) (I->SweepTime * sweep_speed) + sweep_phase; I->LastSweepX = (float) (sweep_angle * sin(ang_cur) / 2); I->LastSweepY = (float) (sweep_angle * sin(ang_cur + shift) / 2); if(I->SweepTime * sweep_speed < PI) { float factor = (float) ((I->SweepTime * sweep_speed) / PI); I->LastSweepX *= factor; I->LastSweepY *= factor; } SceneRotateWithDirty(G, (float) I->LastSweepX, 1.0F, 0.0F, 0.0F, dirty); SceneRotateWithDirty(G, (float) I->LastSweepY, 0.0F, 1.0F, 0.0F, dirty); break; } } /*========================================================================*/ void SceneIdle(PyMOLGlobals * G) { CScene *I = G->Scene; double renderTime; double minTime; int frameFlag = false; if(I->PossibleSingleClick == 2) { double now = UtilGetSeconds(G); double single_click_delay = I->SingleClickDelay; double diff = now - I->LastReleaseTime; if(diff > single_click_delay) { /* post a single click processing event */ SceneDeferClickWhen(I, I->LastButton + P_GLUT_SINGLE_LEFT, I->LastWinX, I->LastWinY, I->LastClickTime, I->LastMod); /* push a click onto the queue */ I->PossibleSingleClick = 0; OrthoDirty(G); /* force an update */ } } if(!OrthoDeferredWaiting(G)) { if(MoviePlaying(G)) { renderTime = UtilGetSeconds(G) - I->LastFrameTime; { float fps = SettingGetGlobal_f(G, cSetting_movie_fps); if(fps <= 0.0F) { if(fps < 0.0) minTime = 0.0; /* negative fps means full speed */ else /* 0 fps means use movie_delay instead */ minTime = SettingGetGlobal_f(G, cSetting_movie_delay) / 1000.0; if(minTime >= 0) fps = 1.0 / minTime; else fps = 1000.0F; } else { minTime = 1.0 / fps; } if(renderTime >= (minTime - I->LastFrameAdjust)) { float adjust = (renderTime - minTime); if((fabs(adjust) < minTime) && (fabs(I->LastFrameAdjust) < minTime)) { float new_adjust = (renderTime - minTime) + I->LastFrameAdjust; I->LastFrameAdjust = (new_adjust + fps * I->LastFrameAdjust) / (1 + fps); } else { I->LastFrameAdjust = 0.0F; } frameFlag = true; } } } else if(ControlRocking(G)) { renderTime = -I->LastSweepTime + UtilGetSeconds(G); minTime = SettingGetGlobal_f(G, cSetting_rock_delay) / 1000.0; if(renderTime >= minTime) { I->RenderTime = renderTime; SceneUpdateCameraRock(G, true); } } if(MoviePlaying(G) && frameFlag) { I->LastFrameTime = UtilGetSeconds(G); if((SettingGetGlobal_i(G, cSetting_frame) - 1) == (I->NFrame - 1)) { if(SettingGetGlobal_b(G, cSetting_movie_loop)) { SceneSetFrame(G, 7, 0); } else MoviePlay(G, cMovieStop); } else { SceneSetFrame(G, 5, 1); } PyMOL_NeedRedisplay(G->PyMOL); } } } /*========================================================================*/ /* * Zoom to location and radius */ void SceneWindowSphere(PyMOLGlobals * G, float *location, float radius) { CScene *I = G->Scene; float v0[3]; float dist = 2.f * radius / GetFovWidth(G); /* find where this point is in relationship to the origin */ subtract3f(I->Origin, location, v0); MatrixTransformC44fAs33f3f(I->RotMatrix, v0, I->Pos); /* convert to view-space */ if (I->Height > I->Width && I->Height && I->Width) dist *= I->Height / I->Width; I->Pos[2] -= dist; I->Front = (-I->Pos[2] - radius * 1.2F); I->Back = (-I->Pos[2] + radius * 1.2F); UpdateFrontBackSafe(I); SceneRovingDirty(G); } /*========================================================================*/ void SceneRelocate(PyMOLGlobals * G, float *location) { CScene *I = G->Scene; float v0[3]; float slab_width; float dist; slab_width = I->Back - I->Front; /* find out how far camera was from previous origin */ dist = I->Pos[2]; // stay in front of camera, empirical value to show at least 1 bond if (dist > -5.f) dist = -5.f; /* find where this point is in relationship to the origin */ subtract3f(I->Origin, location, v0); /* printf("%8.3f %8.3f %8.3f\n",I->Front,I->Pos[2],I->Back); */ MatrixTransformC44fAs33f3f(I->RotMatrix, v0, I->Pos); /* convert to view-space */ I->Pos[2] = dist; I->Front = (-I->Pos[2] - (slab_width * 0.50F)); I->Back = (-I->Pos[2] + (slab_width * 0.50F)); UpdateFrontBackSafe(I); SceneRovingDirty(G); } /*========================================================================*/ /* * Get the origin of rotation in model space * cmd.get_view()[12:15] */ void SceneOriginGet(PyMOLGlobals * G, float *origin) { CScene *I = G->Scene; copy3f(I->Origin, origin); } /*========================================================================*/ void SceneOriginSet(PyMOLGlobals * G, float *origin, int preserve) { CScene *I = G->Scene; float v0[3], v1[3]; if(preserve) { /* preserve current viewing location */ subtract3f(origin, I->Origin, v0); /* model-space translation */ MatrixTransformC44fAs33f3f(I->RotMatrix, v0, v1); /* convert to view-space */ add3f(I->Pos, v1, I->Pos); /* offset view to compensate */ } I->Origin[0] = origin[0]; /* move origin */ I->Origin[1] = origin[1]; I->Origin[2] = origin[2]; SceneInvalidate(G); } /*========================================================================*/ int SceneObjectAdd(PyMOLGlobals * G, CObject * obj) { CScene *I = G->Scene; obj->Enabled = true; I->Obj.push_back(obj); if(obj->type == cObjectGadget) { I->GadgetObjs.push_back(obj); } else { I->NonGadgetObjs.push_back(obj); } SceneCountFrames(G); SceneChanged(G); SceneInvalidatePicking(G); // PYMOL-2793 return 1; } /*========================================================================*/ int SceneObjectIsActive(PyMOLGlobals * G, CObject * obj) { int result = false; CScene *I = G->Scene; if (find(I->Obj.begin(), I->Obj.end(), obj) != I->Obj.end()) result = true; return result; } int SceneObjectDel(PyMOLGlobals * G, CObject * obj, int allow_purge) { CScene *I = G->Scene; int defer_builds_mode = SettingGetGlobal_i(G, cSetting_defer_builds_mode); if(!obj) { /* deletes all members */ if(allow_purge && (defer_builds_mode >= 3)) { for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) { /* purge graphics representation when no longer used */ (*it)->invalidate(cRepAll, cRepInvPurge, -1); } } I->Obj.clear(); I->GadgetObjs.clear(); I->NonGadgetObjs.clear(); } else { auto &obj_list = (obj->type == cObjectGadget) ? I->GadgetObjs : I->NonGadgetObjs; auto itg = find(obj_list.begin(), obj_list.end(), obj); if (itg != obj_list.end()) obj_list.erase(itg); auto it = find(I->Obj.begin(), I->Obj.end(), obj); if (it != I->Obj.end()){ if(allow_purge && (defer_builds_mode >= 3)) { /* purge graphics representation when no longer used */ (*it)->invalidate(cRepAll, cRepInvPurge, -1); } obj->Enabled = false; I->Obj.erase(it); } } SceneCountFrames(G); SceneInvalidate(G); return 0; } /*========================================================================*/ int SceneLoadPNG(PyMOLGlobals * G, const char *fname, int movie_flag, int stereo, int quiet) { CScene *I = G->Scene; int ok = false; if(I->Image) { if(I->MovieOwnsImageFlag) { I->MovieOwnsImageFlag = false; I->Image = NULL; } else { ScenePurgeImage(G); } I->CopyType = false; OrthoInvalidateDoDraw(G); // right now, need to invalidate since text could be shown } I->Image = Calloc(ImageType, 1); if(MyPNGRead(fname, (unsigned char **) &I->Image->data, (unsigned int *) &I->Image->width, (unsigned int *) &I->Image->height)) { I->Image->size = I->Image->width * I->Image->height * 4; if(!quiet) { PRINTFB(G, FB_Scene, FB_Details) " Scene: loaded image from '%s'.\n", fname ENDFB(G); } if((stereo > 0) || ((stereo < 0) && (I->Image->width == 2 * I->Width) && (I->Image->height == I->Height))) { unsigned char *tmp = Alloc(unsigned char, I->Image->size); if(tmp) { I->Image->width /= 2; I->Image->stereo = true; I->Image->size /= 2; deinterlace((unsigned int *) tmp, (unsigned int *) I->Image->data, I->Image->width, I->Image->height, (stereo == 2)); FreeP(I->Image->data); I->Image->data = tmp; } } I->CopyType = true; I->CopyForced = true; OrthoRemoveSplash(G); SettingSetGlobal_b(G, cSetting_text, 0); if(movie_flag && I->Image && I->Image->data) { MovieSetImage(G, MovieFrameToImage(G, SettingGetGlobal_i(G, cSetting_frame) - 1) , I->Image); I->MovieOwnsImageFlag = true; I->MovieFrameFlag = true; } else { I->MovieOwnsImageFlag = false; I->DirtyFlag = false; /* make sure we don't overwrite image */ } OrthoDirty(G); ok = true; } else { if(!quiet) { PRINTFB(G, FB_Scene, FB_Errors) " Scene: unable to load image from '%s'.\n", fname ENDFB(G); } } return (ok); } /*static unsigned int byte_max(unsigned int value) { return (value>0xFF) ? 0xFF : value; } */ #define SceneClickMargin DIP2PIXEL(2) #define SceneTopMargin 0 #define SceneToggleMargin DIP2PIXEL(2) #define SceneRightMargin 0 #define SceneToggleWidth DIP2PIXEL(17) #define SceneToggleSize DIP2PIXEL(16) #define SceneToggleTextShift DIP2PIXEL(4) #define SceneTextLeftMargin DIP2PIXEL(1) #define SceneScrollBarMargin DIP2PIXEL(1) #define SceneScrollBarWidth DIP2PIXEL(13) #ifndef _PYMOL_NOPY static void draw_button(int x2, int y2, int z, int w, int h, float *light, float *dark, float *inside ORTHOCGOARG) { if (orthoCGO){ CGOColorv(orthoCGO, light); CGOBegin(orthoCGO, GL_TRIANGLE_STRIP); CGOVertex(orthoCGO, x2, y2, z); CGOVertex(orthoCGO, x2, y2 + h, z); CGOVertex(orthoCGO, x2 + w, y2, z); CGOVertex(orthoCGO, x2 + w, y2 + h, z); CGOEnd(orthoCGO); } else { glColor3fv(light); glBegin(GL_POLYGON); glVertex3i(x2, y2, z); glVertex3i(x2, y2 + h, z); glVertex3i(x2 + w, y2 + h, z); glVertex3i(x2 + w, y2, z); glEnd(); } if (orthoCGO){ CGOColorv(orthoCGO, dark); CGOBegin(orthoCGO, GL_TRIANGLE_STRIP); CGOVertex(orthoCGO, x2 + 1, y2, z); CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, z); CGOVertex(orthoCGO, x2 + w, y2, z); CGOVertex(orthoCGO, x2 + w, y2 + h - 1, z); CGOEnd(orthoCGO); } else { glColor3fv(dark); glBegin(GL_POLYGON); glVertex3i(x2 + 1, y2, z); glVertex3i(x2 + 1, y2 + h - 1, z); glVertex3i(x2 + w, y2 + h - 1, z); glVertex3i(x2 + w, y2, z); glEnd(); } if(inside) { if (orthoCGO){ CGOColorv(orthoCGO, inside); CGOBegin(orthoCGO, GL_TRIANGLE_STRIP); CGOVertex(orthoCGO, x2 + 1, y2 + 1, z); CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, z); CGOVertex(orthoCGO, x2 + w - 1, y2 + 1, z); CGOVertex(orthoCGO, x2 + w - 1, y2 + h - 1, z); CGOEnd(orthoCGO); } else { glColor3fv(inside); glBegin(GL_POLYGON); glVertex3i(x2 + 1, y2 + 1, z); glVertex3i(x2 + 1, y2 + h - 1, z); glVertex3i(x2 + w - 1, y2 + h - 1, z); glVertex3i(x2 + w - 1, y2 + 1, z); glEnd(); } } else { /* rainbow */ if (orthoCGO){ CGOBegin(orthoCGO, GL_TRIANGLE_STRIP); CGOColor(orthoCGO, 0.1F, 1.0F, 0.1F); // green CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, z); CGOColor(orthoCGO, 1.0F, 1.0F, 0.1F); // yellow CGOVertex(orthoCGO, x2 + w - 1, y2 + h - 1, z); CGOColor(orthoCGO, 1.f, 0.1f, 0.1f); // red CGOVertex(orthoCGO, x2 + 1, y2 + 1, z); CGOColor(orthoCGO, 0.1F, 0.1F, 1.0F); // blue CGOVertex(orthoCGO, x2 + w - 1, y2 + 1, z); CGOEnd(orthoCGO); } else { glBegin(GL_POLYGON); glColor3f(1.0F, 0.1F, 0.1F); glVertex3i(x2 + 1, y2 + 1, z); glColor3f(0.1F, 1.0F, 0.1F); glVertex3i(x2 + 1, y2 + h - 1, z); glColor3f(1.0F, 1.0F, 0.1F); glVertex3i(x2 + w - 1, y2 + h - 1, z); glColor3f(0.1F, 0.1F, 1.0F); glVertex3i(x2 + w - 1, y2 + 1, z); glEnd(); } } } #endif /* * Update the G->Scene->SceneVLA names array which is used for scene buttons */ void SceneSetNames(PyMOLGlobals * G, const std::vector<std::string> &list) { CScene *I = G->Scene; I->NScene = (int)list.size(); VLACheck(I->SceneVLA, SceneElem, I->NScene); SceneElem *elem = I->SceneVLA; for(int a = 0; a < I->NScene; ++a) { elem->name = (char*) list[a].c_str(); elem->len = (int)list[a].length(); elem->drawn = false; elem++; } OrthoDirty(G); } /*========================================================================*/ static void SceneDrawButtons(Block * block, int draw_for_real ORTHOCGOARG) { #ifndef _PYMOL_NOPY PyMOLGlobals *G = block->m_G; CScene *I = G->Scene; int x, y, xx, x2; const char *c = NULL; float enabledColor[3] = { 0.5F, 0.5F, 0.5F }; float pressedColor[3] = { 0.7F, 0.7F, 0.7F }; float disabledColor[3] = { 0.25F, 0.25F, 0.25F }; float lightEdge[3] = { 0.6F, 0.6F, 0.6F }; float darkEdge[3] = { 0.35F, 0.35F, 0.35F }; int charWidth = DIP2PIXEL(8); int n_ent; int n_disp; int skip = 0; int row = -1; int lineHeight = DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_control_size)); int text_lift = (lineHeight / 2) - DIP2PIXEL(5); int op_cnt = 1; if(((G->HaveGUI && G->ValidContext) || (!draw_for_real)) && ((block->rect.right - block->rect.left) > 6) && (I->NScene)) { int max_char; int nChar; I->ButtonsShown = true; /* do we have enough structures to warrant a scroll bar? */ n_ent = I->NScene; n_disp = (((I->rect.top - I->rect.bottom) - (SceneTopMargin)) / lineHeight) - 1; if(n_disp < 1) n_disp = 1; { int i; for(i = 0; i < I->NScene; i++) I->SceneVLA[i].drawn = false; } if(n_ent > n_disp) { int bar_maxed = I->m_ScrollBar.isMaxed(); if(!I->ScrollBarActive) { I->m_ScrollBar.setLimits(n_ent, n_disp); if(bar_maxed) { I->m_ScrollBar.maxOut(); I->NSkip = static_cast<int>(I->m_ScrollBar.getValue()); } else { I->m_ScrollBar.setValue(0); I->NSkip = 0; } } else { I->m_ScrollBar.setLimits(n_ent, n_disp); if(bar_maxed) I->m_ScrollBar.maxOut(); I->NSkip = static_cast<int>(I->m_ScrollBar.getValue()); } I->ScrollBarActive = 1; } else { I->ScrollBarActive = 0; I->NSkip = 0; } max_char = (((I->rect.right - I->rect.left) - (SceneTextLeftMargin + SceneRightMargin + 4)) - (op_cnt * SceneToggleWidth)); if(I->ScrollBarActive) { max_char -= (SceneScrollBarMargin + SceneScrollBarWidth); } max_char /= charWidth; if(I->ScrollBarActive) { I->m_ScrollBar.setBox(I->rect.top - SceneScrollBarMargin, I->rect.left + SceneScrollBarMargin, I->rect.bottom + 2, I->rect.left + SceneScrollBarMargin + SceneScrollBarWidth); if(draw_for_real) I->m_ScrollBar.draw(orthoCGO); } skip = I->NSkip; x = I->rect.left + SceneTextLeftMargin; /* y = ((I->rect.top-lineHeight)-SceneTopMargin)-lineHeight; */ { int n_vis = n_disp; if(n_ent < n_vis) n_vis = n_ent; y = (I->rect.bottom + SceneBottomMargin) + (n_vis - 1) * lineHeight; } /* xx = I->rect.right-SceneRightMargin-SceneToggleWidth*(cRepCnt+op_cnt); */ xx = I->rect.right - SceneRightMargin - SceneToggleWidth * (op_cnt); if(I->ScrollBarActive) { x += SceneScrollBarWidth + SceneScrollBarMargin; } { int i; for(i = 0; i < n_ent; i++) { if(skip) { skip--; } else { row++; x2 = xx; nChar = max_char; if((x - SceneToggleMargin) - (xx - SceneToggleMargin) > -10) { x2 = x + 10; } { float toggleColor[3] = { 0.5F, 0.5F, 1.0F }; if(draw_for_real) { glColor3fv(toggleColor); TextSetColor(G, I->TextColor); TextSetPos2i(G, x + DIP2PIXEL(2), y + text_lift); } { int len; const char *cur_name = SettingGetGlobal_s(G, cSetting_scene_current_name); SceneElem *elem = I->SceneVLA + i; int item = I->NSkip + row; c = elem->name; len = elem->len; x2 = xx; if(len > max_char) len = max_char; x2 = x + len * charWidth + DIP2PIXEL(6); /* store rectangles for finding clicks */ elem->drawn = true; elem->x1 = x; elem->y1 = y; elem->x2 = x2; elem->y2 = y + lineHeight; if(I->ButtonMargin < x2) I->ButtonMargin = x2; if(draw_for_real) { if((item == I->Pressed) && (item == I->Over)) { draw_button(x, y, 0, (x2 - x) - 1, (lineHeight - 1), lightEdge, darkEdge, pressedColor ORTHOCGOARGVAR); } else if(cur_name && elem->name && (!strcmp(elem->name, cur_name))) { draw_button(x, y, 0, (x2 - x) - 1, (lineHeight - 1), lightEdge, darkEdge, enabledColor ORTHOCGOARGVAR); } else { draw_button(x, y, 0, (x2 - x) - 1, (lineHeight - 1), lightEdge, darkEdge, disabledColor ORTHOCGOARGVAR); } TextSetColor(G, I->TextColor); if(c) { while(*c) { if((nChar--) > 0) TextDrawChar(G, *(c++) ORTHOCGOARGVAR); else break; } } } } } y -= lineHeight; if(y < (I->rect.bottom)) break; } } } I->HowFarDown = y; I->ButtonsValid = true; } #endif } int SceneDrawImageOverlay(PyMOLGlobals * G, int override ORTHOCGOARG){ CScene *I = G->Scene; int drawn = false; int text = SettingGetGlobal_b(G, cSetting_text); /* is the text/overlay (ESC) on? */ int overlay = OrthoGetOverlayStatus(G); if(((!text) || overlay) && (override || I->CopyType == true) && I->Image && I->Image->data) { /* show transparent bg as checkboard? */ int show_alpha = SettingGetGlobal_b(G, cSetting_show_alpha_checker); const float *bg_color = ColorGet(G, SettingGet_color(G, NULL, NULL, cSetting_bg_rgb)); unsigned int bg_rr, bg_r = (unsigned int) (255 * bg_color[0]); unsigned int bg_gg, bg_g = (unsigned int) (255 * bg_color[1]); unsigned int bg_bb, bg_b = (unsigned int) (255 * bg_color[2]); int width = I->Image->width; int height = I->Image->height; unsigned char *data = I->Image->data; if(I->Image->stereo) { int buffer; glGetIntegerv(GL_DRAW_BUFFER, (GLint *) & buffer); if(buffer == GL_BACK_RIGHT) /* hardware stereo */ data += I->Image->size; else { int stereo = SettingGetGlobal_i(G, cSetting_stereo); if (stereo){ switch (OrthoGetRenderMode(G)) { case cStereo_geowall: data += I->Image->size; break; } } } /* if drawing the right buffer, then draw the right image */ } if((height > I->Height) || (width > I->Width)) { /* image is oversize */ { int factor = 1; int shift = 0; int tmp_height = I->Image->height; int tmp_width = I->Image->width; int src_row_bytes = I->Image->width * 4; unsigned int color_word; float rgba[4] = { 0.0F, 0.0F, 0.0F, 1.0F }; ColorGetBkrdContColor(G, rgba, false); color_word = ColorGet32BitWord(G, rgba); while(tmp_height && tmp_width && ((tmp_height > (I->Height - 3)) || (tmp_width > (I->Width - 3)))) { tmp_height = (tmp_height >> 1); tmp_width = (tmp_width >> 1); factor = (factor << 1); shift++; } tmp_width += 2; tmp_height += 2; if(tmp_height && tmp_width) { unsigned int buffer_size = tmp_height * tmp_width * 4; unsigned char *buffer = Alloc(unsigned char, buffer_size); if(buffer && data) { unsigned char *p = data; unsigned char *q = buffer; unsigned char *pp, *ppp, *pppp; int a, b, c, d; unsigned int c1, c2, c3, c4, alpha, tot, bg; unsigned int factor_col_bytes = factor * 4; unsigned int factor_row_bytes = factor * src_row_bytes; shift = shift + shift; for(a = 0; a < tmp_width; a++) { /* border, first row */ *((unsigned int *) (q)) = color_word; q += 4; } for(b = 1; b < tmp_height-1; b++) { /* rows */ pp = p; *((unsigned int *) (q)) = color_word; /* border */ q += 4; for(a = 1; a < tmp_width-1; a++) { /* cols */ ppp = pp; c1 = c2 = c3 = c4 = tot = 0; if(show_alpha && (((a >> 4) + (b >> 4)) & 0x1)) { /* introduce checkerboard */ bg_rr = ((bg_r & 0x80) ? bg_r - TRN_BKG : bg_r + TRN_BKG); bg_gg = ((bg_g & 0x80) ? bg_g - TRN_BKG : bg_g + TRN_BKG); bg_bb = ((bg_b & 0x80) ? bg_b - TRN_BKG : bg_b + TRN_BKG); } else { bg_rr = bg_r; bg_gg = bg_g; bg_bb = bg_b; } for(d = 0; d < factor; d++) { /* box rows */ pppp = ppp; for(c = 0; c < factor; c++) { /* box cols */ alpha = pppp[3]; c1 += *(pppp++) * alpha; c2 += *(pppp++) * alpha; c3 += *(pppp++) * alpha; pppp++; c4 += alpha; tot += 0xFF; } ppp += src_row_bytes; } if(c4) { bg = tot - c4; *(q++) = (c1 + bg_rr * bg) / tot; *(q++) = (c2 + bg_gg * bg) / tot; *(q++) = (c3 + bg_bb * bg) / tot; *(q++) = 0xFF; } else { *(q++) = bg_rr; *(q++) = bg_gg; *(q++) = bg_bb; *(q++) = 0xFF; } pp += factor_col_bytes; } *((unsigned int *) (q)) = color_word; /* border */ q += 4; p += factor_row_bytes; } for(a = 0; a < tmp_width; a++) { /* border, last row */ *((unsigned int *) (q)) = color_word; q += 4; } #ifndef PURE_OPENGL_ES_2 glRasterPos3i((int) ((I->Width - tmp_width) / 2 + I->rect.left), (int) ((I->Height - tmp_height) / 2 + I->rect.bottom), -10); #endif PyMOLDrawPixels(tmp_width, tmp_height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); drawn = true; } FreeP(buffer); } { char buffer[255]; int text_pos = (I->Height - tmp_height) / 2 - 15; int x_pos, y_pos; if(text_pos < 0) { text_pos = (I->Height - tmp_height) / 2 + 3; x_pos = (I->Width - tmp_width) / 2 + 3; y_pos = text_pos; } else { x_pos = (I->Width - tmp_width) / 2; y_pos = text_pos; } sprintf(buffer, "Image size = %d x %d", I->Image->width, I->Image->height); TextSetColor3f(G, rgba[0], rgba[1], rgba[2]); TextDrawStrAt(G, buffer, x_pos + I->rect.left, y_pos + I->rect.bottom ORTHOCGOARGVAR); } } } else if(((width < I->Width) || (height < I->Height)) && ((I->Width - width) > 2) && ((I->Height - height) > 2)) { /* but a border around image */ unsigned int color_word; float rgba[4] = { 0.0F, 0.0F, 0.0F, 1.0F }; unsigned int tmp_height = height + 2; unsigned int tmp_width = width + 2; unsigned int border = 1; unsigned int upscale = 1; // Upscale for Retina/4K if (DIP2PIXEL(height) == I->Height && DIP2PIXEL(width) == I->Width) { upscale = DIP2PIXEL(1); tmp_height = DIP2PIXEL(height); tmp_width = DIP2PIXEL(width); border = 0; } unsigned int n_word = tmp_height * tmp_width; unsigned int *tmp_buffer = Alloc(unsigned int, n_word); ColorGetBkrdContColor(G, rgba, false); color_word = ColorGet32BitWord(G, rgba); if(tmp_buffer) { unsigned int a, b; unsigned int *p = (unsigned int *) data; unsigned int *q = tmp_buffer; // top border for(a = 0; a < border; ++a) { for(b = 0; b < tmp_width; b++) *(q++) = color_word; } for(a = border; a < tmp_height - border; a++) { // left border for(b = 0; b < border; ++b) { *(q++) = color_word; } for(b = border; b < tmp_width - border; b++) { unsigned char *qq = (unsigned char *) q; unsigned char *pp = (unsigned char *) p; unsigned char bg; if(show_alpha && (((a >> 4) + (b >> 4)) & 0x1)) { /* introduce checkerboard */ bg_rr = ((bg_r & 0x80) ? bg_r - TRN_BKG : bg_r + TRN_BKG); bg_gg = ((bg_g & 0x80) ? bg_g - TRN_BKG : bg_g + TRN_BKG); bg_bb = ((bg_b & 0x80) ? bg_b - TRN_BKG : bg_b + TRN_BKG); } else { bg_rr = bg_r; bg_gg = bg_g; bg_bb = bg_b; } if(pp[3]) { bg = 0xFF - pp[3]; *(qq++) = (pp[0] * pp[3] + bg_rr * bg) / 0xFF; *(qq++) = (pp[1] * pp[3] + bg_gg * bg) / 0xFF; *(qq++) = (pp[2] * pp[3] + bg_bb * bg) / 0xFF; *(qq++) = 0xFF; } else { *(qq++) = bg_rr; *(qq++) = bg_gg; *(qq++) = bg_bb; *(qq++) = 0xFF; } q++; if ((b + 1 - border) % upscale == 0) { p++; } } if ((a + 1 - border) % upscale != 0) { // read row again p -= width; } // right border for(b = 0; b < border; ++b) { *(q++) = color_word; } } // bottom border for(a = 0; a < border; ++a) { for(b = 0; b < tmp_width; b++) *(q++) = color_word; } #ifndef PURE_OPENGL_ES_2 glRasterPos3i((int) ((I->Width - tmp_width) / 2 + I->rect.left), (int) ((I->Height - tmp_height) / 2 + I->rect.bottom), -10); #endif PyMOLDrawPixels(tmp_width, tmp_height, GL_RGBA, GL_UNSIGNED_BYTE, tmp_buffer); drawn = true; } FreeP(tmp_buffer); } else if(I->CopyForced) { /* near-exact fit */ float rgba[4] = { 0.0F, 0.0F, 0.0F, 1.0F }; unsigned int n_word = height * width; unsigned int *tmp_buffer = Alloc(unsigned int, n_word); ColorGetBkrdContColor(G, rgba, false); if(tmp_buffer) { unsigned int a, b; unsigned int *p = (unsigned int *) data; unsigned int *q = tmp_buffer; for(a = 0; a < (unsigned int) height; a++) { for(b = 0; b < (unsigned int) width; b++) { unsigned char *qq = (unsigned char *) q; unsigned char *pp = (unsigned char *) p; unsigned char bg; if(show_alpha && (((a >> 4) + (b >> 4)) & 0x1)) { /* introduce checkerboard */ bg_rr = ((bg_r & 0x80) ? bg_r - TRN_BKG : bg_r + TRN_BKG); bg_gg = ((bg_g & 0x80) ? bg_g - TRN_BKG : bg_g + TRN_BKG); bg_bb = ((bg_b & 0x80) ? bg_b - TRN_BKG : bg_b + TRN_BKG); } else { bg_rr = bg_r; bg_gg = bg_g; bg_bb = bg_b; } if(pp[3]) { bg = 0xFF - pp[3]; *(qq++) = (pp[0] * pp[3] + bg_rr * bg) / 0xFF; *(qq++) = (pp[1] * pp[3] + bg_gg * bg) / 0xFF; *(qq++) = (pp[2] * pp[3] + bg_bb * bg) / 0xFF; *(qq++) = 0xFF; } else { *(qq++) = bg_rr; *(qq++) = bg_gg; *(qq++) = bg_bb; *(qq++) = 0xFF; } q++; p++; } } } #ifndef PURE_OPENGL_ES_2 glRasterPos3i((int) ((I->Width - width) / 2 + I->rect.left), (int) ((I->Height - height) / 2 + I->rect.bottom), -10); #endif PyMOLDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, tmp_buffer); drawn = true; FreeP(tmp_buffer); } else { /* not a forced copy, so don't show/blend alpha */ #ifndef PURE_OPENGL_ES_2 glRasterPos3i((int) ((I->Width - width) / 2 + I->rect.left), (int) ((I->Height - height) / 2 + I->rect.bottom), -10); #endif PyMOLDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); drawn = true; } I->LastRender = UtilGetSeconds(G); } return drawn; } void CScene::draw(CGO* orthoCGO) /* returns true if scene was drawn (using a cached image) */ { PyMOLGlobals *G = m_G; CScene *I = G->Scene; int drawn = false; if(G->HaveGUI && G->ValidContext) { I->ButtonsShown = false; drawn = SceneDrawImageOverlay(G, 0 ORTHOCGOARGVAR); if(SettingGetGlobal_b(G, cSetting_scene_buttons)) { SceneDrawButtons(this, true ORTHOCGOARGVAR); } else { I->ButtonMargin = 0; } } if(drawn) OrthoDrawWizardPrompt(G ORTHOCGOARGVAR); /* ugly hack necessitated because wizard prompt is overwritten when image is drawn */ } int SceneGetButtonMargin(PyMOLGlobals * G) { CScene *I = G->Scene; return I->ButtonMargin; } /*========================================================================*/ static int SceneRelease(Block * block, int button, int x, int y, int mod, double when) { PyMOLGlobals *G = block->m_G; CScene *I = G->Scene; int release_handled = false; if(I->ButtonsShown && I->PressMode) { if(I->ScrollBarActive) { if((x - I->rect.left) < (SceneScrollBarWidth + SceneScrollBarMargin)) { I->m_ScrollBar.release(button, x, y, mod); release_handled = true; } } if(!release_handled) { int ungrab = true; if(I->PressMode) { int i; SceneElem *elem = I->SceneVLA; I->Over = -1; for(i = 0; i < I->NScene; i++) { if(elem->drawn && (x >= elem->x1) && (y >= elem->y1) && (x < elem->x2) && (y < elem->y2)) { I->Over = i; break; } elem++; } if(I->Over >= 0) { release_handled = true; switch (I->PressMode) { case 1: if(I->Over == I->Pressed) { OrthoLineType buffer; sprintf(buffer, "cmd.scene('''%s''')", elem->name); PParse(G, buffer); PFlush(G); PLog(G, buffer, cPLog_pym); } break; case 2: { const char *cur_name = SettingGetGlobal_s(G, cSetting_scene_current_name); if(cur_name && elem->name && (strcmp(cur_name, elem->name))) { OrthoLineType buffer; sprintf(buffer, "cmd.scene('''%s''')", elem->name); PParse(G, buffer); PFlush(G); PLog(G, buffer, cPLog_pym); } } break; case 3: if(I->Pressed == I->Over) { Block *block = MenuActivate1Arg(G, I->LastWinX, I->LastWinY + 20, /* scene menu */ I->LastWinX, I->LastWinY, true, "scene_menu", elem->name); if (block) block->drag(x, y, mod); ungrab = false; } break; } } } I->LastPickVertexFlag = false; I->Pressed = -1; I->Over = -1; I->PressMode = 0; if(ungrab) OrthoUngrab(G); } } if(!release_handled) { ObjectMolecule *obj; I->LastReleaseTime = when; if(I->PossibleSingleClick == 1) { double slowest_single_click = 0.25F; double diff = when - I->LastClickTime; slowest_single_click += I->ApproxRenderTime; if((diff < 0.0) || (diff > slowest_single_click)) I->PossibleSingleClick = 0; else { int but = -1; I->PossibleSingleClick = 2; I->SingleClickDelay = 0.15; switch (I->LastButton) { case P_GLUT_LEFT_BUTTON: but = P_GLUT_DOUBLE_LEFT; break; case P_GLUT_MIDDLE_BUTTON: but = P_GLUT_DOUBLE_MIDDLE; break; case P_GLUT_RIGHT_BUTTON: but = P_GLUT_DOUBLE_RIGHT; break; } if(but > 0) { int mode = ButModeTranslate(G, but, mod); if(mode == cButModeNone) I->SingleClickDelay = 0.0; /* no double-click set? force immediate single click */ } } } if(I->LoopFlag) { I->PossibleSingleClick = 0; return SceneLoopRelease(block, button, x, y, mod); } OrthoUngrab(G); I->LoopFlag = false; if(I->SculptingFlag) { /* SettingSet(G,cSetting_sculpting,1); */ obj = (ObjectMolecule *) I->LastPicked.context.object; if(obj) { obj->AtomInfo[I->LastPicked.src.index].protekted = I->SculptingSave; } I->SculptingFlag = 0; } } if(I->ReinterpolateFlag && I->ReinterpolateObj) { if(ExecutiveValidateObjectPtr(G, I->ReinterpolateObj, 0)) { ObjectMotionReinterpolate(I->ReinterpolateObj); } I->ReinterpolateFlag = true; I->ReinterpolateObj = NULL; } if(I->MotionGrabbedObj) { if(ExecutiveValidateObjectPtr(G, I->MotionGrabbedObj, 0)) { I->MotionGrabbedObj->Grabbed = false; I->MotionGrabbedObj = NULL; } } return 1; } /*========================================================================*/ static void SceneDoRoving(PyMOLGlobals * G, float old_front, float old_back, float old_origin, int adjust_flag, int zoom_flag) { EditorFavorOrigin(G, NULL); if(SettingGetGlobal_b(G, cSetting_roving_origin)) { CScene *I = G->Scene; float delta_front, delta_back; float front_weight, back_weight, slab_width; float z_buffer = 3.0; float old_pos2 = 0.0F; float v2[3]; z_buffer = SettingGetGlobal_f(G, cSetting_roving_origin_z_cushion); delta_front = I->Front - old_front; delta_back = I->Back - old_back; zero3f(v2); slab_width = I->Back - I->Front; /* first, check to make sure that the origin isn't too close to either plane */ if((z_buffer * 2) > slab_width) z_buffer = slab_width * 0.5F; if(old_origin < (I->Front + z_buffer)) { /* old origin behind front plane */ front_weight = 1.0F; delta_front = (I->Front + z_buffer) - old_origin; /* move origin into allowed regioin */ } else if(old_origin > (I->Back - z_buffer)) { /* old origin was behind back plane */ front_weight = 0.0F; delta_back = (I->Back - z_buffer) - old_origin; } else if(slab_width >= R_SMALL4) { /* otherwise, if slab exists */ front_weight = (old_back - old_origin) / slab_width; /* weight based on relative proximity */ } else { front_weight = 0.5F; } back_weight = 1.0F - front_weight; if((front_weight > 0.2) && (back_weight > 0.2)) { /* origin not near edge */ if(delta_front * delta_back > 0.0F) { /* planes moving in same direction */ if(fabs(delta_front) > fabs(delta_back)) { /* so stick with whichever moves less */ v2[2] = delta_back; } else { v2[2] = delta_front; } } else { /* planes moving in opposite directions (increasing slab size) */ /* don't move origin */ } } else { /* origin is near edge -- move origin with plane having highest weight */ if(front_weight < back_weight) { v2[2] = delta_back; } else { v2[2] = delta_front; } } old_pos2 = I->Pos[2]; MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2); /* transform offset into realspace */ subtract3f(I->Origin, v2, v2); /* calculate new origin location */ SceneOriginSet(G, v2, true); /* move origin, preserving camera location */ if(SettingGetGlobal_b(G, cSetting_ortho) || zoom_flag) { /* we're orthoscopic, so we don't want the effective field of view to change. Thus, we have to hold Pos[2] constant, and instead move the planes. */ float delta = old_pos2 - I->Pos[2]; I->Pos[2] += delta; SceneClipSet(G, I->Front - delta, I->Back - delta); } slab_width = I->Back - I->Front; /* first, check to make sure that the origin isn't too close to either plane */ if((z_buffer * 2) > slab_width) z_buffer = slab_width * 0.5F; } if((adjust_flag) && SettingGetGlobal_b(G, cSetting_roving_detail)) { SceneRovingPostpone(G); } if(SettingGetGlobal_b(G, cSetting_roving_detail)) { SceneRovingDirty(G); } } #define cDoubleTime 0.35 static void SceneNoteMouseInteraction(PyMOLGlobals * G) { SceneAbortAnimation(G); if(SettingGet_b(G, NULL, NULL, cSetting_mouse_restart_movie_delay)) { SceneRestartFrameTimer(G); } } /*========================================================================*/ static int SceneClick(Block * block, int button, int x, int y, int mod, double when) { PyMOLGlobals *G = block->m_G; CScene *I = G->Scene; CObject *obj; ObjectMolecule *objMol; OrthoLineType buffer, buf1, buf2; WordType selName = ""; int mode = 0; /* trying to work around something... */ int atIndex; const char *sel_mode_kw = ""; int is_single_click = ((button == P_GLUT_SINGLE_LEFT) || (button == P_GLUT_SINGLE_MIDDLE) || (button == P_GLUT_SINGLE_RIGHT)); int click_handled = false; int click_side = 0; if(!is_single_click) { int click_handled = false; if(I->ButtonsShown) { int i; SceneElem *elem = I->SceneVLA; /* check & handle a click on the scrollbar */ if(I->ScrollBarActive) { if((x - I->rect.left) < (SceneScrollBarWidth + SceneScrollBarMargin)) { click_handled = true; I->m_ScrollBar.click(button, x, y, mod); } } if(!click_handled) { for(i = 0; i < I->NScene; i++) { if(elem->drawn && (x >= elem->x1) && (y >= elem->y1) && (x < elem->x2) && (y < elem->y2)) { click_handled = true; break; } elem++; } } } if(!click_handled) { // check for double click (within 0.35s and 10sq. pixels if(((ButModeCheckPossibleSingleClick(G, button, mod) || (!mod)) && ((when - I->LastClickTime) < cDoubleTime))) { int dx, dy; dx = abs(I->LastWinX - x); dy = abs(I->LastWinY - y); if((dx < 10) && (dy < 10) && (I->LastButton == button)) { switch (button) { case P_GLUT_LEFT_BUTTON: button = P_GLUT_DOUBLE_LEFT; break; case P_GLUT_MIDDLE_BUTTON: button = P_GLUT_DOUBLE_MIDDLE; break; case P_GLUT_RIGHT_BUTTON: button = P_GLUT_DOUBLE_RIGHT; break; } } } } // end not click handled if(ButModeCheckPossibleSingleClick(G, button, mod) || (!mod)) { I->PossibleSingleClick = 1; } else { const char *but_mode_name = SettingGetGlobal_s(G, cSetting_button_mode_name); if(but_mode_name && but_mode_name[0] == '1') { I->PossibleSingleClick = 1; } else { I->PossibleSingleClick = 0; } } } // end not single-click I->LastWinX = x; I->LastWinY = y; I->LastClickTime = when; I->LastButton = button; I->LastMod = mod; I->Threshold = 0; SceneGetCenter(G, I->LastClickVertex); { float vScale = SceneGetExactScreenVertexScale(G, I->LastClickVertex); float v[3]; v[0] = -(I->Width / 2 - (x - I->rect.left)) * vScale; v[1] = -(I->Height / 2 - (y - I->rect.bottom)) * vScale; v[2] = 0; MatrixInvTransformC44fAs33f3f(I->RotMatrix, v, v); add3f(v, I->LastClickVertex, I->LastClickVertex); } if(I->ButtonsShown) { int i; SceneElem *elem = I->SceneVLA; if(I->ScrollBarActive) { if((x - I->rect.left) < (SceneScrollBarWidth + SceneScrollBarMargin)) { click_handled = true; I->m_ScrollBar.click(button, x, y, mod); } } if(!click_handled) { for(i = 0; i < I->NScene; i++) { if(elem->drawn && (x >= elem->x1) && (y >= elem->y1) && (x < elem->x2) && (y < elem->y2)) { switch (button) { case P_GLUT_LEFT_BUTTON: /* normal activate (with interpolation) */ I->Pressed = i; I->Over = i; I->PressMode = 1; SceneDirty(G); click_handled = true; break; case P_GLUT_MIDDLE_BUTTON: /* rapid browse mode */ I->Pressed = i; I->PressMode = 2; I->Over = i; click_handled = true; { const char *cur_name = SettingGetGlobal_s(G, cSetting_scene_current_name); int animate = -1; if(mod & cOrthoCTRL) animate = 0; if(cur_name && elem->name && (strcmp(cur_name, elem->name))) { OrthoLineType buffer; sprintf(buffer, "cmd.scene('''%s''',animate=%d)", elem->name, animate); PParse(G, buffer); PFlush(G); PLog(G, buffer, cPLog_pym); } } break; case P_GLUT_RIGHT_BUTTON: /* drag or menu... */ I->Pressed = i; I->PressMode = 3; I->Over = i; click_handled = true; break; } break; } elem++; } } } if(!click_handled) { mode = ButModeTranslate(G, button, mod); I->Button = button; I->SculptingSave = 0; switch (mode) { case cButModeScaleSlabExpand: SceneNoteMouseInteraction(G); SceneClip(G, 5, 1.0F + (0.2 * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale)), NULL, 0); break; case cButModeScaleSlabShrink: SceneNoteMouseInteraction(G); SceneClip(G, 5, 1.0F - (0.2 * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale)), NULL, 0); break; case cButModeMoveSlabForward: SceneNoteMouseInteraction(G); { float old_front = I->Front; float old_back = I->Back; float old_origin = -I->Pos[2]; SceneClip(G, 6, 0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), NULL, 0); SceneDoRoving(G, old_front, old_back, old_origin, true, false); } break; case cButModeMoveSlabBackward: SceneNoteMouseInteraction(G); { float old_front = I->Front; float old_back = I->Back; float old_origin = -I->Pos[2]; SceneClip(G, 6, -0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), NULL, 0); SceneDoRoving(G, old_front, old_back, old_origin, true, false); } break; case cButModeZoomForward: SceneNoteMouseInteraction(G); { float factor = -((I->FrontSafe + I->BackSafe) / 2) * 0.1 * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale); if(factor <= 0.0F) { I->Pos[2] += factor; I->Front -= factor; I->Back -= factor; UpdateFrontBackSafe(I); } } break; case cButModeZoomBackward: SceneNoteMouseInteraction(G); { float factor = ((I->FrontSafe + I->BackSafe) / 2) * 0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale); if(factor >= 0.0F) { I->Pos[2] += factor; I->Front -= factor; I->Back -= factor; UpdateFrontBackSafe(I); } } break; case cButModeMoveSlabAndZoomForward: SceneNoteMouseInteraction(G); { float old_front = I->Front; float old_back = I->Back; float old_origin = -I->Pos[2]; SceneClip(G, 6, 0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), NULL, 0); SceneDoRoving(G, old_front, old_back, old_origin, true, true); } break; case cButModeMoveSlabAndZoomBackward: SceneNoteMouseInteraction(G); { float old_front = I->Front; float old_back = I->Back; float old_origin = -I->Pos[2]; SceneClip(G, 6, -0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), NULL, 0); SceneDoRoving(G, old_front, old_back, old_origin, true, true); } break; case cButModeRectAdd: /* deprecated */ case cButModeRectSub: /* deprecated */ case cButModeRect: /* deprecated */ case cButModeSeleAddBox: case cButModeSeleSetBox: case cButModeSeleSubBox: return SceneLoopClick(block, button, x, y, mod); break; case cButModeRotDrag: case cButModeMovDrag: case cButModeMovDragZ: SceneNoteMouseInteraction(G); SceneDontCopyNext(G); y = y - I->margin.bottom; x = x - I->margin.left; if(stereo_via_adjacent_array(I->StereoMode)) x = get_stereo_x(x, NULL, I->Width, NULL); I->LastX = x; I->LastY = y; EditorReadyDrag(G, SettingGetGlobal_i(G, cSetting_state) - 1); if(EditorDraggingObjectMatrix(G)) { obj = EditorDragObject(G); if(obj) { if(SettingGetGlobal_b(G,cSetting_movie_auto_store)) { ObjectTranslateTTT(obj, NULL, true); I->MotionGrabbedObj = obj; obj->Grabbed = true; if(SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) { I->ReinterpolateFlag = true; I->ReinterpolateObj = obj; } } else { ObjectTranslateTTT(obj,NULL,false); } } } break; case cButModeRotXYZ: case cButModeTransXY: case cButModeTransZ: case cButModeClipNF: case cButModeClipN: case cButModeClipF: case cButModeRotZ: case cButModeInvRotZ: case cButModeRotL: case cButModeMovL: case cButModeMvzL: SceneNoteMouseInteraction(G); SceneDontCopyNext(G); y = y - I->margin.bottom; x = x - I->margin.left; if(stereo_via_adjacent_array(I->StereoMode)) x = get_stereo_x(x, NULL, I->Width, NULL); I->LastX = x; I->LastY = y; break; case cButModePickAtom1: case cButModePickAtom: case cButModeMenu: if(stereo_via_adjacent_array(I->StereoMode)) x = get_stereo_x(x, NULL, I->Width, NULL); if(SceneDoXYPick(G, x, y, click_side)) { obj = (CObject *) I->LastPicked.context.object; y = y - I->margin.bottom; x = x - I->margin.left; I->LastX = x; I->LastY = y; switch (obj->type) { case cObjectMolecule: switch (mode) { case cButModeMenu: { ObjectMolecule *objMol = (ObjectMolecule *) obj; int active_sele = ExecutiveGetActiveSele(G); if(active_sele && SelectorIsMember(G, objMol->AtomInfo[I->LastPicked.src.index].selEntry, active_sele)) { /* user clicked on a selected atom */ ObjectNameType name; ExecutiveGetActiveSeleName(G, name, false, SettingGetGlobal_i(G, cSetting_logging)); MenuActivate2Arg(G, I->LastWinX, I->LastWinY + 20, /* selection menu */ I->LastWinX, I->LastWinY, is_single_click, "pick_sele", name, name); } else { /* user clicked on an atom not in a selection */ obj->fDescribeElement(obj, I->LastPicked.src.index, buffer); ObjectMoleculeGetAtomSeleLog((ObjectMolecule *) obj, I->LastPicked.src.index, buf1, false); MenuActivate2Arg(G, I->LastWinX, I->LastWinY + 20, I->LastWinX, I->LastWinY, is_single_click, "pick_menu", buffer, buf1); } } break; case cButModePickAtom1: if(obj && obj->type == cObjectMolecule) { if(Feedback(G, FB_Scene, FB_Results)) { if(obj->fDescribeElement) obj->fDescribeElement(obj, I->LastPicked.src.index, buffer); PRINTF " You clicked %s -> (%s)\n", buffer, cEditorSele1 ENDF(G); } if(SettingGetGlobal_i(G, cSetting_logging)) { objMol = (ObjectMolecule *) obj; ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buffer, false); sprintf(buf2, "cmd.edit(\"%s\",pkresi=1)", buffer); PLog(G, buf2, cPLog_pym); } OrthoRestorePrompt(G); sprintf(buffer, "%s`%d", obj->Name, I->LastPicked.src.index + 1); EditorInactivate(G); SelectorCreate(G, cEditorSele1, buffer, NULL, true, NULL); EditorActivate(G, SettingGetGlobal_i(G, cSetting_state) - 1, false); if(EditorActive(G)) { EditorDefineExtraPks(G); } WizardDoPick(G, 0, I->LastPicked.context.state); } break; case cButModePickAtom: if(obj && obj->type == cObjectMolecule) { WordType name; if(obj->fDescribeElement) obj->fDescribeElement(obj, I->LastPicked.src.index, buffer); if(EditorIsBondMode(G) /* &&!(EditorIsAnActiveObject(G,(ObjectMolecule*)obj)) */ ) { EditorInactivate(G); EditorLogState(G, false); } if((!EditorIsBondMode(G)) && EditorDeselectIfSelected(G, (ObjectMolecule *) obj, I->LastPicked.src.index, true)) { PRINTF " You unpicked %s.", buffer ENDF(G); if(EditorActive(G)) EditorDefineExtraPks(G); EditorLogState(G, false); } else { if(EditorIsBondMode(G) && EditorDeselectIfSelected(G, (ObjectMolecule *) obj, I->LastPicked.src.index, false)) { EditorInactivate(G); } EditorGetNextMultiatom(G, name); PRINTFB(G, FB_Scene, FB_Results) " You clicked %s -> (%s)\n", buffer, name ENDFB(G); /* TODO: logging */ sprintf(buffer, "%s`%d", obj->Name, I->LastPicked.src.index + 1); ExecutiveDelete(G, name); SelectorCreate(G, name, buffer, NULL, true, NULL); EditorActivate(G, SettingGetGlobal_i(G, cSetting_state) - 1, false); if(EditorActive(G)) { EditorDefineExtraPks(G); } EditorLogState(G, false); WizardDoPick(G, 0, I->LastPicked.context.state); } } break; } break; case cObjectGadget: break; default: EditorInactivate(G); break; } } else { /* no atom picked */ switch (mode) { case cButModeMenu: MenuActivate3fv(G, I->LastWinX, I->LastWinY, I->LastWinX, I->LastWinY, is_single_click, "main_menu", I->LastClickVertex); break; default: EditorInactivate(G); if(SettingGetGlobal_i(G, cSetting_logging)) { PLog(G, "cmd.edit()", cPLog_pym); } break; } } SceneDirty(G); break; case cButModePickBond: case cButModePkTorBnd: if(stereo_via_adjacent_array(I->StereoMode)) x = get_stereo_x(x, NULL, I->Width, &click_side); if(SceneDoXYPick(G, x, y, click_side)) { obj = (CObject *) I->LastPicked.context.object; y = y - I->margin.bottom; x = x - I->margin.left; I->LastX = x; I->LastY = y; if(mode == cButModePkTorBnd) { I->Threshold = 3; I->ThresholdX = x; I->ThresholdY = y; } switch (obj->type) { case cObjectMolecule: EditorInactivate(G); if(Feedback(G, FB_Scene, FB_Results)) { if(obj->fDescribeElement) obj->fDescribeElement(obj, I->LastPicked.src.index, buffer); PRINTF " You clicked %s -> (%s)", buffer, cEditorSele1 ENDF(G); OrthoRestorePrompt(G); } /* ObjectMoleculeChooseBondDir(objMol,I->LastPicked.bond, &I->LastPicked.src.index,&atIndex); */ sprintf(buffer, "%s`%d", obj->Name, I->LastPicked.src.index + 1); SelectorCreate(G, cEditorSele1, buffer, NULL, true, NULL); objMol = (ObjectMolecule *) obj; if(I->LastPicked.src.bond >= 0) { atIndex = objMol->Bond[I->LastPicked.src.bond].index[0]; if(atIndex == I->LastPicked.src.index) atIndex = objMol->Bond[I->LastPicked.src.bond].index[1]; if(Feedback(G, FB_Scene, FB_Results)) { if(obj->fDescribeElement) obj->fDescribeElement(obj, atIndex, buffer); PRINTF " You clicked %s -> (%s)", buffer, cEditorSele2 ENDF(G); OrthoRestorePrompt(G); } if(SettingGetGlobal_i(G, cSetting_logging)) { objMol = (ObjectMolecule *) obj; ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false); ObjectMoleculeGetAtomSeleLog(objMol, atIndex, buf2, false); sprintf(buffer, "cmd.edit(\"%s\",\"%s\")", buf1, buf2); PLog(G, buffer, cPLog_pym); } sprintf(buffer, "%s`%d", obj->Name, atIndex + 1); SelectorCreate(G, cEditorSele2, buffer, NULL, true, NULL); EditorActivate(G, SettingGetGlobal_i(G, cSetting_state) - 1, true); if(mode == cButModePkTorBnd) { /* get ready to drag */ SceneDontCopyNext(G); switch (obj->type) { case cObjectMolecule: objMol = (ObjectMolecule *) obj; EditorPrepareDrag(G, obj, -1, I->LastPicked.src.index, SettingGetGlobal_i(G, cSetting_state) - 1, mode); I->SculptingFlag = 1; I->SculptingSave = objMol->AtomInfo[I->LastPicked.src.index].protekted; objMol->AtomInfo[I->LastPicked.src.index].protekted = 2; break; } } WizardDoPick(G, 1, I->LastPicked.context.state); } else { WizardDoPick(G, 0, I->LastPicked.context.state); } if(SettingGetGlobal_b(G, cSetting_auto_hide_selections)) ExecutiveHideSelections(G); break; case cObjectGadget: break; default: EditorInactivate(G); break; } } else { EditorInactivate(G); EditorLogState(G, false); } SceneInvalidate(G); break; case cButModeRotObj: case cButModeMovObj: case cButModeMovObjZ: case cButModeRotView: case cButModeMovView: case cButModeMovViewZ: case cButModeRotFrag: case cButModeMovFrag: case cButModeMovFragZ: case cButModeTorFrag: case cButModeMoveAtom: case cButModeMoveAtomZ: if(stereo_via_adjacent_array(I->StereoMode)) x = get_stereo_x(x, NULL, I->Width, &click_side); if(SceneDoXYPick(G, x, y, click_side)) { obj = (CObject *) I->LastPicked.context.object; y = y - I->margin.bottom; x = x - I->margin.left; I->LastX = x; I->LastY = y; switch (obj->type) { case cObjectMolecule: if(I->LastPicked.src.bond == cPickableLabel) { /* if user picks a label with move object/move fragment, then move the object/fragment, not the label */ switch (mode) { case cButModeRotObj: case cButModeMovObj: case cButModeMovObjZ: case cButModeRotFrag: case cButModeMovFrag: case cButModeMovFragZ: case cButModeMovViewZ: case cButModeRotView: case cButModeMovView: I->LastPicked.src.bond = cPickableAtom; break; } } switch(mode) { case cButModeMovViewZ: case cButModeRotView: case cButModeMovView: { if(SettingGetGlobal_b(G,cSetting_movie_auto_store)) { ObjectTranslateTTT(obj, NULL, true); I->MotionGrabbedObj = obj; obj->Grabbed = true; if(SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) { I->ReinterpolateFlag = true; I->ReinterpolateObj = obj; } } else { ObjectTranslateTTT(obj, NULL, false); } } break; } if(I->LastPicked.src.bond >= cPickableAtom) { if(Feedback(G, FB_Scene, FB_Results)) { if(obj->fDescribeElement) obj->fDescribeElement(obj, I->LastPicked.src.index, buffer); PRINTF " You clicked %s", buffer ENDF(G); OrthoRestorePrompt(G); } } objMol = (ObjectMolecule *) obj; EditorPrepareDrag(G, obj, -1, I->LastPicked.src.index, SettingGetGlobal_i(G, cSetting_state) - 1, mode); if(I->LastPicked.src.bond >= cPickableAtom) { I->SculptingFlag = 1; I->SculptingSave = objMol->AtomInfo[I->LastPicked.src.index].protekted; objMol->AtomInfo[I->LastPicked.src.index].protekted = 2; } break; case cObjectSlice: if(ObjectSliceGetVertex((ObjectSlice *) obj, I->LastPicked.src.index, I->LastPicked.src.bond, I->LastPickVertex)) { I->LastPickVertexFlag = true; } break; case cObjectMeasurement: break; case cObjectGadget: break; default: EditorInactivate(G); break; } } break; case cButModeSeleSet: case cButModeSeleToggle: sel_mode_kw = SceneGetSeleModeKeyword(G); /* intentional pass through */ case cButModeLB: case cButModeMB: case cButModeRB: case cButModeAddToLB: case cButModeAddToMB: case cButModeAddToRB: case cButModeSimpleClick: case cButModeOrigAt: case cButModeCent: case cButModeDragMol: case cButModeDragObj: if(stereo_via_adjacent_array(I->StereoMode)) x = get_stereo_x(x, NULL, I->Width, &click_side); if(SceneDoXYPick(G, x, y, click_side)) { obj = (CObject *) I->LastPicked.context.object; switch (obj->type) { case cObjectMolecule: if(Feedback(G, FB_Scene, FB_Results)) { if(obj->fDescribeElement) obj->fDescribeElement(obj, I->LastPicked.src.index, buffer); PRINTF " You clicked %s", buffer ENDF(G); OrthoRestorePrompt(G); } sprintf(buffer, "%s`%d", obj->Name, I->LastPicked.src.index + 1); switch (mode) { case cButModeLB: case cButModeAddToLB: strcpy(selName, "lb"); break; case cButModeMB: case cButModeAddToMB: strcpy(selName, "mb"); break; case cButModeRB: case cButModeAddToRB: strcpy(selName, "rb"); break; case cButModeSeleSet: case cButModeSeleToggle: ExecutiveGetActiveSeleName(G, selName, true, SettingGetGlobal_i(G, cSetting_logging)); break; case cButModeDragMol: { objMol = (ObjectMolecule *) obj; ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false); sprintf(buffer, "cmd.drag(\"bymol (%s)\")", buf1); PParse(G, buffer); PLog(G, buffer, cPLog_pym); } break; case cButModeDragObj: { objMol = (ObjectMolecule *) obj; ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false); sprintf(buffer, "cmd.drag(\"byobject (%s)\")", buf1); PParse(G, buffer); PLog(G, buffer, cPLog_pym); } break; case cButModeOrigAt: SceneNoteMouseInteraction(G); { float v1[3]; if(ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *) obj, I->LastPicked.context.state, I->LastPicked.src.index, v1)) { EditorFavorOrigin(G, v1); ExecutiveOrigin(G, NULL, true, NULL, v1, 0); } } if(obj->type == cObjectMolecule) { if(SettingGetGlobal_i(G, cSetting_logging)) { objMol = (ObjectMolecule *) obj; ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false); sprintf(buffer, "cmd.origin(\"%s\")", buf1); PLog(G, buffer, cPLog_pym); } if(Feedback(G, FB_Scene, FB_Results)) { if(obj->fDescribeElement) obj->fDescribeElement(obj, I->LastPicked.src.index, buffer); PRINTF " You clicked %s", buffer ENDF(G); OrthoRestorePrompt(G); } } PRINTFB(G, FB_Scene, FB_Actions) " Scene: Origin set.\n" ENDFB(G); break; case cButModeCent: SceneNoteMouseInteraction(G); { float v1[3]; if(ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *) obj, I->LastPicked.context.state, I->LastPicked.src.index, v1)) { ExecutiveCenter(G, NULL, 0, true, -1, v1, true); } } if(SettingGetGlobal_i(G, cSetting_logging)) { objMol = (ObjectMolecule *) obj; ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false); sprintf(buffer, "cmd.center(\"%s\",state=%d)", buf1, I->LastPicked.context.state + 1); PLog(G, buffer, cPLog_pym); } break; } switch (mode) { case cButModeSimpleClick: { float pos_store[3], *pos = pos_store; int index = I->LastPicked.src.index; /* 1-based */ int state = ObjectGetCurrentState(obj, true); if(!( (obj->type == cObjectMolecule) && (I->LastPicked.src.bond != cPickableNoPick ) && ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *)obj,-1,index, pos))) pos = NULL; PyMOL_SetClickReady(G->PyMOL, obj->Name, I->LastPicked.src.index, button, mod, I->LastWinX, I->Height - (I->LastWinY + 1), pos, state + 1); /* send a 1-based state index */ } break; case cButModeLB: case cButModeMB: case cButModeRB: case cButModeSeleSet: sprintf(buf2, "(%s(%s))", sel_mode_kw, buffer); SelectorCreate(G, selName, buf2, NULL, false, NULL); if(SettingGetGlobal_b(G, cSetting_auto_hide_selections)) ExecutiveHideSelections(G); if(SettingGetGlobal_b(G, cSetting_auto_show_selections)) ExecutiveSetObjVisib(G, selName, 1, false); if(obj->type == cObjectMolecule) { if(SettingGetGlobal_i(G, cSetting_logging)) { objMol = (ObjectMolecule *) obj; ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false); sprintf(buffer, "cmd.select('%s',\"%s(%s)\",enable=1)", selName, sel_mode_kw, buf1); PLog(G, buffer, cPLog_pym); } } WizardDoSelect(G, selName, I->LastPicked.context.state); break; case cButModeAddToLB: case cButModeAddToMB: case cButModeAddToRB: case cButModeSeleToggle: if(SelectorIndexByName(G, selName) >= 0) { sprintf(buf2, "(((%s) or %s(%s)) and not ((%s(%s)) and %s(%s)))", selName, sel_mode_kw, buffer, sel_mode_kw, buffer, sel_mode_kw, selName); SelectorCreate(G, selName, buf2, NULL, false, NULL); if(obj->type == cObjectMolecule) { if(SettingGetGlobal_i(G, cSetting_logging)) { objMol = (ObjectMolecule *) obj; ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buffer, false); sprintf(buf2, "(((%s) or %s(%s)) and not ((%s(%s)) and %s(%s)))", selName, sel_mode_kw, buffer, sel_mode_kw, buffer, sel_mode_kw, selName); sprintf(buffer, "cmd.select('%s',\"%s(%s)\",enable=1)", selName, sel_mode_kw, buf2); PLog(G, buffer, cPLog_pym); } } } else { sprintf(buf2, "%s(%s)", sel_mode_kw, buffer); SelectorCreate(G, selName, buf2, NULL, false, NULL); if(obj->type == cObjectMolecule) { if(SettingGetGlobal_i(G, cSetting_logging)) { objMol = (ObjectMolecule *) obj; ObjectMoleculeGetAtomSeleLog(objMol, I->LastPicked.src.index, buf1, false); sprintf(buffer, "cmd.select('%s',\"%s(%s)\")", selName, sel_mode_kw, buf1); PLog(G, buffer, cPLog_pym); } } } if(SettingGetGlobal_b(G, cSetting_auto_hide_selections)) ExecutiveHideSelections(G); if(SettingGetGlobal_b(G, cSetting_auto_show_selections)) ExecutiveSetObjVisib(G, selName, 1, false); WizardDoSelect(G, selName, I->LastPicked.context.state); break; } case cObjectGadget: break; default: EditorInactivate(G); break; } } else { switch (mode) { case cButModeSeleSet: { OrthoLineType buf2; ObjectNameType name; if(ExecutiveGetActiveSeleName (G, name, false, SettingGetGlobal_i(G, cSetting_logging))) { SelectorCreate(G, name, "none", NULL, true, NULL); if(SettingGetGlobal_i(G, cSetting_logging)) { sprintf(buf2, "cmd.select('%s','none')\n", name); PLog(G, buf2, cPLog_no_flush); } SeqDirty(G); } } case cButModeSeleToggle: { OrthoLineType buf2; ObjectNameType name; if(ExecutiveGetActiveSeleName (G, name, false, SettingGetGlobal_i(G, cSetting_logging))) { ExecutiveSetObjVisib(G, name, 0, false); if(SettingGetGlobal_i(G, cSetting_logging)) { sprintf(buf2, "cmd.disable('%s')\n", name); PLog(G, buf2, cPLog_no_flush); } } } break; case cButModeSimpleClick: PyMOL_SetClickReady(G->PyMOL, "", -1, button, mod, I->LastWinX, I->Height - (I->LastWinY + 1), NULL, 0); break; } PRINTFB(G, FB_Scene, FB_Blather) " SceneClick: no atom found nearby.\n" ENDFB(G); SceneInvalidate(G); /* this here to prevent display weirdness after an unsuccessful picking pass... not sure it helps though */ OrthoRestorePrompt(G); } } I->StartX = I->LastX; I->StartY = I->LastY; } return (1); } float ScenePushRasterMatrix(PyMOLGlobals * G, float *v) { float scale = SceneGetExactScreenVertexScale(G, v); CScene *I = G->Scene; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef(v[0], v[1], v[2]); /* go to this position */ glMultMatrixf(I->InvMatrix); glScalef(scale, scale, scale); return scale; } void ScenePopRasterMatrix(PyMOLGlobals * G) { glMatrixMode(GL_MODELVIEW); glPopMatrix(); } /* * Compose the ModelViewMatrix from Pos, RotMatrix and Origin * See also: CScene.ModMatrix (queried from OpenGL) */ static void SceneComposeModelViewMatrix(CScene * I, float * modelView) { identity44f(modelView); MatrixTranslateC44f(modelView, I->Pos[0], I->Pos[1], I->Pos[2]); MatrixMultiplyC44f(I->RotMatrix, modelView); MatrixTranslateC44f(modelView, -I->Origin[0], -I->Origin[1], -I->Origin[2]); } /*========================================================================*/ void SceneGetEyeNormal(PyMOLGlobals * G, float *v1, float *normal) { CScene *I = G->Scene; float p1[4], p2[4]; float modelView[16]; SceneComposeModelViewMatrix(I, modelView); copy3f(v1, p1); p1[3] = 1.0; MatrixTransformC44f4f(modelView, p1, p2); /* modelview transformation */ copy3f(p2, p1); normalize3f(p1); MatrixInvTransformC44fAs33f3f(I->RotMatrix, p1, p2); invert3f3f(p2, normal); } /* * Return true if the v1 is within the safe clipping planes */ short SceneGetVisible(PyMOLGlobals * G, float *v1) { CScene *I = G->Scene; float depth = SceneGetRawDepth(G, v1); return (I->BackSafe >= depth && depth >= I->FrontSafe); } /* * Get the depth (camera space Z) of v1 * * v1: point (3f) in world space or NULL (= origin) */ float SceneGetRawDepth(PyMOLGlobals * G, float *v1) { CScene *I = G->Scene; float vt[3]; float modelView[16]; if(!v1 || SettingGetGlobal_i(G, cSetting_ortho)) return -I->Pos[2]; SceneComposeModelViewMatrix(I, modelView); MatrixTransformC44f3f(modelView, v1, vt); return -vt[2]; } /* * Get the depth (camera space Z) of v1 in normalized clip space * from 0.0 (near) to 1.0 (far) * * v1: point (3f) in world space or NULL (= origin) */ float SceneGetDepth(PyMOLGlobals * G, float *v1) { CScene *I = G->Scene; float rawDepth = SceneGetRawDepth(G, v1); return ((rawDepth - I->FrontSafe)/(I->BackSafe-I->FrontSafe)); } /*========================================================================*/ /* * Get the angstrom per pixel factor at v1. If v1 is NULL, return the * factor at the origin, but clamped to an empirical positive value. * * v1: point (3f) in world space or NULL (= origin) */ float SceneGetScreenVertexScale(PyMOLGlobals * G, float *v1) /* does not require OpenGL-provided matrices */ { float depth = SceneGetRawDepth(G, v1); float ratio = depth * GetFovWidth(G) / G->Scene->Height; if(!v1 && ratio < R_SMALL4) // origin depth, return a safe clipped value (origin must not be // behind or very close in front of the camera) ratio = R_SMALL4; return ratio; } void SceneRovingChanged(PyMOLGlobals * G) { CScene *I = G->Scene; SceneRovingDirty(G); I->RovingCleanupFlag = true; } static void SceneRovingCleanup(PyMOLGlobals * G) { CScene *I = G->Scene; const char *s; char buffer[OrthoLineLength]; I->RovingCleanupFlag = false; s = SettingGet_s(G, NULL, NULL, cSetting_roving_selection); sprintf(buffer, "cmd.hide('lines','''%s''')", s); PParse(G, buffer); PFlush(G); sprintf(buffer, "cmd.hide('sticks','''%s''')", s); PParse(G, buffer); PFlush(G); sprintf(buffer, "cmd.hide('spheres','''%s''')", s); PParse(G, buffer); PFlush(G); sprintf(buffer, "cmd.hide('ribbon','''%s''')", s); PParse(G, buffer); PFlush(G); sprintf(buffer, "cmd.hide('cartoon','''%s''')", s); PParse(G, buffer); PFlush(G); sprintf(buffer, "cmd.hide('labels','''%s''')", s); PParse(G, buffer); PFlush(G); sprintf(buffer, "cmd.hide('nonbonded','''%s''')", s); PParse(G, buffer); PFlush(G); sprintf(buffer, "cmd.hide('nb_spheres','''%s''')", s); PParse(G, buffer); PFlush(G); } void SceneRovingUpdate(PyMOLGlobals * G) { CScene *I = G->Scene; char buffer[OrthoLineLength]; float sticks, lines, spheres, labels, ribbon, cartoon; float polar_contacts, polar_cutoff, nonbonded, nb_spheres; char byres[10] = "byres"; char not_[4] = "not"; char empty[1] = ""; char *p1; char *p2; const char *s; int refresh_flag = false; const char *name; float level; float isosurface, isomesh; if(I->RovingDirtyFlag && ((UtilGetSeconds(G) - I->RovingLastUpdate) > fabs(SettingGetGlobal_f(G, cSetting_roving_delay)))) { if(I->RovingCleanupFlag) SceneRovingCleanup(G); s = SettingGet_s(G, NULL, NULL, cSetting_roving_selection); sticks = SettingGetGlobal_f(G, cSetting_roving_sticks); lines = SettingGetGlobal_f(G, cSetting_roving_lines); labels = SettingGetGlobal_f(G, cSetting_roving_labels); spheres = SettingGetGlobal_f(G, cSetting_roving_spheres); ribbon = SettingGetGlobal_f(G, cSetting_roving_ribbon); cartoon = SettingGetGlobal_f(G, cSetting_roving_cartoon); polar_contacts = SettingGetGlobal_f(G, cSetting_roving_polar_contacts); polar_cutoff = SettingGetGlobal_f(G, cSetting_roving_polar_cutoff); nonbonded = SettingGetGlobal_f(G, cSetting_roving_nonbonded); nb_spheres = SettingGetGlobal_f(G, cSetting_roving_nb_spheres); isomesh = SettingGetGlobal_f(G, cSetting_roving_isomesh); isosurface = SettingGetGlobal_f(G, cSetting_roving_isosurface); if(SettingGetGlobal_b(G, cSetting_roving_byres)) p2 = byres; else p2 = empty; if(sticks != 0.0F) { if(sticks < 0.0F) { p1 = not_; sticks = (float) fabs(sticks); } else { p1 = empty; } sprintf(buffer, "cmd.hide('sticks','''%s''');cmd.show('sticks','%s & enabled & %s %s (center expand %1.3f)')", s, s, p1, p2, sticks); PParse(G, buffer); PFlush(G); refresh_flag = true; } if(lines != 0.0F) { if(lines < 0.0F) { p1 = not_; lines = (float) fabs(lines); } else { p1 = empty; } sprintf(buffer, "cmd.hide('lines','''%s''');cmd.show('lines','%s & enabled & %s %s (center expand %1.3f)')", s, s, p1, p2, lines); PParse(G, buffer); PFlush(G); refresh_flag = true; } if(labels != 0.0F) { if(labels < 0.0F) { p1 = not_; labels = (float) fabs(labels); } else { p1 = empty; } sprintf(buffer, "cmd.hide('labels','''%s''');cmd.show('labels','%s & enabled & %s %s (center expand %1.3f)')", s, s, p1, p2, labels); PParse(G, buffer); PFlush(G); refresh_flag = true; } if(spheres != 0.0F) { if(spheres < 0.0F) { p1 = not_; spheres = (float) fabs(spheres); } else { p1 = empty; } sprintf(buffer, "cmd.hide('spheres','''%s''');cmd.show('spheres','%s & enabled & %s %s (center expand %1.3f)')", s, s, p1, p2, spheres); PParse(G, buffer); PFlush(G); refresh_flag = true; } if(cartoon != 0.0F) { if(cartoon < 0.0F) { p1 = not_; cartoon = (float) fabs(cartoon); } else { p1 = empty; } sprintf(buffer, "cmd.hide('cartoon','''%s''');cmd.show('cartoon','%s & enabled & %s %s (center expand %1.3f)')", s, s, p1, p2, cartoon); PParse(G, buffer); PFlush(G); refresh_flag = true; } if(ribbon != 0.0F) { if(ribbon < 0.0F) { p1 = not_; ribbon = (float) fabs(ribbon); } else { p1 = empty; } sprintf(buffer, "cmd.hide('ribbon','''%s''');cmd.show('ribbon','%s & enabled & %s %s (center expand %1.3f)')", s, s, p1, p2, ribbon); PParse(G, buffer); PFlush(G); refresh_flag = true; } if(polar_contacts != 0.0F) { int label_flag = 0; if(polar_contacts < 0.0F) { p1 = not_; polar_contacts = (float) fabs(polar_contacts); } else { p1 = empty; } if(polar_cutoff < 0.0F) { label_flag = true; polar_cutoff = (float) fabs(polar_cutoff); } sprintf(buffer, "cmd.delete('rov_pc');cmd.dist('rov_pc','%s & enabled & %s %s (center expand %1.3f)','same',%1.4f,mode=2,label=%d,quiet=2)", s, p1, p2, polar_contacts, polar_cutoff, label_flag); PParse(G, buffer); PFlush(G); refresh_flag = true; } if(nonbonded != 0.0F) { if(nonbonded < 0.0F) { p1 = not_; nonbonded = (float) fabs(nonbonded); } else { p1 = empty; } sprintf(buffer, "cmd.hide('nonbonded','''%s''');cmd.show('nonbonded','%s & enabled & %s %s (center expand %1.3f)')", s, s, p1, p2, nonbonded); PParse(G, buffer); PFlush(G); refresh_flag = true; } if(nb_spheres != 0.0F) { if(nb_spheres < 0.0F) { p1 = not_; nb_spheres = (float) fabs(nb_spheres); } else { p1 = empty; } sprintf(buffer, "cmd.hide('nb_spheres','''%s''');cmd.show('nb_spheres','%s & enabled & %s %s (center expand %1.3f)')", s, s, p1, p2, nb_spheres); PParse(G, buffer); PFlush(G); refresh_flag = true; } if(isomesh != 0.0F) { int auto_save; auto_save = SettingGetGlobal_i(G, cSetting_auto_zoom); SettingSetGlobal_i(G, cSetting_auto_zoom, 0); name = SettingGet_s(G, NULL, NULL, cSetting_roving_map1_name); if(name) if(name[0]) if(ExecutiveFindObjectByName(G, name)) { level = SettingGetGlobal_f(G, cSetting_roving_map1_level); sprintf(buffer, "cmd.isomesh('rov_m1','%s',%8.6f,'center',%1.3f)", name, level, isomesh); PParse(G, buffer); PFlush(G); refresh_flag = true; } name = SettingGet_s(G, NULL, NULL, cSetting_roving_map2_name); if(name) if(name[0]) if(ExecutiveFindObjectByName(G, name)) { level = SettingGetGlobal_f(G, cSetting_roving_map2_level); sprintf(buffer, "cmd.isomesh('rov_m2','%s',%8.6f,'center',%1.3f)", name, level, isomesh); PParse(G, buffer); PFlush(G); refresh_flag = true; } name = SettingGet_s(G, NULL, NULL, cSetting_roving_map3_name); if(name) if(name[0]) if(ExecutiveFindObjectByName(G, name)) { level = SettingGetGlobal_f(G, cSetting_roving_map3_level); sprintf(buffer, "cmd.isomesh('rov_m3','%s',%8.6f,'center',%1.3f)", name, level, isomesh); PParse(G, buffer); PFlush(G); refresh_flag = true; } SettingSetGlobal_i(G, cSetting_auto_zoom, auto_save); } if(isosurface != 0.0F) { int auto_save; auto_save = SettingGetGlobal_i(G, cSetting_auto_zoom); SettingSetGlobal_i(G, cSetting_auto_zoom, 0); name = SettingGet_s(G, NULL, NULL, cSetting_roving_map1_name); if(name) if(name[0]) if(ExecutiveFindObjectByName(G, name)) { level = SettingGetGlobal_f(G, cSetting_roving_map1_level); sprintf(buffer, "cmd.isosurface('rov_s1','%s',%8.6f,'center',%1.3f)", name, level, isosurface); PParse(G, buffer); PFlush(G); refresh_flag = true; } name = SettingGet_s(G, NULL, NULL, cSetting_roving_map2_name); if(name) if(name[0]) if(ExecutiveFindObjectByName(G, name)) { level = SettingGetGlobal_f(G, cSetting_roving_map2_level); sprintf(buffer, "cmd.isosurface('rov_s2','%s',%8.6f,'center',%1.3f)", name, level, isosurface); PParse(G, buffer); PFlush(G); refresh_flag = true; } name = SettingGet_s(G, NULL, NULL, cSetting_roving_map3_name); if(name) if(name[0]) if(ExecutiveFindObjectByName(G, name)) { level = SettingGetGlobal_f(G, cSetting_roving_map3_level); sprintf(buffer, "cmd.isosurface('rov_s3','%s',%8.6f,'center',%1.3f)", name, level, isosurface); PParse(G, buffer); PFlush(G); refresh_flag = true; } SettingSetGlobal_i(G, cSetting_auto_zoom, auto_save); } if(refresh_flag) { PParse(G, "cmd.refresh()"); PFlush(G); } I->RovingLastUpdate = UtilGetSeconds(G); I->RovingDirtyFlag = false; } } /*========================================================================*/ static int SceneDrag(Block * block, int x, int y, int mod, double when) { PyMOLGlobals *G = block->m_G; CScene *I = G->Scene; float scale, vScale; float v1[3], v2[3], n1[3], n2[3], r1, r2, cp[3], v3[3]; float dx, dy, dt; float axis[3], axis2[3], theta, omega; float old_front, old_back, old_origin; int mode; int eff_width; int moved_flag; int adjust_flag; int drag_handled = false; int virtual_trackball; CObject *obj; if(I->PossibleSingleClick) { double slowest_single_click_drag = 0.15; if((when - I->LastClickTime) > slowest_single_click_drag) { I->PossibleSingleClick = 0; } } if(I->LoopFlag) { return SceneLoopDrag(block, x, y, mod); } if(I->ButtonsShown && I->PressMode) { if(I->ButtonsValid) { SceneElem *elem = I->SceneVLA; int i; drag_handled = true; I->Over = -1; for(i = 0; i < I->NScene; i++) { if(elem->drawn && (x >= elem->x1) && (y >= elem->y1) && (x < elem->x2) && (y < elem->y2)) { I->Over = i; OrthoDirty(G); break; } elem++; } switch (I->PressMode) { case 2: if(I->Over >= 0) { if(I->Pressed != I->Over) { const char *cur_name = SettingGetGlobal_s(G, cSetting_scene_current_name); if(cur_name && elem->name && (strcmp(cur_name, elem->name))) { OrthoLineType buffer; int animate = -1; if(mod & cOrthoCTRL) animate = 0; sprintf(buffer, "cmd.scene('''%s''',animate=%d)", elem->name, animate); PParse(G, buffer); PFlush(G); PLog(G, buffer, cPLog_pym); } I->Pressed = I->Over; } } else { I->Pressed = -1; } case 3: if((I->Over >= 0) && (I->Pressed != I->Over)) I->PressMode = 4; /* activate dragging */ break; } if(I->PressMode == 4) { /* dragging */ if((I->Over >= 0) && (I->Pressed != I->Over) && (I->Pressed >= 0)) { SceneElem *pressed = I->SceneVLA + I->Pressed; OrthoLineType buffer; if(I->Over > 0) { /* not over the first scene in list */ SceneElem *first = elem - 1; SceneElem *second = pressed; if(first >= pressed) { first = elem; second = pressed; } sprintf(buffer, "cmd.scene_order('''%s %s''')", first->name, second->name); } else { sprintf(buffer, "cmd.scene_order('''%s''',location='top')", pressed->name); } PParse(G, buffer); PFlush(G); PLog(G, buffer, cPLog_pym); I->Pressed = I->Over; I->ButtonsValid = false; if(SettingGetGlobal_b(G, cSetting_scene_buttons)){ OrthoInvalidateDoDraw(G); } } } } } if(!drag_handled) { mode = ButModeTranslate(G, I->Button, mod); y = y - I->margin.bottom; scale = (float) I->Height; if(scale > I->Width) scale = (float) I->Width; scale = 0.45F * scale; SceneInvalidateCopy(G, false); SceneDontCopyNext(G); switch (mode) { case cButModePickAtom: obj = (CObject *) I->LastPicked.context.object; if(obj) switch (obj->type) { case cObjectGadget: { ObjectGadget *gad; gad = (ObjectGadget *) obj; ObjectGadgetGetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond, v1); vScale = SceneGetExactScreenVertexScale(G, v1); if(stereo_via_adjacent_array(I->StereoMode)) { x = get_stereo_x(x, &I->LastX, I->Width, NULL); } /* transform into model coodinate space */ switch (obj->Context) { case 1: { float divisor; divisor = (float) I->Width; if(I->Height < I->Width) divisor = (float) I->Height; v2[0] = (x - I->LastX) / divisor; v2[1] = (y - I->LastY) / divisor; v2[2] = 0; } break; default: case 0: v2[0] = (x - I->LastX) * vScale; v2[1] = (y - I->LastY) * vScale; v2[2] = 0; MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2); break; } add3f(v1, v2, v2); ObjectGadgetSetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond, v2); if (I->LastPicked.src.index){ // pick id on gadget is 1 for band (to change values), // 0 for everything else (to move it around) SceneChanged(G); // changing values, need to update gadget text } else { PyMOL_NeedRedisplay(G->PyMOL); // moving gadget, just re-draw } } break; } I->LastX = x; I->LastY = y; break; case cButModeRotDrag: eff_width = I->Width; if(stereo_via_adjacent_array(I->StereoMode)) { eff_width = I->Width / 2; x = get_stereo_x(x, &I->LastX, I->Width, NULL); } virtual_trackball = SettingGet_i(G, NULL, NULL, cSetting_virtual_trackball); if (virtual_trackball==2 && (!I->prev_no_z_rotation1 || !I->prev_no_z_rotation2)) { /* when virtual_trackball=2 and twisting, need to set v1,v2 relative to orig */ v2[0] = (float) (eff_width / 2) - I->orig_x_rotation; v2[1] = (float) (I->Height / 2) - I->orig_y_rotation; v1[0] = v2[0] - (float) (x - I->LastX); v1[1] = v2[1] - (float) (y - I->LastY); } else if(virtual_trackball==1){ v1[0] = (float) (eff_width / 2) - x; v1[1] = (float) (I->Height / 2) - y; v2[0] = (float) (eff_width / 2) - I->LastX; v2[1] = (float) (I->Height / 2) - I->LastY; } else { v1[0] = (float) (I->LastX) - x; v1[1] = (float) (I->LastY) - y; v2[0] = 0; v2[1] = 0; } r1 = (float) sqrt1f(v1[0] * v1[0] + v1[1] * v1[1]); r2 = (float) sqrt1f(v2[0] * v2[0] + v2[1] * v2[1]); { short r1lt, r2lt; if(virtual_trackball==2) { r1lt = I->prev_no_z_rotation1; r2lt = I->prev_no_z_rotation2; } else { r1lt = r1 < scale; r2lt = r2 < scale; I->prev_no_z_rotation1 = r1 < scale; I->prev_no_z_rotation2 = r2 < scale; I->orig_x_rotation = x; I->orig_y_rotation = y; } if(r1lt) { v1[2] = (float) sqrt1f(scale * scale - r1 * r1); } else { v1[2] = 0.0; } if(r2lt) { v2[2] = (float) sqrt1f(scale * scale - r2 * r2); } else { v2[2] = 0.0; } } normalize23f(v1, n1); normalize23f(v2, n2); cross_product3f(n1, n2, cp); theta = (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_scale) * 2 * 180 * asin(sqrt1f(cp[0] * cp[0] + cp[1] * cp[1] + cp[2] * cp[2])) / cPI); dx = (v1[0] - v2[0]); dy = (v1[1] - v2[1]); dt = (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_limit) * sqrt1f(dx * dx + dy * dy) / scale); if(theta > dt) theta = dt; normalize23f(cp, axis); axis[2] = -axis[2]; theta = theta / (1.0F + (float) fabs(axis[2])); /* transform into model coodinate space */ MatrixInvTransformC44fAs33f3f(I->RotMatrix, axis, v2); v1[0] = (float) (cPI * theta / 180.0); EditorDrag(G, NULL, -1, mode, SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, v3); I->LastX = x; I->LastY = y; break; case cButModeMovDrag: case cButModeMovDragZ: if(I->Threshold) { if((abs(x - I->ThresholdX) > I->Threshold) || (abs(y - I->ThresholdY) > I->Threshold)) { I->Threshold = 0; } } if(!I->Threshold) { copy3f(I->Origin, v1); vScale = SceneGetExactScreenVertexScale(G, v1); if(stereo_via_adjacent_array(I->StereoMode)) { x = get_stereo_x(x, &I->LastX, I->Width, NULL); } if(mode == cButModeMovDragZ) { v2[0] = 0; v2[1] = 0; v2[2] = -(y - I->LastY) * vScale; } else { v2[0] = (x - I->LastX) * vScale; v2[1] = (y - I->LastY) * vScale; v2[2] = 0; } v3[0] = 0.0F; v3[1] = 0.0F; v3[2] = 1.0F; /* transform into model coodinate space */ MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2); MatrixInvTransformC44fAs33f3f(I->RotMatrix, v3, v3); EditorDrag(G, NULL, -1, mode, SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, v3); } I->LastX = x; I->LastY = y; break; case cButModeRotObj: case cButModeMovObj: case cButModeMovObjZ: case cButModeRotView: case cButModeMovView: case cButModeMovViewZ: case cButModeRotFrag: case cButModeMovFrag: case cButModeMovFragZ: case cButModeTorFrag: case cButModeMoveAtom: case cButModeMoveAtomZ: case cButModePkTorBnd: obj = (CObject *) I->LastPicked.context.object; if(obj) { if(I->Threshold) { if((abs(x - I->ThresholdX) > I->Threshold) || (abs(y - I->ThresholdY) > I->Threshold)) { I->Threshold = 0; } } if(!I->Threshold) switch (obj->type) { case cObjectGadget: /* note repeated above */ { ObjectGadget *gad; gad = (ObjectGadget *) obj; ObjectGadgetGetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond, v1); vScale = SceneGetExactScreenVertexScale(G, v1); if(stereo_via_adjacent_array(I->StereoMode)) { x = get_stereo_x(x, &I->LastX, I->Width, NULL); } /* transform into model coodinate space */ switch (obj->Context) { case 1: { float divisor; divisor = (float) I->Width; if(I->Height < I->Width) divisor = (float) I->Height; v2[0] = (x - I->LastX) / divisor; v2[1] = (y - I->LastY) / divisor; v2[2] = 0; } break; default: case 0: v2[0] = (x - I->LastX) * vScale; v2[1] = (y - I->LastY) * vScale; v2[2] = 0; MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2); break; } add3f(v1, v2, v2); ObjectGadgetSetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond, v2); if (I->LastPicked.src.index){ // pick id on gadget is 1 for band (to change values), // 0 for everything else (to move it around) SceneChanged(G); // changing values, need to update gadget text } else { PyMOL_NeedRedisplay(G->PyMOL); // moving gadget, just re-draw } } break; case cObjectMolecule: if(ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *) obj, I->LastPicked.context.state, I->LastPicked.src.index, v1)) { /* scale properly given the current projection matrix */ vScale = SceneGetExactScreenVertexScale(G, v1); if(stereo_via_adjacent_array(I->StereoMode)) { x = get_stereo_x(x, &I->LastX, I->Width, NULL); } switch (mode) { case cButModeMovFragZ: case cButModeMovObjZ: case cButModeMovViewZ: case cButModeMoveAtomZ: v2[0] = 0; v2[1] = 0; v2[2] = -(y - I->LastY) * vScale; break; default: v2[0] = (x - I->LastX) * vScale; v2[1] = (y - I->LastY) * vScale; v2[2] = 0; break; } v3[0] = 0.0F; v3[1] = 0.0F; v3[2] = 1.0F; /* transform into model coodinate space */ MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2); MatrixInvTransformC44fAs33f3f(I->RotMatrix, v3, v3); if(I->LastPicked.src.bond >= cPickableAtom) { if((mode != cButModeMoveAtom) && (mode != cButModeMoveAtomZ)) { EditorDrag(G, obj, I->LastPicked.src.index, mode, SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, v3); switch(mode) { case cButModeMovViewZ: case cButModeRotView: case cButModeMovView: if(SettingGetGlobal_i(G,cSetting_movie_auto_store) && SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) { I->ReinterpolateFlag = true; I->ReinterpolateObj = obj; } break; } } else { int log_trans = SettingGetGlobal_b(G, cSetting_log_conformations); ObjectMolecule *objOM = (ObjectMolecule *) obj; ObjectMoleculeMoveAtom(objOM, I->LastPicked.context.state, I->LastPicked.src.index, v2, 1, log_trans); /* -- JV - if this object knows about distances, then move them if necessary */ /* check the dynamic_measures setting and make sure the object has a distance measure, first */ /* obviated by new method if (SettingGetGlobal_i(G, cSetting_dynamic_measures)) ObjectMoleculeMoveDist( (ObjectMolecule *) obj, SettingGetGlobal_i(G, cSetting_state)-1, I->LastPicked.src.index, v2, 1, log_trans); */ SceneInvalidate(G); } } else { int log_trans = SettingGetGlobal_b(G, cSetting_log_conformations); ObjectMolecule *objOM = (ObjectMolecule *) obj; float diffInPixels[2]; diffInPixels[0] = (x - I->LastX); diffInPixels[1] = (y - I->LastY); ObjectMoleculeMoveAtomLabel(objOM, I->LastPicked.context.state, I->LastPicked.src.index, v2, log_trans, diffInPixels); SceneInvalidate(G); } } break; case cObjectSlice: { ObjectSlice *slice = (ObjectSlice *) obj; if(I->LastPickVertexFlag) { copy3f(I->LastPickVertex, v1); vScale = SceneGetExactScreenVertexScale(G, v1); if(stereo_via_adjacent_array(I->StereoMode)) { x = get_stereo_x(x, &I->LastX, I->Width, NULL); } v2[0] = (x - I->LastX) * vScale; v2[1] = (y - I->LastY) * vScale; v2[2] = 0; v3[0] = 0.0F; v3[1] = 0.0F; v3[2] = 1.0F; /* transform into model coodinate space */ MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2); MatrixInvTransformC44fAs33f3f(I->RotMatrix, v3, v3); ObjectSliceDrag(slice, SceneGetState(G), mode, v1, v2, v3); } } break; case cObjectMeasurement: if(ObjectDistGetLabelTxfVertex((ObjectDist *) obj, I->LastPicked.context.state, I->LastPicked.src.index, v1)) { /* scale properly given the current projection matrix */ vScale = SceneGetExactScreenVertexScale(G, v1); if(stereo_via_adjacent_array(I->StereoMode)) { x = get_stereo_x(x, &I->LastX, I->Width, NULL); } switch (mode) { case cButModeMovFragZ: case cButModeMovObjZ: case cButModeMovViewZ: case cButModeMoveAtomZ: v2[0] = 0; v2[1] = 0; v2[2] = -(y - I->LastY) * vScale; break; default: v2[0] = (x - I->LastX) * vScale; v2[1] = (y - I->LastY) * vScale; v2[2] = 0; break; } v3[0] = 0.0F; v3[1] = 0.0F; v3[2] = 1.0F; /* transform into model coodinate space */ MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2); MatrixInvTransformC44fAs33f3f(I->RotMatrix, v3, v3); if(I->LastPicked.src.bond == cPickableLabel) { int log_trans = SettingGetGlobal_b(G, cSetting_log_conformations); ObjectDistMoveLabel((ObjectDist *) obj, I->LastPicked.context.state, I->LastPicked.src.index, v2, 1, log_trans); SceneInvalidate(G); } } break; default: break; } } I->LastX = x; I->LastY = y; break; case cButModeTransXY: SceneNoteMouseInteraction(G); vScale = SceneGetExactScreenVertexScale(G, I->Origin); if(stereo_via_adjacent_array(I->StereoMode)) { x = get_stereo_x(x, &I->LastX, I->Width, NULL); } v2[0] = (x - I->LastX) * vScale; v2[1] = (y - I->LastY) * vScale; v2[2] = 0.0F; moved_flag = false; if(I->LastX != x) { I->Pos[0] += v2[0]; I->LastX = x; SceneInvalidate(G); moved_flag = true; } if(I->LastY != y) { I->Pos[1] += v2[1]; I->LastY = y; SceneInvalidate(G); moved_flag = true; } EditorFavorOrigin(G, NULL); if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_origin)) { SceneGetCenter(G, v2); /* gets position of center of screen */ SceneOriginSet(G, v2, true); } if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_detail)) { SceneRovingDirty(G); } break; case cButModeRotXYZ: case cButModeRotZ: case cButModeInvRotZ: case cButModeTransZ: case cButModeClipNF: case cButModeClipN: case cButModeClipF: case cButModeRotL: case cButModeMovL: case cButModeMvzL: SceneNoteMouseInteraction(G); virtual_trackball = SettingGet_i(G, NULL, NULL, cSetting_virtual_trackball); eff_width = I->Width; if(stereo_via_adjacent_array(I->StereoMode)) { eff_width = I->Width / 2; x = get_stereo_x(x, &I->LastX, I->Width, NULL); } if (virtual_trackball==2 && (!I->prev_no_z_rotation1 || !I->prev_no_z_rotation2)) { /* when virtual_trackball=2 and twisting, need to set v1,v2 relative to orig */ v2[0] = (float) (eff_width / 2) - I->orig_x_rotation; v2[1] = (float) (I->Height / 2) - I->orig_y_rotation; v1[0] = v2[0] - (float) (x - I->LastX); v1[1] = v2[1] - (float) (y - I->LastY); } else if(virtual_trackball==1){ v1[0] = (float) (eff_width / 2) - x; v1[1] = (float) (I->Height / 2) - y; v2[0] = (float) (eff_width / 2) - I->LastX; v2[1] = (float) (I->Height / 2) - I->LastY; } else { v1[0] = (float) (I->LastX) - x; v1[1] = (float) (I->LastY) - y; v2[0] = 0; v2[1] = 0; } r1 = (float) sqrt1f(v1[0] * v1[0] + v1[1] * v1[1]); r2 = (float) sqrt1f(v2[0] * v2[0] + v2[1] * v2[1]); { short r1lt, r2lt; if(SettingGet_i(G, NULL, NULL, cSetting_virtual_trackball)==2) { r1lt = I->prev_no_z_rotation1; r2lt = I->prev_no_z_rotation2; } else { r1lt = r1 < scale; r2lt = r2 < scale; I->prev_no_z_rotation1 = r1 < scale; I->prev_no_z_rotation2 = r2 < scale; I->orig_x_rotation = x; I->orig_y_rotation = y; } if(r1lt) { float val = scale * scale - r1 * r1; short isNeg = val < 0.f; v1[2] = (float) sqrt(fabs(val)) * (isNeg ? -1.f : 1.f); } else { v1[2] = 0.0; } if(r2lt) { float val = scale * scale - r2 * r2; short isNeg = val < 0.f; v2[2] = (float) sqrt(fabs(val)) * (isNeg ? -1.f : 1.f); } else { v2[2] = 0.0; } } normalize23f(v1, n1); normalize23f(v2, n2); cross_product3f(n1, n2, cp); theta = (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_scale) * 2 * 180 * asin(sqrt1f(cp[0] * cp[0] + cp[1] * cp[1] + cp[2] * cp[2])) / cPI); dx = (v1[0] - v2[0]); dy = (v1[1] - v2[1]); dt = (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_limit) * sqrt1f(dx * dx + dy * dy) / scale); if(theta > dt) theta = dt; normalize23f(cp, axis); theta = theta / (1.0F + (float) fabs(axis[2])); v1[2] = 0.0; v2[2] = 0.0; normalize23f(v1, n1); normalize23f(v2, n2); cross_product3f(n1, n2, cp); omega = (float) (2 * 180 * asin(sqrt1f(cp[0] * cp[0] + cp[1] * cp[1] + cp[2] * cp[2])) / cPI); normalize23f(cp, axis2); old_front = I->Front; old_back = I->Back; old_origin = -I->Pos[2]; moved_flag = false; adjust_flag = false; switch (mode) { case cButModeRotXYZ: if(I->LastX != x) { SceneRotate(G, theta, axis[0], axis[1], -axis[2]); I->LastX = x; adjust_flag = true; } if(I->LastY != y) { SceneRotate(G, theta, axis[0], axis[1], -axis[2]); I->LastY = y; adjust_flag = true; } break; case cButModeRotZ: if(I->LastX != x) { SceneRotate(G, omega, axis2[0], axis2[1], -axis2[2]); I->LastX = x; adjust_flag = true; } if(I->LastY != y) { SceneRotate(G, omega, axis2[0], axis2[1], -axis2[2]); I->LastY = y; adjust_flag = true; } break; case cButModeInvRotZ: if(I->LastX != x) { SceneRotate(G, (I->LastX - x) / 2.0F, 0.0F, 0.0F, 1.0F); I->LastX = x; adjust_flag = true; } break; case cButModeTransZ: // zoom if(I->LastY != y) { { // the 5.0 min depth below is an empirical value float factor = SettingGetGlobal_f(G, cSetting_mouse_z_scale) * (y - I->LastY) / 400.0 * std::max(5.f, -I->Pos[2]); if(!SettingGetGlobal_b(G, cSetting_legacy_mouse_zoom)) factor = -factor; I->Pos[2] += factor; I->Front -= factor; I->Back -= factor; UpdateFrontBackSafe(I); } I->LastY = y; SceneInvalidate(G); adjust_flag = true; } break; case cButModeClipNF: if(I->LastX != x) { I->Back -= (((float) x) - I->LastX) / 10; I->LastX = x; moved_flag = true; } if(I->LastY != y) { I->Front -= (((float) y) - I->LastY) / 10; I->LastY = y; moved_flag = true; } if(moved_flag) { SceneClipSet(G, I->Front, I->Back); } break; case cButModeClipN: if(I->LastX != x || I->LastY != y) { I->Front -= (x - I->LastX + y - I->LastY) / 10.f; I->LastX = x; I->LastY = y; SceneClipSet(G, I->Front, I->Back); moved_flag = true; } break; case cButModeClipF: if(I->LastX != x || I->LastY != y) { I->Back -= (x - I->LastX + y - I->LastY) / 10.f; I->LastX = x; I->LastY = y; SceneClipSet(G, I->Front, I->Back); moved_flag = true; } break; case cButModeRotL: case cButModeMovL: { /* when light_count == 1, there is an ambient light; * when light_count == 2, there are two lights, the ambient and * a directional, called "light". When there are three, it's the * ambient, light and the third is light2, and so on. * * Should we use an off-by-one here to make this easier for the * user to understand? light1 is ambient, light2 is first directional * */ float pos[3]; int which_light; float ms = 0.01; which_light = light_setting_indices[glm::clamp( SettingGetGlobal_i(G, cSetting_edit_light), 1, 9) - 1]; copy3f(SettingGet<const float *>(G, which_light), pos); pos[0] += (float) dx * ms; pos[1] += (float) dy * ms; SettingSet_3fv(G->Setting, which_light, pos); SettingGenerateSideEffects(G, which_light, NULL, 0, 1); I->LastX = x; I->LastY = y; } break; case cButModeMvzL: { float pos[3]; int which_light; float ms = 0.01; float factor = 0.f; /* when light_count == 1, there is an ambient light; * when light_count == 2, there are two lights, the ambient and * a directional, called "light". When there are three, it's the * ambient, light and the third is light2, and so on. */ which_light = light_setting_indices[glm::clamp( SettingGetGlobal_i(G, cSetting_edit_light), 1, 9) - 1]; if(I->LastY != y) { factor = 400 / ((I->FrontSafe + I->BackSafe) / 2); if(factor >= 0.0F) { factor = SettingGetGlobal_f(G, cSetting_mouse_z_scale) * (((float) y) - I->LastY) / factor; if(!SettingGetGlobal_b(G, cSetting_legacy_mouse_zoom)) factor = -factor; } } copy3f(SettingGet<const float *>(G, which_light), pos); SettingGenerateSideEffects(G, which_light, NULL, 0, 1); pos[2] -= factor * ms; SettingSet_3fv(G->Setting, which_light, pos); I->LastX = x; I->LastY = y; } break; } if(moved_flag) SceneDoRoving(G, old_front, old_back, old_origin, adjust_flag, false); } } if(I->PossibleSingleClick) { int max_single_click_drag = 4; int dx = abs(I->StartX - I->LastX); int dy = abs(I->StartY - I->LastY); if((dx > max_single_click_drag) || (dy > max_single_click_drag)) { I->PossibleSingleClick = false; } } return (1); } static int SceneDeferredClick(DeferredMouse * dm) { if(!SceneClick(dm->block, dm->button, dm->x, dm->y, dm->mod, dm->when)) { } return 1; } /* * Will call cmd.raw_image_callback(img) with the current RGBA image, copied * to a WxHx4 numpy array. Return false if no callback is defined * (cmd.raw_image_callback == None). */ static bool call_raw_image_callback(PyMOLGlobals * G) { bool done = false; #ifndef _PYMOL_NOPY int blocked = PAutoBlock(G); auto raw_image_callback = PyObject_GetAttrString(G->P_inst->cmd, "raw_image_callback"); if (raw_image_callback != Py_None) { #ifdef _PYMOL_NUMPY auto& image = G->Scene->Image; // RGBA image as uint8 numpy array import_array1(0); npy_intp dims[3] = {image->width, image->height, 4}; auto py = PyArray_SimpleNew(3, dims, NPY_UINT8); memcpy(PyArray_DATA((PyArrayObject *)py), image->data, dims[0] * dims[1] * 4); PYOBJECT_CALLFUNCTION(raw_image_callback, "O", py); Py_DECREF(py); done = true; #else PRINTFB(G, FB_Scene, FB_Errors) " raw_image_callback-Error: no numpy support\n" ENDFB(G); #endif } Py_XDECREF(raw_image_callback); PAutoUnblock(G, blocked); #endif return done; } static int SceneDeferredImage(DeferredImage * di) { PyMOLGlobals *G = di->G; SceneMakeSizedImage(G, di->width, di->height, di->antialias); if(!di->filename.empty()) { ScenePNG(G, di->filename.c_str(), di->dpi, di->quiet, false, di->format); } else if(call_raw_image_callback(G)) { } else if(G->HaveGUI && SettingGetGlobal_b(G, cSetting_auto_copy_images)) { #ifdef _PYMOL_IP_EXTRAS if(IncentiveCopyToClipboard(G, di->quiet)) { } #else #ifdef PYMOL_EVAL PRINTFB(G, FB_Scene, FB_Warnings) " Warning: Clipboard image transfers disabled in Evaluation builds.\n" ENDFB(G); #endif #endif } return 1; } int SceneDeferImage(PyMOLGlobals * G, int width, int height, const char *filename, int antialias, float dpi, int format, int quiet) { auto di = pymol::make_unique<DeferredImage>(G); if(di) { di->width = width; di->height = height; di->antialias = antialias; di->fn = (DeferredFn *) SceneDeferredImage; di->dpi = dpi; di->format = format; di->quiet = quiet; if(filename){ di->filename = filename; } } OrthoDefer(G, std::move(di)); return 1; } int CScene::click(int button, int x, int y, int mod) // Originally SceneDeferClick!! { return SceneDeferClickWhen(this, button, x, y, UtilGetSeconds(m_G), mod); } static int SceneDeferClickWhen(Block * block, int button, int x, int y, double when, int mod) { PyMOLGlobals *G = block->m_G; auto dm = pymol::make_unique<DeferredMouse>(G); if(dm) { dm->block = block; dm->button = button; dm->x = x; dm->y = y; dm->when = when; dm->mod = mod; dm->fn = (DeferredFn *) SceneDeferredClick; } OrthoDefer(G, std::move(dm)); return 1; } static int SceneDeferredDrag(DeferredMouse * dm) { SceneDrag(dm->block, dm->x, dm->y, dm->mod, dm->when); return 1; } int CScene::drag(int x, int y, int mod) //Originally SceneDeferDrag { PyMOLGlobals *G = m_G; auto dm = pymol::make_unique<DeferredMouse>(G); if(dm) { dm->block = this; dm->x = x; dm->y = y; dm->mod = mod; dm->when = UtilGetSeconds(G); dm->fn = (DeferredFn *) SceneDeferredDrag; } OrthoDefer(G, std::move(dm)); return 1; } static int SceneDeferredRelease(DeferredMouse * dm) { SceneRelease(dm->block, dm->button, dm->x, dm->y, dm->mod, dm->when); return 1; } int CScene::release(int button, int x, int y, int mod) // Originally SceneDeferRelease { PyMOLGlobals *G = m_G; auto dm = pymol::make_unique<DeferredMouse>(G); if(dm) { dm->block = this; dm->button = button; dm->x = x; dm->y = y; dm->mod = mod; dm->when = UtilGetSeconds(G); dm->fn = (DeferredFn *) SceneDeferredRelease; } OrthoDefer(G, std::move(dm)); return 1; } /*========================================================================*/ void SceneFree(PyMOLGlobals * G) { CScene *I = G->Scene; #if !defined(PURE_OPENGL_ES_2) || defined(_WEBGL) CGOFree(I->offscreenCGO); #endif CGOFree(I->AlphaCGO); CGOFree(I->offscreenCGO); CGOFree(I->offscreenOIT_CGO); CGOFree(I->offscreenOIT_CGO_copy); VLAFreeP(I->SceneVLA); VLAFreeP(I->SceneNameVLA); VLAFreeP(I->SlotVLA); I->Obj.clear(); I->GadgetObjs.clear(); I->NonGadgetObjs.clear(); ScenePurgeImage(G); CGOFree(G->DebugCGO); delete G->Scene; } /*========================================================================*/ void SceneResetMatrix(PyMOLGlobals * G) { CScene *I = G->Scene; identity44f(I->RotMatrix); SceneUpdateInvMatrix(G); } /*========================================================================*/ void SceneSetDefaultView(PyMOLGlobals * G) { CScene *I = G->Scene; identity44f(I->RotMatrix); SceneUpdateInvMatrix(G); I->ViewNormal[0] = 0.0F; I->ViewNormal[1] = 0.0F; I->ViewNormal[2] = 1.0F; I->Pos[0] = 0.0F; I->Pos[1] = 0.0F; I->Pos[2] = -50.0F; I->Origin[0] = 0.0F; I->Origin[1] = 0.0F; I->Origin[2] = 0.0F; I->Front = 40.0F; I->Back = 100.0F; UpdateFrontBackSafe(I); I->Scale = 1.0F; } int SceneReinitialize(PyMOLGlobals * G) { int ok = true; SceneSetDefaultView(G); SceneCountFrames(G); SceneSetFrame(G, 0, 0); SceneInvalidate(G); G->Scene->NScene = 0; return (ok); } /*========================================================================*/ int SceneInit(PyMOLGlobals * G) { CScene *I = NULL; I = (G->Scene = new CScene(G)); if(I) { /* all defaults to zero, so only initialize non-zero elements */ G->DebugCGO = CGONew(G); I->TextColor[0] = 0.2F; I->TextColor[1] = 1.0F; I->TextColor[2] = 0.2F; I->LastClickTime = UtilGetSeconds(G); SceneSetDefaultView(G); I->Scale = 1.0; I->active = true; OrthoAttach(G, I, cOrthoScene); I->DirtyFlag = true; I->LastRender = UtilGetSeconds(G); I->LastFrameTime = UtilGetSeconds(G); I->LastSweepTime = UtilGetSeconds(G); I->LastStateBuilt = -1; I->CopyNextFlag = true; I->invPick = true; SceneRestartFrameTimer(G); SceneRestartPerfTimer(G); I->Width = 640; /* standard defaults */ I->Height = 480; I->VertexScale = 0.01F; /* scene list */ I->Pressed = -1; I->Over = -1; I->SceneNameVLA = VLAlloc(char, 10); I->SceneVLA = VLAlloc(SceneElem, 10); return 1; } else return 0; } /*========================================================================*/ void CScene::reshape(int width, int height) { PyMOLGlobals *G = m_G; CScene *I = G->Scene; if(I->margin.right) { width -= I->margin.right; if(width < 1) width = 1; } if(I->margin.top) { height -= I->margin.top; } I->Width = width; I->Height = height; I->rect.top = I->Height; I->rect.left = 0; I->rect.bottom = 0; I->rect.right = I->Width; if(I->margin.bottom) { height -= I->margin.bottom; if(height < 1) height = 1; I->Height = height; I->rect.bottom = I->rect.top - I->Height; } SceneDirty(G); if(I->CopyType && (!I->CopyForced)) { SceneInvalidateCopy(G, false); } /*MovieClearImages(G); */ MovieSetSize(G, I->Width, I->Height); SceneInvalidateStencil(G); } /*========================================================================*/ void SceneDone(PyMOLGlobals * G) { CScene *I = G->Scene; } /*========================================================================*/ void SceneResetNormal(PyMOLGlobals * G, int lines) { CScene *I = G->Scene; if(G->HaveGUI && G->ValidContext) { if(lines) glNormal3fv(I->LinesNormal); else glNormal3fv(I->ViewNormal); } } void SceneResetNormalCGO(PyMOLGlobals * G, CGO *cgo, int lines) { CScene *I = G->Scene; if(G->HaveGUI && G->ValidContext) { if(lines) CGONormalv(cgo, I->LinesNormal); else CGONormalv(cgo, I->ViewNormal); } } void SceneResetNormalToViewVector(PyMOLGlobals * G, short use_shader) { CScene *I = G->Scene; if(G->HaveGUI && G->ValidContext) { #if defined(PURE_OPENGL_ES_2) glVertexAttrib3f(VERTEX_NORMAL, I->ModMatrix[2], I->ModMatrix[6], I->ModMatrix[10]); #else if (use_shader){ glVertexAttrib3f(VERTEX_NORMAL, I->ModMatrix[2], I->ModMatrix[6], I->ModMatrix[10]); } else { glNormal3f(I->ModMatrix[2], I->ModMatrix[6], I->ModMatrix[10]); } #endif } } void SceneResetNormalUseShader(PyMOLGlobals * G, int lines, short use_shader) { CScene *I = G->Scene; if(G->HaveGUI && G->ValidContext) { #ifdef PURE_OPENGL_ES_2 if(lines) glVertexAttrib3fv(VERTEX_NORMAL, I->LinesNormal); else glVertexAttrib3fv(VERTEX_NORMAL, I->ViewNormal); #else if (use_shader){ if(lines) glVertexAttrib3fv(VERTEX_NORMAL, I->LinesNormal); else glVertexAttrib3fv(VERTEX_NORMAL, I->ViewNormal); } else { if(lines) glNormal3fv(I->LinesNormal); else glNormal3fv(I->ViewNormal); } #endif } } void SceneResetNormalUseShaderAttribute(PyMOLGlobals * G, int lines, short use_shader, int attr) { CScene *I = G->Scene; if(G->HaveGUI && G->ValidContext) { #ifdef PURE_OPENGL_ES_2 if (attr < 0) return; if(lines) glVertexAttrib3fv(attr, I->LinesNormal); else glVertexAttrib3fv(attr, I->ViewNormal); #else if (use_shader){ if(lines) glVertexAttrib3fv(attr, I->LinesNormal); else glVertexAttrib3fv(attr, I->ViewNormal); } else { if(lines) glNormal3fv(I->LinesNormal); else glNormal3fv(I->ViewNormal); } #endif } } void SceneGetResetNormal(PyMOLGlobals * G, float *normal, int lines) { CScene *I = G->Scene; float *norm; if(G->HaveGUI && G->ValidContext) { if(lines) norm = I->LinesNormal; else norm = I->ViewNormal; normal[0] = norm[0]; normal[1] = norm[1]; normal[2] = norm[2]; } } /*========================================================================*/ void SceneApplyImageGamma(PyMOLGlobals * G, unsigned int *buffer, int width, int height) { float gamma = SettingGetGlobal_f(G, cSetting_gamma); if(gamma > R_SMALL4) gamma = 1.0F / gamma; else gamma = 1.0F; if(buffer && height && width) { float _inv3 = 1 / (255 * 3.0F); float _1 = 1 / 3.0F; unsigned char *p; int x, y; float c1, c2, c3, inp, sig; unsigned int i1, i2, i3; p = (unsigned char *) buffer; for(y = 0; y < height; y++) { for(x = 0; x < width; x++) { c1 = p[0]; c2 = p[1]; c3 = p[2]; inp = (c1 + c2 + c3) * _inv3; if(inp < R_SMALL4) sig = _1; else sig = (float) (pow(inp, gamma) / inp); i1 = (unsigned int) (sig * c1); i2 = (unsigned int) (sig * c2); i3 = (unsigned int) (sig * c3); if(i1 > 255) i1 = 255; if(i2 > 255) i2 = 255; if(i3 > 255) i3 = 255; p[0] = i1; p[1] = i2; p[2] = i3; p += 4; } } } } void SceneUpdateAnimation(PyMOLGlobals * G) { CScene *I = G->Scene; int rockFlag = false; int dirty = false; int movie_rock = SettingGetGlobal_i(G, cSetting_movie_rock); if(movie_rock < 0) movie_rock = ControlRocking(G); if(MoviePlaying(G) && movie_rock) { if(MovieGetRealtime(G) && !SettingGetGlobal_b(G, cSetting_movie_animate_by_frame)) { I->RenderTime = UtilGetSeconds(G) - I->LastSweepTime; rockFlag = true; dirty = true; /* force a subsequent update */ } else { float fps = SceneGetFPS(G); /* guaranteed to be >= 0.0F */ if(fps > 0.0F) { int rock_frame = SceneGetFrame(G); if(rock_frame != I->RockFrame) { I->RockFrame = rock_frame; rockFlag = true; I->RenderTime = 1.0 / fps; } } else { I->RenderTime = UtilGetSeconds(G) - I->LastSweepTime; rockFlag = true; } } } else dirty = true; if(I->cur_ani_elem < I->n_ani_elem) { /* play motion animation */ double now; int cur = I->cur_ani_elem; if(I->AnimationStartFlag) { /* allow animation timing to lag since it may take a few seconds to get here given geometry updates, etc. */ I->AnimationLagTime = UtilGetSeconds(G) - I->AnimationStartTime; I->AnimationStartFlag = false; } if((!MoviePlaying(G)) || ((MovieGetRealtime(G) && !SettingGetGlobal_b(G, cSetting_movie_animate_by_frame)))) { now = UtilGetSeconds(G) - I->AnimationLagTime; } else { float fps = SceneGetFPS(G); /* guaranteed to be >= 0.0F */ int frame = SceneGetFrame(G); int n_frame = 0; cur = 0; /* allow backwards interpolation */ if(frame >= I->AnimationStartFrame) { n_frame = frame - I->AnimationStartFrame; } else { n_frame = frame + (I->NFrame - I->AnimationStartFrame); } now = I->AnimationStartTime + n_frame / fps; } while(I->ani_elem[cur].timing < now) { cur++; if(cur >= I->n_ani_elem) { cur = I->n_ani_elem; break; } } I->cur_ani_elem = cur; SceneFromViewElem(G, I->ani_elem + cur, dirty); OrthoDirty(G); } if(rockFlag && (I->RenderTime != 0.0)) { SceneUpdateCameraRock(G, dirty); } } int SceneGetDrawFlag(GridInfo * grid, int *slot_vla, int slot) { int draw_flag = false; if(grid && grid->active) { switch (grid->mode) { case 1: /* assigned grid slots (usually by group) */ { if(((slot < 0) && grid->slot) || ((slot == 0) && (grid->slot == 0)) || (slot_vla && (slot_vla[slot] == grid->slot))) { draw_flag = true; } } break; case 2: /* each state in a separate slot */ case 3: /* each object-state */ draw_flag = true; break; } } else { draw_flag = true; } return draw_flag; } int SceneGetDrawFlagGrid(PyMOLGlobals * G, GridInfo * grid, int slot) { CScene *I = G->Scene; return SceneGetDrawFlag(grid, I->SlotVLA, slot); } /*========================================================================*/ void SceneCopy(PyMOLGlobals * G, GLenum buffer, int force, int entire_window) { CScene *I = G->Scene; unsigned int buffer_size; if (buffer == GL_BACK) { buffer = G->DRAW_BUFFER0; } if(force || (!(I->StereoMode || SettingGetGlobal_b(G, cSetting_stereo_double_pump_mono) || I->ButtonsShown))) { /* no copies while in stereo mode */ if(force || ((!I->DirtyFlag) && (!I->CopyType))) { int x, y, w, h; if(entire_window) { x = 0; y = 0; h = OrthoGetHeight(G); w = OrthoGetWidth(G); } else { x = I->rect.left; y = I->rect.bottom; w = I->Width; h = I->Height; } ScenePurgeImage(G); buffer_size = 4 * w * h; if(buffer_size) { I->Image = Calloc(ImageType, 1); I->Image->data = Alloc(unsigned char, buffer_size); I->Image->size = buffer_size; I->Image->width = w; I->Image->height = h; if(G->HaveGUI && G->ValidContext) { glReadBuffer(buffer); PyMOLReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, I->Image->data); } } I->CopyType = true; I->Image->needs_alpha_reset = true; I->CopyForced = force; } } } /*========================================================================*/ int SceneRovingCheckDirty(PyMOLGlobals * G) { CScene *I = G->Scene; return (I->RovingDirtyFlag); } struct _CObjectUpdateThreadInfo { CObject *obj; }; void SceneObjectUpdateThread(CObjectUpdateThreadInfo * T) { if(T->obj && T->obj->fUpdate) { T->obj->fUpdate(T->obj); } } #ifndef _PYMOL_NOPY static void SceneObjectUpdateSpawn(PyMOLGlobals * G, CObjectUpdateThreadInfo * Thread, int n_thread, int n_total) { if(n_total == 1) { SceneObjectUpdateThread(Thread); } else if(n_total) { int blocked; PyObject *info_list; int a, n = 0; blocked = PAutoBlock(G); PRINTFB(G, FB_Scene, FB_Blather) " Scene: updating objects with %d threads...\n", n_thread ENDFB(G); info_list = PyList_New(n_total); for(a = 0; a < n_total; a++) { PyList_SetItem(info_list, a, PyCObject_FromVoidPtr(Thread + a, NULL)); n++; } PXDecRef(PYOBJECT_CALLMETHOD (G->P_inst->cmd, "_object_update_spawn", "Oi", info_list, n_thread)); Py_DECREF(info_list); PAutoUnblock(G, blocked); } } #endif static void SceneStencilCheck(PyMOLGlobals *G) { CScene *I = G->Scene; if( I->StereoMode == cStereo_stencil_by_row ) { int bottom = 0; #ifndef _PYMOL_PRETEND_GLUT if(G->Main) #endif bottom = p_glutGet(P_GLUT_WINDOW_Y) + p_glutGet(P_GLUT_WINDOW_HEIGHT); int parity = bottom & 0x1; if(parity != I->StencilParity) { I->StencilValid = false; I->StencilParity = parity; SceneDirty(G); } } } /*========================================================================*/ void SceneUpdate(PyMOLGlobals * G, int force) { CScene *I = G->Scene; int cur_state = SettingGetGlobal_i(G, cSetting_state) - 1; int defer_builds_mode = SettingGetGlobal_i(G, cSetting_defer_builds_mode); PRINTFD(G, FB_Scene) " SceneUpdate: entered.\n" ENDFD; OrthoBusyPrime(G); WizardDoPosition(G, false); WizardDoView(G, false); EditorUpdate(G); SceneStencilCheck(G); if(defer_builds_mode == 0) { if(SettingGetGlobal_i(G, cSetting_draw_mode) == -2) { defer_builds_mode = 1; } } if(force || I->ChangedFlag || ((cur_state != I->LastStateBuilt) && (defer_builds_mode > 0))) { SceneCountFrames(G); if(force || (defer_builds_mode != 5)) { /* mode 5 == immediate mode */ PyMOL_SetBusy(G->PyMOL, true); /* race condition -- may need to be fixed */ /* update all gadgets first (single-threaded since they're thread-unsafe) */ for ( auto it = I->GadgetObjs.begin(); it != I->GadgetObjs.end(); ++it) { (*it)->update(); } { #ifndef _PYMOL_NOPY int n_thread = SettingGetGlobal_i(G, cSetting_max_threads); int multithread = SettingGetGlobal_i(G, cSetting_async_builds); if(multithread && (n_thread > 1)) { int min_start = -1; int max_stop = -1; int n_frame = SceneGetNFrame(G, NULL); int n_obj = 0; for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) { int start = 0; int stop = n_frame; n_obj++; if((*it)->fGetNFrame) { stop = (*it)->fGetNFrame(*it); } /* set start/stop to define the range for this object * depending upon various build settings */ ObjectAdjustStateRebuildRange(*it, &start, &stop); if(min_start < 0) { min_start = start; max_stop = stop; } else { if(min_start > start) min_start = start; if(max_stop < stop) max_stop = stop; } } n_frame = max_stop - min_start; if(n_frame > n_thread) { n_thread = 1; /* prevent n_thread * n_thread -- only multithread within individual object states (typically more balanced) */ } else if(n_frame > 1) { n_thread = n_thread / n_frame; } if(n_thread < 1) n_thread = 1; } /* Note: we might want to optimize this by doing multi-threaded updates for all objects. */ if(multithread && (n_thread > 1)) { /* multi-threaded geometry update */ int cnt = I->NonGadgetObjs.size(); if(cnt) { CObjectUpdateThreadInfo *thread_info = Alloc(CObjectUpdateThreadInfo, cnt); if(thread_info) { cnt = 0; for ( auto it = I->NonGadgetObjs.begin(); it != I->NonGadgetObjs.end(); ++it) { thread_info[cnt++].obj = *it; } SceneObjectUpdateSpawn(G, thread_info, n_thread, cnt); FreeP(thread_info); } } } else #endif /* single-threaded update */ for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) { (*it)->update(); } } PyMOL_SetBusy(G->PyMOL, false); /* race condition -- may need to be fixed */ } else { /* defer builds mode == 5 -- for now, only update non-molecular objects */ /* single-threaded update */ for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) { if((*it)->type != cObjectMolecule) { (*it)->update(); } } } I->ChangedFlag = false; if((defer_builds_mode >= 2) && (force || (defer_builds_mode != 5)) && (cur_state != I->LastStateBuilt)) { /* purge graphics representation when no longer used */ if(I->LastStateBuilt >= 0) { for ( auto it = I->Obj.begin(); it != I->Obj.end(); ++it) { if((*it)->fInvalidate && (((*it)->type != cObjectMolecule) || force || defer_builds_mode != 5)) { int static_singletons = SettingGet_b(G, (*it)->Setting, NULL, cSetting_static_singletons); int async_builds = SettingGet_b(G, (*it)->Setting, NULL, cSetting_async_builds); int max_threads = SettingGet_i(G, (*it)->Setting, NULL, cSetting_max_threads); int nFrame = 0; if((*it)->fGetNFrame) nFrame = (*it)->fGetNFrame(*it); else nFrame = 0; if((nFrame > 1) || (!static_singletons)) { int start = I->LastStateBuilt; int stop = start + 1; int ste; if(async_builds && (max_threads > 1)) { if((start / max_threads) == (cur_state / max_threads)) { stop = start; /* don't purge current batch */ } else { int base = start / max_threads; /* now purge previous batch */ start = base * max_threads; stop = (base + 1) * max_threads; } } for(ste = start; ste < stop; ste++) { (*it)->invalidate(cRepAll, cRepInvPurge, ste); } } } } } } I->LastStateBuilt = cur_state; WizardDoScene(G); if(!MovieDefined(G)) { if(SettingGetGlobal_i(G, cSetting_frame) != (cur_state + 1)) SettingSetGlobal_i(G, cSetting_frame, (cur_state + 1)); } } PRINTFD(G, FB_Scene) " SceneUpdate: leaving...\n" ENDFD; } /*========================================================================*/ int SceneRenderCached(PyMOLGlobals * G) { /* sets up a cached image buffer is one is available, or if we are * using cached images by default */ CScene *I = G->Scene; ImageType *image; int renderedFlag = false; int draw_mode = SettingGetGlobal_i(G, cSetting_draw_mode); PRINTFD(G, FB_Scene) " SceneRenderCached: entered.\n" ENDFD; G->ShaderMgr->Check_Reload(); if(I->DirtyFlag) { int moviePlaying = MoviePlaying(G); if(I->MovieFrameFlag || (moviePlaying && SettingGetGlobal_b(G, cSetting_cache_frames))) { I->MovieFrameFlag = false; image = MovieGetImage(G, MovieFrameToImage(G, SettingGetGlobal_i(G, cSetting_frame) - 1)); if(image) { if(I->Image && (!I->MovieOwnsImageFlag)) ScenePurgeImage(G); I->MovieOwnsImageFlag = true; I->CopyType = true; I->Image = image; OrthoDirty(G); renderedFlag = true; } else { SceneMakeMovieImage(G, true, false, cSceneImage_Default); renderedFlag = true; } } else if(draw_mode == 3) { auto show_progress = SettingGet<int>(G, cSetting_show_progress); SettingSetGlobal_i(G, cSetting_show_progress, 0); SceneRay(G, 0, 0, SettingGetGlobal_i(G, cSetting_ray_default_renderer), NULL, NULL, 0.0F, 0.0F, false, NULL, false, -1); SettingSetGlobal_i(G,cSetting_show_progress, show_progress); } else if(moviePlaying && SettingGetGlobal_b(G, cSetting_ray_trace_frames)) { SceneRay(G, 0, 0, SettingGetGlobal_i(G, cSetting_ray_default_renderer), NULL, NULL, 0.0F, 0.0F, false, NULL, true, -1); } else if((moviePlaying && SettingGetGlobal_b(G, cSetting_draw_frames)) || (draw_mode == 2)) { SceneMakeSizedImage(G, 0, 0, SettingGetGlobal_i(G, cSetting_antialias)); } else if(I->CopyType == true) { /* true vs. 2 */ renderedFlag = true; } else { renderedFlag = false; } } else if(I->CopyType == true) { /* true vs. 2 */ renderedFlag = true; } PRINTFD(G, FB_Scene) " SceneRenderCached: leaving...renderedFlag %d\n", renderedFlag ENDFD; return (renderedFlag); } float SceneGetSpecularValue(PyMOLGlobals * G, float spec, int limit) { int n_light = SettingGetGlobal_i(G, cSetting_spec_count); if(n_light < 0) n_light = SettingGetGlobal_i(G, cSetting_light_count); if(n_light > limit) n_light = limit; if(n_light > 2) { spec = spec / pow(n_light - 1, 0.6F); } return (spec > 1.F) ? 1.F : (spec < 0.F) ? 0.F : spec; } float SceneGetReflectScaleValue(PyMOLGlobals * G, int limit) { float result = 1.0F; int n_light = SettingGetGlobal_i(G, cSetting_light_count); if(n_light > limit) n_light = limit; if(n_light > 1) { float tmp[3]; float sum = 0.0F; for (int i = 0; i < n_light - 1; ++i) { copy3f(SettingGetGlobal_3fv(G, light_setting_indices[i]), tmp); normalize3f(tmp); sum += 1.f - tmp[2]; } sum *= 0.5; return result / sum; } return result; } /* * Get specular and shininess, adjusted to the number of lights. * * ptr_spec: specular for lights 2-N * ptr_spec_power: shininess for lights 2-N * ptr_spec_direct: specular for light 1 * ptr_spec_direct_power: shininess for light 1 */ void SceneGetAdjustedLightValues(PyMOLGlobals * G, float *ptr_spec, float *ptr_spec_power, float *ptr_spec_direct, float *ptr_spec_direct_power, int limit) { float specular = SettingGetGlobal_f(G, cSetting_specular); if (specular == 1.0F) specular = SettingGetGlobal_f(G, cSetting_specular_intensity); if (specular < R_SMALL4) specular = 0.0F; float spec_power = SettingGetGlobal_f(G, cSetting_spec_power); if (spec_power < 0.0F) spec_power = SettingGetGlobal_f(G, cSetting_shininess); float spec_reflect = SettingGetGlobal_f(G, cSetting_spec_reflect); if (spec_reflect < 0.0F) spec_reflect = specular; float spec_direct = SettingGetGlobal_f(G, cSetting_spec_direct); if (spec_direct < 0.0F) spec_direct = specular; float spec_direct_power = SettingGetGlobal_f(G, cSetting_spec_direct_power); if (spec_direct_power < 0.0F) spec_direct_power = spec_power; *ptr_spec = SceneGetSpecularValue(G, spec_reflect, limit); *ptr_spec_power = spec_power; *ptr_spec_direct = spec_direct > 1.F ? 1.F : spec_direct; *ptr_spec_direct_power = spec_direct_power; } /* * Shader attribute names */ #define TEMPLATE(i) "g_LightSource[" #i "].position" const char * lightsource_position_names[] = { TEMPLATE(0), TEMPLATE(1), TEMPLATE(2), TEMPLATE(3), TEMPLATE(4), TEMPLATE(5), TEMPLATE(6), TEMPLATE(7), TEMPLATE(8), TEMPLATE(9) }; #undef TEMPLATE #define TEMPLATE(i) "g_LightSource[" #i "].diffuse" const char * lightsource_diffuse_names[] = { TEMPLATE(0), TEMPLATE(1), TEMPLATE(2), TEMPLATE(3), TEMPLATE(4), TEMPLATE(5), TEMPLATE(6), TEMPLATE(7), TEMPLATE(8), TEMPLATE(9) }; #undef TEMPLATE /* * Sets up lighting for immediate mode if shaderPrg=NULL, otherwise * sets lighting uniforms for the given shader program. * * Supports up to light_count=8 */ void SceneProgramLighting(PyMOLGlobals * G, CShaderPrg * shaderPrg) { /* load up the light positions relative to the camera while MODELVIEW still has the identity */ int n_light = glm::clamp(SettingGetGlobal_i(G, cSetting_light_count), 0, 8); int spec_count = SettingGetGlobal_i(G, cSetting_spec_count); float direct = SettingGetGlobal_f(G, cSetting_direct); float reflect = SettingGetGlobal_f(G, cSetting_reflect) * SceneGetReflectScaleValue(G, n_light); float spec[4]; float diff[4]; const float zero[4] = { 0.0F, 0.0F, 0.0F, 1.0F }; float vv[4] = {0.F, 0.F, 1.F, 0.F}; // position float spec_value, shine, spec_direct, spec_direct_power; SceneGetAdjustedLightValues(G, &spec_value, &shine, &spec_direct, &spec_direct_power, n_light); if (n_light < 2) { direct += reflect; if(direct > 1.0F) direct = 1.0F; } if(spec_count < 0) { spec_count = n_light; } // light 0 white4f(diff, SettingGetGlobal_f(G, cSetting_ambient)); #ifndef PURE_OPENGL_ES_2 if (!shaderPrg) { glEnable(GL_LIGHTING); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, diff); glLightfv(GL_LIGHT0, GL_POSITION, vv); glLightfv(GL_LIGHT0, GL_AMBIENT, zero); if(direct > R_SMALL4) { white4f(diff, direct); white4f(spec, spec_direct); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_DIFFUSE, diff); glLightfv(GL_LIGHT0, GL_SPECULAR, spec); } else { glLightfv(GL_LIGHT0, GL_DIFFUSE, zero); glLightfv(GL_LIGHT0, GL_SPECULAR, zero); } } else #endif { shaderPrg->Set4fv("g_LightModel.ambient", diff); white4f(diff, (direct > R_SMALL4) ? direct : 0.f); shaderPrg->Set4fv(lightsource_diffuse_names[0], diff); shaderPrg->Set4fv(lightsource_position_names[0], vv); } // light 1-N white4f(spec, spec_value); white4f(diff, reflect); for (int i = 1; i < n_light; ++i) { // normalized/inverted light direction copy3f(SettingGetGlobal_3fv(G, light_setting_indices[i - 1]), vv); normalize3f(vv); invert3f(vv); #ifndef PURE_OPENGL_ES_2 if (!shaderPrg) { glEnable(GL_LIGHT0 + i); glLightfv(GL_LIGHT0 + i, GL_POSITION, vv); glLightfv(GL_LIGHT0 + i, GL_SPECULAR, (spec_count >= i) ? spec : zero); glLightfv(GL_LIGHT0 + i, GL_AMBIENT, zero); glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, diff); } else #endif { shaderPrg->Set4fv(lightsource_position_names[i], vv); shaderPrg->Set4fv(lightsource_diffuse_names[i], diff); } } #ifndef PURE_OPENGL_ES_2 if (!shaderPrg) { // TODO: this was depending on two_sided_lighting, surface_cavity_mode // and transparency_mode glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); // disable unused lights for (int i = 7; i >= n_light; --i) { glDisable(GL_LIGHT0 + i); } // material white4f(spec, 1.F); glMaterialfv(GL_FRONT, GL_SPECULAR, spec); glMaterialf(GL_FRONT, GL_SHININESS, glm::clamp(shine, 0.F, 128.F)); } #endif } #ifdef _PYMOL_SHARP3D void sharp3d_begin_left_stereo(void); void sharp3d_switch_to_right_stereo(void); void sharp3d_end_stereo(void); #endif /* * Set up the Scene Fog* member variables and immediate mode fog (incl. * gl_Fog struct for non-ES2 shaders) */ int SceneSetFog(PyMOLGlobals *G){ CScene *I = G->Scene; int fog_active = false; float fog_density = SettingGetGlobal_f(G, cSetting_fog); I->FogStart = (I->BackSafe - I->FrontSafe) * SettingGetGlobal_f(G, cSetting_fog_start) + I->FrontSafe; if((fog_density > R_SMALL8) && (fog_density != 1.0F)) { I->FogEnd = I->FogStart + (I->BackSafe - I->FogStart) / fog_density; } else { I->FogEnd = I->BackSafe; } if(SettingGetGlobal_b(G, cSetting_depth_cue) && fog_density != 0.0F) { fog_active = true; } #ifndef PURE_OPENGL_ES_2 if (ALWAYS_IMMEDIATE_OR(!SettingGetGlobal_b(G, cSetting_use_shaders))) { const float *bg_rgb = ColorGet(G, SettingGetGlobal_color(G, cSetting_bg_rgb)); float fog[4] = {bg_rgb[0], bg_rgb[1], bg_rgb[2], 1.0}; glFogf(GL_FOG_MODE, GL_LINEAR); glFogf(GL_FOG_START, I->FogStart); glFogf(GL_FOG_END, I->FogEnd); glFogf(GL_FOG_DENSITY, fog_density > R_SMALL8 ? fog_density : 1.0F); glFogfv(GL_FOG_COLOR, fog); if (fog_active) glEnable(GL_FOG); else glDisable(GL_FOG); } #endif return fog_active; } /* * Set the g_Fog_* uniforms for ES2 shaders */ void SceneSetFogUniforms(PyMOLGlobals * G, CShaderPrg * shaderPrg) { CScene *I = G->Scene; if (shaderPrg) { float fogScale = 1.0f / (I->FogEnd - I->FogStart); shaderPrg->Set1f("g_Fog_end", I->FogEnd); shaderPrg->Set1f("g_Fog_scale", fogScale); } } void SceneSetupGLPicking(PyMOLGlobals * G){ /* picking mode: we want flat, unshaded, unblended, unsmooth colors */ glDisable(GL_FOG); glDisable(GL_COLOR_MATERIAL); glDisable(GL_LIGHTING); glDisable(GL_LINE_SMOOTH); glDisable(GL_DITHER); glDisable(GL_BLEND); glDisable(GL_POLYGON_SMOOTH); if(G->Option->multisample) glDisable(0x809D); /* GL_MULTISAMPLE_ARB */ glShadeModel(GL_FLAT); } /*========================================================================*/ void SceneRestartFrameTimer(PyMOLGlobals * G) { CScene *I = G->Scene; I->LastFrameTime = UtilGetSeconds(G); } static void SceneRestartPerfTimer(PyMOLGlobals * G) { CScene *I = G->Scene; I->LastRender = UtilGetSeconds(G); I->RenderTime = 0.0; } void SceneRestartSweepTimer(PyMOLGlobals * G) { CScene *I = G->Scene; I->LastSweep = 0.0F; /* continue to defer rocking until this is done */ I->LastSweepX = 0.0F; I->LastSweepY = 0.0F; I->SweepTime = 0.0; I->LastSweepTime = UtilGetSeconds(G); SceneRestartPerfTimer(G); } /*========================================================================*/ void ScenePrepareMatrix(PyMOLGlobals * G, int mode) { CScene *I = G->Scene; float stAng, stShift; { if (!mode){ SceneComposeModelViewMatrix(I, I->ModelViewMatrix); } else { /* stereo */ float tmpMatrix[16]; stAng = SettingGetGlobal_f(G, cSetting_stereo_angle);// * cPI / 180.f; stShift = SettingGetGlobal_f(G, cSetting_stereo_shift); stShift = (float) (stShift * fabs(I->Pos[2]) / 100.0); stAng = (float) (-stAng * atan(stShift / fabs(I->Pos[2])) / 2.f); if(mode == 2) { /* left hand */ stAng = -stAng; stShift = -stShift; } PRINTFD(G, FB_Scene) " StereoMatrix-Debug: mode %d stAng %8.3f stShift %8.3f \n", mode, stAng, stShift ENDFD; identity44f(tmpMatrix); identity44f(I->ModelViewMatrix); MatrixRotateC44f(I->ModelViewMatrix, stAng, 0.f, 1.f, 0.f); MatrixTranslateC44f(tmpMatrix, I->Pos[0] + stShift, I->Pos[1], I->Pos[2]); MatrixMultiplyC44f(tmpMatrix, I->ModelViewMatrix); MatrixMultiplyC44f(I->RotMatrix, I->ModelViewMatrix); MatrixTranslateC44f(I->ModelViewMatrix, -I->Origin[0], -I->Origin[1], -I->Origin[2]); } } #ifndef PURE_OPENGL_ES_2 if (ALWAYS_IMMEDIATE_OR(!SettingGetGlobal_b(G, cSetting_use_shaders))) { glLoadMatrixf(I->ModelViewMatrix); } #endif } /*========================================================================*/ static void SceneRotateWithDirty(PyMOLGlobals * G, float angle, float x, float y, float z, int dirty) { CScene *I = G->Scene; float temp[16]; int a; angle = (float) (-PI * angle / 180.0); identity44f(temp); MatrixRotateC44f(temp, angle, x, y, z); MatrixMultiplyC44f(I->RotMatrix, temp); for(a = 0; a < 16; a++) I->RotMatrix[a] = temp[a]; SceneUpdateInvMatrix(G); if(dirty) { SceneInvalidate(G); } else { SceneInvalidateCopy(G, false); } PyMOL_NeedRedisplay(G->PyMOL); /* glPushMatrix(); glLoadIdentity(); glRotatef(angle,x,y,z); glMultMatrixf(I->RotMatrix); glGetFloatv(GL_MODELVIEW_MATRIX,I->RotMatrix); glPopMatrix(); */ } void SceneRotate(PyMOLGlobals * G, float angle, float x, float y, float z) { SceneRotateWithDirty(G, angle, x, y, z, true); } /*========================================================================*/ void SceneApplyMatrix(PyMOLGlobals * G, float *m) { CScene *I = G->Scene; MatrixMultiplyC44f(m, I->RotMatrix); SceneDirty(G); /* glPushMatrix(); glLoadIdentity(); glMultMatrixf(m); glMultMatrixf(I->RotMatrix); glGetFloatv(GL_MODELVIEW_MATRIX,I->RotMatrix); glPopMatrix(); */ } /*========================================================================*/ void SceneScale(PyMOLGlobals * G, float scale) { CScene *I = G->Scene; I->Scale *= scale; SceneInvalidate(G); } void SceneZoom(PyMOLGlobals * G, float scale){ CScene *I = G->Scene; float factor = -((I->FrontSafe + I->BackSafe) / 2) * 0.1 * scale; /* SettingGetGlobal_f(G, cSetting_mouse_wheel_scale); */ I->Pos[2] += factor; I->Front -= factor; I->Back -= factor; UpdateFrontBackSafe(I); SceneInvalidate(G); } int SceneGetTwoSidedLighting(PyMOLGlobals * G){ return SceneGetTwoSidedLightingSettings(G, NULL, NULL); } int SceneGetTwoSidedLightingSettings(PyMOLGlobals * G, const CSetting *set1, const CSetting *set2) { int two_sided_lighting = SettingGet_b(G, set1, set2, cSetting_two_sided_lighting); if(two_sided_lighting<0) { if(SettingGet_i(G, set1, set2, cSetting_surface_cavity_mode)) two_sided_lighting = true; else two_sided_lighting = false; } two_sided_lighting = two_sided_lighting || (SettingGet_i(G, set1, set2, cSetting_transparency_mode) ==1); return two_sided_lighting; } float SceneGetDynamicLineWidth(RenderInfo * info, float line_width) { if(info && info->dynamic_width) { float factor; if(info->vertex_scale > R_SMALL4) { factor = info->dynamic_width_factor / info->vertex_scale; if(factor > info->dynamic_width_max) factor = info->dynamic_width_max; if(factor < info->dynamic_width_min) { factor = info->dynamic_width_min; } } else { factor = info->dynamic_width_max; } return factor * line_width; } return line_width; } float SceneGetLineWidthForCylinders(PyMOLGlobals * G, RenderInfo * info, float line_width_arg){ float line_width = SceneGetDynamicLineWidth(info, line_width_arg); float pixel_scale_value = SettingGetGlobal_f(G, cSetting_ray_pixel_scale); if(pixel_scale_value < 0) pixel_scale_value = 1.0F; /* the radius of the cylinders is the vertex_scale * ray_pixel_scale */ /* this turns out to be exactly right, but changes if the scene or user moves */ return info->vertex_scale * pixel_scale_value * line_width / 2.f; } void ScenePushModelViewMatrix(PyMOLGlobals * G) { CScene *I = G->Scene; auto& stack = I->m_ModelViewMatrixStack; auto& depth = I->m_ModelViewMatrixStackDepth; stack.resize(16 * (depth + 1)); copy44f(I->ModelViewMatrix, &stack[16 * depth++]); } void ScenePopModelViewMatrix(PyMOLGlobals * G, bool immediate) { CScene *I = G->Scene; auto& stack = I->m_ModelViewMatrixStack; auto& depth = I->m_ModelViewMatrixStackDepth; if (depth == 0) { printf("ERROR: depth == 0\n"); return; } copy44f(&stack[--depth * 16], I->ModelViewMatrix); #ifndef PURE_OPENGL_ES_2 if (ALWAYS_IMMEDIATE_OR(immediate)) { glMatrixMode(GL_MODELVIEW); glLoadMatrixf(I->ModelViewMatrix); } #endif } float *SceneGetModelViewMatrix(PyMOLGlobals * G){ CScene *I = G->Scene; return (I->ModelViewMatrix); } float *SceneGetProjectionMatrix(PyMOLGlobals * G){ CScene *I = G->Scene; return (I->ProjectionMatrix); } void SceneSetBackgroundColorAlreadySet(PyMOLGlobals * G, int background_color_already_set){ CScene *I = G->Scene; I->background_color_already_set = background_color_already_set; } int SceneGetBackgroundColorAlreadySet(PyMOLGlobals * G){ CScene *I = G->Scene; return (I->background_color_already_set); } void SceneSetDoNotClearBackground(PyMOLGlobals * G, int do_not_clear){ CScene *I = G->Scene; I->do_not_clear = do_not_clear; } int SceneGetDoNotClearBackground(PyMOLGlobals * G){ CScene *I = G->Scene; return (I->do_not_clear); } #ifdef _PYMOL_IOS void SceneTranslateSceneXYWithScale(PyMOLGlobals * G, float x, float y){ CScene *I = G->Scene; int moved_flag; float v2[3], vScale; float old_front, old_back, old_origin; old_front = I->Front; old_back = I->Back; old_origin = -I->Pos[2]; vScale = SceneGetExactScreenVertexScale(G, I->Origin); /* if(stereo_via_adjacent_array(I->StereoMode)) { x = get_stereo_x(x, &I->LastX, I->Width, NULL); }*/ v2[0] = x * vScale; v2[1] = y * vScale; v2[2] = 0.0F; moved_flag = false; if(x != 0.f) { I->Pos[0] += v2[0]; I->LastX = x; SceneInvalidate(G); moved_flag = true; } if(y != 0.f) { I->Pos[1] += v2[1]; I->LastY = y; SceneInvalidate(G); moved_flag = true; } EditorFavorOrigin(G, NULL); if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_origin)) { SceneGetCenter(G, v2); /* gets position of center of screen */ SceneOriginSet(G, v2, true); } if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_detail)) { SceneRovingDirty(G); } if(moved_flag) SceneDoRoving(G, old_front, old_back, old_origin, 0 /* adjust flag */, false); PyMOL_NeedRedisplay(G->PyMOL); } int SceneIsTwisting(PyMOLGlobals * G){ CScene *I = G->Scene; return (!I->prev_no_z_rotation1 || !I->prev_no_z_rotation2); } #endif void SceneGLClear(PyMOLGlobals * G, GLbitfield mask){ glClear(mask); } int SceneIncrementTextureRefreshes(PyMOLGlobals * G){ CScene *I = G->Scene; return ++(I->n_texture_refreshes); } void SceneResetTextureRefreshes(PyMOLGlobals * G){ CScene *I = G->Scene; I->n_texture_refreshes = 0; } void SceneGetScaledAxesAtPoint(PyMOLGlobals * G, float *pt, float *xn, float *yn) { CScene *I = G->Scene; float xn0[3] = { 1.0F, 0.0F, 0.0F }; float yn0[3] = { 0.0F, 1.0F, 0.0F }; float v_scale; v_scale = SceneGetScreenVertexScale(G, pt); MatrixInvTransformC44fAs33f3f(I->RotMatrix, xn0, xn0); MatrixInvTransformC44fAs33f3f(I->RotMatrix, yn0, yn0); scale3f(xn0, v_scale, xn); scale3f(yn0, v_scale, yn); } void SceneGetScaledAxes(PyMOLGlobals * G, CObject *obj, float *xn, float *yn) { CScene *I = G->Scene; float *v; float vt[3]; float xn0[3] = { 1.0F, 0.0F, 0.0F }; float yn0[3] = { 0.0F, 1.0F, 0.0F }; float v_scale; v = TextGetPos(G); if(obj->TTTFlag) { transformTTT44f3f(obj->TTT, v, vt); } else { copy3f(v, vt); } v_scale = SceneGetScreenVertexScale(G, vt); MatrixInvTransformC44fAs33f3f(I->RotMatrix, xn0, xn0); MatrixInvTransformC44fAs33f3f(I->RotMatrix, yn0, yn0); scale3f(xn0, v_scale, xn); scale3f(yn0, v_scale, yn); } int SceneGetCopyType(PyMOLGlobals * G) { return G->Scene->CopyType; } void SceneGenerateMatrixToAnotherZFromZ(PyMOLGlobals *G, float *convMatrix, float *curpt, float *pt){ CScene *I = G->Scene; float scaleMatrix[16]; float cscale = SceneGetExactScreenVertexScale(G, curpt); float pscale = SceneGetExactScreenVertexScale(G, pt); identity44f(scaleMatrix); MatrixSetScaleC44f(scaleMatrix, pscale); identity44f(convMatrix); MatrixSetScaleC44f(convMatrix, 1.f/cscale); MatrixMultiplyC44f(I->RotMatrix, convMatrix); MatrixTranslateC44f(convMatrix, pt[0]-curpt[0], pt[1]-curpt[1], pt[2]-curpt[2]); MatrixMultiplyC44f(I->InvMatrix, convMatrix); MatrixMultiplyC44f(scaleMatrix, convMatrix); } void SceneAdjustZtoScreenZ(PyMOLGlobals *G, float *pos, float zarg){ CScene *I = G->Scene; float clipRange = (I->BackSafe-I->FrontSafe); float z = (zarg + 1.f) / 2.f; float zInPreProj = -(z * clipRange + I->FrontSafe); float pos4[4], tpos[4], npos[4]; float InvModMatrix[16]; copy3f(pos, pos4); pos4[3] = 1.f; MatrixTransformC44f4f(I->ModMatrix, pos4, tpos); normalize4f(tpos); /* NEED TO ACCOUNT FOR ORTHO */ if (SettingGetGlobal_b(G, cSetting_ortho)){ npos[0] = tpos[0]; npos[1] = tpos[1]; } else { npos[0] = zInPreProj * tpos[0] / tpos[2]; npos[1] = zInPreProj * tpos[1] / tpos[2]; } npos[2] = zInPreProj; npos[3] = 1.f; MatrixInvertC44f(I->ModMatrix, InvModMatrix); MatrixTransformC44f4f(InvModMatrix, npos, npos); normalize4f(npos); copy3f(npos, pos); } /* this function takes a screen point, where z is normalized between the clipping planes, and converts it to the world coordinates */ void SceneSetPointToWorldScreenRelative(PyMOLGlobals *G, float *pos, float *screenPt) { float npos[4]; float InvPmvMatrix[16]; int width, height; SceneGetWidthHeightStereo(G, &width, &height); npos[0] = (.5f + floor(screenPt[0]*width)) /width ; // add .5, in middle of pixels? npos[1] = (.5f + floor(screenPt[1]*height)) /height ; // add .5, in middle of pixels? npos[2] = 0.f; npos[3] = 1.f; MatrixInvertC44f(SceneGetPmvMatrix(G), InvPmvMatrix); MatrixTransformC44f4f(InvPmvMatrix, npos, npos); normalize4f(npos); SceneAdjustZtoScreenZ(G, npos, screenPt[2]); copy3f(npos, pos); } float SceneGetCurrentBackSafe(PyMOLGlobals *G){ CScene *I = G->Scene; return (I->BackSafe); } float SceneGetCurrentFrontSafe(PyMOLGlobals *G){ CScene *I = G->Scene; return (I->FrontSafe); } /* * Get the field-of-view width at a depth of 1.0 */ float GetFovWidth(PyMOLGlobals * G) { float fov = SettingGetGlobal_f(G, cSetting_field_of_view); return 2.f * tanf(fov * PI / 360.f); } void SceneInvalidatePicking(PyMOLGlobals * G){ CScene *I = G->Scene; I->invPick = true; }