Files
pymol-open-source/layer1/SceneMouse.cpp
2024-08-13 14:55:36 -04:00

2036 lines
65 KiB
C++

#include "SceneMouse.h"
#include "ButMode.h"
#include "Editor.h"
#include "Executive.h"
#include "Matrix.h"
#include "Menu.h"
#include "ObjectDist.h"
#include "ObjectGadget.h"
#include "ObjectMolecule.h"
#include "ObjectSlice.h"
#include "P.h"
#include "PyMOLGlobals.h"
#include "PyMOLObject.h"
#include "SceneDef.h"
#include "ScenePicking.h"
#include "Selector.h"
#include "Seq.h"
#include "Wizard.h"
#include "Feedback.h"
#include <glm/vec3.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/type_ptr.hpp>
#define cDoubleTime 0.35
NamedPicking::NamedPicking(const Picking& pick)
: src(pick.src)
{
if (pick.context.object != nullptr) {
context.name = pick.context.object->Name;
}
context.state = pick.context.state;
}
static void SceneNoteMouseInteraction(PyMOLGlobals* G)
{
SceneAbortAnimation(G);
if (SettingGet_b(G, nullptr, nullptr, cSetting_mouse_restart_movie_delay)) {
SceneRestartFrameTimer(G);
}
}
static void 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);
}
static void 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);
}
static void 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);
}
void SceneClickButtonAddTo(PyMOLGlobals* G, pymol::CObject* obj,
pymol::zstring_view selName, pymol::zstring_view buffer,
pymol::zstring_view sel_mode_kw)
{
CScene* I = G->Scene;
if (SelectorIndexByName(G, selName.c_str()) >= 0) {
auto buf2 = pymol::string_format(
"(((%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.c_str(), buf2.c_str(), nullptr, false, nullptr);
if (obj->type == cObjectMolecule) {
if (SettingGetGlobal_i(G, cSetting_logging)) {
auto objMol = (ObjectMolecule*) obj;
auto atomSele = ObjectMoleculeGetAtomSeleLog(
objMol, I->LastPicked.src.index, false);
auto select = pymol::string_format(
"(((%s) or %s(%s)) and not ((%s(%s)) and %s(%s)))", selName,
sel_mode_kw, atomSele, sel_mode_kw, atomSele, sel_mode_kw, selName);
auto pLogBuffer =
pymol::string_format("cmd.select('%s',\"%s(%s)\",enable=1)",
selName, sel_mode_kw, select);
PLog(G, pLogBuffer, cPLog_pym);
}
}
} else {
auto buf2 = pymol::string_format("%s(%s)", sel_mode_kw, buffer);
SelectorCreate(G, selName.c_str(), buf2.c_str(), nullptr, false, nullptr);
if (obj->type == cObjectMolecule) {
if (SettingGet<int>(G, cSetting_logging)) {
auto objMol = (ObjectMolecule*) obj;
auto atomSele = ObjectMoleculeGetAtomSeleLog(
objMol, I->LastPicked.src.index, false);
auto select = pymol::string_format(
"cmd.select('%s',\"%s(%s)\")", selName, sel_mode_kw, atomSele);
PLog(G, select, cPLog_pym);
}
}
}
if (SettingGet<bool>(G, cSetting_auto_hide_selections))
ExecutiveHideSelections(G);
if (SettingGet<bool>(G, cSetting_auto_show_selections))
ExecutiveSetObjVisib(G, selName, 1, false);
WizardDoSelect(G, selName.c_str(), I->LastPicked.context.state);
}
/**
* Check for Double click
* @param x cursor X position
* @param Y cursor Y position
* @param mod modifier key (Shift, ctrl, etc...)
* @param when Time which this click happened
* @return new button
*/
static int SceneClickCheckDoubleClick(
PyMOLGlobals* G, int button, int x, int y, int mod, double when)
{
auto I = G->Scene;
// check for double click (within 0.35s and 10sq. pixels
if (((ButModeCheckPossibleSingleClick(G, button, mod) || (!mod)) &&
((when - I->LastClickTime) < cDoubleTime))) {
int dx = abs(I->LastWinX - x);
int 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;
}
}
}
return button;
}
/**
* @param button which button pressed
* @param x Cursor X position
* @param Y Cursor Y position
* @param mod modifier key (Shift, ctrl, etc...)
* @return true if click was handled (resolved with action)
*/
static int SceneClickSceneButton(
PyMOLGlobals* G, int button, int x, int y, int mod)
{
auto I = G->Scene;
for (std::size_t i = 0u; i < I->SceneVec.size(); i++) {
auto& elem = I->SceneVec[i];
if (elem.drawn && elem.rect.contains(x, y)) {
switch (button) {
case P_GLUT_LEFT_BUTTON: /* normal activate (with interpolation) */
I->Pressed = i;
I->Over = i;
I->PressMode = 1;
SceneDirty(G);
return true;
case P_GLUT_MIDDLE_BUTTON: /* rapid browse mode */
I->Pressed = i;
I->PressMode = 2;
I->Over = i;
{
const char* cur_name =
SettingGetGlobal_s(G, cSetting_scene_current_name);
int animate = -1;
if (mod & cOrthoCTRL)
animate = 0;
if (cur_name && elem.name != cur_name) {
auto buffer = pymol::string_format(
"cmd.scene('''%s''',animate=%d)", elem.name, animate);
PParse(G, buffer);
PFlush(G);
PLog(G, buffer, cPLog_pym);
}
}
return true;
case P_GLUT_RIGHT_BUTTON: /* drag or menu... */
I->Pressed = i;
I->PressMode = 3;
I->Over = i;
return true;
}
break;
}
}
return false;
}
/**
* Handles object-related click events
* @param obj clicked object
* @param LastPicked last picked info
* @param mode mouse button mode
* @param sel_mode_kw Current selection mode operator for the mouse
*
*/
void SceneClickObject(PyMOLGlobals* G, pymol::CObject* obj, const NamedPicking& LastPicked,
int mode, pymol::zstring_view sel_mode_kw)
{
std::string selName;
switch (obj->type) {
case cObjectMolecule: {
if (Feedback(G, FB_Scene, FB_Results)) {
auto buffer = obj->describeElement(LastPicked.src.index);
PRINTF " You clicked %s", buffer.c_str() ENDF(G);
OrthoRestorePrompt(G);
}
auto buffer =
pymol::string_format("%s`%d", obj->Name, LastPicked.src.index + 1);
switch (mode) { // Populate SelName/buffers
case cButModeLB:
case cButModeAddToLB:
selName = "lb";
break;
case cButModeMB:
case cButModeAddToMB:
selName = "mb";
break;
case cButModeRB:
case cButModeAddToRB:
selName = "rb";
break;
case cButModeSeleSet:
case cButModeSeleToggle:
ExecutiveGetActiveSeleName(
G, selName, true, SettingGet<int>(G, cSetting_logging));
break;
case cButModeDragMol: {
auto objMol = (ObjectMolecule*) obj;
auto atomSele =
ObjectMoleculeGetAtomSeleLog(objMol, LastPicked.src.index, false);
auto pLogBuffer =
pymol::string_format("cmd.drag(\"bymol (%s)\")", atomSele);
PParse(G, pLogBuffer);
PLog(G, buffer, cPLog_pym);
} break;
case cButModeDragObj: {
auto objMol = (ObjectMolecule*) obj;
auto atomSele =
ObjectMoleculeGetAtomSeleLog(objMol, LastPicked.src.index, false);
auto pLogBuffer =
pymol::string_format("cmd.drag(\"byobject (%s)\")", atomSele);
PParse(G, pLogBuffer);
PLog(G, pLogBuffer, cPLog_pym);
} break;
case cButModeOrigAt:
SceneNoteMouseInteraction(G);
{
float v1[3];
if (ObjectMoleculeGetAtomTxfVertex((ObjectMolecule*) obj,
LastPicked.context.state, LastPicked.src.index, v1)) {
EditorFavorOrigin(G, v1);
ExecutiveOrigin(G, nullptr, true, nullptr, v1, 0);
}
}
if (obj->type == cObjectMolecule) {
if (SettingGetGlobal_i(G, cSetting_logging)) {
auto objMol = (ObjectMolecule*) obj;
auto atomSele =
ObjectMoleculeGetAtomSeleLog(objMol, LastPicked.src.index, false);
auto pLogBuffer =
pymol::string_format("cmd.origin(\"%s\")", atomSele);
PLog(G, pLogBuffer, cPLog_pym);
}
if (Feedback(G, FB_Scene, FB_Results)) {
auto buffer = obj->describeElement(LastPicked.src.index);
PRINTF " You clicked %s", buffer.c_str() 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,
LastPicked.context.state, LastPicked.src.index, v1)) {
ExecutiveCenter(G, nullptr, 0, true, -1, v1, true);
}
}
if (SettingGet<int>(G, cSetting_logging)) {
auto objMol = (ObjectMolecule*) obj;
auto atomSele =
ObjectMoleculeGetAtomSeleLog(objMol, LastPicked.src.index, false);
auto pLogBuffer = pymol::string_format("cmd.center(\"%s\",state=%d)",
atomSele, LastPicked.context.state + 1);
PLog(G, pLogBuffer, cPLog_pym);
}
break;
}
switch (mode) {
case cButModeSimpleClick:
break;
case cButModeLB:
case cButModeMB:
case cButModeRB:
case cButModeSeleSet: {
auto buf2 = pymol::string_format("(%s(%s))", sel_mode_kw, buffer);
SelectorCreate(G, selName.c_str(), buf2.c_str(), nullptr, false, nullptr);
}
if (SettingGet<bool>(G, cSetting_auto_hide_selections))
ExecutiveHideSelections(G);
if (SettingGet<bool>(G, cSetting_auto_show_selections))
ExecutiveSetObjVisib(G, selName, 1, false);
if (obj->type == cObjectMolecule) {
if (SettingGet<int>(G, cSetting_logging)) {
auto objMol = (ObjectMolecule*) obj;
auto buf1 =
ObjectMoleculeGetAtomSeleLog(objMol, LastPicked.src.index, false);
auto pLogBuffer =
pymol::string_format("cmd.select('%s',\"%s(%s)\",enable=1)",
selName, sel_mode_kw, buf1);
PLog(G, pLogBuffer, cPLog_pym);
}
}
WizardDoSelect(G, selName.c_str(), LastPicked.context.state);
break;
case cButModeAddToLB:
case cButModeAddToMB:
case cButModeAddToRB:
case cButModeSeleToggle:
SceneClickButtonAddTo(G, obj, selName, buffer.c_str(), sel_mode_kw);
break;
}
}
case cObjectGadget:
break;
default:
EditorInactivate(G);
break;
}
}
void SceneClickTransformObject(
PyMOLGlobals* G, pymol::CObject* obj, const NamedPicking& LastPicked, int mode, bool is_single_click)
{
auto I = G->Scene;
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[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 */
auto buffer = obj->describeElement(LastPicked.src.index);
auto atomSele = ObjectMoleculeGetAtomSeleLog(
(ObjectMolecule*) obj, LastPicked.src.index, false);
MenuActivate2Arg(G, I->LastWinX, I->LastWinY + 20, I->LastWinX,
I->LastWinY, is_single_click, "pick_menu", buffer.c_str(),
atomSele.c_str());
}
} break;
case cButModePickAtom1:
if (obj && obj->type == cObjectMolecule) {
if (Feedback(G, FB_Scene, FB_Results)) {
auto buffer = obj->describeElement(LastPicked.src.index);
PRINTF " You clicked %s -> (%s)\n", buffer.c_str(), cEditorSele1 ENDF(G);
}
if (SettingGet<int>(G, cSetting_logging)) {
auto objMol = (ObjectMolecule*) obj;
auto atomSele = ObjectMoleculeGetAtomSeleLog(
objMol, LastPicked.src.index, false);
auto pLogBuffer =
pymol::string_format("cmd.edit(\"%s\",pkresi=1)", atomSele);
PLog(G, pLogBuffer, cPLog_pym);
}
OrthoRestorePrompt(G);
auto buffer = pymol::string_format(
"%s`%d", obj->Name, LastPicked.src.index + 1);
EditorInactivate(G);
SelectorCreate(G, cEditorSele1, buffer.c_str(), nullptr, true, nullptr);
EditorActivate(G, SettingGet<int>(G, cSetting_state) - 1, false);
if (EditorActive(G)) {
EditorDefineExtraPks(G);
}
WizardDoPick(G, 0, LastPicked.context.state);
}
break;
case cButModePickAtom:
if (obj && obj->type == cObjectMolecule) {
auto buffer = obj->describeElement(LastPicked.src.index);
if (EditorIsBondMode(G)
/* &&!(EditorIsAnActiveObject(G,(ObjectMolecule*)obj)) */
) {
EditorInactivate(G);
EditorLogState(G, false);
}
if ((!EditorIsBondMode(G)) &&
EditorDeselectIfSelected(
G, (ObjectMolecule*) obj, LastPicked.src.index, true)) {
PRINTF " You unpicked %s.", buffer.c_str() ENDF(G);
if (EditorActive(G))
EditorDefineExtraPks(G);
EditorLogState(G, false);
} else {
if (EditorIsBondMode(G) &&
EditorDeselectIfSelected(
G, (ObjectMolecule*) obj, LastPicked.src.index, false)) {
EditorInactivate(G);
}
WordType name;
EditorGetNextMultiatom(G, name);
PRINTFB(G, FB_Scene, FB_Results)
" You clicked %s -> (%s)\n", buffer.c_str(), name ENDFB(G);
/* TODO: logging */
auto buffer = pymol::string_format(
"%s`%d", obj->Name, LastPicked.src.index + 1);
ExecutiveDelete(G, name);
SelectorCreate(G, name, buffer.c_str(), nullptr, true, nullptr);
EditorActivate(G, SettingGet<int>(G, cSetting_state) - 1, false);
if (EditorActive(G)) {
EditorDefineExtraPks(G);
}
EditorLogState(G, false);
WizardDoPick(G, 0, LastPicked.context.state);
}
}
break;
}
break;
case cObjectGadget:
break;
default:
EditorInactivate(G);
break;
}
}
/**
* Handle logic for objects with pickable bonds
* @param x cursor X
* @param y cursor Y
* @param mode mouse button mode
* @param lastPicked last picked info
*/
void SceneClickPickBond(PyMOLGlobals* G, int x, int y, int mode, const NamedPicking& LastPicked)
{
auto I = G->Scene;
auto obj = ExecutiveFindObject<ObjectMolecule>(G, LastPicked.context.name);
EditorInactivate(G);
if (!obj) {
return;
}
if (Feedback(G, FB_Scene, FB_Results)) {
auto buffer = obj->describeElement(I->LastPicked.src.index);
PRINTF " You clicked %s -> (%s)", buffer.c_str(), cEditorSele1 ENDF(G);
OrthoRestorePrompt(G);
}
/* ObjectMoleculeChooseBondDir(objMol,I->LastPicked.bond,
&I->LastPicked.src.index,&atIndex); */
{
auto buffer = pymol::string_format("%s`%d", obj->Name, I->LastPicked.src.index + 1);
SelectorCreate(G, cEditorSele1, buffer.c_str(), nullptr, true, nullptr);
}
if (LastPicked.src.bond >= 0) {
int atIndex = obj->Bond[LastPicked.src.bond].index[0];
if (atIndex == LastPicked.src.index)
atIndex = obj->Bond[LastPicked.src.bond].index[1];
if (Feedback(G, FB_Scene, FB_Results)) {
auto buffer = obj->describeElement(atIndex);
PRINTF " You clicked %s -> (%s)", buffer.c_str(), cEditorSele2 ENDF(G);
OrthoRestorePrompt(G);
}
if (SettingGetGlobal_i(G, cSetting_logging)) {
auto buf1 = ObjectMoleculeGetAtomSeleLog(
obj, LastPicked.src.index, false);
auto buf2 = ObjectMoleculeGetAtomSeleLog(obj, atIndex, false);
auto buffer = pymol::string_format("cmd.edit(\"%s\",\"%s\")", buf1, buf2);
PLog(G, buffer, cPLog_pym);
}
auto buffer = pymol::string_format("%s`%d", obj->Name, atIndex + 1);
SelectorCreate(G, cEditorSele2, buffer.c_str(), nullptr, true, nullptr);
EditorActivate(G, SettingGetGlobal_i(G, cSetting_state) - 1, true);
if (mode == cButModePkTorBnd) {
/* get ready to drag */
SceneDontCopyNext(G);
EditorPrepareDrag(G, obj, -1, LastPicked.src.index,
SettingGetGlobal_i(G, cSetting_state) - 1, mode);
I->SculptingFlag = 1;
I->SculptingSave =
obj->AtomInfo[LastPicked.src.index].protekted;
obj->AtomInfo[LastPicked.src.index].protekted = cAtomProtected_drag;
}
WizardDoPick(G, 1, LastPicked.context.state);
} else {
WizardDoPick(G, 0, LastPicked.context.state);
}
if (SettingGetGlobal_b(G, cSetting_auto_hide_selections))
ExecutiveHideSelections(G);
}
/**
* Handle logic for picking nothing
* @param button which button pressed
* @param mod modifier key (Shift, ctrl, etc...)
* @param mode mouse button mode
*/
void SceneClickPickNothing(PyMOLGlobals* G, int button, int mod, int mode)
{
auto I = G->Scene;
switch (mode) {
case cButModeSeleSet: {
ObjectNameType name;
if (ExecutiveGetActiveSeleName(
G, name, false, SettingGet<int>(G, cSetting_logging))) {
SelectorCreate(G, name, "none", nullptr, true, nullptr);
if (SettingGet<int>(G, cSetting_logging)) {
auto buf2 = pymol::string_format("cmd.select('%s','none')\n", name);
PLog(G, buf2, cPLog_no_flush);
}
SeqDirty(G);
}
}
case cButModeSeleToggle: {
ObjectNameType name;
if (ExecutiveGetActiveSeleName(
G, name, false, SettingGet<int>(G, cSetting_logging))) {
ExecutiveSetObjVisib(G, name, 0, false);
if (SettingGet<int>(G, cSetting_logging)) {
auto buf2 = pymol::string_format("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), nullptr, 0);
break;
}
PRINTFB(G, FB_Scene, FB_Blather)
" %s: no atom found nearby.\n", __func__ ENDFB(G);
/* this here to prevent display weirdness after
an unsuccessful picking pass... not sure it helps though */
SceneInvalidate(G);
OrthoRestorePrompt(G);
}
static int get_stereo_x(int x, int* last_x, int width, ClickSide* 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 = ClickSide::Right;
} else {
if (click_side)
*click_side = ClickSide::Left;
}
} else {
if ((x - (*last_x)) > width_3) {
x -= width_2;
if (click_side)
*click_side = ClickSide::Right;
} else if (((*last_x) - x) > width_3) {
x += width_2;
if (click_side)
*click_side = ClickSide::Right;
} else {
if (click_side)
*click_side = ClickSide::Left;
}
}
return x;
}
/*========================================================================*/
void SceneClick(Block* block, int button, int x, int y, int mod, double when)
{
PyMOLGlobals* G = block->m_G;
CScene* I = G->Scene;
const char* sel_mode_kw = "";
int is_single_click =
((button == P_GLUT_SINGLE_LEFT) || (button == P_GLUT_SINGLE_MIDDLE) ||
(button == P_GLUT_SINGLE_RIGHT));
if (!is_single_click) {
int click_handled = false;
if (I->ButtonsShown) {
/* 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 (const auto& elem : I->SceneVec) {
if (elem.drawn && elem.rect.contains(x, y)) {
click_handled = true;
break;
}
}
}
}
if (!click_handled) {
button = SceneClickCheckDoubleClick(G, button, x, y, mod, when);
} // 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
int click_handled = false;
auto click_side = ClickSide::None;
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(glm::value_ptr(I->m_view.rotMatrix()), v, v);
add3f(v, I->LastClickVertex, I->LastClickVertex);
}
if (I->ButtonsShown) {
if (I->ScrollBarActive) {
if ((x - I->rect.left) < (SceneScrollBarWidth + SceneScrollBarMargin)) {
click_handled = true;
I->m_ScrollBar.click(button, x, y, mod);
}
}
if (!click_handled) {
click_handled = SceneClickSceneButton(G, button, x, y, mod);
}
}
if (!click_handled) {
int mode = ButModeTranslate(
G, button, mod); /* trying to work around something... */
I->Button = button;
I->SculptingSave = cAtomProtected_off;
switch (mode) {
case cButModeScaleSlabExpand:
SceneNoteMouseInteraction(G);
SceneClip(G, SceneClipMode::Scaling,
1.0F + (0.2 * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale)),
nullptr, 0);
break;
case cButModeScaleSlabShrink:
SceneNoteMouseInteraction(G);
SceneClip(G, SceneClipMode::Scaling,
1.0F - (0.2 * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale)),
nullptr, 0);
break;
case cButModeMoveSlabForward:
SceneNoteMouseInteraction(G);
{
float old_front = I->m_view.m_clip().m_front;
float old_back = I->m_view.m_clip().m_back;
float old_origin = -I->m_view.pos().z;
SceneClip(G, SceneClipMode::Proportional,
0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), nullptr, 0);
SceneDoRoving(G, old_front, old_back, old_origin, true, false);
}
break;
case cButModeMoveSlabBackward:
SceneNoteMouseInteraction(G);
{
float old_front = I->m_view.m_clip().m_front;
float old_back = I->m_view.m_clip().m_back;
float old_origin = -I->m_view.pos().z;
SceneClip(G, SceneClipMode::Proportional,
-0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), nullptr, 0);
SceneDoRoving(G, old_front, old_back, old_origin, true, false);
}
break;
case cButModeZoomForward:
SceneNoteMouseInteraction(G);
{
float factor =
-((I->m_view.m_clipSafe().m_front + I->m_view.m_clipSafe().m_back) /
2) *
0.1 * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale);
if (factor <= 0.0F) {
I->m_view.translate(0.0f, 0.0f, factor);
I->m_view.m_clip().m_front -= factor;
I->m_view.m_clip().m_back -= factor;
UpdateFrontBackSafe(I);
}
}
break;
case cButModeZoomBackward:
SceneNoteMouseInteraction(G);
{
float factor =
((I->m_view.m_clipSafe().m_front + I->m_view.m_clipSafe().m_back) / 2) *
0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale);
if (factor >= 0.0F) {
I->m_view.translate(0.0f, 0.0f, factor);
I->m_view.m_clip().m_front -= factor;
I->m_view.m_clip().m_back -= factor;
UpdateFrontBackSafe(I);
}
}
break;
case cButModeMoveSlabAndZoomForward:
SceneNoteMouseInteraction(G);
{
float old_front = I->m_view.m_clip().m_front;
float old_back = I->m_view.m_clip().m_back;
float old_origin = -I->m_view.pos().z;
SceneClip(G, SceneClipMode::Proportional,
0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), nullptr, 0);
SceneDoRoving(G, old_front, old_back, old_origin, true, true);
}
break;
case cButModeMoveSlabAndZoomBackward:
SceneNoteMouseInteraction(G);
{
float old_front = I->m_view.m_clip().m_front;
float old_back = I->m_view.m_clip().m_back;
float old_origin = -I->m_view.pos().z;
SceneClip(G, SceneClipMode::Proportional,
-0.1F * SettingGetGlobal_f(G, cSetting_mouse_wheel_scale), nullptr, 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:
SceneLoopClick(block, button, x, y, mod);
return;
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, nullptr, I->Width, nullptr);
I->LastX = x;
I->LastY = y;
EditorReadyDrag(G, SettingGetGlobal_i(G, cSetting_state) - 1);
if (EditorDraggingObjectMatrix(G)) {
auto obj = EditorDragObject(G);
if (obj) {
if (SettingGetGlobal_b(G, cSetting_movie_auto_store)) {
ObjectTranslateTTT(obj, nullptr, true);
I->MotionGrabbedObj = obj;
obj->Grabbed = true;
if (SettingGetGlobal_i(G, cSetting_movie_auto_interpolate)) {
I->ReinterpolateFlag = true;
I->ReinterpolateObj = obj;
}
} else {
ObjectTranslateTTT(obj, nullptr, 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, nullptr, I->Width, nullptr);
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, nullptr, I->Width, nullptr);
if (SceneDoXYPick(G, x, y, click_side)) {
auto obj = I->LastPicked.context.object;
y = y - I->margin.bottom;
x = x - I->margin.left;
I->LastX = x;
I->LastY = y;
SceneClickTransformObject(G, obj, I->LastPicked, mode, is_single_click);
} 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, nullptr, I->Width, &click_side);
if (SceneDoXYPick(G, x, y, click_side)) {
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;
}
SceneClickPickBond(G, x, y, mode, I->LastPicked);
} 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, nullptr, I->Width, &click_side);
if (SceneDoXYPick(G, x, y, click_side)) {
auto obj = 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, nullptr, true);
I->MotionGrabbedObj = obj;
obj->Grabbed = true;
if (SettingGetGlobal_i(G, cSetting_movie_auto_interpolate)) {
I->ReinterpolateFlag = true;
I->ReinterpolateObj = obj;
}
} else {
ObjectTranslateTTT(obj, nullptr, false);
}
} break;
}
if (I->LastPicked.src.bond >= cPickableAtom) {
if (Feedback(G, FB_Scene, FB_Results)) {
auto buffer = obj->describeElement(I->LastPicked.src.index);
PRINTF " You clicked %s", buffer.c_str() ENDF(G);
OrthoRestorePrompt(G);
}
}
{
auto 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 = cAtomProtected_drag;
}
}
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, nullptr, I->Width, &click_side);
if (SceneDoXYPick(G, x, y, click_side)) {
auto obj = I->LastPicked.context.object;
assert(I->LastPicked.src.bond != cPickableNoPick);
if (mode == cButModeSimpleClick) {
float pos_store[3];
const float* pos = nullptr;
int const index = I->LastPicked.src.index; /* 1-based */
int const state = I->LastPicked.context.state;
auto objmol = dynamic_cast<ObjectMolecule const*>(obj);
if (objmol &&
ObjectMoleculeGetAtomTxfVertex(objmol, state, index, pos_store))
pos = pos_store;
PyMOL_SetClickReady(G->PyMOL, obj->Name, index, button, mod,
I->LastWinX, I->Height - (I->LastWinY + 1), pos,
state + 1 /* 1-based */, I->LastPicked.src.bond);
}
SceneClickObject(G, obj, I->LastPicked, mode, sel_mode_kw);
} else { // Nothing picked
SceneClickPickNothing(G, button, mod, mode);
}
}
I->StartX = I->LastX;
I->StartY = I->LastY;
}
}
/*========================================================================*/
void 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) {
I->Over = -1;
auto elem = &I->SceneVec.front();
for (std::size_t i = 0u; i < I->SceneVec.size(); i++) {
auto& elem_ = I->SceneVec[i];
if (elem_.drawn && elem_.rect.contains(x, y)) {
elem = &elem_;
I->Over = i;
break;
}
}
if (I->Over >= 0) {
release_handled = true;
switch (I->PressMode) {
case 1:
if (I->Over == I->Pressed) {
auto buffer = pymol::string_format("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 != cur_name) {
auto buffer = pymol::string_format("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.c_str());
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;
SceneLoopRelease(block, button, x, y, mod);
return;
}
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 = nullptr;
}
if (I->MotionGrabbedObj) {
if (ExecutiveValidateObjectPtr(G, I->MotionGrabbedObj, 0)) {
I->MotionGrabbedObj->Grabbed = false;
I->MotionGrabbedObj = nullptr;
}
}
}
/*========================================================================*/
void 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;
pymol::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) {
SceneLoopDrag(block, x, y, mod);
return;
}
if (I->ButtonsShown && I->PressMode) {
if (I->ButtonsValid) {
drag_handled = true;
I->Over = -1;
auto elem = &I->SceneVec.front();
for (std::size_t i = 0u; i < I->SceneVec.size(); i++) {
auto& elem_ = I->SceneVec[i];
if (elem_.drawn && elem_.rect.contains(x, y)) {
elem = &elem_;
I->Over = i;
OrthoDirty(G);
break;
}
}
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 != cur_name) {
int animate = -1;
if (mod & cOrthoCTRL)
animate = 0;
auto buffer = pymol::string_format("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)) {
auto pressed = &I->SceneVec[I->Pressed];
std::string 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;
}
buffer = pymol::string_format("cmd.scene_order(['''%s''','''%s'''])", first->name,
second->name);
} else {
buffer = pymol::string_format("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 = I->LastPicked.context.object;
if (obj)
switch (obj->type) {
case cObjectGadget: {
auto gad = static_cast<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, nullptr);
}
/* transform into model coodinate space */
switch (obj->getRenderContext()) {
case pymol::RenderContext::UnitWindow: {
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 pymol::RenderContext::Camera:
v2[0] = (x - I->LastX) * vScale;
v2[1] = (y - I->LastY) * vScale;
v2[2] = 0;
MatrixInvTransformC44fAs33f3f(glm::value_ptr(I->m_view.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, nullptr);
}
virtual_trackball =
SettingGet_i(G, nullptr, nullptr, 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, nullptr, nullptr, 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, nullptr, nullptr, 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(glm::value_ptr(I->m_view.rotMatrix()), axis, v2);
v1[0] = (float) (cPI * theta / 180.0);
EditorDrag(G, nullptr, -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(glm::value_ptr(I->m_view.origin()), v1);
vScale = SceneGetExactScreenVertexScale(G, v1);
if (stereo_via_adjacent_array(I->StereoMode)) {
x = get_stereo_x(x, &I->LastX, I->Width, nullptr);
}
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(glm::value_ptr(I->m_view.rotMatrix()), v2, v2);
MatrixInvTransformC44fAs33f3f(glm::value_ptr(I->m_view.rotMatrix()), v3, v3);
EditorDrag(G, nullptr, -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 = 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 */
{
auto gad = static_cast<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, nullptr);
}
/* transform into model coodinate space */
switch (obj->getRenderContext()) {
case pymol::RenderContext::UnitWindow: {
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 pymol::RenderContext::Camera:
v2[0] = (x - I->LastX) * vScale;
v2[1] = (y - I->LastY) * vScale;
v2[2] = 0;
MatrixInvTransformC44fAs33f3f(glm::value_ptr(I->m_view.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, nullptr);
}
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(glm::value_ptr(I->m_view.rotMatrix()), v2, v2);
MatrixInvTransformC44fAs33f3f(glm::value_ptr(I->m_view.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) {
auto state = SceneGetState(G);
copy3f(I->LastPickVertex, v1);
vScale = SceneGetExactScreenVertexScale(G, v1);
if (stereo_via_adjacent_array(I->StereoMode)) {
x = get_stereo_x(x, &I->LastX, I->Width, nullptr);
}
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(glm::value_ptr(I->m_view.rotMatrix()), v2, v2);
MatrixInvTransformC44fAs33f3f(glm::value_ptr(I->m_view.rotMatrix()), v3, v3);
ObjectSliceDrag(slice, state, 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, nullptr);
}
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(glm::value_ptr(I->m_view.rotMatrix()), v2, v2);
MatrixInvTransformC44fAs33f3f(glm::value_ptr(I->m_view.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, glm::value_ptr(I->m_view.origin()));
if (stereo_via_adjacent_array(I->StereoMode)) {
x = get_stereo_x(x, &I->LastX, I->Width, nullptr);
}
v2[0] = (x - I->LastX) * vScale;
v2[1] = (y - I->LastY) * vScale;
v2[2] = 0.0F;
moved_flag = false;
if (I->LastX != x) {
I->m_view.translate(v2[0], 0.0f, 0.0f);
I->LastX = x;
SceneInvalidate(G);
moved_flag = true;
}
if (I->LastY != y) {
I->m_view.translate(0.0f, v2[1], 0.0f);
I->LastY = y;
SceneInvalidate(G);
moved_flag = true;
}
EditorFavorOrigin(G, nullptr);
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, nullptr, nullptr, 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, nullptr);
}
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, nullptr, nullptr, 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, nullptr, nullptr, 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, nullptr, nullptr, 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->m_view.m_clip().m_front;
old_back = I->m_view.m_clip().m_back;
old_origin = -I->m_view.pos().z;
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->m_view.pos().z);
if (!SettingGetGlobal_b(G, cSetting_legacy_mouse_zoom))
factor = -factor;
I->m_view.translate(0.0f, 0.0f, factor);
I->m_view.m_clip().m_front -= factor;
I->m_view.m_clip().m_back -= factor;
UpdateFrontBackSafe(I);
}
I->LastY = y;
SceneInvalidate(G);
adjust_flag = true;
}
break;
case cButModeClipNF:
if (I->LastX != x) {
I->m_view.m_clip().m_back -= (((float) x) - I->LastX) / 10;
I->LastX = x;
moved_flag = true;
}
if (I->LastY != y) {
I->m_view.m_clip().m_front -= (((float) y) - I->LastY) / 10;
I->LastY = y;
moved_flag = true;
}
if (moved_flag) {
SceneClipSet(G, I->m_view.m_clip().m_front, I->m_view.m_clip().m_back);
}
break;
case cButModeClipN:
if (I->LastX != x || I->LastY != y) {
I->m_view.m_clip().m_front -= (x - I->LastX + y - I->LastY) / 10.f;
I->LastX = x;
I->LastY = y;
SceneClipSet(G, I->m_view.m_clip().m_front, I->m_view.m_clip().m_back);
moved_flag = true;
}
break;
case cButModeClipF:
if (I->LastX != x || I->LastY != y) {
I->m_view.m_clip().m_back -= (x - I->LastX + y - I->LastY) / 10.f;
I->LastX = x;
I->LastY = y;
SceneClipSet(G, I->m_view.m_clip().m_front, I->m_view.m_clip().m_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, nullptr, 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->m_view.m_clipSafe().m_front + I->m_view.m_clipSafe().m_back) /
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, nullptr, 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;
}
}
}