mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-03 19:54:24 +08:00
17759 lines
527 KiB
C++
17759 lines
527 KiB
C++
/*
|
|
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:
|
|
-*
|
|
-*
|
|
-*
|
|
Z* -------------------------------------------------------------------
|
|
*/
|
|
#include "os_gl.h"
|
|
#include "os_predef.h"
|
|
#include "os_python.h"
|
|
#include "os_std.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <clocale>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
#include "pymol/type_traits.h"
|
|
|
|
#ifdef PYMOL_OPENMP
|
|
#include <omp.h>
|
|
#endif
|
|
|
|
#include "AtomIterators.h"
|
|
#include "Base.h"
|
|
#include "ButMode.h"
|
|
#include "CifFile.h"
|
|
#include "Color.h"
|
|
#include "Control.h"
|
|
#include "Editor.h"
|
|
#include "Executive.h"
|
|
#include "Feedback.h"
|
|
#include "Lex.h"
|
|
#include "List.h"
|
|
#include "List.h"
|
|
#include "ListMacros.h"
|
|
#include "Map.h"
|
|
#include "Match.h"
|
|
#include "Matrix.h"
|
|
#include "Menu.h"
|
|
#include "Movie.h"
|
|
#include "MyPNG.h"
|
|
#include "ObjectAlignment.h"
|
|
#include "ObjectCGO.h"
|
|
#include "ObjectCallback.h"
|
|
#include "ObjectCurve.h"
|
|
#include "ObjectDist.h"
|
|
#include "ObjectGadgetRamp.h"
|
|
#include "ObjectGroup.h"
|
|
#include "ObjectMap.h"
|
|
#include "ObjectMesh.h"
|
|
#include "ObjectMolecule3.h"
|
|
#include "ObjectSlice.h"
|
|
#include "ObjectSurface.h"
|
|
#include "ObjectVolume.h"
|
|
#include "Ortho.h"
|
|
#include "P.h"
|
|
#include "PConv.h"
|
|
#include "Parse.h"
|
|
#include "PlugIOManager.h"
|
|
#include "PyMOL.h"
|
|
#include "PyMOLOptions.h"
|
|
#include "RepDot.h"
|
|
#include "Scene.h"
|
|
#include "ScenePicking.h"
|
|
#include "SceneRay.h"
|
|
#include "ScrollBar.h"
|
|
#include "SculptCache.h"
|
|
#include "Seeker.h"
|
|
#include "Selector.h"
|
|
#include "Seq.h"
|
|
#include "Setting.h"
|
|
#include "SpecRec.h"
|
|
#include "TTT.h"
|
|
#include "Text.h"
|
|
#include "Tracker.h"
|
|
#include "TrackerList.h"
|
|
#include "Util.h"
|
|
#include "Util2.h"
|
|
#include "Vector.h"
|
|
#include "Version.h"
|
|
#include "Wizard.h"
|
|
#include "Word.h"
|
|
#include "main.h"
|
|
|
|
#include "OVContext.h"
|
|
#include "OVLexicon.h"
|
|
|
|
#include "ExecutiveLoad.h"
|
|
#include "File.h"
|
|
#include "FileStream.h"
|
|
#include "ShaderMgr.h"
|
|
|
|
#include "MovieScene.h"
|
|
#include "Texture.h"
|
|
|
|
#include "Property.h"
|
|
|
|
#ifdef _PYMOL_OPENVR
|
|
#include "OpenVRMode.h"
|
|
#endif
|
|
|
|
#ifndef _PYMOL_NOPY
|
|
#include "ce_types.h"
|
|
#endif
|
|
|
|
#include <glm/gtc/quaternion.hpp>
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
#include <glm/vec3.hpp>
|
|
|
|
#ifdef WIN32
|
|
#define mkstemp _mktemp_s
|
|
#endif
|
|
|
|
#define cTempRectSele "_rect"
|
|
#define cLeftButSele "lb"
|
|
#define cIndicateSele "indicate"
|
|
|
|
#ifndef NO_MMLIBS
|
|
#include "mmpymolx.h"
|
|
#endif
|
|
|
|
/**
|
|
* Macro with error handling to get a selection ID from a non-empty expression.
|
|
* @param expression Selection expression
|
|
* @param[out] tmpsele Name of the pymol::Result<SelectorTmp> instance
|
|
* @param[out] seleID Name of the SelectorID_t instance
|
|
*/
|
|
#define SETUP_SELE(expression, tmpsele, seleID) \
|
|
auto tmpsele = SelectorTmp::make(G, expression); \
|
|
p_return_if_error(tmpsele); \
|
|
SelectorID_t seleID = tmpsele->getIndex(); \
|
|
if (seleID < 0) { \
|
|
return pymol::make_error("This should not happen - PyMOL may have a bug"); \
|
|
} \
|
|
assert(seleID != cSelectionInvalid)
|
|
|
|
/**
|
|
* Macro with error handling to get a selection ID from a non-empty expression
|
|
* `sN`. Assigns `tmpseleN` and `seleN`.
|
|
*/
|
|
#define SETUP_SELE_DEFAULT(N) SETUP_SELE(s##N, tmpsele##N, sele##N)
|
|
|
|
/**
|
|
* Macro with error handling to get a selection ID from a non-empty expression
|
|
* `sN`. Assigns `tmpseleN` and `seleN`.
|
|
* Adds "Selection N: " prefix to error messages.
|
|
* @param same_value If `sN` is "same" then use this value for `seleN`
|
|
*/
|
|
#define SETUP_SELE_DEFAULT_PREFIXED(N, same_value) \
|
|
pymol::Result<SelectorTmp> tmpsele##N; \
|
|
SelectorID_t sele##N = same_value; \
|
|
if (!WordMatchExact(G, s##N, cKeywordSame, true)) { \
|
|
tmpsele##N = SelectorTmp::make(G, s##N); \
|
|
p_return_if_error_prefixed(tmpsele##N, "Selection " #N ": "); \
|
|
sele##N = (tmpsele##N)->getIndex(); \
|
|
} \
|
|
if (sele##N == cSelectionInvalid) { \
|
|
return pymol::make_error("Invalid selection " #N); \
|
|
}
|
|
|
|
/* routines that still need to be updated for Tracker list iteration
|
|
|
|
Low priority:
|
|
|
|
ExecutiveSculptIterate
|
|
ExecutiveSculptActivate
|
|
ExecutiveSculptDeactivate
|
|
ExecutiveRenameObjectAtoms
|
|
ExecutiveSpheroid
|
|
|
|
*/
|
|
|
|
/**
|
|
* @brief Processes CIF string and manages either Map or Molecule object
|
|
* @param I Pointer to a possibly existing object
|
|
* @param object_name Name of the object
|
|
* @param st CIF string
|
|
* @param frame State/Frame number
|
|
* @param discrete Discrete object flag
|
|
* @param quiet Suppress output
|
|
* @param multiplex Multiplex flag
|
|
* @param zoom Zoom flag
|
|
*/
|
|
static pymol::Result<pymol::CObject*> ExecutiveProcessCif(PyMOLGlobals* G,
|
|
pymol::CObject* I, const char* object_name, const char* st, int frame,
|
|
int discrete, int quiet, int multiplex, int zoom);
|
|
|
|
int ExecutiveGetNamesListFromPattern(
|
|
PyMOLGlobals* G, const char* name, int allow_partial, int expand_groups);
|
|
|
|
pymol::TrackerAdapter<SpecRec> ExecutiveGetSpecRecsFromPattern(PyMOLGlobals* G,
|
|
pymol::zstring_view str, bool allow_partial, bool expand_groups)
|
|
{
|
|
return pymol::TrackerAdapter<SpecRec>(
|
|
G->Executive->Tracker, ExecutiveGetNamesListFromPattern(
|
|
G, str.c_str(), allow_partial, expand_groups));
|
|
}
|
|
|
|
/**
|
|
* Get the list of objects which match the pattern, or all objects
|
|
* if pattern equals 'all'.
|
|
* @param str pattern string provided by user
|
|
* @return List of borrowed object pointers
|
|
*/
|
|
static std::vector<pymol::CObject*> ExecutiveGetObjectsFromPattern(
|
|
PyMOLGlobals* G, pymol::zstring_view pattern)
|
|
{
|
|
std::vector<pymol::CObject*> objects;
|
|
|
|
for (auto& rec : ExecutiveGetSpecRecsFromPattern(G, pattern)) {
|
|
switch (rec.type) {
|
|
case cExecObject:
|
|
objects.push_back(rec.obj);
|
|
break;
|
|
case cExecAll:
|
|
for (auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
|
|
if (rec.type == cExecObject) {
|
|
objects.push_back(rec.obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return objects;
|
|
}
|
|
|
|
static void ExecutiveSpecEnable(
|
|
PyMOLGlobals* G, SpecRec* rec, int parents, int log);
|
|
static void ExecutiveSetAllRepVisMask(PyMOLGlobals* G, int repmask, int state);
|
|
static int ExecutiveSetObjectMatrix2(
|
|
PyMOLGlobals* G, pymol::CObject* obj, int state, double* matrix);
|
|
static int ExecutiveGetObjectMatrix2(PyMOLGlobals* G, pymol::CObject* obj,
|
|
int state, double** matrix, int incl_ttt);
|
|
static pymol::Result<> ExecutiveTransformObjectSelection2(PyMOLGlobals* G,
|
|
pymol::CObject* obj, int state, const char* s1, int log,
|
|
const float* matrix, int homogenous, int global);
|
|
|
|
/**
|
|
* ObjectIterator methods
|
|
*/
|
|
void ObjectIterator::reset()
|
|
{
|
|
rec = G->Executive->Spec;
|
|
|
|
// DEBUG assume first element is always "all"
|
|
if (rec->type != cExecAll)
|
|
printf("Error: first SpecRec is not cExecAll\n");
|
|
}
|
|
|
|
bool ObjectIterator::next()
|
|
{
|
|
if (!rec || !(rec = rec->next))
|
|
return false;
|
|
|
|
if (rec->type != cExecObject)
|
|
return next();
|
|
|
|
return true;
|
|
}
|
|
|
|
pymol::CObject* ObjectIterator::getObject()
|
|
{
|
|
return rec->obj;
|
|
}
|
|
|
|
/**
|
|
* Find object of given type, or delete object if it exists but has the wrong
|
|
* type.
|
|
* @param name Object name
|
|
* @return nullptr if object can't be found or has the wrong type
|
|
*/
|
|
template <typename ObjectT>
|
|
ObjectT* ExecutiveFindOrDeleteObject(PyMOLGlobals* G, pymol::zstring_view name)
|
|
{
|
|
auto anyObj = ExecutiveFindObjectByName(G, name.c_str());
|
|
auto obj = dynamic_cast<ObjectT*>(anyObj);
|
|
if (anyObj && !obj) {
|
|
// incompatible object with the same name
|
|
ExecutiveDelete(G, name.c_str());
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* True if `rec` and all its parent groups are enabled
|
|
*/
|
|
static bool SpecRecIsEnabled(const SpecRec* rec)
|
|
{
|
|
while (rec->visible && (rec = rec->group)) {
|
|
}
|
|
return !rec;
|
|
}
|
|
|
|
bool ExecutiveIsObjectType(const SpecRec& rec, int cObjectType)
|
|
{
|
|
return rec.type == cExecObject && rec.obj->type == cObjectType;
|
|
}
|
|
|
|
/* ======================================================= */
|
|
|
|
static void ReportEnabledChange(PyMOLGlobals* G, SpecRec* rec)
|
|
{
|
|
#ifdef _PYMOL_LIB
|
|
if (G->CallbackObject && G->enabledCallback) {
|
|
G->enabledCallback(G->CallbackObject, rec->name, rec->visible);
|
|
}
|
|
#endif
|
|
OrthoInvalidateDoDraw(G);
|
|
ExecutiveInvalidateSelectionIndicatorsCGO(G);
|
|
}
|
|
|
|
int ExecutiveGroupMotionModify(PyMOLGlobals* G, pymol::CObject* group,
|
|
ViewElemAction action, int index, int count, int target, int freeze)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int result = true;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetExpandedGroupList(G, group->Name);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type != cObjectGroup) {
|
|
ObjectMotionModify(
|
|
rec->obj, action, index, count, target, freeze, true);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
return result;
|
|
}
|
|
|
|
int ExecutiveGroupMotion(PyMOLGlobals* G, pymol::CObject* group,
|
|
MViewAction action, int first, int last, float power, float bias,
|
|
int simple, float linear, int wrap, int hand, int window, int cycles,
|
|
int state, int quiet)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int result = true;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetExpandedGroupList(G, group->Name);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type != cObjectGroup) {
|
|
ObjectMotion(rec->obj, action, first, last, power, bias, simple,
|
|
linear, wrap, hand, window, cycles, state, quiet);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
return result;
|
|
}
|
|
|
|
int ExecutiveGroupCombineTTT(PyMOLGlobals* G, pymol::CObject* group,
|
|
const float* ttt, int reverse_order, int store)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int result = true;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetExpandedGroupList(G, group->Name);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type != cObjectGroup) {
|
|
ObjectCombineTTT(rec->obj, ttt, reverse_order, store);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
return result;
|
|
}
|
|
|
|
int ExecutiveGroupTranslateTTT(
|
|
PyMOLGlobals* G, pymol::CObject* group, const float* v, int store)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int result = true;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetExpandedGroupList(G, group->Name);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type != cObjectGroup) {
|
|
ObjectTranslateTTT(rec->obj, v, store);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
return result;
|
|
}
|
|
|
|
int ExecutiveMotionView(PyMOLGlobals* G, MViewAction action, int first,
|
|
int last, float power, float bias, int simple, float linear,
|
|
const char* name, int wrap, int hand, int window, int cycles,
|
|
const char* scene_name, float scene_cut, int state, int quiet, int autogen)
|
|
{
|
|
int ok = true;
|
|
|
|
CExecutive* I = G->Executive;
|
|
|
|
if (wrap < 0) {
|
|
wrap = SettingGetGlobal_b(G, cSetting_movie_loop);
|
|
}
|
|
|
|
if ((!name) || (!name[0]) || (!strcmp(name, cKeywordNone)) ||
|
|
(!strcmp(name, cKeywordAll)) || (!strcmp(name, cKeywordSame))) {
|
|
if (autogen) {
|
|
/* if autogenerated, then use current settings */
|
|
power = SettingGetGlobal_f(G, cSetting_motion_power);
|
|
bias = SettingGetGlobal_f(G, cSetting_motion_bias);
|
|
linear = SettingGetGlobal_f(G, cSetting_motion_linear);
|
|
hand = SettingGetGlobal_i(G, cSetting_motion_hand);
|
|
}
|
|
/* camera */
|
|
|
|
ok = MovieView(G, action, first, last, power, bias, true, linear, wrap,
|
|
hand, window, cycles, scene_name, scene_cut, state, quiet);
|
|
|
|
if (name && name[0] && strcmp(name, cKeywordNone)) {
|
|
/* also do all other objects */
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (autogen) {
|
|
power = SettingGet_f(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_power);
|
|
bias = SettingGet_f(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_bias);
|
|
simple = SettingGet_i(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_simple);
|
|
linear = SettingGet_f(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_linear);
|
|
hand = SettingGet_i(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_hand);
|
|
}
|
|
if ((ObjectGetSpecLevel(rec->obj, 0) >= 0) ||
|
|
(!strcmp(name, cKeywordAll))) {
|
|
ok = ObjectMotion(rec->obj, action, first, last, power, bias,
|
|
simple < 0 ? 0 : 1, linear, wrap, hand, window, cycles, state,
|
|
quiet);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else { /* pattern */
|
|
CTracker* I_Tracker = I->Tracker;
|
|
SpecRec* rec = nullptr;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (autogen) {
|
|
power = SettingGet_f(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_power);
|
|
bias = SettingGet_f(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_bias);
|
|
simple = SettingGet_i(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_simple);
|
|
linear = SettingGet_f(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_linear);
|
|
hand = SettingGet_i(
|
|
G, nullptr, rec->obj->Setting.get(), cSetting_motion_hand);
|
|
}
|
|
|
|
ok = ObjectMotion(rec->obj, action, first, last, power, bias,
|
|
simple < 0 ? 0 : simple, linear, wrap, hand, window, cycles,
|
|
state, quiet);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
|
|
// fix for PYMOL-1465
|
|
OrthoReshape(G, -1, -1, false);
|
|
}
|
|
ExecutiveCountMotions(G);
|
|
return ok;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveMotionViewModify(PyMOLGlobals* G,
|
|
ViewElemAction action, int index, int count, int target, const char* name,
|
|
int freeze, int quiet)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
if ((!name) || (!name[0]) || (!strcmp(name, cKeywordNone)) ||
|
|
(!strcmp(name, cKeywordSame)) || (!strcmp(name, cKeywordAll))) {
|
|
/* camera */
|
|
if (MovieGetSpecLevel(G, 0) >= 0) {
|
|
MovieViewModify(G, action, index, count, target, true, true);
|
|
}
|
|
if ((!name) || strcmp(name, cKeywordNone)) {
|
|
/* also do all other objects */
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, 0) >= 0) {
|
|
ObjectMotionModify(
|
|
rec->obj, action, index, count, target, true, true);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
ExecutiveMotionTrim(G);
|
|
} else {
|
|
ExecutiveMotionExtend(G, true);
|
|
}
|
|
|
|
if ((!freeze) && SettingGetGlobal_i(G, cSetting_movie_auto_interpolate)) {
|
|
ExecutiveMotionReinterpolate(G);
|
|
}
|
|
} else { /* pattern */
|
|
CTracker* I_Tracker = I->Tracker;
|
|
SpecRec* rec = nullptr;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, 0) >= 0) {
|
|
/* only modify objects with motion matrices */
|
|
ObjectMotionModify(
|
|
rec->obj, action, index, count, target, freeze, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
ExecutiveCountMotions(G);
|
|
SceneCountFrames(G);
|
|
return {};
|
|
}
|
|
|
|
void ExecutiveMotionReinterpolate(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
if (MovieGetSpecLevel(G, 0) >= 0) {
|
|
MovieViewReinterpolate(G);
|
|
}
|
|
break;
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, 0) >= 0) {
|
|
ObjectMotionReinterpolate(rec->obj);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExecutiveMotionTrim(PyMOLGlobals* G)
|
|
{
|
|
int n_frame = MovieGetLength(G);
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, 0) >= 0) {
|
|
ObjectMotionTrim(rec->obj, n_frame);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExecutiveMotionExtend(PyMOLGlobals* G, int freeze)
|
|
{
|
|
int n_frame = 0;
|
|
int max_length = 0;
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
if (MovieGetSpecLevel(G, -1) > 0)
|
|
n_frame = MovieGetLength(G);
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, -1) > 0) {
|
|
int length = ObjectMotionGetLength(rec->obj);
|
|
if (max_length < length)
|
|
max_length = length;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (max_length) {
|
|
if (n_frame < max_length)
|
|
MovieViewTrim(G, max_length);
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, -1) > 0) {
|
|
ObjectMotionTrim(rec->obj, max_length);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ((!freeze) && SettingGetGlobal_i(G, cSetting_movie_auto_interpolate)) {
|
|
ExecutiveMotionReinterpolate(G);
|
|
}
|
|
}
|
|
|
|
int ExecutiveCountMotions(PyMOLGlobals* G)
|
|
{
|
|
int count = 0;
|
|
CExecutive* I = G->Executive;
|
|
if (MovieGetLength(G)) {
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
if (MovieGetSpecLevel(G, 0) >= 0)
|
|
count++;
|
|
break;
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, 0) >= 0)
|
|
count++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count < 1 && SceneGetNFrame(G) > 1)
|
|
count = 1;
|
|
|
|
if (count != I->LastMotionCount) {
|
|
if (SettingGetGlobal_i(G, cSetting_movie_panel)) {
|
|
OrthoDoViewportWhenReleased(G);
|
|
}
|
|
I->LastMotionCount = count;
|
|
}
|
|
|
|
return (count);
|
|
}
|
|
|
|
void ExecutiveMotionDraw(
|
|
PyMOLGlobals* G, BlockRect* rect, int expected, CGO* orthoCGO)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
int frames = MovieGetLength(G);
|
|
BlockRect draw_rect = *rect;
|
|
int count = 0;
|
|
int height = rect->top - rect->bottom;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
if (MovieGetSpecLevel(G, 0) >= 0) {
|
|
int presentation = SettingGetGlobal_b(G, cSetting_presentation);
|
|
if (presentation) {
|
|
expected = 1;
|
|
}
|
|
draw_rect.top = rect->top - (height * count) / expected;
|
|
draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
|
|
MovieDrawViewElem(G, &draw_rect, frames, orthoCGO);
|
|
count++;
|
|
if (presentation) {
|
|
goto done;
|
|
}
|
|
}
|
|
break;
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, 0) >= 0) {
|
|
draw_rect.top = rect->top - (height * count) / expected;
|
|
draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
|
|
ObjectDrawViewElem(rec->obj, &draw_rect, frames, orthoCGO);
|
|
count++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
done:
|
|
return;
|
|
}
|
|
|
|
void ExecutiveMotionMenuActivate(PyMOLGlobals* G, BlockRect* rect, int expected,
|
|
int passive, int x, int y, int same)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
BlockRect draw_rect = *rect;
|
|
int count = 0;
|
|
int height = rect->top - rect->bottom;
|
|
if (same) {
|
|
if (MovieGetSpecLevel(G, 0) >= 0) {
|
|
int n_frame = MovieGetLength(G);
|
|
int frame = MovieXtoFrame(G, &draw_rect, n_frame, x, false);
|
|
WordType frame_str = "0";
|
|
if ((frame >= 0) && (frame < n_frame)) {
|
|
sprintf(frame_str, "%d", frame + 1);
|
|
}
|
|
MenuActivate2Arg(
|
|
G, x, y, x, y, passive, "obj_motion", cKeywordSame, frame_str);
|
|
}
|
|
} else {
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
if (MovieGetSpecLevel(G, 0) >= 0) {
|
|
draw_rect.top = rect->top - (height * count) / expected;
|
|
draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
|
|
if ((y > draw_rect.bottom) && (y < draw_rect.top)) {
|
|
int n_frame = MovieGetLength(G);
|
|
int frame = MovieXtoFrame(G, &draw_rect, n_frame, x, false);
|
|
WordType frame_str = "0";
|
|
if ((frame >= 0) && (frame < n_frame)) {
|
|
sprintf(frame_str, "%d", frame + 1);
|
|
}
|
|
MenuActivate1Arg(
|
|
G, x, y, x, y, passive, "camera_motion", frame_str);
|
|
goto done;
|
|
}
|
|
count++;
|
|
}
|
|
break;
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, 0) >= 0) {
|
|
draw_rect.top = rect->top - (height * count) / expected;
|
|
draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
|
|
if ((y > draw_rect.bottom) && (y < draw_rect.top)) {
|
|
int n_frame = MovieGetLength(G);
|
|
int frame = MovieXtoFrame(G, &draw_rect, n_frame, x, false);
|
|
WordType frame_str = "0";
|
|
if ((frame >= 0) && (frame < n_frame)) {
|
|
sprintf(frame_str, "%d", frame + 1);
|
|
}
|
|
MenuActivate2Arg(G, x, y, x, y, passive, "obj_motion",
|
|
rec->obj->Name, frame_str);
|
|
goto done;
|
|
}
|
|
count++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
done:
|
|
return;
|
|
}
|
|
|
|
void ExecutiveMotionClick(PyMOLGlobals* G, BlockRect* rect, int mode,
|
|
int expected, int x, int y, int nearest)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
BlockRect draw_rect = *rect;
|
|
int count = 0;
|
|
int height = rect->top - rect->bottom;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
if (MovieGetSpecLevel(G, 0) >= 0) {
|
|
draw_rect.top = rect->top - (height * count) / expected;
|
|
draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
|
|
if ((y >= draw_rect.bottom) && (y <= draw_rect.top)) {
|
|
MoviePrepareDrag(G, &draw_rect, NULL, mode, x, y, nearest);
|
|
goto done;
|
|
}
|
|
count++;
|
|
}
|
|
break;
|
|
case cExecObject:
|
|
if (ObjectGetSpecLevel(rec->obj, 0) >= 0) {
|
|
MoviePrepareDrag(G, rect, NULL, mode, x, y, nearest);
|
|
draw_rect.top = rect->top - (height * count) / expected;
|
|
draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
|
|
if ((y >= draw_rect.bottom) && (y <= draw_rect.top)) {
|
|
MoviePrepareDrag(G, &draw_rect, rec->obj, mode, x, y, nearest);
|
|
goto done;
|
|
}
|
|
count++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
done:
|
|
return;
|
|
}
|
|
|
|
int ExecutiveReference(
|
|
PyMOLGlobals* G, int action, const char* sele, int state, int quiet)
|
|
{
|
|
int result = -1;
|
|
int s1 = SelectorIndexByName(G, sele);
|
|
if (s1 >= 0) {
|
|
ObjectMoleculeOpRec op;
|
|
ObjectMoleculeOpRecInit(&op);
|
|
|
|
switch (action) {
|
|
case 1:
|
|
op.code = OMOP_ReferenceStore;
|
|
break;
|
|
case 2:
|
|
op.code = OMOP_ReferenceRecall;
|
|
break;
|
|
case 3:
|
|
op.code = OMOP_ReferenceValidate;
|
|
break;
|
|
case 4:
|
|
op.code = OMOP_ReferenceSwap;
|
|
break;
|
|
}
|
|
op.i1 = state;
|
|
op.i2 = 0;
|
|
ExecutiveObjMolSeleOp(G, s1, &op);
|
|
result = op.i2;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Does the object and state validation which is common to isosurface, isomesh,
|
|
* and volume creation.
|
|
*/
|
|
template <typename ObjectT>
|
|
static pymol::Result<> EtcHelper(PyMOLGlobals* G, //
|
|
const char* obj_name, int& obj_state, ObjectT*& origObj, //
|
|
const char* map_name, int& map_state, ObjectMap*& mapObj, //
|
|
int& multi)
|
|
{
|
|
if (obj_state < -3) {
|
|
return pymol::make_error("Invalid state ", obj_state + 1);
|
|
}
|
|
|
|
if (map_state < -3) {
|
|
return pymol::make_error("Invalid source_state ", map_state + 1);
|
|
}
|
|
|
|
mapObj = ExecutiveFindObject<ObjectMap>(G, map_name);
|
|
if (!mapObj) {
|
|
return pymol::make_error("Map object \"", map_name, "\" not found");
|
|
}
|
|
|
|
// Fail if object with same name but different type exists
|
|
auto anyOrigObj = ExecutiveFindObjectByName(G, obj_name);
|
|
origObj = dynamic_cast<ObjectT*>(anyOrigObj);
|
|
if (anyOrigObj && !origObj) {
|
|
return pymol::make_error("Object name ", obj_name,
|
|
" already exists as another type. Cannot overwrite.");
|
|
}
|
|
|
|
switch (obj_state) {
|
|
case -1:
|
|
// all states
|
|
obj_state = 0;
|
|
map_state = -1;
|
|
break;
|
|
case -2:
|
|
// current state
|
|
obj_state = SceneGetState(G);
|
|
if (map_state < 0)
|
|
map_state = obj_state;
|
|
break;
|
|
case -3:
|
|
// append mode
|
|
obj_state = origObj ? origObj->getNFrame() : 0;
|
|
if (map_state < 0)
|
|
map_state = obj_state;
|
|
break;
|
|
}
|
|
|
|
switch (map_state) {
|
|
case -1:
|
|
// all states
|
|
map_state = 0;
|
|
multi = true;
|
|
break;
|
|
case -2:
|
|
// current state
|
|
map_state = SceneGetState(G);
|
|
break;
|
|
case -3:
|
|
// append mode
|
|
map_state = mapObj->getNFrame() - 1;
|
|
break;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveIsosurfaceEtc(PyMOLGlobals* G, const char* surf_name,
|
|
const char* map_name, float lvl, const char* sele, float fbuf, int state,
|
|
float carve, int map_state, int side, int quiet, int surf_mode)
|
|
{
|
|
int c;
|
|
ObjectSurface *obj = nullptr, *origObj = nullptr;
|
|
ObjectMap* mapObj;
|
|
float mn[3] = {0, 0, 0};
|
|
float mx[3] = {15, 15, 15};
|
|
pymol::vla<float> vert_vla;
|
|
ObjectMapState* ms;
|
|
int multi = false;
|
|
|
|
auto res0 = EtcHelper(
|
|
G, surf_name, state, origObj, map_name, map_state, mapObj, multi);
|
|
if (!res0) {
|
|
return res0.error();
|
|
}
|
|
|
|
{
|
|
while (1) {
|
|
ms = ObjectMapStateGetActive(mapObj, map_state);
|
|
if (ms) {
|
|
int box_mode = (sele && sele[0]) ? 1 : 0;
|
|
switch (box_mode) {
|
|
case 0: /* using map to get extents */
|
|
for (c = 0; c < 3; c++) {
|
|
mn[c] = ms->Corner[c];
|
|
mx[c] = ms->Corner[3 * 7 + c];
|
|
}
|
|
if (!ms->Matrix.empty()) {
|
|
transform44d3f(ms->Matrix.data(), mn, mn);
|
|
transform44d3f(ms->Matrix.data(), mx, mx);
|
|
{
|
|
float tmp;
|
|
int a;
|
|
for (a = 0; a < 3; a++)
|
|
if (mn[a] > mx[a]) {
|
|
tmp = mn[a];
|
|
mn[a] = mx[a];
|
|
mx[a] = tmp;
|
|
}
|
|
}
|
|
}
|
|
carve = 0.0F;
|
|
break;
|
|
case 1: /* using selection to get extents */
|
|
{
|
|
auto tmpsele = SelectorTmp2::make(G, sele);
|
|
p_return_if_error(tmpsele);
|
|
auto s1 = tmpsele->getName();
|
|
ExecutiveGetExtent(G, s1, mn, mx, false, -1, false); /* TODO state */
|
|
if (carve != 0.0F) {
|
|
vert_vla =
|
|
pymol::vla_take_ownership(ExecutiveGetVertexVLA(G, s1, state));
|
|
if (fbuf <= R_SMALL4)
|
|
fbuf = fabs(carve);
|
|
}
|
|
}
|
|
for (c = 0; c < 3; c++) {
|
|
mn[c] -= fbuf;
|
|
mx[c] += fbuf;
|
|
}
|
|
break;
|
|
}
|
|
PRINTFB(G, FB_CCmd, FB_Blather)
|
|
" Isosurface: buffer %8.3f carve %8.3f\n", fbuf, carve ENDFB(G);
|
|
obj = ObjectSurfaceFromBox(G, origObj, mapObj, map_state, state, mn, mx,
|
|
lvl, static_cast<cIsosurfaceMode>(surf_mode), carve,
|
|
std::move(vert_vla), static_cast<cIsosurfaceSide>(side), quiet);
|
|
/* copy the map's TTT */
|
|
ExecutiveMatrixCopy2(G, mapObj, obj, 1, 1, -1, -1, false, 0, quiet);
|
|
|
|
if (!origObj) {
|
|
ObjectSetName(obj, surf_name);
|
|
ExecutiveManageObject(G, obj, -1, quiet);
|
|
}
|
|
if (SettingGetGlobal_b(G, cSetting_isomesh_auto_state))
|
|
if (obj)
|
|
ObjectGotoState(obj, state);
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_ObjectSurface, FB_Actions)
|
|
" Isosurface: created \"%s\", setting level to %5.3f\n", surf_name,
|
|
lvl ENDFB(G);
|
|
}
|
|
} else if (!multi) {
|
|
return pymol::make_error(
|
|
"state ", map_state + 1, " not present in map \"", map_name, "\"");
|
|
}
|
|
if (multi) {
|
|
origObj = obj;
|
|
map_state++;
|
|
state++;
|
|
if (map_state >= mapObj->State.size())
|
|
break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveIsomeshEtc(PyMOLGlobals* G, const char* mesh_name,
|
|
const char* map_name, float lvl, const char* sele, float fbuf, int state,
|
|
float carve, int map_state, int quiet, int mesh_mode_, float alt_lvl)
|
|
{
|
|
auto const mesh_mode = static_cast<cIsomeshMode>(mesh_mode_);
|
|
ObjectMesh *obj = nullptr, *origObj = nullptr;
|
|
ObjectMap* mapObj;
|
|
float mn[3] = {0, 0, 0};
|
|
float mx[3] = {15, 15, 15};
|
|
float* vert_vla = nullptr;
|
|
int multi = false;
|
|
ObjectMapState* ms;
|
|
ObjectMolecule* sele_obj = nullptr;
|
|
CSymmetry* symm;
|
|
|
|
auto res0 = EtcHelper(
|
|
G, mesh_name, state, origObj, map_name, map_state, mapObj, multi);
|
|
if (!res0) {
|
|
return res0.error();
|
|
}
|
|
|
|
{
|
|
while (1) {
|
|
ms = ObjectMapStateGetActive(mapObj, map_state);
|
|
if (ms) {
|
|
int box_mode = (sele && sele[0]) ? 1 : 0;
|
|
switch (box_mode) {
|
|
case 0: /* do the whole map */
|
|
{
|
|
int c;
|
|
for (c = 0; c < 3; c++) {
|
|
mn[c] = ms->Corner[c];
|
|
mx[c] = ms->Corner[3 * 7 + c];
|
|
}
|
|
}
|
|
if (!ms->Matrix.empty()) {
|
|
transform44d3f(ms->Matrix.data(), mn, mn);
|
|
transform44d3f(ms->Matrix.data(), mx, mx);
|
|
{
|
|
float tmp;
|
|
int a;
|
|
for (a = 0; a < 3; a++)
|
|
if (mn[a] > mx[a]) {
|
|
tmp = mn[a];
|
|
mn[a] = mx[a];
|
|
mx[a] = tmp;
|
|
}
|
|
}
|
|
}
|
|
carve = -0.0; /* impossible */
|
|
break;
|
|
case 1: /* just do area around selection */
|
|
/* determine the selected object */
|
|
{
|
|
auto tmpsele = SelectorTmp2::make(G, sele);
|
|
p_return_if_error(tmpsele);
|
|
int sele1 = tmpsele->getIndex();
|
|
if (sele1 >= 0)
|
|
sele_obj = SelectorGetSingleObjectMolecule(G, sele1);
|
|
auto s1 = tmpsele->getName();
|
|
ExecutiveGetExtent(
|
|
G, s1, mn, mx, false, -1, false); /* TODO state */
|
|
if (carve != 0.0) {
|
|
vert_vla = ExecutiveGetVertexVLA(G, s1, state);
|
|
if (fbuf <= R_SMALL4)
|
|
fbuf = fabs(carve);
|
|
}
|
|
}
|
|
{
|
|
int c;
|
|
for (c = 0; c < 3; c++) {
|
|
mn[c] -= fbuf;
|
|
mx[c] += fbuf;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
PRINTFB(G, FB_CCmd, FB_Blather)
|
|
" Isomesh: buffer %8.3f carve %8.3f \n", fbuf, carve ENDFB(G);
|
|
|
|
symm = nullptr;
|
|
if (sele_obj && ObjectMapValidXtal(mapObj, state)) {
|
|
if (SettingGet_b(G, nullptr, sele_obj->Setting.get(),
|
|
cSetting_map_auto_expand_sym) &&
|
|
(sele_obj->Symmetry)) {
|
|
// legacy default: take symmetry from molecular object
|
|
symm = sele_obj->Symmetry.get();
|
|
} else if (SettingGet_b(G, nullptr, mapObj->Setting.get(),
|
|
cSetting_map_auto_expand_sym)) {
|
|
// fallback: take symmetry from map state
|
|
symm = ms->Symmetry.get();
|
|
}
|
|
}
|
|
|
|
if (symm) {
|
|
obj = ObjectMeshFromXtalSym(G, origObj, mapObj, symm, map_state,
|
|
state, mn, mx, lvl, mesh_mode, carve, vert_vla, alt_lvl, quiet);
|
|
} else {
|
|
obj = nullptr;
|
|
}
|
|
if (!obj) {
|
|
obj = ObjectMeshFromBox(G, origObj, mapObj, map_state, state, mn, mx,
|
|
lvl, mesh_mode, carve, vert_vla, alt_lvl, quiet);
|
|
}
|
|
/* copy the map's TTT */
|
|
ExecutiveMatrixCopy2(G, mapObj, obj, 1, 1, -1, -1, false, 0, quiet);
|
|
|
|
if (!origObj) {
|
|
ObjectSetName(obj, mesh_name);
|
|
ExecutiveManageObject(G, obj, false, quiet);
|
|
}
|
|
|
|
if (SettingGetGlobal_b(G, cSetting_isomesh_auto_state))
|
|
if (obj)
|
|
ObjectGotoState(obj, state);
|
|
if (!quiet) {
|
|
if (mesh_mode != cIsomeshMode::gradient) {
|
|
PRINTFB(G, FB_ObjectMesh, FB_Actions)
|
|
" Isomesh: created \"%s\", setting level to %5.3f\n", mesh_name,
|
|
lvl ENDFB(G);
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMesh, FB_Actions)
|
|
" Gradient: created \"%s\"\n", mesh_name ENDFB(G);
|
|
}
|
|
}
|
|
} else if (!multi) {
|
|
return pymol::make_error(
|
|
"state ", map_state + 1, " not present in map \"", map_name, "\"");
|
|
}
|
|
if (multi) {
|
|
origObj = obj;
|
|
map_state++;
|
|
state++;
|
|
if (map_state >= mapObj->State.size())
|
|
break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveVolume(PyMOLGlobals* G, const char* volume_name,
|
|
const char* map_name, float lvl, const char* sele, float fbuf, int state,
|
|
float carve, int map_state, int quiet)
|
|
{
|
|
ObjectVolume *obj = nullptr, *origObj = nullptr;
|
|
ObjectMap* mapObj;
|
|
float mn[3] = {0, 0, 0};
|
|
float mx[3] = {15, 15, 15};
|
|
float* vert_vla = nullptr;
|
|
int multi = false;
|
|
ObjectMapState* ms;
|
|
ObjectMolecule* sele_obj = nullptr;
|
|
CSymmetry* symm;
|
|
|
|
/* Goal: make a new volume map from the object or selection
|
|
*
|
|
* If the user specifies an VOLUME OBJECT NAME that already exists, then
|
|
* check to make sure it's a volume. If it is, keep it, else abort
|
|
*
|
|
* If the user specifies a MAP OBJECT NAME that already exists, then
|
|
* make sure it's really a map. Keep it if it is (to append the state)
|
|
* otherwise, delete it.
|
|
*
|
|
* Given that the MAP is valid, find the current states for the MAP object
|
|
* and the scene/protein.
|
|
*
|
|
* If the user is loading a map with N-states, into an N-state object, we need
|
|
* to loop over each state. So,
|
|
*
|
|
* FOR EACH state in the MAP:
|
|
* - determine the extents of the MAP for THIS STATE
|
|
*
|
|
* - create the VOLUME object from the MAP/XTAL SYMM
|
|
* - if VOLUME CREATION didn't work, then try creating from a BOX
|
|
*
|
|
* - transform (translate/rotate) the VOLUME vis so that it
|
|
* matches the map's ObjectMatrix.
|
|
*
|
|
* - if this is a new VOLUME, (eg., origObj==nullptr) then set the internal
|
|
* PyMOL state to manage the object and add the name to the object list
|
|
*
|
|
* - update the map state for the next loop and re-iterate
|
|
*/
|
|
|
|
auto res0 = EtcHelper(
|
|
G, volume_name, state, origObj, map_name, map_state, mapObj, multi);
|
|
if (!res0) {
|
|
return res0.error();
|
|
}
|
|
|
|
{
|
|
/* do for each state */
|
|
while (1) {
|
|
ms = ObjectMapStateGetActive(mapObj, map_state);
|
|
if (ms) {
|
|
/* determine extents */
|
|
int box_mode = (sele && sele[0]) ? 1 : 0;
|
|
switch (box_mode) {
|
|
case 0: /* do the whole map */
|
|
{
|
|
int c;
|
|
for (c = 0; c < 3; c++) {
|
|
mn[c] = ms->Corner[c];
|
|
mx[c] = ms->Corner[3 * 7 + c];
|
|
}
|
|
}
|
|
if (!ms->Matrix.empty()) {
|
|
transform44d3f(ms->Matrix.data(), mn, mn);
|
|
transform44d3f(ms->Matrix.data(), mx, mx);
|
|
{
|
|
float tmp;
|
|
int a;
|
|
for (a = 0; a < 3; a++)
|
|
if (mn[a] > mx[a]) {
|
|
tmp = mn[a];
|
|
mn[a] = mx[a];
|
|
mx[a] = tmp;
|
|
}
|
|
}
|
|
}
|
|
carve = -0.0; /* impossible */
|
|
break;
|
|
case 1: /* just do area around selection */
|
|
{
|
|
auto tmpsele = SelectorTmp2::make(G, sele);
|
|
p_return_if_error(tmpsele);
|
|
int sele1 = tmpsele->getIndex();
|
|
if (sele1 >= 0)
|
|
sele_obj = SelectorGetSingleObjectMolecule(G, sele1);
|
|
auto const s1 = tmpsele->getName();
|
|
ExecutiveGetExtent(G, s1, mn, mx, false, -1, false); /* TODO state */
|
|
if (carve != 0.0) {
|
|
vert_vla = ExecutiveGetVertexVLA(G, s1, state);
|
|
if (fbuf <= R_SMALL4)
|
|
fbuf = fabs(carve);
|
|
}
|
|
}
|
|
{
|
|
int c;
|
|
for (c = 0; c < 3; c++) {
|
|
mn[c] -= fbuf;
|
|
mx[c] += fbuf;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
PRINTFB(G, FB_CCmd, FB_Blather)
|
|
" Volume: buffer %8.3f carve %8.3f \n", fbuf, carve ENDFB(G);
|
|
|
|
symm = nullptr;
|
|
if (sele_obj && ObjectMapValidXtal(mapObj, state)) {
|
|
if (SettingGet_b(G, nullptr, sele_obj->Setting.get(),
|
|
cSetting_map_auto_expand_sym) &&
|
|
(sele_obj->Symmetry)) {
|
|
// legacy default: take symmetry from molecular object
|
|
symm = sele_obj->Symmetry.get();
|
|
} else if (SettingGet_b(G, nullptr, mapObj->Setting.get(),
|
|
cSetting_map_auto_expand_sym)) {
|
|
// fallback: take symmetry from map state
|
|
symm = ms->Symmetry.get();
|
|
}
|
|
}
|
|
|
|
if (symm) {
|
|
obj = ObjectVolumeFromXtalSym(G, origObj, mapObj, symm, map_state,
|
|
state, mn, mx, lvl, box_mode, carve, vert_vla, quiet);
|
|
} else {
|
|
obj = nullptr;
|
|
}
|
|
|
|
if (!obj) {
|
|
obj = ObjectVolumeFromBox(G, origObj, mapObj, map_state, state, mn,
|
|
mx, lvl, box_mode, carve, vert_vla, quiet);
|
|
}
|
|
/* copy the map's TTT */
|
|
ExecutiveMatrixCopy2(G, mapObj, obj, 1, 1, -1, -1, false, 0, quiet);
|
|
/* set the object name
|
|
* manage the object in the UI */
|
|
if (!origObj) {
|
|
ObjectSetName(obj, volume_name);
|
|
ExecutiveManageObject(G, obj, false, quiet);
|
|
}
|
|
|
|
if (SettingGetGlobal_b(G, cSetting_isomesh_auto_state))
|
|
if (obj)
|
|
ObjectGotoState(obj, state);
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_ObjectVolume, FB_Actions)
|
|
" Volume: created \"%s\"\n", volume_name ENDFB(G);
|
|
}
|
|
} else if (!multi) {
|
|
return pymol::make_error(
|
|
"state ", map_state + 1, " not present in map \"", map_name, "\"");
|
|
}
|
|
if (multi) {
|
|
origObj = obj;
|
|
map_state++;
|
|
state++;
|
|
if (map_state >= mapObj->State.size())
|
|
break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::string ExecutivePreparePseudoatomName(
|
|
PyMOLGlobals* G, pymol::zstring_view object_name)
|
|
{
|
|
std::string new_object_name;
|
|
if (object_name.empty()) {
|
|
new_object_name = ExecutiveGetUnusedName(G, "pseudo");
|
|
} else {
|
|
ObjectNameType valid_name{};
|
|
assert(object_name.size() < sizeof(ObjectNameType));
|
|
std::copy_n(object_name.c_str(), object_name.size(), valid_name);
|
|
ObjectMakeValidName(G, valid_name);
|
|
new_object_name = valid_name;
|
|
}
|
|
return new_object_name;
|
|
}
|
|
|
|
pymol::Result<> ExecutivePseudoatom(PyMOLGlobals* G,
|
|
pymol::zstring_view object_name_view, const char* sele, const char* name,
|
|
const char* resn, const char* resi, const char* chain, const char* segi,
|
|
const char* elem, float vdw, int hetatm, float b, float q,
|
|
const char* label, const float* pos, int color, int state, int mode,
|
|
int quiet)
|
|
{
|
|
pymol::Result<SelectorTmp> s1;
|
|
|
|
auto object_name = object_name_view.c_str();
|
|
auto obj = ExecutiveFindObject<ObjectMolecule>(G, object_name);
|
|
|
|
int is_new = false;
|
|
int sele_index = -1;
|
|
float local_pos[3];
|
|
|
|
if (sele && sele[0]) {
|
|
if (WordMatchExact(G, cKeywordCenter, sele, true)) {
|
|
SceneGetCenter(G, local_pos);
|
|
pos = local_pos;
|
|
} else if (WordMatchExact(G, cKeywordOrigin, sele, true)) {
|
|
SceneOriginGet(G, local_pos);
|
|
pos = local_pos;
|
|
} else {
|
|
s1 = SelectorTmp::make(G, sele);
|
|
p_return_if_error(s1);
|
|
sele_index = s1->getIndex();
|
|
assert(sele_index >= 0);
|
|
}
|
|
}
|
|
if (!obj) {
|
|
/* new object */
|
|
is_new = true;
|
|
obj = new ObjectMolecule(G, false);
|
|
ObjectSetName(obj, object_name);
|
|
}
|
|
|
|
if (ObjectMoleculeAddPseudoatom(obj, sele_index, name, resn, resi, chain,
|
|
segi, elem, vdw, hetatm, b, q, label, pos, color, state, mode,
|
|
quiet)) {
|
|
if (is_new) {
|
|
ExecutiveDelete(G, object_name); /* just in case */
|
|
ExecutiveManageObject(G, obj, false, true);
|
|
} else {
|
|
ExecutiveUpdateObjectSelection(G, obj);
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
static void ExecutiveInvalidateGridSlots(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
I->ValidGridSlots = false;
|
|
}
|
|
|
|
static void ExecutiveSetGridSlot(SpecRec* rec, int new_grid_slot)
|
|
{
|
|
if (rec->grid_slot != new_grid_slot) {
|
|
CGOFree(rec->gridSlotSelIndicatorsCGO);
|
|
rec->gridSlotSelIndicatorsCGO = nullptr;
|
|
rec->grid_slot = new_grid_slot;
|
|
}
|
|
}
|
|
static void ExecutiveUpdateGridSlots(PyMOLGlobals* G, int force)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int grid_slot_count = 0;
|
|
int grid_by_group = 1; /* grid slots are inherited this many levels */
|
|
|
|
ExecutiveUpdateGroups(G, false);
|
|
if (force || (!I->ValidGridSlots)) {
|
|
CTracker* I_Tracker = I->Tracker;
|
|
I->ValidGridSlots = true;
|
|
{
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
ExecutiveSetGridSlot(rec, 0);
|
|
if (rec->type == cExecObject) {
|
|
/* make sure every object (potentially) needing a grid slot gets one
|
|
*/
|
|
switch (rec->obj->type) {
|
|
case cObjectMolecule:
|
|
case cObjectMap:
|
|
case cObjectMesh:
|
|
case cObjectMeasurement:
|
|
case cObjectCallback:
|
|
case cObjectCGO:
|
|
case cObjectSurface:
|
|
case cObjectSlice:
|
|
case cObjectGadget:
|
|
case cObjectGroup:
|
|
case cObjectVolume:
|
|
ExecutiveSetGridSlot(rec, ++grid_slot_count);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (grid_by_group) {
|
|
SpecRec *rec = nullptr, *group_rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
OVreturn_word result;
|
|
if (OVreturn_IS_OK((result = OVLexicon_BorrowFromCString(
|
|
I->Lex, rec->group_name)))) {
|
|
auto keyRes = I->Key.find(result.word);
|
|
if (keyRes != I->Key.end()) {
|
|
if (TrackerGetCandRef(I_Tracker, keyRes->second,
|
|
(TrackerRef**) (void*) &group_rec)) {
|
|
int grid_slot_group_depth = grid_by_group;
|
|
{
|
|
SpecRec* check_rec = group_rec;
|
|
while (check_rec && grid_slot_group_depth) {
|
|
if (grid_slot_group_depth == 1)
|
|
ExecutiveSetGridSlot(rec, check_rec->grid_slot);
|
|
if (check_rec == rec) { /* cycle */
|
|
break;
|
|
} else {
|
|
check_rec = check_rec->group;
|
|
grid_slot_group_depth--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
int obj_slot = SettingGet_i(
|
|
G, rec->obj->Setting.get(), nullptr, cSetting_grid_slot);
|
|
if (obj_slot == -1) {
|
|
rec->obj->grid_slot = rec->grid_slot;
|
|
} else
|
|
rec->obj->grid_slot = obj_slot;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExecutiveInvalidatePanelList(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
I->Panel.clear();
|
|
ExecutiveInvalidateGridSlots(G);
|
|
}
|
|
|
|
/**
|
|
* Add all members which belong to group
|
|
*/
|
|
static void PanelListGroup(
|
|
CExecutive* I, SpecRec const* group, unsigned level, bool hide_underscore)
|
|
{
|
|
for (auto& rec : pymol::make_list_adapter(I->Spec)) {
|
|
if (rec.group != group) {
|
|
continue;
|
|
}
|
|
|
|
assert(!rec.in_panel);
|
|
|
|
if (rec.isHiddenNotRecursive(hide_underscore)) {
|
|
continue;
|
|
}
|
|
|
|
if (!level) {
|
|
assert(!rec.group_name[0]);
|
|
rec.group_name[0] =
|
|
0; /* force open any cycles which have been created... */
|
|
}
|
|
|
|
I->Panel.emplace_back(&rec, level);
|
|
rec.in_panel = true;
|
|
|
|
if (auto obj_group = dynamic_cast<ObjectGroup*>(rec.obj)) {
|
|
auto& panelitem = I->Panel.back();
|
|
panelitem.is_group = true;
|
|
if (obj_group->OpenOrClosed) {
|
|
panelitem.is_open = true;
|
|
PanelListGroup(I, &rec, level + 1, hide_underscore);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ExecutiveUpdatePanelList(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
|
|
if (I->Panel.empty()) {
|
|
for (auto& rec : pymol::make_list_adapter(I->Spec)) {
|
|
rec.in_panel = false;
|
|
}
|
|
|
|
/* brute-force & inefficient -- need to optimize algorithm */
|
|
PanelListGroup(I, nullptr, 0, hide_underscore);
|
|
}
|
|
}
|
|
|
|
void ExecutiveInvalidateSceneMembers(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
I->ValidSceneMembers = false;
|
|
}
|
|
|
|
void ExecutiveUpdateSceneMembers(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
ExecutiveUpdateGroups(G, false);
|
|
ExecutiveUpdateGridSlots(G, false);
|
|
if (!I->ValidSceneMembers) {
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
int visible = rec->visible;
|
|
SpecRec* group_rec = rec->group;
|
|
while (visible && group_rec) { /* visibility is a group issue... */
|
|
if (!group_rec->visible)
|
|
visible = false;
|
|
else
|
|
group_rec = group_rec->group;
|
|
}
|
|
if (rec->in_scene && !visible) {
|
|
rec->in_scene = SceneObjectDel(G, rec->obj, true);
|
|
} else if (visible && !rec->in_scene) {
|
|
rec->in_scene = SceneObjectAdd(G, rec->obj);
|
|
}
|
|
}
|
|
}
|
|
I->ValidSceneMembers = true;
|
|
}
|
|
}
|
|
|
|
void ExecutiveInvalidateGroups(PyMOLGlobals* G, bool force)
|
|
{
|
|
auto I = G->Executive;
|
|
if (!(force || I->ValidGroups)) {
|
|
return;
|
|
}
|
|
for (auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
|
|
rec.group = nullptr;
|
|
if (ExecutiveIsObjectType(rec, cObjectGroup)) {
|
|
if (rec.group_member_list_id) {
|
|
TrackerDelList(I->Tracker, rec.group_member_list_id);
|
|
}
|
|
rec.group_member_list_id = 0; /* not a list */
|
|
}
|
|
}
|
|
I->ValidGroups = false;
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
ExecutiveInvalidatePanelList(G);
|
|
/* any changes to group structure means that we need to check scene
|
|
members */
|
|
}
|
|
|
|
void ExecutiveUpdateGroups(PyMOLGlobals* G, bool force)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
|
|
if (force || (!I->ValidGroups)) {
|
|
CTracker* I_Tracker = I->Tracker;
|
|
|
|
/* first, get rid of existing group lists */
|
|
|
|
if (force || I->ValidGroups)
|
|
ExecutiveInvalidateGroups(G, true);
|
|
|
|
/* create empty lists for each group (also init grid_slot) */
|
|
|
|
for (auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
|
|
rec.group = nullptr;
|
|
if (ExecutiveIsObjectType(rec, cObjectGroup)) {
|
|
rec.group_member_list_id = TrackerNewList(I_Tracker, nullptr);
|
|
}
|
|
}
|
|
|
|
/* iterate through and populate groups lists with their members */
|
|
|
|
for (auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
|
|
OVreturn_word result;
|
|
if (OVreturn_IS_OK(
|
|
(result = OVLexicon_BorrowFromCString(I->Lex, rec.group_name)))) {
|
|
auto keyRes = I->Key.find(result.word);
|
|
if (keyRes != I->Key.end()) {
|
|
SpecRec* group_rec = nullptr;
|
|
if (TrackerGetCandRef(I_Tracker, keyRes->second,
|
|
(TrackerRef**) (void*) &group_rec)) {
|
|
int cycle = false;
|
|
{ /* don't close infinite loops */
|
|
SpecRec* check_rec = group_rec;
|
|
while (check_rec) {
|
|
if (check_rec == &rec) {
|
|
cycle = true;
|
|
break;
|
|
} else {
|
|
check_rec = check_rec->group;
|
|
}
|
|
}
|
|
}
|
|
if (!cycle) {
|
|
rec.group = group_rec;
|
|
TrackerLink(
|
|
I_Tracker, rec.cand_id, group_rec->group_member_list_id, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* note that it is possible to have infinite loops -- these must be
|
|
allowed for later in the group expansion routine(s) */
|
|
I->ValidGroups = true;
|
|
ExecutiveInvalidatePanelList(G);
|
|
}
|
|
}
|
|
|
|
static int ExecutiveGetObjectParentList(PyMOLGlobals* G, SpecRec* child)
|
|
{
|
|
int list_id = 0;
|
|
ExecutiveUpdateGroups(G, false);
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int priority = 1; /* generations removed from child */
|
|
int repeat_flag = true;
|
|
SpecRec* group_rec = nullptr;
|
|
|
|
list_id = TrackerNewList(I_Tracker, nullptr);
|
|
while (child && child->group && repeat_flag) {
|
|
OVreturn_word result;
|
|
repeat_flag = false;
|
|
if (OVreturn_IS_OK((result = OVLexicon_BorrowFromCString(
|
|
I->Lex, child->group_name)))) {
|
|
auto keyRes = I->Key.find(result.word);
|
|
if (keyRes != I->Key.end()) {
|
|
if (TrackerGetCandRef(I_Tracker, keyRes->second,
|
|
(TrackerRef**) (void*) &group_rec)) {
|
|
if (TrackerLink(I_Tracker, keyRes->second, list_id, priority++)) {
|
|
/* checking this prevents infinite loops */
|
|
if (group_rec->group) {
|
|
repeat_flag = true;
|
|
child = group_rec;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return list_id;
|
|
}
|
|
|
|
pymol::TrackerAdapter<SpecRec> ExecutiveGetSpecRecParents(
|
|
PyMOLGlobals* G, SpecRec& rec)
|
|
{
|
|
return pymol::TrackerAdapter<SpecRec>(
|
|
G->Executive->Tracker, ExecutiveGetObjectParentList(G, &rec));
|
|
}
|
|
|
|
int ExecutiveVdwFit(PyMOLGlobals* G, const char* s1, int state1, const char* s2,
|
|
int state2, float buffer, int quiet)
|
|
{
|
|
SelectorTmp tmpsele1(G, s1);
|
|
SelectorTmp tmpsele2(G, s2);
|
|
int sele1 = tmpsele1.getIndex();
|
|
int sele2 = tmpsele2.getIndex();
|
|
|
|
int ok = true;
|
|
|
|
if ((sele1 >= 0) && (sele2 >= 0)) {
|
|
ok = SelectorVdwFit(G, sele1, state1, sele2, state2, buffer, quiet);
|
|
} else {
|
|
ok = false;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static int get_op_cnt(PyMOLGlobals* G)
|
|
{
|
|
int result = 5;
|
|
if (!strcmp(
|
|
SettingGetGlobal_s(G, cSetting_button_mode_name), "3-Button Motions"))
|
|
result = 6;
|
|
return result;
|
|
}
|
|
|
|
static int ExecutiveAddKey(CExecutive* I, SpecRec* rec)
|
|
{
|
|
int ok = false;
|
|
OVreturn_word result;
|
|
if (OVreturn_IS_OK((result = OVLexicon_GetFromCString(I->Lex, rec->name)))) {
|
|
I->Key[result.word] = rec->cand_id;
|
|
ok = true;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static int ExecutiveDelKey(CExecutive* I, SpecRec* rec)
|
|
{
|
|
int ok = false;
|
|
OVreturn_word result;
|
|
if (OVreturn_IS_OK(
|
|
(result = OVLexicon_BorrowFromCString(I->Lex, rec->name)))) {
|
|
if (OVreturn_IS_OK(OVLexicon_DecRef(I->Lex, result.word))) {
|
|
auto res = I->Key.find(result.word);
|
|
if (res != I->Key.end()) {
|
|
I->Key.erase(res);
|
|
ok = true;
|
|
}
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static SpecRec* ExecutiveUnambiguousNameMatch(PyMOLGlobals* G, const char* name)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* result = nullptr;
|
|
SpecRec* rec = nullptr;
|
|
int best = 0;
|
|
int wm;
|
|
int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
wm = WordMatch(G, name, rec->name, ignore_case);
|
|
if (wm < 0) { /* exact match, so this is valid */
|
|
result = rec;
|
|
best = wm;
|
|
break;
|
|
} else if ((wm > 0) && (best < wm)) {
|
|
result = rec;
|
|
best = wm;
|
|
} else if ((wm > 0) && (best == wm)) { /* ambiguous match... no good */
|
|
result = nullptr;
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
static SpecRec* ExecutiveAnyCaseNameMatch(PyMOLGlobals* G, const char* name)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* result = nullptr;
|
|
SpecRec* rec = nullptr;
|
|
|
|
int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (WordMatchExact(G, name, rec->name, ignore_case)) {
|
|
result = rec;
|
|
break;
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/**
|
|
* Scroll the i'th match in the object menu panel to the top.
|
|
* Scroll to last match if i < 0 and to first match if i > #-1.
|
|
* Open groups if hit is inside.
|
|
* Highlight the hit (same as mouse click highlight).
|
|
*
|
|
* Returns the number of hits
|
|
*/
|
|
int ExecutiveScrollTo(PyMOLGlobals* G, const char* name, int i)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int pos = 0, numhits = 0;
|
|
ObjectGroup* group;
|
|
SpecRec *tmp, *spec = nullptr, *first = nullptr;
|
|
int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
|
|
int j, lendiff, plen = strlen(name);
|
|
|
|
ok_assert(1, I->Spec);
|
|
|
|
// i'th substring match, skip the "all" item
|
|
for (tmp = I->Spec->next; tmp; tmp = tmp->next) {
|
|
lendiff = strlen(tmp->name) - plen;
|
|
for (j = 0; j <= lendiff; j++)
|
|
if (WordMatchNoWild(G, name, tmp->name + j, ignore_case)) {
|
|
if (numhits++ == i || i < 0)
|
|
spec = tmp;
|
|
if (!first)
|
|
first = tmp;
|
|
break;
|
|
}
|
|
tmp->hilight = 0;
|
|
}
|
|
|
|
// if i was out of range
|
|
if (!spec)
|
|
spec = first;
|
|
|
|
ok_assert(1, spec);
|
|
|
|
// flash button until panel is clicked for the next time
|
|
spec->hilight = 1;
|
|
|
|
// open parent groups
|
|
for (tmp = spec->group; tmp; tmp = tmp->group) {
|
|
if (!(tmp->type == cExecObject && tmp->obj->type == cObjectGroup))
|
|
break;
|
|
group = (ObjectGroup*) tmp->obj;
|
|
if (!group->OpenOrClosed) {
|
|
group->OpenOrClosed = 1;
|
|
ExecutiveInvalidatePanelList(G);
|
|
}
|
|
}
|
|
|
|
// in case any parent got opened
|
|
ExecutiveUpdatePanelList(G);
|
|
|
|
// scroll that record to the top
|
|
for (auto const& panelitem : I->Panel) {
|
|
if (panelitem.spec == spec) {
|
|
I->m_ScrollBar.setValueNoCheck(pos);
|
|
return numhits;
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
ok_except1:
|
|
return numhits;
|
|
}
|
|
|
|
void ExecutiveUpdateColorDepends(PyMOLGlobals* G, ObjectMolecule* mol)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectGadget) {
|
|
ObjectGadget* gadget = (ObjectGadget*) rec->obj;
|
|
if (gadget->GadgetType == cGadgetRamp) {
|
|
ObjectGadgetRamp* ramp = (ObjectGadgetRamp*) gadget;
|
|
if (ramp->RampType == cRampMol) {
|
|
if (ramp->Mol == mol) {
|
|
ExecutiveInvalidateRep(G, cKeywordAll, cRepAll, cRepInvColor);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExecutiveUpdateCoordDepends(PyMOLGlobals* G, ObjectMolecule* mol)
|
|
{ /* nasty, ugly, inefficient hack */
|
|
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectGadget* gadget;
|
|
int done_inv_all = false;
|
|
int dynamic_measures = SettingGet_b(G, mol ? mol->Setting.get() : nullptr,
|
|
nullptr, cSetting_dynamic_measures);
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
switch (rec->obj->type) {
|
|
case cObjectGadget:
|
|
if (done_inv_all)
|
|
break;
|
|
gadget = (ObjectGadget*) rec->obj;
|
|
if (gadget->GadgetType == cGadgetRamp) {
|
|
ObjectGadgetRamp* ramp = (ObjectGadgetRamp*) gadget;
|
|
if (ramp->RampType == cRampMol) {
|
|
if (ramp->Mol == mol) {
|
|
ExecutiveInvalidateRep(G, cKeywordAll, cRepAll, cRepInvColor);
|
|
done_inv_all = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cObjectMeasurement:
|
|
if (dynamic_measures)
|
|
ObjectDistMoveWithObject((ObjectDist*) rec->obj, mol);
|
|
break;
|
|
case cObjectAlignment:
|
|
rec->obj->invalidate(
|
|
cRepAll, cRepInvRep, cSelectorUpdateTableAllStates);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int ExecutiveValidNamePattern(PyMOLGlobals* G, const char* name)
|
|
{
|
|
int result = false;
|
|
CWordMatcher* matcher;
|
|
CWordMatchOptions options;
|
|
const char* wildcard = SettingGetGlobal_s(G, cSetting_wildcard);
|
|
|
|
WordMatchOptionsConfigNameList(
|
|
&options, *wildcard, SettingGetGlobal_b(G, cSetting_ignore_case));
|
|
matcher = WordMatcherNew(G, name, &options, false);
|
|
if (matcher) { /* this appears to be a pattern */
|
|
result = true;
|
|
WordMatcherFree(matcher);
|
|
} else if (ExecutiveUnambiguousNameMatch(G, name)) {
|
|
/* this does not appear to be a pattern, so it is an unambiguous partial
|
|
* name? */
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#define cExecNoExpand false
|
|
#define cExecExpandGroups true
|
|
#define cExecExpandKeepGroups 2
|
|
|
|
static void ExecutiveExpandGroupsInList(
|
|
PyMOLGlobals* G, int list_id, int expand_groups)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int new_member_added = true;
|
|
SpecRec* rec;
|
|
ExecutiveUpdateGroups(G, false);
|
|
while (new_member_added) { /* keep adding til we can't add no more */
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
int cand_id;
|
|
new_member_added = false;
|
|
if (iter_id) {
|
|
while ((cand_id = TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec))) {
|
|
if (rec && (rec->type == cExecObject) && rec->group_member_list_id &&
|
|
(rec->obj->type == cObjectGroup)) {
|
|
int group_iter_id =
|
|
TrackerNewIter(I_Tracker, 0, rec->group_member_list_id);
|
|
int group_cand_id;
|
|
SpecRec* group_rec;
|
|
if (group_iter_id) {
|
|
while ((group_cand_id = TrackerIterNextCandInList(I_Tracker,
|
|
group_iter_id, (TrackerRef**) (void*) &group_rec))) {
|
|
if (group_rec && group_cand_id) {
|
|
if (TrackerLink(I_Tracker, group_cand_id, list_id, 1))
|
|
new_member_added = true;
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, group_iter_id);
|
|
}
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
}
|
|
/* now purge all groups from the expanded list */
|
|
if (expand_groups != cExecExpandKeepGroups) {
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
int cand_id;
|
|
while ((cand_id = TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec))) {
|
|
if (rec && (rec->type == cExecObject) &&
|
|
(rec->obj->type == cObjectGroup)) {
|
|
TrackerUnlink(I_Tracker, cand_id, list_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* DON'T FORGET TO RELEASE LIST WHEN DONE!!! */
|
|
int ExecutiveGetNamesListFromPattern(
|
|
PyMOLGlobals* G, const char* name, int allow_partial, int expand_groups)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int result = 0;
|
|
CWordMatcher* matcher;
|
|
CWordMatchOptions options;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
const char* wildcard = SettingGetGlobal_s(G, cSetting_wildcard);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, I->all_names_list_id);
|
|
int cand_id;
|
|
int group_found = false;
|
|
SpecRec* rec;
|
|
|
|
if (!name)
|
|
return -1;
|
|
|
|
// sanity check: name patterns are not object selections, bail if
|
|
// parenthesis or operators in pattern
|
|
if (strchr(name, '(') || strchr(name, ')') || strchr(name, '|')) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" Names-Pattern-Error: Pattern looks like an atom selection"
|
|
" (has parenthesis or operators), this is not supported for"
|
|
" object name patterns.\n" ENDFB(G);
|
|
return -1;
|
|
}
|
|
|
|
// special case: allow "not ..."
|
|
bool match_not = false;
|
|
if (WordMatchNoWild(G, "not ", name, false)) {
|
|
match_not = true;
|
|
name += 4;
|
|
} else if (name[0] == '!') {
|
|
match_not = true;
|
|
name += 1;
|
|
}
|
|
|
|
// skip whitespace
|
|
while (name[0] == ' ') {
|
|
++name;
|
|
}
|
|
|
|
bool match_enabled = WordMatchExact(G, "enabled", name, false);
|
|
|
|
// ignore % and ? prefixes
|
|
while (name[0] && (name[0] == '%' || name[0] == '?'))
|
|
name++;
|
|
|
|
WordMatchOptionsConfigNameList(
|
|
&options, *wildcard, SettingGetGlobal_b(G, cSetting_ignore_case));
|
|
matcher = WordMatcherNew(G, name, &options, /* force= */ match_not);
|
|
if (matcher || match_enabled) {
|
|
if (iter_id) {
|
|
while ((cand_id = TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec))) {
|
|
if (rec && !(rec->type == cExecAll)) {
|
|
bool test = match_enabled ? SpecRecIsEnabled(rec)
|
|
: WordMatcherMatchAlpha(matcher, rec->name);
|
|
if (test ^ match_not) {
|
|
if ((rec->type == cExecObject) && (rec->obj->type == cObjectGroup))
|
|
group_found = true;
|
|
if (!result)
|
|
result = TrackerNewList(I_Tracker, nullptr);
|
|
if (result) {
|
|
TrackerLink(I_Tracker, cand_id, result, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if ((rec = ExecutiveFindSpec(G, name))) { /* only one name in list */
|
|
if ((rec->type == cExecObject) && (rec->obj->type == cObjectGroup))
|
|
group_found = true;
|
|
result = TrackerNewList(I_Tracker, nullptr);
|
|
TrackerLink(I_Tracker, rec->cand_id, result, 1);
|
|
} else if (allow_partial && (rec = ExecutiveUnambiguousNameMatch(G, name))) {
|
|
if ((rec->type == cExecObject) && (rec->obj->type == cObjectGroup))
|
|
group_found = true;
|
|
result = TrackerNewList(I_Tracker, nullptr);
|
|
TrackerLink(I_Tracker, rec->cand_id, result, 1);
|
|
}
|
|
if (matcher)
|
|
WordMatcherFree(matcher);
|
|
if (iter_id)
|
|
TrackerDelIter(I->Tracker, iter_id);
|
|
if (group_found && expand_groups) {
|
|
ExecutiveExpandGroupsInList(G, result, expand_groups);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int ExecutiveUngroup(PyMOLGlobals* G, const char* members, int quiet)
|
|
{
|
|
for (auto& rec : ExecutiveGetSpecRecsFromPattern(G, members)) {
|
|
rec.group_name[0] = 0;
|
|
rec.group = nullptr;
|
|
}
|
|
ExecutiveInvalidateGroups(G, true);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Removes recs from a group rec
|
|
*
|
|
* @param[in] rec group rec to remove recs from
|
|
* @param[out] discarded discarded recs
|
|
*
|
|
* Note: Caller is responsible for calling ExecutivePurgeSpec on discarded recs
|
|
*/
|
|
|
|
static void ExecutiveGroupPurge(PyMOLGlobals* G, SpecRec* rec,
|
|
std::vector<DiscardedRec>* const discarded = nullptr)
|
|
{
|
|
std::vector<DiscardedRec> recs;
|
|
auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
|
|
SpecRec* rec2 = nullptr;
|
|
while (ListIterate(G->Executive->Spec, rec2, next)) {
|
|
if ((rec2->group == rec) ||
|
|
WordMatchExact(G, rec2->group_name, rec->name, ignore_case)) {
|
|
bool save = discarded != nullptr;
|
|
auto result = ExecutiveDelete(G, rec2->name, save);
|
|
if (discarded && result) {
|
|
discarded->insert(discarded->end(), result->begin(), result->end());
|
|
}
|
|
rec2 = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ExecutiveGroup(PyMOLGlobals* G, pymol::zstring_view nameView,
|
|
pymol::zstring_view membersView, int action, int quiet)
|
|
{
|
|
auto name = nameView.c_str();
|
|
auto members = membersView.c_str();
|
|
if (action == cExecutiveGroupUngroup) {
|
|
// Up to PyMOL 2.3 the member argument was ignored and 'name' used for
|
|
// ungrouping
|
|
if (name[0]) {
|
|
members = name;
|
|
}
|
|
return ExecutiveUngroup(G, members, quiet);
|
|
}
|
|
|
|
int ok = true;
|
|
CExecutive* I = G->Executive;
|
|
auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
|
|
|
|
ObjectNameType valid_name;
|
|
UtilNCopy(valid_name, name, sizeof(ObjectNameType));
|
|
ObjectMakeValidName(G, valid_name);
|
|
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, valid_name);
|
|
|
|
if (obj && (obj->type != cObjectGroup)) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" Group-Error: object '%s' is not a group object.", name ENDFB(G);
|
|
ok = false;
|
|
} else {
|
|
if ((!obj) && (action == cExecutiveGroupAdd)) {
|
|
obj = new ObjectGroup(G);
|
|
if (obj) {
|
|
ObjectSetName(obj, valid_name);
|
|
ExecutiveManageObject(G, obj, false, true);
|
|
}
|
|
}
|
|
}
|
|
if ((!members[0]) &&
|
|
((action == cExecutiveGroupOpen) || (action == cExecutiveGroupClose) ||
|
|
(action == cExecutiveGroupToggle) ||
|
|
(action == cExecutiveGroupEmpty) ||
|
|
(action == cExecutiveGroupPurge) ||
|
|
(action == cExecutiveGroupExcise) ||
|
|
(action == cExecutiveGroupRaise))) {
|
|
ExecutiveUpdateGroups(G, false);
|
|
{
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, false);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
ObjectGroup* objGroup = nullptr;
|
|
if ((rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
|
|
objGroup = (ObjectGroup*) rec->obj;
|
|
}
|
|
|
|
switch (action) {
|
|
case cExecutiveGroupOpen:
|
|
if (objGroup)
|
|
objGroup->OpenOrClosed = 1;
|
|
break;
|
|
case cExecutiveGroupClose:
|
|
if (objGroup)
|
|
objGroup->OpenOrClosed = 0;
|
|
break;
|
|
case cExecutiveGroupToggle:
|
|
if (objGroup)
|
|
objGroup->OpenOrClosed = !objGroup->OpenOrClosed;
|
|
break;
|
|
case cExecutiveGroupEmpty:
|
|
if (objGroup) {
|
|
SpecRec* rec2 = nullptr;
|
|
while (ListIterate(I->Spec, rec2, next)) {
|
|
if ((rec2->group == rec) || WordMatchExact(G, rec2->group_name,
|
|
rec->name, ignore_case)) {
|
|
rec2->group = nullptr;
|
|
rec2->group_name[0] = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecutiveGroupPurge:
|
|
if (objGroup) {
|
|
ExecutiveGroupPurge(G, rec);
|
|
}
|
|
break;
|
|
case cExecutiveGroupExcise:
|
|
if (objGroup) {
|
|
|
|
if (rec->group_name[0]) {
|
|
/* cascade group members up to the surrounding group */
|
|
SpecRec* rec2 = nullptr;
|
|
while (ListIterate(I->Spec, rec2, next)) {
|
|
if ((rec2->group == rec) ||
|
|
WordMatch(G, rec->name, rec2->group_name, ignore_case)) {
|
|
strcpy(rec2->group_name, rec->group_name);
|
|
rec2->group = rec->group;
|
|
}
|
|
}
|
|
} else if ((rec->type == cExecObject) &&
|
|
(rec->obj->type == cObjectGroup)) {
|
|
/* and/or delete their group membership */
|
|
SpecRec* rec2 = nullptr;
|
|
while (ListIterate(I->Spec, rec2, next)) {
|
|
if ((rec2->group == rec) ||
|
|
WordMatch(G, rec->name, rec2->group_name, ignore_case)) {
|
|
rec2->group_name[0] = 0;
|
|
rec2->group = nullptr;
|
|
}
|
|
}
|
|
}
|
|
ExecutiveDelete(G, rec->name);
|
|
}
|
|
break;
|
|
case cExecutiveGroupRaise:
|
|
if (objGroup) {
|
|
rec->group = nullptr;
|
|
rec->group_name[0] = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
ExecutiveInvalidateGroups(G, true);
|
|
}
|
|
} else {
|
|
if (obj && (obj->type == cObjectGroup)) {
|
|
ObjectGroup* objGroup = (ObjectGroup*) obj;
|
|
switch (action) {
|
|
case cExecutiveGroupOpen:
|
|
objGroup->OpenOrClosed = 1;
|
|
break;
|
|
case cExecutiveGroupClose:
|
|
objGroup->OpenOrClosed = 0;
|
|
break;
|
|
case cExecutiveGroupToggle:
|
|
objGroup->OpenOrClosed = !objGroup->OpenOrClosed;
|
|
break;
|
|
}
|
|
if (members[0] && (action != cExecutiveGroupRemove))
|
|
action = cExecutiveGroupAdd;
|
|
|
|
switch (action) {
|
|
case cExecutiveGroupAdd: {
|
|
for (auto& rec :
|
|
ExecutiveGetSpecRecsFromPattern(G, members, true, false)) {
|
|
if (rec.type != cExecObject ||
|
|
(rec.type == cExecObject && rec.obj != obj)) {
|
|
UtilNCopy(rec.group_name, valid_name, sizeof(WordType));
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Executive: adding '%s' to group '%s'.\n", rec.name,
|
|
rec.group_name ENDFB(G);
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
|
|
ExecutiveInvalidateGroups(G, true);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static void ExecutiveGetUniqueIDAtomVLADict(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
I->m_eoo.clear();
|
|
I->m_eoo.reserve(1000);
|
|
|
|
std::size_t n_oi = 0;
|
|
auto recs = pymol::make_list_adapter(G->Executive->Spec);
|
|
for (auto& rec : recs) {
|
|
if (rec.type != cExecObject) {
|
|
continue;
|
|
}
|
|
if (rec.obj->type != cObjectMolecule) {
|
|
continue;
|
|
}
|
|
auto obj = static_cast<ObjectMolecule*>(rec.obj);
|
|
auto n_atom = obj->NAtom;
|
|
const AtomInfoType* ai = obj->AtomInfo.data();
|
|
for (int a = 0; a < n_atom; a++) {
|
|
auto id = ai->unique_id;
|
|
if (id != 0) {
|
|
auto o2oIt = I->m_id2eoo.find(id);
|
|
if (o2oIt == I->m_id2eoo.end()) {
|
|
I->m_id2eoo[id] = n_oi++;
|
|
I->m_eoo.push_back(ExecutiveObjectOffset{obj, a});
|
|
}
|
|
}
|
|
ai++;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ExecutiveDrawCmd(PyMOLGlobals* G, int width, int height, int antialias,
|
|
int entire_window, int quiet)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
Extent2D extent{
|
|
static_cast<std::uint32_t>(width), static_cast<std::uint32_t>(height)};
|
|
if (width == 0 && height == 0) {
|
|
extent = SceneGetExtent(G);
|
|
}
|
|
if (antialias < 0)
|
|
antialias = SettingGetGlobal_i(G, cSetting_antialias);
|
|
if (entire_window) {
|
|
SceneInvalidateCopy(G, false);
|
|
OrthoDirty(G);
|
|
I->CaptureFlag = true;
|
|
} else {
|
|
if (SettingGetGlobal_i(G, cSetting_draw_mode) == -1) {
|
|
ExecutiveSetSettingFromString(
|
|
G, cSetting_draw_mode, "-2", "", -1, true, true);
|
|
SceneUpdate(G, false);
|
|
}
|
|
SceneDeferImage(
|
|
G, extent, nullptr, antialias, -1.0, cMyPNG_FormatPNG, quiet, nullptr);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int ExecutiveMatrixCopy2(PyMOLGlobals* G, pymol::CObject* source_obj,
|
|
pymol::CObject* target_obj, int source_mode, int target_mode,
|
|
int source_state, int target_state, int target_undo, int log, int quiet)
|
|
{
|
|
/* mode 0: raw coordinates, as per the txf history
|
|
mode 1: object TTT matrix
|
|
mode 2: state matrix */
|
|
|
|
int ok = true;
|
|
int copy_ttt_too = false;
|
|
int matrix_mode = SettingGetGlobal_i(G, cSetting_matrix_mode);
|
|
if (matrix_mode < 0)
|
|
matrix_mode = 0; /* for now */
|
|
|
|
if ((source_mode < 0) && (target_mode < 0)) {
|
|
copy_ttt_too = true;
|
|
}
|
|
if (source_mode < 0)
|
|
source_mode = matrix_mode;
|
|
if (target_mode < 0)
|
|
target_mode = matrix_mode;
|
|
|
|
switch (source_mode) {
|
|
case 0: /* txf history is the source matrix */
|
|
{
|
|
double* history = nullptr;
|
|
int found =
|
|
ExecutiveGetObjectMatrix2(G, source_obj, source_state, &history, false);
|
|
if (found) {
|
|
switch (target_mode) {
|
|
case 0: /* apply changes to coordinates in the target object */
|
|
{
|
|
double temp_inverse[16];
|
|
if (target_undo) {
|
|
double* target_history = nullptr;
|
|
int target_found = ExecutiveGetObjectMatrix2(
|
|
G, source_obj, target_state, &target_history, false);
|
|
if (target_found && target_history) {
|
|
invert_special44d44d(target_history, temp_inverse);
|
|
if (history) {
|
|
right_multiply44d44d(temp_inverse, history);
|
|
history = temp_inverse;
|
|
} else {
|
|
history = temp_inverse;
|
|
}
|
|
}
|
|
{
|
|
float historyf[16];
|
|
if (history) {
|
|
convert44d44f(history, historyf);
|
|
} else {
|
|
identity44f(historyf);
|
|
}
|
|
ExecutiveTransformObjectSelection2(
|
|
G, target_obj, target_state, "", log, historyf, true, false);
|
|
}
|
|
}
|
|
if (copy_ttt_too) {
|
|
const float* tttf;
|
|
int found = ObjectGetTTT(source_obj, &tttf, -1);
|
|
if (found) {
|
|
ObjectSetTTT(target_obj, tttf, -1, -1);
|
|
target_obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
}
|
|
}
|
|
} break;
|
|
case 1: /* applying changes to the object's TTT matrix */
|
|
if (history) {
|
|
float tttf[16];
|
|
convertR44dTTTf(history, tttf);
|
|
ObjectSetTTT(target_obj, tttf, -1, -1);
|
|
} else {
|
|
ObjectSetTTT(target_obj, nullptr, -1, -1);
|
|
}
|
|
target_obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
break;
|
|
case 2: /* applying changes to the state matrix */
|
|
ok = ExecutiveSetObjectMatrix2(G, target_obj, target_state, history);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
} break;
|
|
case 1: /* from the TTT matrix */
|
|
{
|
|
/* note that for now we're forcing states to be -1 */
|
|
/* in the future, we may have per-state TTTs -- though right now the
|
|
view matrices serve that purpose */
|
|
|
|
const float* tttf;
|
|
int found = ObjectGetTTT(source_obj, &tttf, -1);
|
|
if (found) {
|
|
switch (target_mode) {
|
|
case 0: /* coordinates & history unsupported.. */
|
|
/* should complain */
|
|
break;
|
|
case 1: /* TTT */
|
|
ObjectSetTTT(target_obj, tttf, -1, -1);
|
|
target_obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
break;
|
|
case 2: /* State */
|
|
if (tttf) {
|
|
double homo[16];
|
|
convertTTTfR44d(tttf, homo);
|
|
ok = ExecutiveSetObjectMatrix2(G, target_obj, -1, homo);
|
|
} else {
|
|
ok = ExecutiveSetObjectMatrix2(G, target_obj, -1, nullptr);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} break;
|
|
case 2: /* from the state matrix */
|
|
{
|
|
double* homo;
|
|
int found =
|
|
ExecutiveGetObjectMatrix2(G, source_obj, source_state, &homo, false);
|
|
if (found) {
|
|
switch (target_mode) {
|
|
case 0: /* coordinates & history */
|
|
/* TODO */
|
|
break;
|
|
case 1: /* TTT */
|
|
if (homo) {
|
|
float tttf[16];
|
|
convertR44dTTTf(homo, tttf);
|
|
ObjectSetTTT(target_obj, tttf, -1, -1);
|
|
target_obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
} else {
|
|
ObjectSetTTT(target_obj, nullptr, -1, -1);
|
|
target_obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
}
|
|
break;
|
|
case 2: /* State */
|
|
ok = ExecutiveSetObjectMatrix2(G, target_obj, target_state, homo);
|
|
if (copy_ttt_too) {
|
|
const float* tttf;
|
|
int found = ObjectGetTTT(source_obj, &tttf, -1);
|
|
if (found) {
|
|
ObjectSetTTT(target_obj, tttf, -1, -1);
|
|
target_obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
SceneInvalidate(G);
|
|
return ok;
|
|
}
|
|
|
|
int ExecutiveMatrixCopy(PyMOLGlobals* G, const char* source_name,
|
|
const char* target_name, int source_mode, int target_mode, int source_state,
|
|
int target_state, int target_undo, int log, int quiet)
|
|
{
|
|
/* mode 0: raw coordinates, as per the txf history
|
|
mode 1: object TTT matrix
|
|
mode 2: state matrix
|
|
mode 3 (source only): camera matrix transformation */
|
|
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
SpecRec* src_rec = nullptr;
|
|
int ok = true;
|
|
int copy_ttt_too = false;
|
|
int matrix_mode = SettingGetGlobal_i(G, cSetting_matrix_mode);
|
|
if (matrix_mode < 0)
|
|
matrix_mode = 0; /* for now */
|
|
|
|
if ((source_mode < 0) && (target_mode < 0)) {
|
|
copy_ttt_too = true;
|
|
}
|
|
|
|
if (source_mode < 0)
|
|
source_mode = matrix_mode;
|
|
if (target_mode < 0)
|
|
target_mode = matrix_mode;
|
|
if (source_name[0] == 0) {
|
|
source_mode = 3;
|
|
target_undo = 0;
|
|
} else
|
|
src_rec = ExecutiveFindSpec(G, source_name);
|
|
|
|
if (source_mode != 3 && !src_rec) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
" %s-Warning: Can't find source object '%s'.\n", __FUNCTION__,
|
|
source_name ENDFB(G);
|
|
}
|
|
|
|
int list_id = ExecutiveGetNamesListFromPattern(
|
|
G, target_name, true, cExecExpandKeepGroups);
|
|
|
|
if (!list_id) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
" %s-Warning: No match for target '%s'.\n", __FUNCTION__,
|
|
target_name ENDFB(G);
|
|
}
|
|
|
|
switch (source_mode) {
|
|
case 0: /* txf history is the source matrix */
|
|
{
|
|
double* src_history = nullptr;
|
|
int found =
|
|
ExecutiveGetObjectMatrix(G, source_name, source_state, &src_history, false);
|
|
if (found) {
|
|
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec && (rec != src_rec)) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
|
|
switch (target_mode) {
|
|
case 0: /* apply changes to coordinates in the target object */
|
|
{
|
|
/* Use a per-target copy of the source history so that
|
|
modifications for one target don't corrupt the matrix
|
|
for subsequent targets (avoids pointer aliasing bug) */
|
|
double history_buf[16];
|
|
double* history = nullptr;
|
|
if (src_history) {
|
|
copy44d(src_history, history_buf);
|
|
history = history_buf;
|
|
}
|
|
double temp_inverse[16];
|
|
if (target_undo) {
|
|
double* target_history = nullptr;
|
|
int target_found = ExecutiveGetObjectMatrix(
|
|
G, rec->name, target_state, &target_history, false);
|
|
if (target_found && target_history) {
|
|
invert_special44d44d(target_history, temp_inverse);
|
|
if (history) {
|
|
right_multiply44d44d(temp_inverse, history);
|
|
history = temp_inverse;
|
|
} else {
|
|
history = temp_inverse;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
float historyf[16];
|
|
if (history) {
|
|
convert44d44f(history, historyf);
|
|
} else {
|
|
identity44f(historyf);
|
|
}
|
|
ExecutiveTransformObjectSelection(
|
|
G, rec->name, target_state, "", log, historyf, true, false);
|
|
}
|
|
if (copy_ttt_too) {
|
|
const float* tttf;
|
|
int found =
|
|
ExecutiveGetObjectTTT(G, source_name, &tttf, -1, quiet);
|
|
if (found) {
|
|
ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
|
|
}
|
|
}
|
|
} break;
|
|
case 1: /* applying changes to the object's TTT matrix */
|
|
if (src_history) {
|
|
float tttf[16];
|
|
convertR44dTTTf(src_history, tttf);
|
|
ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
|
|
} else {
|
|
ExecutiveSetObjectTTT(G, rec->name, nullptr, -1, quiet, -1);
|
|
}
|
|
/* to do: logging, return values, etc. */
|
|
break;
|
|
case 2: /* applying changes to the state matrix */
|
|
ok =
|
|
ExecutiveSetObjectMatrix(G, rec->name, target_state, src_history);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
} break;
|
|
case 1: /* from the TTT matrix */
|
|
{
|
|
/* note that for now we're forcing states to be -1 */
|
|
/* in the future, we may have per-state TTTs -- though right now the
|
|
view matrices serve that purpose */
|
|
|
|
const float* tttf;
|
|
int found = ExecutiveGetObjectTTT(G, source_name, &tttf, -1, quiet);
|
|
if (found) {
|
|
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec && (rec != src_rec)) {
|
|
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
|
|
switch (target_mode) {
|
|
case 0: /* coordinates & history unsupported.. */
|
|
/* should complain */
|
|
break;
|
|
case 1: /* TTT */
|
|
ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
|
|
break;
|
|
case 2: /* State */
|
|
if (tttf) {
|
|
double homo[16];
|
|
convertTTTfR44d(tttf, homo);
|
|
ok = ExecutiveSetObjectMatrix(G, rec->name, -1, homo);
|
|
} else {
|
|
ok = ExecutiveSetObjectMatrix(G, rec->name, -1, nullptr);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
} break;
|
|
case 2: /* from the state matrix */
|
|
{
|
|
double* homo;
|
|
int found =
|
|
ExecutiveGetObjectMatrix(G, source_name, source_state, &homo, false);
|
|
if (found) {
|
|
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec && (rec != src_rec)) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
|
|
switch (target_mode) {
|
|
case 0: /* coordinates & history */
|
|
/* TODO */
|
|
break;
|
|
case 1: /* TTT */
|
|
if (homo) {
|
|
float tttf[16];
|
|
convertR44dTTTf(homo, tttf);
|
|
ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
|
|
} else {
|
|
ExecutiveSetObjectTTT(G, rec->name, nullptr, -1, quiet, -1);
|
|
}
|
|
break;
|
|
case 2: /* State */
|
|
ok = ExecutiveSetObjectMatrix(G, rec->name, target_state, homo);
|
|
if (copy_ttt_too) {
|
|
const float* tttf;
|
|
int found =
|
|
ExecutiveGetObjectTTT(G, source_name, &tttf, -1, quiet);
|
|
if (found) {
|
|
ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
} break;
|
|
case 3: /* camera */
|
|
{
|
|
SceneViewType view;
|
|
double homo[16], *history;
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
SceneGetView(G, view);
|
|
homo[0] = view[0];
|
|
homo[1] = view[4];
|
|
homo[2] = view[8];
|
|
homo[3] = -(view[0] * view[19] + view[4] * view[20] + view[8] * view[21]);
|
|
homo[4] = view[1];
|
|
homo[5] = view[5];
|
|
homo[6] = view[9];
|
|
homo[7] = -(view[1] * view[19] + view[5] * view[20] + view[9] * view[21]);
|
|
homo[8] = view[2];
|
|
homo[9] = view[6];
|
|
homo[10] = view[10];
|
|
homo[11] = -(view[2] * view[19] + view[6] * view[20] + view[10] * view[21]);
|
|
homo[12] = 0.0;
|
|
homo[13] = 0.0;
|
|
homo[14] = 0.0;
|
|
homo[15] = 1.0;
|
|
history = homo;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec && (rec != src_rec)) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
|
|
switch (target_mode) {
|
|
case 0: /* apply changes to coordinates in the target object */
|
|
{
|
|
double temp_inverse[16];
|
|
if (target_undo) {
|
|
double* target_history = nullptr;
|
|
int target_found = ExecutiveGetObjectMatrix(
|
|
G, rec->name, target_state, &target_history, false);
|
|
if (target_found && target_history) {
|
|
invert_special44d44d(target_history, temp_inverse);
|
|
if (history) {
|
|
right_multiply44d44d(temp_inverse, history);
|
|
history = temp_inverse;
|
|
} else {
|
|
history = temp_inverse;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
float historyf[16];
|
|
if (history) {
|
|
convert44d44f(history, historyf);
|
|
} else {
|
|
identity44f(historyf);
|
|
}
|
|
ExecutiveTransformObjectSelection(
|
|
G, rec->name, target_state, "", log, historyf, true, false);
|
|
}
|
|
} break;
|
|
case 1: /* applying changes to the object's TTT matrix */
|
|
if (history) {
|
|
float tttf[16];
|
|
convertR44dTTTf(history, tttf);
|
|
ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
|
|
} else {
|
|
ExecutiveSetObjectTTT(G, rec->name, nullptr, -1, quiet, -1);
|
|
}
|
|
/* to do: logging, return values, etc. */
|
|
break;
|
|
case 2: /* applying changes to the state matrix */
|
|
ok = ExecutiveSetObjectMatrix(G, rec->name, target_state, history);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
TrackerDelList(I_Tracker, list_id);
|
|
|
|
SceneInvalidate(G);
|
|
return ok;
|
|
}
|
|
|
|
void ExecutiveInvalidateMapDependents(
|
|
PyMOLGlobals* G, const char* map_name, const char* new_name)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
switch (rec->obj->type) {
|
|
case cObjectMesh:
|
|
ObjectMeshInvalidateMapName((ObjectMesh*) rec->obj, map_name, new_name);
|
|
break;
|
|
case cObjectSurface:
|
|
ObjectSurfaceInvalidateMapName(
|
|
(ObjectSurface*) rec->obj, map_name, new_name);
|
|
break;
|
|
case cObjectVolume:
|
|
ObjectVolumeInvalidateMapName(
|
|
(ObjectVolume*) rec->obj, map_name, new_name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
SceneInvalidate(G);
|
|
}
|
|
|
|
pymol::Result<> ExecutiveResetMatrix(
|
|
PyMOLGlobals* G, const char* name, int mode, int state, int log, int quiet)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
int matrix_mode = SettingGetGlobal_i(G, cSetting_matrix_mode);
|
|
if (matrix_mode < 0)
|
|
matrix_mode = 0; /* for now */
|
|
|
|
if (mode < 0)
|
|
mode = matrix_mode;
|
|
|
|
bool obj_found = false;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec && (rec->type == cExecObject)) {
|
|
/* pymol::CObject *obj = ExecutiveFindObjectByName(G,name); */
|
|
pymol::CObject* obj = rec->obj;
|
|
if (obj) {
|
|
obj_found = true;
|
|
switch (obj->type) {
|
|
case cObjectMolecule:
|
|
switch (mode) {
|
|
case 0: /* transformations already applied to the coordinates */
|
|
for (StateIterator iter(rec->obj, state); iter.next();) {
|
|
double* history = nullptr;
|
|
bool found = ExecutiveGetObjectMatrix2(
|
|
G, rec->obj, iter.state, &history, false);
|
|
if (found && history) {
|
|
double temp_inverse[16];
|
|
float historyf[16];
|
|
invert_special44d44d(history, temp_inverse);
|
|
convert44d44f(temp_inverse, historyf);
|
|
ExecutiveTransformObjectSelection2(
|
|
G, rec->obj, iter.state, "", log, historyf, true, false);
|
|
}
|
|
}
|
|
break;
|
|
case 1: /* operate on the TTT display matrix */
|
|
ObjectResetTTT(
|
|
obj, SettingGetGlobal_b(G, cSetting_movie_auto_store));
|
|
obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
|
|
break;
|
|
case 2: /* applying changes to the state matrix */
|
|
{
|
|
double ident[16];
|
|
identity44d(ident);
|
|
ExecutiveSetObjectMatrix(G, rec->name, state, ident);
|
|
} break;
|
|
}
|
|
break;
|
|
default:
|
|
if (auto* objstate = obj->getObjectState(state)) {
|
|
ObjectStateResetMatrix(objstate);
|
|
obj->invalidate(cRepNone, cRepInvExtents, state);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!obj_found) {
|
|
return pymol::make_error("No object found");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
static double ret_mat[16]; /* UGH ..not thread-safe */
|
|
|
|
static int ExecutiveGetObjectMatrix2(PyMOLGlobals* G, pymol::CObject* obj,
|
|
int state, double** matrix, int incl_ttt)
|
|
{
|
|
/* right now, this only makes sense for molecule objects -- but in
|
|
time all objects should have per-state matrices */
|
|
|
|
int ok = false;
|
|
if (state < 0) {
|
|
/* to do -- TTT only */
|
|
} else {
|
|
if (auto* objstate = obj->getObjectState(state)) {
|
|
*matrix = ObjectStateGetMatrix(objstate);
|
|
ok = true;
|
|
}
|
|
|
|
if (ok && incl_ttt) {
|
|
const float* ttt;
|
|
double tttd[16];
|
|
if (ObjectGetTTT(obj, &ttt, -1)) {
|
|
convertTTTfR44d(ttt, tttd);
|
|
if (*matrix) {
|
|
copy44d(*matrix, ret_mat);
|
|
} else {
|
|
identity44d(ret_mat);
|
|
}
|
|
left_multiply44d44d(tttd, ret_mat);
|
|
*matrix = ret_mat;
|
|
}
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
int ExecutiveGetObjectMatrix(
|
|
PyMOLGlobals* G, const char* name, int state, double** matrix, int incl_ttt)
|
|
{
|
|
int ok = false;
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, name);
|
|
if (obj) {
|
|
return ExecutiveGetObjectMatrix2(G, obj, state, matrix, incl_ttt);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static int ExecutiveSetObjectMatrix2(
|
|
PyMOLGlobals* G, pymol::CObject* obj, int state, double* matrix)
|
|
{
|
|
/* -1 for the TTT matrix, 0 or greater for the state matrix */
|
|
|
|
/* right now, this only makes sense for molecule objects -- but in
|
|
time all objects should have per-state matrices */
|
|
int ok = false;
|
|
if (state < 0) {
|
|
|
|
} else {
|
|
if (auto* objstate = obj->getObjectState(state)) {
|
|
ObjectStateSetMatrix(objstate, matrix);
|
|
ok = true;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
int ExecutiveSetObjectMatrix(
|
|
PyMOLGlobals* G, const char* name, int state, double* matrix)
|
|
{
|
|
int ok = false;
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, name);
|
|
if (obj) {
|
|
return ExecutiveSetObjectMatrix2(G, obj, state, matrix);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static int ExecutiveCountNames(PyMOLGlobals* G)
|
|
{
|
|
int count = 0;
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next))
|
|
count++;
|
|
|
|
return (count);
|
|
}
|
|
|
|
static int ReorderOrderFn(
|
|
PyMOLGlobals* G, const SpecRec* const* rec, int l, int r)
|
|
{
|
|
return (WordCompare(G, rec[l]->name, rec[r]->name, true) <= 0);
|
|
}
|
|
|
|
static int SpecRecListPopulate(
|
|
SpecRec** list, SpecRec* first, const char* group_name)
|
|
{
|
|
/* Add items to list such that group items are ordered behind their group
|
|
* records. author: Thomas Holder, 2013-08 runtime: O(N * G) where N is the
|
|
* number of SpecRecs and G the number of groups args: list: pointer to the
|
|
* end of the list first: pointer to G->Executive->Spec (root of linked list)
|
|
* group_name: if empty string, append ungrouped items, otherwise append
|
|
* group items returns: number of appended items
|
|
*/
|
|
SpecRec* rec;
|
|
int a = 0;
|
|
for (rec = first; rec; rec = rec->next) {
|
|
if (!strcmp(group_name, rec->group_name)) {
|
|
list[a++] = rec;
|
|
if (rec->type == cExecObject && rec->obj->type == cObjectGroup)
|
|
a += SpecRecListPopulate(list + a, first, rec->name);
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveOrder(
|
|
PyMOLGlobals* G, pymol::zstring_view s1View, int sort, int location)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
auto s1 = s1View.c_str();
|
|
CWordList* word_list = WordListNew(G, s1);
|
|
int n_names = ExecutiveCountNames(G);
|
|
|
|
if (n_names) {
|
|
SpecRec **list, **subset, **sorted;
|
|
int* index = nullptr;
|
|
int n_sel;
|
|
int source_row = -1;
|
|
int min_row = -1;
|
|
list = pymol::malloc<SpecRec*>(n_names);
|
|
subset = pymol::calloc<SpecRec*>(n_names);
|
|
sorted = pymol::calloc<SpecRec*>(n_names);
|
|
index = pymol::malloc<int>(n_names);
|
|
if (list && subset) {
|
|
/* create an array of current names */
|
|
{
|
|
int a = 0;
|
|
/* copy all names into array */
|
|
/* update Thomas Holder 2013-08: order group members behind group.
|
|
* fixes PYMOL-1382 (eventually).
|
|
* I don't know if this is the best place for the fix, but it's my best
|
|
* guess.
|
|
*/
|
|
#ifndef NDEBUG
|
|
auto const list_size =
|
|
#endif
|
|
SpecRecListPopulate(list, I->Spec, "");
|
|
assert(list_size == n_names);
|
|
/* unlink them */
|
|
for (a = 0; a < n_names; a++) {
|
|
list[a]->next = nullptr;
|
|
}
|
|
}
|
|
/* transfer matching names to the subset array */
|
|
{
|
|
int a;
|
|
int entry;
|
|
int min_entry = word_list->n_word;
|
|
const char* word = nullptr;
|
|
int word_iter = 0;
|
|
while (WordListIterate(G, word_list, &word, &word_iter)) {
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, word, true, false);
|
|
SpecRec* rec = nullptr;
|
|
entry = word_iter - 1;
|
|
for (a = n_names - 1; a > 0; a--) { /* skipping zeroth */
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec == list[a]) {
|
|
if ((a < min_row) || (min_row < 0))
|
|
min_row = a;
|
|
if (entry <= min_entry) {
|
|
source_row = a; /* where will new list be inserted... */
|
|
min_entry = entry;
|
|
}
|
|
/* ensure that each record appears only once */
|
|
rec->next = subset[entry];
|
|
subset[entry] = rec;
|
|
list[a] = nullptr;
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
}
|
|
if (word_list->n_word &&
|
|
WordMatchExact(G, word_list->start[0], cKeywordAll, true))
|
|
location = -1; /* set to top if "all" is first in list */
|
|
}
|
|
/* expand the selected entries */
|
|
{
|
|
SpecRec *rec, *last;
|
|
int b;
|
|
n_sel = 0;
|
|
for (b = 0; b < word_list->n_word; b++) {
|
|
rec = subset[b];
|
|
while (rec) {
|
|
sorted[n_sel++] = rec;
|
|
last = rec;
|
|
rec = rec->next;
|
|
last->next = nullptr;
|
|
}
|
|
}
|
|
}
|
|
/* sort the selected entries, if requested */
|
|
if (sort) {
|
|
UtilCopyMem(subset, sorted, sizeof(SpecRec*) * n_sel);
|
|
{
|
|
int a;
|
|
UtilSortIndexGlobals(
|
|
G, n_sel, subset, index, (UtilOrderFnGlobals*) ReorderOrderFn);
|
|
for (a = 0; a < n_sel; a++) {
|
|
sorted[a] = subset[index[a]];
|
|
}
|
|
}
|
|
}
|
|
/* reassemble the list using the new order */
|
|
{
|
|
SpecRec* spec = nullptr;
|
|
SpecRec* last = nullptr;
|
|
int a, b;
|
|
int flag;
|
|
for (a = 0; a < n_names; a++) {
|
|
flag = false;
|
|
if (sorted) { /* not yet added */
|
|
switch (location) {
|
|
case -1: /* top */
|
|
if (a == 1)
|
|
flag = true;
|
|
break;
|
|
case -2: /* upper */
|
|
if (min_row >= 0) {
|
|
if (a == min_row)
|
|
flag = true;
|
|
} else if (!list[a])
|
|
flag = true;
|
|
break;
|
|
case 0: /* current */
|
|
if (source_row >= 0) {
|
|
if (a == source_row)
|
|
flag = true;
|
|
} else if (!list[a])
|
|
flag = true;
|
|
break;
|
|
}
|
|
}
|
|
if (flag) {
|
|
for (b = 0; b < n_sel; b++) {
|
|
if (sorted[b]) {
|
|
if (last)
|
|
last->next = sorted[b];
|
|
last = sorted[b];
|
|
if (!spec)
|
|
spec = last;
|
|
}
|
|
}
|
|
FreeP(sorted);
|
|
}
|
|
if (list[a]) {
|
|
if (last)
|
|
last->next = list[a];
|
|
last = list[a];
|
|
if (!spec)
|
|
spec = last;
|
|
}
|
|
}
|
|
if (sorted) { /* still not yet readded? */
|
|
for (b = 0; b < n_sel; b++) {
|
|
if (sorted[b]) {
|
|
if (last)
|
|
last->next = sorted[b];
|
|
last = sorted[b];
|
|
if (!spec)
|
|
spec = last;
|
|
}
|
|
}
|
|
}
|
|
I->Spec = spec;
|
|
OrthoDirty(G);
|
|
SeqChanged(G);
|
|
}
|
|
FreeP(index);
|
|
FreeP(sorted);
|
|
FreeP(list);
|
|
FreeP(subset);
|
|
}
|
|
ExecutiveInvalidatePanelList(G);
|
|
}
|
|
WordListFreeP(word_list);
|
|
return {};
|
|
}
|
|
|
|
pymol::vla<ObjectMolecule*> ExecutiveGetObjectMoleculeVLA(
|
|
PyMOLGlobals* G, const char* sele)
|
|
{
|
|
ObjectMolecule** result = nullptr;
|
|
int s1 = SelectorIndexByName(G, sele);
|
|
if (s1 >= 0) {
|
|
ObjectMoleculeOpRec op;
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_GetObjects;
|
|
op.obj1VLA = (ObjectMolecule**) VLAlloc(ObjectMolecule*, 10);
|
|
op.i1 = 0;
|
|
ExecutiveObjMolSeleOp(G, s1, &op);
|
|
result = (ObjectMolecule**) op.obj1VLA;
|
|
VLASize(result, ObjectMolecule*, op.i1);
|
|
}
|
|
return pymol::vla_take_ownership(result);
|
|
}
|
|
|
|
/* #define ExecLineHeight 18 */
|
|
#define ExecClickMargin DIP2PIXEL(2)
|
|
#define ExecTopMargin 0
|
|
#define ExecToggleMargin DIP2PIXEL(2)
|
|
#define ExecLeftMargin DIP2PIXEL(1)
|
|
#define ExecRightMargin 0
|
|
#define ExecToggleWidth DIP2PIXEL(17)
|
|
#define ExecToggleSize DIP2PIXEL(16)
|
|
#define ExecToggleTextShift DIP2PIXEL(4)
|
|
|
|
int ExecutiveSetDrag(PyMOLGlobals* G, const char* name, int quiet, int mode)
|
|
{
|
|
char drag_name[] = cEditorDrag;
|
|
int set_flag = false;
|
|
int need_sele = true;
|
|
int result = true;
|
|
if (name[0]) {
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, name);
|
|
if (obj) {
|
|
EditorSetDrag(G, obj, -1, quiet, SceneGetState(G));
|
|
set_flag = true;
|
|
} else {
|
|
SpecRec* rec = ExecutiveFindSpec(G, name);
|
|
if (rec) {
|
|
if (rec->type == cExecSelection) {
|
|
SelectorCreate(G, drag_name, name, nullptr, true, nullptr);
|
|
need_sele = false;
|
|
{
|
|
int sele = SelectorIndexByName(G, drag_name);
|
|
ObjectMolecule* objMol = SelectorGetSingleObjectMolecule(G, sele);
|
|
if (objMol) {
|
|
if (mode > 0)
|
|
sele = -1; /* force drag by matrix */
|
|
EditorSetDrag(G, objMol, sele, quiet, SceneGetState(G));
|
|
set_flag = true;
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" Drag-Error: selection spans more than one object.\n" ENDFB(G);
|
|
}
|
|
}
|
|
} else if (rec->type == cExecObject) {
|
|
switch (rec->obj->type) {
|
|
case cObjectGroup:
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" Drag-Error: cannot drag group objects yet.\n" ENDFB(G);
|
|
break;
|
|
}
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
result = set_flag;
|
|
if (!result) {
|
|
EditorInactivate(G);
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" Drag-Error: invalid or empty selection." ENDFB(G);
|
|
} else if (EditorDraggingObjectMatrix(G)) {
|
|
SelectorCreate(G, drag_name, "none", nullptr, true, nullptr);
|
|
} else if (need_sele && (obj->type == cObjectMolecule) &&
|
|
(!EditorDraggingObjectMatrix(G))) {
|
|
SelectorCreate(G, drag_name, obj->Name, (ObjectMolecule*) (void*) obj,
|
|
true, nullptr); /* for indication only */
|
|
}
|
|
} else {
|
|
EditorInactivate(G);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int ExecutivePop(
|
|
PyMOLGlobals* G, const char* target, const char* source, int quiet)
|
|
{
|
|
int ok = true;
|
|
int src;
|
|
int result = 0;
|
|
|
|
ExecutiveDelete(G, target);
|
|
if (ExecutiveFindObjectMoleculeByName(G, source)) {
|
|
ok = false;
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" Pop-Error: source selection '%s' can't be an object.\n", source ENDFB(G);
|
|
|
|
} else {
|
|
src = SelectorIndexByName(G, source);
|
|
if (src < 0)
|
|
ok = false;
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" Pop-Error: invalid source selection name '%s'\n", source ENDFB(G);
|
|
} else {
|
|
ObjectMoleculeOpRec op;
|
|
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_Pop;
|
|
SelectorCreateEmpty(G, target, true);
|
|
op.i1 = SelectorIndexByName(G, target);
|
|
op.i2 = 1;
|
|
op.i3 = 0;
|
|
ExecutiveObjMolSeleOp(G, src, &op);
|
|
result = op.i3;
|
|
}
|
|
}
|
|
if (!result)
|
|
ExecutiveDelete(G, target);
|
|
if (!ok)
|
|
return -1;
|
|
else
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Return the selector index of the "active" alignment.
|
|
*/
|
|
int ExecutiveGetActiveAlignmentSele(PyMOLGlobals* G)
|
|
{
|
|
const char* alignment = ExecutiveGetActiveAlignment(G);
|
|
if (alignment && alignment[0]) {
|
|
return SelectorIndexByName(G, alignment);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Return the name of the "active" alignment. That is:
|
|
*
|
|
* 1) The "seq_view_alignment" setting if it's set
|
|
* 2) or the name of the first enabled alignment object
|
|
* 3) or nullptr
|
|
*/
|
|
const char* ExecutiveGetActiveAlignment(PyMOLGlobals* G)
|
|
{
|
|
const char* alignment = SettingGetGlobal_s(G, cSetting_seq_view_alignment);
|
|
if (alignment && alignment[0]) { /* explicit alignment setting name */
|
|
return alignment;
|
|
} else { /* otherwise, use the first active alignment */
|
|
SpecRec* rec = nullptr;
|
|
CExecutive* I = G->Executive;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->visible) {
|
|
if (rec->type == cExecObject)
|
|
if (rec->obj->type == cObjectAlignment) {
|
|
return rec->obj->Name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int ExecutiveGetActiveSele(PyMOLGlobals* G)
|
|
{
|
|
ObjectNameType name;
|
|
if (ExecutiveGetActiveSeleName(G, name, false, false))
|
|
return SelectorIndexByName(G, name);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int ExecutiveGetActiveSeleName(
|
|
PyMOLGlobals* G, char* name, int create_new, int log)
|
|
{
|
|
/* TODO: cache/optimize to avoid table scan */
|
|
|
|
int result = false;
|
|
SpecRec* rec = nullptr;
|
|
CExecutive* I = G->Executive;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecSelection) {
|
|
if (rec->visible) {
|
|
strcpy(name, rec->name);
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
if ((!result) && create_new) {
|
|
if (SettingGetGlobal_b(G, cSetting_auto_number_selections)) {
|
|
int sel_num = SettingGetGlobal_i(G, cSetting_sel_counter) + 1;
|
|
|
|
SettingSetGlobal_i(G, cSetting_sel_counter, sel_num);
|
|
sprintf(name, "sel%02d", sel_num);
|
|
SelectorCreateEmpty(G, name, -1);
|
|
if (log) {
|
|
if (SettingGetGlobal_i(G, cSetting_logging)) {
|
|
OrthoLineType buf2;
|
|
sprintf(buf2, "cmd.select('%s','none')\n", name);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
}
|
|
} else {
|
|
sprintf(name, "sele");
|
|
SelectorCreateEmpty(G, name, -1);
|
|
if (log) {
|
|
OrthoLineType buf2;
|
|
sprintf(buf2, "cmd.select('%s','none')\n", name);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int ExecutiveGetActiveSeleName(
|
|
PyMOLGlobals* G, std::string& name, int create_new, int log)
|
|
{
|
|
ObjectNameType name_arg;
|
|
auto result = ExecutiveGetActiveSeleName(G, name_arg, create_new, log);
|
|
name = name_arg;
|
|
return result;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveFixChemistry(
|
|
PyMOLGlobals* G, const char* s1, const char* s2, int invalidate, int quiet)
|
|
{
|
|
SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
|
|
SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
|
|
|
|
SpecRec* rec = nullptr;
|
|
CExecutive* I = G->Executive;
|
|
|
|
assert((sele1 >= 0) && (sele2 >= 0));
|
|
{
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject)
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
ObjectMoleculeFixChemistry(
|
|
(ObjectMolecule*) rec->obj, sele1, sele2, invalidate);
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSetObjectColor(
|
|
PyMOLGlobals* G, const char* name, const char* color, int quiet)
|
|
{
|
|
int col_ind = ColorGetIndex(G, color);
|
|
auto obj = ExecutiveFindObjectByName(G, name);
|
|
if (obj) {
|
|
obj->Color = col_ind;
|
|
} else {
|
|
return pymol::make_error("Object ", name, " not found.");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
int ExecutiveGetObjectColorIndex(PyMOLGlobals* G, const char* name)
|
|
{
|
|
int result = -1;
|
|
pymol::CObject* obj = nullptr;
|
|
obj = ExecutiveFindObjectByName(G, name);
|
|
if (obj) {
|
|
result = obj->Color;
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
pymol::Result<std::array<float, 3>> ExecutiveGetAtomVertex(
|
|
PyMOLGlobals* G, const char* s1, int state, int index)
|
|
{
|
|
auto tmpsele1 = SelectorTmp::make(G, s1);
|
|
p_return_if_error(tmpsele1);
|
|
|
|
switch (tmpsele1->getAtomCount()) {
|
|
case 0:
|
|
return pymol::make_error("Empty Selection");
|
|
case 1:
|
|
return SelectorGetSingleAtomVertex(G, tmpsele1->getIndex(), state);
|
|
}
|
|
|
|
assert(tmpsele1->getAtomCount() > 0);
|
|
return pymol::make_error("More than one atom found");
|
|
}
|
|
|
|
void ExecutiveMakeUnusedName(PyMOLGlobals* G, char* prefix, int length,
|
|
bool alwaysnumber, int start, const char* pattern)
|
|
{
|
|
|
|
if (!prefix[0])
|
|
strcpy(prefix, "obj");
|
|
|
|
int prefixlen = strlen(prefix);
|
|
int suffixlen = length - prefixlen;
|
|
char* end = prefix + prefixlen;
|
|
|
|
for (int cnt = start; alwaysnumber || ExecutiveValidName(G, prefix); ++cnt) {
|
|
snprintf(end, suffixlen, pattern, cnt);
|
|
alwaysnumber = false;
|
|
}
|
|
}
|
|
|
|
std::string ExecutiveGetUnusedName(
|
|
PyMOLGlobals* G, const char* prefix, bool alwaysnumber)
|
|
{
|
|
|
|
OrthoLineType unused_name;
|
|
strcpy(unused_name, prefix);
|
|
|
|
ObjectMakeValidName(G, unused_name);
|
|
|
|
ExecutiveMakeUnusedName(G, unused_name, OrthoLineLength, alwaysnumber);
|
|
|
|
return std::string(unused_name);
|
|
}
|
|
|
|
int ExecutiveProcessObjectName(
|
|
PyMOLGlobals* G, const char* proposed, char* actual)
|
|
{
|
|
int result = true;
|
|
UtilNCopy(actual, proposed, sizeof(ObjectNameType));
|
|
if (SettingGetGlobal_b(G, cSetting_validate_object_names))
|
|
ObjectMakeValidName(G, actual);
|
|
if (SettingGetGlobal_b(G, cSetting_auto_rename_duplicate_objects) ||
|
|
!proposed[0]) {
|
|
ExecutiveMakeUnusedName(G, actual, sizeof(ObjectNameType), false, 2, "_%d");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
pymol::Result<std::vector<DiscardedRec>> ExecutiveSetName(PyMOLGlobals* G,
|
|
pymol::zstring_view old_name_view, pymol::zstring_view new_name_view,
|
|
bool save)
|
|
{
|
|
std::vector<DiscardedRec> discarded;
|
|
SpecRec* rec = nullptr;
|
|
CExecutive* I = G->Executive;
|
|
int found = false;
|
|
auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
|
|
auto old_name = old_name_view.c_str();
|
|
auto new_name = new_name_view.c_str();
|
|
|
|
ObjectNameType name;
|
|
UtilNCopy(name, new_name, sizeof(ObjectNameType));
|
|
|
|
if (ObjectMakeValidName(name)) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
" Warning: Invalid characters in '%s' have been replaced or stripped\n",
|
|
name ENDFB(G);
|
|
}
|
|
|
|
if (!name[0]) {
|
|
return pymol::make_error("Blank names not allowed.");
|
|
} else if (WordMatchExact(G, name, cKeywordSame, ignore_case) ||
|
|
SelectorNameIsKeyword(G, name)) {
|
|
return pymol::make_error("Name ", name, " is a selection keyword.");
|
|
}
|
|
if (!WordMatchExact(G, name, old_name, ignore_case)) {
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (found)
|
|
break;
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (WordMatchExact(G, rec->obj->Name, old_name, ignore_case)) {
|
|
ExecutiveDelKey(I, rec);
|
|
auto r = ExecutiveDelete(G, name, save);
|
|
discarded = std::move(*r);
|
|
assert(r);
|
|
ObjectSetName(rec->obj, name);
|
|
UtilNCopy(rec->name, rec->obj->Name, WordLength);
|
|
ExecutiveAddKey(I, rec);
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
/*
|
|
SelectorDelete(G,old_name);
|
|
ExecutiveUpdateObjectSelection(G,rec->obj);
|
|
*/
|
|
SelectorSetName(G, name, old_name);
|
|
SceneChanged(G);
|
|
SeqChanged(G);
|
|
}
|
|
if (rec->obj->type == cObjectMap)
|
|
ExecutiveInvalidateMapDependents(G, old_name, name);
|
|
|
|
found = true;
|
|
}
|
|
break;
|
|
case cExecSelection:
|
|
if (WordMatchExact(G, rec->name, old_name, ignore_case)) {
|
|
if (SelectorSetName(G, name, old_name)) {
|
|
auto r = ExecutiveDelete(G, name, save); /* just in case */
|
|
discarded = std::move(*r);
|
|
assert(r);
|
|
ExecutiveDelKey(I, rec);
|
|
UtilNCopy(rec->name, name, WordLength);
|
|
ExecutiveAddKey(I, rec);
|
|
found = true;
|
|
OrthoDirty(G);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
return pymol::make_error("Could not find object named ", name);
|
|
else {
|
|
rec = nullptr;
|
|
int old_name_len = strlen(old_name);
|
|
int new_name_len = strlen(name);
|
|
ObjectNameType childname;
|
|
UtilNCopy(childname, name, sizeof(ObjectNameType));
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (WordMatchExact(G, rec->group_name, old_name, ignore_case)) {
|
|
UtilNCopy(rec->group_name, name, WordLength);
|
|
// rename group members for group_auto_mode
|
|
if (strncmp(rec->name, old_name, old_name_len) == 0 &&
|
|
rec->name[old_name_len] == '.') {
|
|
UtilNCopy(childname + new_name_len, rec->name + old_name_len,
|
|
sizeof(ObjectNameType) - new_name_len);
|
|
ExecutiveSetName(G, rec->name, childname);
|
|
}
|
|
}
|
|
}
|
|
ExecutiveInvalidateGroups(G, false);
|
|
}
|
|
}
|
|
ColorRenameExt(G, old_name_view, new_name_view);
|
|
return discarded;
|
|
}
|
|
|
|
/**
|
|
* Load any file type which is implemented in C.
|
|
*
|
|
* fname: File name, can be empty if content is provided
|
|
* content: File contents, if not nullptr
|
|
* content_length: Length of "content", if not nullptr
|
|
* content_format: File type code
|
|
* object_name: New object name
|
|
* state: Object state to start loading new coordsets in
|
|
* zoom: Zoom on new loaded atoms
|
|
* discrete: Make discrete states
|
|
* finish: update object selection & zoom
|
|
* multiplex: Split new states into objects
|
|
* quiet: Suppress feedback
|
|
* object_props: names of object properties to load
|
|
* atom_props: names of atom properties to load
|
|
*/
|
|
pymol::Result<> ExecutiveLoad(PyMOLGlobals* G, const char* fname,
|
|
const char* content, int content_length, cLoadType_t content_format,
|
|
const char* object_name_proposed, int state, int zoom, int discrete,
|
|
int finish, int multiplex, int quiet, const char* plugin_arg,
|
|
const char* object_props, const char* atom_props, bool mimic)
|
|
{
|
|
auto res = ExecutiveLoadPrepareArgs(G, fname, content, content_length,
|
|
content_format, object_name_proposed, state, zoom, discrete, finish,
|
|
multiplex, quiet, plugin_arg, object_props, atom_props, mimic);
|
|
p_return_if_error(res);
|
|
return ExecutiveLoad(G, res.result());
|
|
}
|
|
|
|
/**
|
|
* Prepare arguments for ExecutiveLoad
|
|
*/
|
|
pymol::Result<ExecutiveLoadArgs> ExecutiveLoadPrepareArgs(PyMOLGlobals* G,
|
|
pymol::null_safe_zstring_view fname, const char* content,
|
|
int content_length, cLoadType_t content_format,
|
|
const char* object_name_proposed, int state, int zoom, int discrete,
|
|
int finish, int multiplex, int quiet, const char* plugin_arg,
|
|
const char* object_props, const char* atom_props, bool mimic)
|
|
{
|
|
ExecutiveLoadArgs args;
|
|
bool fname_null_ok = false;
|
|
|
|
// validate proposed object name
|
|
ObjectNameType object_name = "";
|
|
ExecutiveProcessObjectName(G, object_name_proposed, object_name);
|
|
args.object_name = object_name;
|
|
|
|
// Ensure correct float parsing with scanf. It's possible to change this from
|
|
// Python, so don't rely on a persistent global value.
|
|
std::setlocale(LC_NUMERIC, "C");
|
|
|
|
if (!object_props)
|
|
object_props = SettingGetGlobal_s(G, cSetting_load_object_props_default);
|
|
if (!atom_props)
|
|
atom_props = SettingGetGlobal_s(G, cSetting_load_atom_props_default);
|
|
|
|
// multiplex -2 -> "multiplex" setting
|
|
// multiplex -1 -> file type dependant default
|
|
// multiplex 1 -> split entries (don't try to load into existing object)
|
|
if (multiplex == -2) {
|
|
multiplex = SettingGetGlobal_i(G, cSetting_multiplex);
|
|
}
|
|
|
|
switch (content_format) {
|
|
// string loading functions
|
|
case cLoadTypePDBStr:
|
|
case cLoadTypeVDBStr:
|
|
case cLoadTypeCIFStr:
|
|
case cLoadTypeMMTFStr:
|
|
case cLoadTypeMAEStr:
|
|
case cLoadTypeXPLORStr:
|
|
case cLoadTypeCCP4Str:
|
|
case cLoadTypeCCP4UnspecifiedStr:
|
|
case cLoadTypeMRCStr:
|
|
case cLoadTypePHIStr:
|
|
case cLoadTypeMMDStr:
|
|
case cLoadTypeMOLStr:
|
|
case cLoadTypeMOL2Str:
|
|
case cLoadTypeSDF2Str:
|
|
case cLoadTypeXYZStr:
|
|
case cLoadTypeDXStr:
|
|
case cLoadTypeBCIFStr:
|
|
if (!content) {
|
|
return pymol::make_error("content is nullptr");
|
|
}
|
|
fname_null_ok = true;
|
|
break;
|
|
case cLoadTypePQR:
|
|
case cLoadTypePDBQT:
|
|
case cLoadTypePDB:
|
|
case cLoadTypeCIF:
|
|
case cLoadTypeMMTF:
|
|
case cLoadTypeMAE:
|
|
case cLoadTypeXPLORMap:
|
|
case cLoadTypeCCP4Map:
|
|
case cLoadTypeCCP4Unspecified:
|
|
case cLoadTypeMRC:
|
|
case cLoadTypePHIMap:
|
|
case cLoadTypeMMD:
|
|
case cLoadTypeMOL:
|
|
case cLoadTypeMOL2:
|
|
case cLoadTypeSDF2:
|
|
case cLoadTypeXYZ:
|
|
case cLoadTypeDXMap:
|
|
case cLoadTypeBCIF:
|
|
if (content) {
|
|
fname_null_ok = true;
|
|
break;
|
|
}
|
|
|
|
if (fname.empty()) {
|
|
break;
|
|
}
|
|
|
|
try {
|
|
args.content = pymol::file_get_contents(fname);
|
|
PRINTFB(G, FB_Executive, FB_Blather)
|
|
" %s: Loading from %s.\n", __func__, fname.c_str() ENDFB(G);
|
|
} catch (...) {
|
|
return pymol::make_error(
|
|
pymol::string_format("Unable to open file '%s'", fname.c_str()));
|
|
}
|
|
|
|
break;
|
|
|
|
// molfile_plugin based formats
|
|
case cLoadTypeCUBEMap:
|
|
args.plugin = "cube";
|
|
break;
|
|
case cLoadTypeSpider:
|
|
args.plugin = "spider";
|
|
break;
|
|
case cLoadTypeXTC:
|
|
args.plugin = "xtc";
|
|
break;
|
|
case cLoadTypeTRR:
|
|
args.plugin = "trr";
|
|
break;
|
|
case cLoadTypeGRO:
|
|
args.plugin = "gro";
|
|
break;
|
|
case cLoadTypeG96:
|
|
args.plugin = "g96";
|
|
break;
|
|
case cLoadTypeTRJ2:
|
|
args.plugin = "trj";
|
|
break;
|
|
case cLoadTypeDCD:
|
|
args.plugin = "dcd";
|
|
break;
|
|
case cLoadTypeDTR:
|
|
args.plugin = "dtr";
|
|
break;
|
|
case cLoadTypeCMS:
|
|
args.plugin = "mae";
|
|
break;
|
|
default:
|
|
if (plugin_arg) {
|
|
args.plugin = plugin_arg;
|
|
// Hackish way to request a "sub-plugin". For example the "cube" plugin
|
|
// can load maps or molecules:
|
|
// load example.cube, molobj, format=plugin:cube:1
|
|
// load example.cube, mapobj, format=plugin:cube:4
|
|
auto pos = args.plugin.find(':');
|
|
if (pos != std::string::npos) {
|
|
args.plugin_mask = atoi(args.plugin.c_str() + pos + 1);
|
|
args.plugin.resize(pos);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (fname.empty() && !fname_null_ok) {
|
|
return pymol::make_error("This format requires a filename to load");
|
|
}
|
|
|
|
if (!args.plugin.empty()) {
|
|
content_format = cLoadTypePlugin;
|
|
}
|
|
|
|
if (content) {
|
|
assert(args.content.empty());
|
|
args.content = std::string(content, content_length);
|
|
}
|
|
|
|
args.content_format = content_format;
|
|
args.fname = fname.c_str();
|
|
args.state = state;
|
|
args.zoom = zoom;
|
|
args.discrete = discrete;
|
|
args.finish = finish;
|
|
args.multiplex = multiplex;
|
|
args.quiet = quiet;
|
|
args.object_props = object_props;
|
|
args.atom_props = atom_props;
|
|
args.mimic = mimic;
|
|
return args;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveLoad(PyMOLGlobals* G, ExecutiveLoadArgs const& args)
|
|
{
|
|
pymol::CObject* origObj = nullptr;
|
|
const char* fname = args.fname.c_str();
|
|
const char* content = args.content.data();
|
|
int size = args.content.size();
|
|
const char* object_name = args.object_name.c_str();
|
|
const char* object_props = args.object_props.c_str();
|
|
const char* atom_props = args.atom_props.c_str();
|
|
const char* plugin = args.plugin.c_str();
|
|
auto content_format = args.content_format;
|
|
auto state = args.state;
|
|
auto zoom = args.zoom;
|
|
auto discrete = args.discrete;
|
|
auto finish = args.finish;
|
|
auto multiplex = args.multiplex;
|
|
auto quiet = args.quiet;
|
|
|
|
if (multiplex != 1) {
|
|
origObj = ExecutiveGetExistingCompatible(G, object_name, content_format);
|
|
}
|
|
|
|
// file type dependent multiplex and discrete default
|
|
if (discrete < 0) {
|
|
if (multiplex == 1) {
|
|
discrete = 0;
|
|
} else {
|
|
switch (content_format) {
|
|
case cLoadTypeMOL2:
|
|
case cLoadTypeMOL2Str:
|
|
discrete = -1; /* content-dependent behavior... */
|
|
case cLoadTypeSDF2: /* SDF files currently default to discrete */
|
|
case cLoadTypeSDF2Str:
|
|
case cLoadTypeMAE: /* Let MAE handle itself */
|
|
case cLoadTypeMAEStr:
|
|
break;
|
|
default:
|
|
discrete = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ok = true;
|
|
OrthoLineType buf = "";
|
|
int pdb_variant = PDB_VARIANT_DEFAULT;
|
|
pymol::CObject* obj = nullptr;
|
|
|
|
// downstream file type reading functions
|
|
switch (content_format) {
|
|
case cLoadTypePQR:
|
|
pdb_variant = PDB_VARIANT_PQR;
|
|
case cLoadTypePDBQT:
|
|
if (content_format == cLoadTypePDBQT)
|
|
pdb_variant = PDB_VARIANT_PDBQT;
|
|
case cLoadTypeVDBStr:
|
|
if (ok && content_format == cLoadTypeVDBStr)
|
|
pdb_variant = PDB_VARIANT_VDB;
|
|
case cLoadTypePDB:
|
|
case cLoadTypePDBStr:
|
|
ok = ExecutiveProcessPDBFile(G, origObj, fname, content, object_name, state,
|
|
discrete, finish, buf, pdb_variant, quiet, multiplex, zoom);
|
|
break;
|
|
case cLoadTypeCIF:
|
|
case cLoadTypeCIFStr: {
|
|
auto res = ExecutiveProcessCif(G, static_cast<ObjectMolecule*>(origObj),
|
|
object_name, content, state, discrete, quiet, multiplex, zoom);
|
|
p_return_if_error(res);
|
|
obj = res.result();
|
|
} break;
|
|
case cLoadTypeBCIF:
|
|
case cLoadTypeBCIFStr: {
|
|
auto res = ObjectMoleculeReadBCif(G, static_cast<ObjectMolecule*>(origObj),
|
|
content, size, state, discrete, quiet, multiplex, zoom);
|
|
p_return_if_error(res);
|
|
obj = res.result();
|
|
} break;
|
|
case cLoadTypeMMTF:
|
|
case cLoadTypeMMTFStr:
|
|
obj = ObjectMoleculeReadMmtfStr(G, (ObjectMolecule*) origObj, content, size,
|
|
state, discrete, quiet, multiplex, zoom);
|
|
break;
|
|
case cLoadTypeMAE:
|
|
case cLoadTypeMAEStr:
|
|
#ifndef _PYMOL_IP_EXTRAS
|
|
return pymol::Error::make<pymol::Error::INCENTIVE_ONLY>(
|
|
"'mae' format not supported by this PyMOL build");
|
|
#else
|
|
#error ""
|
|
#endif
|
|
break;
|
|
case cLoadTypeTOP:
|
|
if (origObj) {
|
|
/* always reinitialize topology objects from scratch */
|
|
ExecutiveDelete(G, origObj->Name);
|
|
origObj = nullptr;
|
|
}
|
|
obj = ObjectMoleculeLoadTOPFile(G, nullptr, fname, state, discrete);
|
|
break;
|
|
case cLoadTypeTRJ:
|
|
if (origObj) {
|
|
ObjectMoleculeLoadTRJFile(G, (ObjectMolecule*) origObj, fname, state, 1,
|
|
1, 1, -1, -1, nullptr, 1, nullptr, quiet);
|
|
} else {
|
|
return pymol::make_error(
|
|
"must load object topology before loading trajectory!");
|
|
}
|
|
break;
|
|
case cLoadTypeCRD:
|
|
if (origObj) {
|
|
ObjectMoleculeLoadRSTFile(
|
|
G, (ObjectMolecule*) origObj, fname, state, quiet, 1);
|
|
} else {
|
|
return pymol::make_error(
|
|
"must load object topology before loading coordinate file!");
|
|
}
|
|
break;
|
|
case cLoadTypeRST:
|
|
if (origObj) {
|
|
ObjectMoleculeLoadRSTFile(
|
|
G, (ObjectMolecule*) origObj, fname, state, quiet, 0);
|
|
} else {
|
|
return pymol::make_error(
|
|
"must load object topology before loading restart file!");
|
|
}
|
|
break;
|
|
case cLoadTypePMO:
|
|
return pymol::make_error("PMO format no longer supported.");
|
|
case cLoadTypeDXStr:
|
|
obj = ObjectMapReadDXStr(
|
|
G, dynamic_cast<ObjectMap*>(origObj), content, size, state, quiet);
|
|
break;
|
|
case cLoadTypeDXMap:
|
|
obj = ObjectMapLoadDXFile(G, (ObjectMap*) origObj, fname, state, quiet);
|
|
break;
|
|
case cLoadTypeFLDMap:
|
|
obj = ObjectMapLoadFLDFile(G, (ObjectMap*) origObj, fname, state, quiet);
|
|
break;
|
|
case cLoadTypeBRIXMap:
|
|
obj = ObjectMapLoadBRIXFile(G, (ObjectMap*) origObj, fname, state, quiet);
|
|
break;
|
|
case cLoadTypeGRDMap:
|
|
obj = ObjectMapLoadGRDFile(G, (ObjectMap*) origObj, fname, state, quiet);
|
|
break;
|
|
case cLoadTypeACNTMap:
|
|
obj = ObjectMapLoadACNTFile(G, (ObjectMap*) origObj, fname, state, quiet);
|
|
break;
|
|
case cLoadTypeXPLORMap:
|
|
case cLoadTypeXPLORStr:
|
|
obj = ObjectMapLoadXPLOR(
|
|
G, (ObjectMap*) origObj, content, state, false, quiet);
|
|
break;
|
|
case cLoadTypePHIMap:
|
|
case cLoadTypePHIStr:
|
|
obj = ObjectMapLoadPHI(
|
|
G, (ObjectMap*) origObj, content, state, true, size, quiet);
|
|
break;
|
|
case cLoadTypeCCP4Map:
|
|
case cLoadTypeCCP4Str:
|
|
case cLoadTypeCCP4Unspecified:
|
|
case cLoadTypeCCP4UnspecifiedStr:
|
|
case cLoadTypeMRC:
|
|
case cLoadTypeMRCStr:
|
|
obj = ObjectMapLoadCCP4(G, (ObjectMap*) origObj, content, state, true, size,
|
|
quiet, content_format);
|
|
break;
|
|
case cLoadTypeCGO:
|
|
obj = ObjectCGOFromFloatArray(
|
|
G, (ObjectCGO*) origObj, (float*) content, size, state, quiet);
|
|
break;
|
|
case cLoadTypeMOL:
|
|
case cLoadTypeMOLStr:
|
|
case cLoadTypeMOL2:
|
|
case cLoadTypeMOL2Str:
|
|
case cLoadTypeSDF2:
|
|
case cLoadTypeSDF2Str:
|
|
case cLoadTypeXYZ:
|
|
case cLoadTypeXYZStr:
|
|
case cLoadTypeMMD:
|
|
case cLoadTypeMMDStr: {
|
|
const char* next_entry = content;
|
|
char new_name[WordLength] = "";
|
|
|
|
OVLexicon* loadproplex = nullptr;
|
|
bool loadpropertiesall = false;
|
|
|
|
if (object_props[0]) {
|
|
if (strcmp(object_props, "*") == 0) {
|
|
loadpropertiesall = true;
|
|
} else {
|
|
auto keys = strsplit(object_props);
|
|
loadproplex = OVLexicon_New(G->Context->heap);
|
|
for (auto& key : keys) {
|
|
OVLexicon_GetFromCString(loadproplex, key.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
// (some of) these file types support multiple molecules per file,
|
|
// and we support to load them into separate objects (multiplex).
|
|
do {
|
|
obj = ObjectMoleculeReadStr(G, (ObjectMolecule*) origObj, &next_entry,
|
|
content_format, state, discrete, quiet, multiplex, new_name,
|
|
loadpropertiesall, loadproplex);
|
|
|
|
if (new_name[0]) {
|
|
// multiplexing
|
|
ObjectSetName(obj, new_name);
|
|
ExecutiveDelete(G, obj->Name); // just in case there is a collision
|
|
ExecutiveManageObject(G, obj, zoom, true);
|
|
new_name[0] = 0;
|
|
obj = nullptr;
|
|
}
|
|
} while (next_entry);
|
|
|
|
OVLexicon_Del(loadproplex);
|
|
} break;
|
|
default:
|
|
if (plugin[0]) {
|
|
obj = PlugIOManagerLoad(G, origObj ? &origObj : nullptr, fname, state,
|
|
quiet, plugin, args.plugin_mask);
|
|
} else {
|
|
return pymol::make_error("Unable to read that file type from C (",
|
|
content_format, ", ", plugin, ")");
|
|
}
|
|
}
|
|
|
|
if (origObj && obj) {
|
|
if (finish)
|
|
ExecutiveUpdateObjectSelection(G, origObj);
|
|
|
|
if (fname)
|
|
sprintf(buf, " CmdLoad: \"%s\" appended into object \"%s\", state %d.\n",
|
|
fname, object_name, state + 1);
|
|
} else if (obj) {
|
|
ObjectSetName(obj, object_name);
|
|
ExecutiveManageObject(G, obj, zoom, true);
|
|
|
|
if (fname)
|
|
sprintf(buf, " CmdLoad: \"%s\" loaded as \"%s\".\n", fname, obj->Name);
|
|
else
|
|
sprintf(buf, " CmdLoad: loaded as \"%s\".\n", obj->Name);
|
|
}
|
|
|
|
if (!quiet && buf[0]) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
"%s", buf ENDFB(G);
|
|
}
|
|
|
|
// Reshape movie in case multi-state.
|
|
OrthoReshape(G, -1, -1, false);
|
|
return {};
|
|
}
|
|
|
|
/* ExecutiveGetExistingCompatible
|
|
*
|
|
* PARAMS
|
|
* oname -- object name
|
|
* type -- new object type
|
|
*
|
|
* RETURNS
|
|
* Base-class object ptr
|
|
*
|
|
* SIDE EFFECTS
|
|
* If an object with the same name but different type already exists,
|
|
* then it is deleted.
|
|
*/
|
|
pymol::CObject* ExecutiveGetExistingCompatible(
|
|
PyMOLGlobals* G, const char* oname, cLoadType_t type)
|
|
{
|
|
pymol::CObject* origObj = nullptr;
|
|
origObj = ExecutiveFindObjectByName(G, oname);
|
|
/* check for existing object of right type, delete if not */
|
|
if (origObj) {
|
|
int new_type = -1;
|
|
switch (type) {
|
|
case cLoadTypePlugin:
|
|
// let PlugIOManager delete incompatible objects
|
|
return origObj;
|
|
case cLoadTypeChemPyModel:
|
|
case cLoadTypePDB:
|
|
case cLoadTypePDBStr:
|
|
case cLoadTypeVDBStr:
|
|
case cLoadTypeCIF:
|
|
case cLoadTypeCIFStr:
|
|
case cLoadTypeMMTF:
|
|
case cLoadTypeMMTFStr:
|
|
case cLoadTypeXYZ:
|
|
case cLoadTypeXYZStr:
|
|
case cLoadTypeMOL:
|
|
case cLoadTypeMOLStr:
|
|
case cLoadTypeMMD:
|
|
case cLoadTypeMMDSeparate:
|
|
case cLoadTypeMMDStr:
|
|
case cLoadTypeTOP:
|
|
case cLoadTypeTRJ:
|
|
case cLoadTypeCRD:
|
|
case cLoadTypeRST:
|
|
case cLoadTypeMOL2:
|
|
case cLoadTypeMOL2Str:
|
|
case cLoadTypeSDF2:
|
|
case cLoadTypeSDF2Str:
|
|
case cLoadTypePQR:
|
|
case cLoadTypePDBQT:
|
|
case cLoadTypeXTC:
|
|
case cLoadTypeDTR:
|
|
case cLoadTypeTRR:
|
|
case cLoadTypeGRO:
|
|
case cLoadTypeTRJ2:
|
|
case cLoadTypeG96:
|
|
case cLoadTypeDCD:
|
|
new_type = cObjectMolecule;
|
|
break;
|
|
case cLoadTypeChemPyBrick:
|
|
case cLoadTypeChemPyMap:
|
|
case cLoadTypeXPLORMap:
|
|
case cLoadTypeXPLORStr:
|
|
case cLoadTypeCCP4Map:
|
|
case cLoadTypeCCP4Str:
|
|
case cLoadTypeCCP4Unspecified:
|
|
case cLoadTypeCCP4UnspecifiedStr:
|
|
case cLoadTypeMRC:
|
|
case cLoadTypeMRCStr:
|
|
case cLoadTypeFLDMap:
|
|
case cLoadTypeBRIXMap:
|
|
case cLoadTypeGRDMap:
|
|
case cLoadTypeDXMap:
|
|
case cLoadTypeDXStr:
|
|
new_type = cObjectMap;
|
|
break;
|
|
case cLoadTypeCallback:
|
|
new_type = cObjectCallback;
|
|
break;
|
|
case cLoadTypeCGO:
|
|
new_type = cObjectCGO;
|
|
break;
|
|
}
|
|
if (new_type == -1 || new_type != origObj->type) {
|
|
ExecutiveDelete(G, origObj->Name);
|
|
origObj = nullptr;
|
|
}
|
|
}
|
|
return origObj;
|
|
}
|
|
|
|
int ExecutiveProcessPDBFile(PyMOLGlobals* G, pymol::CObject* origObj,
|
|
const char* fname, const char* buffer, const char* oname, int frame,
|
|
int discrete, int finish, OrthoLineType buf, int variant, int quiet,
|
|
int multiplex, int zoom)
|
|
{
|
|
int ok = true;
|
|
pymol::CObject* obj;
|
|
char pdb_name[WordLength] = "";
|
|
char cur_name[WordLength] = "";
|
|
const char* next_pdb = nullptr;
|
|
int repeat_flag = true;
|
|
int n_processed = 0;
|
|
PDBInfoRec pdb_info_rec, *pdb_info = nullptr;
|
|
int model_number;
|
|
pymol::CObject* deferred_zoom_obj = nullptr;
|
|
|
|
UtilZeroMem(&pdb_info_rec, sizeof(PDBInfoRec));
|
|
pdb_info = &pdb_info_rec;
|
|
pdb_info->multiplex = multiplex;
|
|
pdb_info->variant = variant;
|
|
|
|
while (repeat_flag && ok) {
|
|
const char* start_at = buffer;
|
|
int is_repeat_pass = false;
|
|
int eff_frame = frame;
|
|
int is_new = false;
|
|
|
|
if (next_pdb) {
|
|
start_at = next_pdb;
|
|
is_repeat_pass = true;
|
|
}
|
|
|
|
repeat_flag = false;
|
|
next_pdb = nullptr;
|
|
if (!origObj) {
|
|
|
|
is_new = true;
|
|
pdb_name[0] = 0;
|
|
model_number = 0;
|
|
obj = ObjectMoleculeReadPDBStr(G, (ObjectMolecule*) origObj, start_at,
|
|
eff_frame, discrete, pdb_name, &next_pdb, pdb_info, quiet,
|
|
&model_number);
|
|
|
|
} else {
|
|
model_number = 0;
|
|
ObjectMoleculeReadPDBStr(G, (ObjectMolecule*) origObj, start_at,
|
|
eff_frame, discrete, pdb_name, &next_pdb, pdb_info, quiet,
|
|
&model_number);
|
|
|
|
if (finish) {
|
|
ExecutiveUpdateObjectSelection(G, origObj);
|
|
ExecutiveDoZoom(G, origObj, false, zoom, quiet);
|
|
}
|
|
if (eff_frame < 0)
|
|
eff_frame = ((ObjectMolecule*) origObj)->NCSet - 1;
|
|
if (buf) {
|
|
if (fname)
|
|
sprintf(buf,
|
|
" CmdLoad: \"%s\" appended into object \"%s\", state %d.\n",
|
|
fname, oname, eff_frame + 1);
|
|
else
|
|
sprintf(buf,
|
|
" CmdLoad: PDB-string appended into object \"%s\", state %d.\n",
|
|
oname, eff_frame + 1);
|
|
}
|
|
obj = origObj;
|
|
}
|
|
|
|
if (obj) {
|
|
if (next_pdb) {
|
|
/* NOTE: if set, assume that multiple PDBs are present in the file */
|
|
repeat_flag = true;
|
|
}
|
|
}
|
|
|
|
if (is_new) {
|
|
if (obj) {
|
|
if (next_pdb) {
|
|
if (pdb_name[0] == 0) {
|
|
if (cur_name[0]) {
|
|
sprintf(pdb_name, "%s_%04d", cur_name, n_processed + 1);
|
|
} else {
|
|
sprintf(pdb_name, "%s_%04d", oname, n_processed + 1);
|
|
}
|
|
} else if (multiplex > 0) {
|
|
if (pdb_info->multi_object_status ==
|
|
1) { /* this is a multi-object PDB file */
|
|
strcpy(cur_name, pdb_name);
|
|
} else if (cur_name[0] == 0) {
|
|
strcpy(cur_name, oname);
|
|
}
|
|
if (model_number > 0) {
|
|
sprintf(pdb_name, "%s_%04d", cur_name, model_number);
|
|
} else {
|
|
sprintf(pdb_name, "%s_%04d", cur_name, n_processed + 1);
|
|
}
|
|
}
|
|
ObjectSetName(obj, pdb_name);
|
|
ExecutiveDelete(G, obj->Name); /* just in case */
|
|
} else {
|
|
if (is_repeat_pass) {
|
|
if (pdb_name[0] == 0) {
|
|
if (cur_name[0]) {
|
|
sprintf(pdb_name, "%s_%04d", cur_name, n_processed + 1);
|
|
} else {
|
|
sprintf(pdb_name, "%s_%04d", oname, n_processed + 1);
|
|
}
|
|
} else if (multiplex > 0) {
|
|
if (pdb_info->multi_object_status ==
|
|
1) { /* this is a multi-object PDB file */
|
|
strcpy(cur_name, pdb_name);
|
|
} else if (cur_name[0] == 0) {
|
|
strcpy(cur_name, oname);
|
|
}
|
|
if (model_number > 0) {
|
|
sprintf(pdb_name, "%s_%04d", cur_name, model_number);
|
|
} else {
|
|
sprintf(pdb_name, "%s_%04d", cur_name, n_processed + 1);
|
|
}
|
|
}
|
|
ObjectSetName(obj, pdb_name); /* from PDB */
|
|
ExecutiveDelete(G, obj->Name); /* just in case */
|
|
} else {
|
|
ObjectSetName(obj, oname); /* from filename/parameter */
|
|
}
|
|
}
|
|
|
|
if (obj) {
|
|
int do_zoom = repeat_flag ? 0 : zoom;
|
|
if (do_zoom != zoom)
|
|
deferred_zoom_obj = obj;
|
|
else
|
|
deferred_zoom_obj = nullptr;
|
|
ExecutiveManageObject(G, obj, do_zoom, true);
|
|
if (eff_frame < 0)
|
|
eff_frame = ((ObjectMolecule*) obj)->NCSet - 1;
|
|
if (buf) {
|
|
if (n_processed < 1) {
|
|
if (fname)
|
|
sprintf(
|
|
buf, " CmdLoad: \"%s\" loaded as \"%s\".\n", fname, oname);
|
|
else
|
|
sprintf(buf,
|
|
" CmdLoad: PDB-string loaded into object \"%s\", state "
|
|
"%d.\n",
|
|
oname, eff_frame + 1);
|
|
} else {
|
|
if (fname) {
|
|
sprintf(buf, " CmdLoad: loaded %d objects from \"%s\".\n",
|
|
n_processed + 1, fname);
|
|
} else {
|
|
sprintf(buf, " CmdLoad: loaded %d objects from string.\n",
|
|
n_processed + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj) {
|
|
n_processed++;
|
|
}
|
|
}
|
|
|
|
if (deferred_zoom_obj) {
|
|
ExecutiveDoZoom(G, deferred_zoom_obj, true, zoom, true);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static pymol::Result<pymol::CObject*> ExecutiveProcessCif(PyMOLGlobals* G,
|
|
pymol::CObject* I, const char* object_name, const char* st, int frame,
|
|
int discrete, int quiet, int multiplex, int zoom)
|
|
{
|
|
auto cif = std::make_shared<cif_file_with_error_capture>();
|
|
if (!cif->parse_string(st)) {
|
|
return pymol::make_error("Parsing CIF file failed: ", cif->m_error_msg);
|
|
}
|
|
|
|
auto has_refln = [](const auto& datablock) {
|
|
return datablock.get_arr("_refln.index_h") != nullptr;
|
|
};
|
|
|
|
bool new_obj = I == nullptr;
|
|
|
|
pymol::CObject* obj{};
|
|
for (const auto& [code, datablock] : cif->datablocks()) {
|
|
// Map Coeffs / Reflections
|
|
if (has_refln(datablock)) {
|
|
auto map = ObjectMapReadCifStr(G, datablock);
|
|
p_return_if_error(map);
|
|
obj = map.result();
|
|
} else if (auto mol =
|
|
ObjectMoleculeReadCifData(G, &datablock, discrete, quiet)) {
|
|
obj = mol;
|
|
#ifndef _PYMOL_NOPY
|
|
// we only provide access from the Python API so far
|
|
if (SettingGet<bool>(G, cSetting_cif_keepinmemory)) {
|
|
mol->m_cifdata = &datablock;
|
|
mol->m_ciffile = cif;
|
|
}
|
|
#endif
|
|
}
|
|
if (!obj) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Warnings)
|
|
" mmCIF-Warning: no map coefficients or coordinates found in data_%s\n",
|
|
datablock.code() ENDFB(G);
|
|
continue;
|
|
}
|
|
|
|
if (cif->datablocks().size() == 1 || multiplex == 0) {
|
|
return obj;
|
|
}
|
|
|
|
// multiplexing
|
|
ObjectSetName(obj, datablock.code());
|
|
ExecutiveDelete(G, obj->Name);
|
|
ExecutiveManageObject(G, obj, zoom, true);
|
|
} // end for
|
|
|
|
return obj;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveAssignSS(PyMOLGlobals* G, const char* target,
|
|
int state, const char* context, int preserve, ObjectMolecule* single_object,
|
|
int quiet)
|
|
{
|
|
if (!target[0]) {
|
|
return pymol::make_error("selection must not be empty");
|
|
}
|
|
pymol::Result<SelectorTmp> targetTmp;
|
|
pymol::Result<SelectorTmp> contextTmp;
|
|
int sele0 = -1;
|
|
int sele1 = -1;
|
|
sele0 = SelectorIndexByName(G, target);
|
|
if (sele0 < 0) {
|
|
targetTmp = SelectorTmp::make(G, target);
|
|
p_return_if_error(targetTmp);
|
|
sele0 = targetTmp->getIndex();
|
|
assert(sele0 >= 0);
|
|
}
|
|
if (!context || !context[0]) {
|
|
sele1 = sele0;
|
|
} else {
|
|
contextTmp = SelectorTmp::make(G, context);
|
|
p_return_if_error(contextTmp);
|
|
sele1 = contextTmp->getIndex();
|
|
assert(sele1 >= 0);
|
|
}
|
|
SelectorAssignSS(G, sele0, sele1, state, preserve, single_object, quiet);
|
|
return {};
|
|
}
|
|
|
|
static int* getRepArrayFromBitmask(int visRep);
|
|
|
|
PyObject* ExecutiveGetVisAsPyDict(PyMOLGlobals* G)
|
|
{
|
|
assert(PyGILState_Check());
|
|
|
|
PyObject *result = nullptr, *list;
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
result = PyDict_New();
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->name[0] != '_') {
|
|
list = PyList_New(4);
|
|
PyList_SetItem(list, 0, PyInt_FromLong(rec->visible));
|
|
|
|
PyList_SetItem(list, 1, PyList_New(0));
|
|
|
|
if (rec->type != cExecObject) {
|
|
PyList_SetItem(list, 2, PConvAutoNone(Py_None));
|
|
PyList_SetItem(list, 3, PConvAutoNone(Py_None));
|
|
} else {
|
|
auto vla = getRepArrayFromBitmask(rec->obj->visRep);
|
|
PyList_SetItem(list, 2, PConvIntVLAToPyList(vla));
|
|
VLAFreeP(vla);
|
|
|
|
PyList_SetItem(list, 3, PyInt_FromLong(rec->obj->Color));
|
|
}
|
|
|
|
PyDict_SetItemString(result, rec->name, list);
|
|
Py_DECREF(list);
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
static int* getRepArrayFromBitmask(int visRep)
|
|
{
|
|
int n_vis = 0;
|
|
int* RepVis = VLACalloc(int, cRepCnt);
|
|
|
|
for (int a = 0; a < cRepCnt; a++)
|
|
if (GET_BIT(visRep, a))
|
|
RepVis[n_vis++] = a;
|
|
|
|
VLASize(RepVis, int, n_vis);
|
|
return RepVis;
|
|
}
|
|
|
|
#ifdef _PYMOL_LIB
|
|
/**
|
|
* Returns a list (VLA) of enabled atom representations (AtomInfoType.visRep)
|
|
* in selection (e.g. {cRepLine, cRepNonbonded})
|
|
*/
|
|
int* ExecutiveGetRepsInSceneForObject(PyMOLGlobals* G, const char* name)
|
|
{
|
|
int visRep = 0;
|
|
|
|
for (SeleAtomIterator iter(G, name); iter.next();) {
|
|
AtomInfoType* ai = iter.getAtomInfo();
|
|
visRep |= ai->visRep;
|
|
}
|
|
|
|
return getRepArrayFromBitmask(visRep);
|
|
}
|
|
|
|
/**
|
|
* Returns a list (VLA) of enabled object representations (rec->obj->visRep)
|
|
* (e.g. {cRepCell, cRepExtent})
|
|
*/
|
|
int* ExecutiveGetRepsForObject(PyMOLGlobals* G, const char* name)
|
|
{
|
|
SpecRec* rec = ExecutiveFindSpec(G, (char*) name);
|
|
|
|
if (rec)
|
|
return getRepArrayFromBitmask(rec->obj->visRep);
|
|
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
int ExecutiveSetVisFromPyDict(PyMOLGlobals* G, PyObject* dict)
|
|
{
|
|
#ifdef _PYMOL_NOPY
|
|
return 0;
|
|
#else
|
|
assert(PyGILState_Check());
|
|
|
|
int ok = true;
|
|
WordType name;
|
|
PyObject *key, *list, *col;
|
|
PyObject* vis_list = nullptr;
|
|
Py_ssize_t pos = 0;
|
|
SpecRec *rec, *grec, **recstack = nullptr;
|
|
int n_vis;
|
|
int rep;
|
|
int a;
|
|
int ll = 0;
|
|
if (ok)
|
|
ok = (dict != nullptr);
|
|
if (ok)
|
|
ok = PyDict_Check(dict);
|
|
if (ok) {
|
|
|
|
SceneObjectDel(G, nullptr, true); /* remove all objects from scene */
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
|
|
// stack for putative visible records
|
|
recstack = pymol::calloc<SpecRec*>(PyDict_Size(dict) + 1);
|
|
|
|
while (PyDict_Next(dict, &pos, &key, &list)) {
|
|
if (!PConvPyStrToStr(key, name, sizeof(WordType))) {
|
|
ok = false;
|
|
} else {
|
|
rec = ExecutiveFindSpec(G, name);
|
|
if (rec) {
|
|
if (ok)
|
|
ok = (list != nullptr);
|
|
if (ok)
|
|
ok = PyList_Check(list);
|
|
if (ok)
|
|
ll = PyList_Size(list);
|
|
if (ok)
|
|
ok = (ll >= 2);
|
|
if (ok)
|
|
ok = PConvPyObjectToInt(PyList_GetItem(list, 0), &rec->visible);
|
|
|
|
/* before version 1.8 item 1 was rec reps (repOn) */
|
|
|
|
if (ok && (rec->type == cExecObject)) { /* object properties */
|
|
|
|
if (ll > 2) { /* object visibility */
|
|
vis_list = PyList_GetItem(list, 2);
|
|
if (ok)
|
|
ok = (vis_list != nullptr);
|
|
if (ok) {
|
|
if (PyList_Check(vis_list)) {
|
|
n_vis = PyList_Size(vis_list);
|
|
rec->obj->visRep = 0;
|
|
for (a = 0; a < n_vis; a++) {
|
|
if (PConvPyObjectToInt(PyList_GetItem(vis_list, a), &rep)) {
|
|
if ((rep >= 0) && (rep < cRepCnt))
|
|
SET_BIT(rec->obj->visRep, rep);
|
|
}
|
|
}
|
|
} else if (PyInt_Check(vis_list)) {
|
|
PConvPyObjectToInt(vis_list, &rec->obj->visRep);
|
|
}
|
|
}
|
|
}
|
|
if (ll > 3) { /* object color */
|
|
col = PyList_GetItem(list, 3);
|
|
if (ok)
|
|
ok = (col != nullptr);
|
|
if (ok)
|
|
if (PyInt_Check(col)) {
|
|
ok = PConvPyObjectToInt(col, &rec->obj->Color);
|
|
rec->obj->invalidate(cRepAll, cRepInvColor, -1);
|
|
}
|
|
}
|
|
}
|
|
if (rec->visible && (rec->type == cExecObject)) {
|
|
(*(++recstack)) = rec;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add visible objects to scene
|
|
for (; (rec = *recstack); recstack--) {
|
|
// check visibility of all parent groups
|
|
for (grec = rec; grec->visible && (grec = grec->group);)
|
|
;
|
|
if (!grec) {
|
|
// ok, no invisible parent found
|
|
rec->in_scene = SceneObjectAdd(G, rec->obj);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
}
|
|
}
|
|
mfree(recstack);
|
|
}
|
|
return ok;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* returns a pointer to the data in a volume or map object
|
|
*/
|
|
CField* ExecutiveGetVolumeField(PyMOLGlobals* G, const char* objName, int state)
|
|
{
|
|
ObjectMapState* oms;
|
|
pymol::CObject* obj;
|
|
|
|
obj = ExecutiveFindObjectByName(G, objName);
|
|
ok_assert(1, obj);
|
|
|
|
switch (obj->type) {
|
|
case cObjectVolume:
|
|
return ObjectVolumeGetField((ObjectVolume*) obj);
|
|
case cObjectMap:
|
|
oms = ObjectMapGetState((ObjectMap*) obj, state);
|
|
ok_assert(1, oms && oms->Field);
|
|
return oms->Field->data.get();
|
|
}
|
|
|
|
ok_except1:
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* returns allocated memory
|
|
*/
|
|
pymol::Result<std::vector<float>> ExecutiveGetHistogram(PyMOLGlobals* G,
|
|
const char* objName, int n_points, float min_val, float max_val)
|
|
{
|
|
pymol::CObject* obj;
|
|
ObjectMapState* oms = nullptr;
|
|
|
|
obj = ExecutiveFindObjectByName(G, objName);
|
|
|
|
if (!obj) {
|
|
return pymol::make_error("could not find object ", objName);
|
|
}
|
|
|
|
switch (obj->type) {
|
|
case cObjectMap:
|
|
oms = ObjectMapGetState((ObjectMap*) obj, 0);
|
|
break;
|
|
case cObjectVolume:
|
|
oms = ObjectVolumeGetMapState((ObjectVolume*) obj);
|
|
break;
|
|
default:
|
|
return pymol::make_error("object type must be map or volume");
|
|
}
|
|
|
|
if (oms) {
|
|
auto hist = std::vector<float>(n_points + 4);
|
|
float range = SettingGet_f(
|
|
G, obj->Setting.get(), nullptr, cSetting_volume_data_range);
|
|
ObjectMapStateGetHistogram(
|
|
G, oms, n_points, range, hist.data(), min_val, max_val);
|
|
return hist;
|
|
}
|
|
|
|
return pymol::make_error("failed to get map state");
|
|
}
|
|
|
|
PyObject* ExecutiveGetVolumeRamp(
|
|
PyMOLGlobals* G, const char* objName, int state)
|
|
{
|
|
#ifdef _PYMOL_NOPY
|
|
return nullptr;
|
|
#else
|
|
PyObject* result = nullptr;
|
|
|
|
PRINTFD(G, FB_Executive) "Executive-GetVolumeRamp Entered.\n" ENDFD;
|
|
|
|
if (auto obj = ExecutiveFindObject<ObjectVolume>(G, objName)) {
|
|
result = ObjectVolumeGetRamp(obj, state);
|
|
}
|
|
|
|
PRINTFD(G, FB_Executive) "Executive-GetVolumeRamp Exited.\n" ENDFD;
|
|
|
|
return result;
|
|
|
|
#endif
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSetVolumeRamp(PyMOLGlobals* G, const char* objName,
|
|
std::vector<float> ramp_list, int state)
|
|
{
|
|
auto obj = ExecutiveFindObject<ObjectVolume>(G, objName);
|
|
if (obj) {
|
|
return ObjectVolumeSetRamp(obj, std::move(ramp_list), state);
|
|
}
|
|
|
|
return pymol::make_error("Object ", objName, " not found");
|
|
}
|
|
|
|
pymol::Result<> ExecutiveIsolevel(
|
|
PyMOLGlobals* G, const char* name, float level, int state, int quiet)
|
|
{
|
|
auto obj = ExecutiveFindObjectByName(G, name);
|
|
if (obj) {
|
|
switch (obj->type) {
|
|
case cObjectMesh:
|
|
ObjectMeshSetLevel((ObjectMesh*) obj, level, state, quiet);
|
|
SceneChanged(G);
|
|
return {};
|
|
case cObjectSurface:
|
|
ObjectSurfaceSetLevel((ObjectSurface*) obj, level, state, quiet);
|
|
SceneChanged(G);
|
|
return {};
|
|
default:
|
|
return pymol::make_error("Object ", name, " is of wrong type.");
|
|
}
|
|
}
|
|
return pymol::make_error("Object not found");
|
|
}
|
|
|
|
pymol::Result<float> ExecutiveGetIsolevel(
|
|
PyMOLGlobals* G, const char* name, int state)
|
|
{
|
|
auto obj = ExecutiveFindObjectByName(G, name);
|
|
if (obj) {
|
|
switch (obj->type) {
|
|
case cObjectMesh:
|
|
return ObjectMeshGetLevel((ObjectMesh*) obj, state);
|
|
case cObjectSurface:
|
|
return ObjectSurfaceGetLevel((ObjectSurface*) obj, state);
|
|
default:
|
|
return pymol::make_error("Object ", name, " is of wrong type.");
|
|
}
|
|
}
|
|
return pymol::make_error("Object not found");
|
|
}
|
|
|
|
pymol::Result<std::pair<float, float>> ExecutiveSpectrum(PyMOLGlobals* G,
|
|
pymol::zstring_view s_s1, pymol::zstring_view s_expr, float min, float max,
|
|
int first, int last, pymol::zstring_view s_prefix, int digits, int byres,
|
|
int quiet)
|
|
{
|
|
std::pair<float, float> ret;
|
|
int n_color, n_atom;
|
|
ObjectMoleculeOpRec op;
|
|
WordType buffer;
|
|
std::vector<int> color_index;
|
|
std::vector<float> value;
|
|
int a, b;
|
|
char pat[] = "%0Xd";
|
|
int pref_len;
|
|
char* at;
|
|
float range;
|
|
|
|
auto s1 = s_s1.c_str();
|
|
auto prefix = s_prefix.c_str();
|
|
auto expr = s_expr.c_str();
|
|
|
|
auto tmpsele1 = SelectorTmp::make(G, s1);
|
|
p_return_if_error(tmpsele1);
|
|
int sele1 = tmpsele1->getIndex();
|
|
|
|
if (sele1 >= 0) {
|
|
|
|
if (digits > 9)
|
|
digits = 9;
|
|
pat[2] = ('0' + digits);
|
|
UtilNCopy(buffer, prefix, sizeof(WordType) - digits);
|
|
|
|
pref_len = strlen(prefix);
|
|
at = buffer + pref_len;
|
|
|
|
n_color = abs(first - last) + 1;
|
|
if (n_color) {
|
|
color_index.resize(n_color);
|
|
for (a = 0; a < n_color; a++) {
|
|
b = first + ((last - first) * a) / (n_color - 1);
|
|
sprintf(at, pat, b);
|
|
color_index[a] = ColorGetIndex(G, buffer);
|
|
}
|
|
|
|
// set up iterator
|
|
SeleAtomIterator iter(G, sele1);
|
|
SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
|
|
|
|
// count atoms
|
|
for (n_atom = 0; iter.next();) {
|
|
++n_atom;
|
|
}
|
|
|
|
if (n_atom) {
|
|
value.resize(n_atom);
|
|
|
|
if (WordMatchExact(G, "count", expr, true)) {
|
|
for (a = 0; a < n_atom; a++) {
|
|
value[a] = (float) a + 1;
|
|
}
|
|
} else {
|
|
if (WordMatchExact(G, "pc", expr, true)) {
|
|
expr = "partial_charge";
|
|
} else if (WordMatchExact(G, "resi", expr, true)) {
|
|
expr = "resv";
|
|
}
|
|
|
|
// look up expression definition
|
|
auto ap = PyMOL_GetAtomPropertyInfo(G->PyMOL, expr);
|
|
if (!ap) {
|
|
return pymol::make_error("Unknown expression: ", expr);
|
|
}
|
|
|
|
// for enumerated values
|
|
std::map<size_t, unsigned int> enumerated_values;
|
|
union {
|
|
size_t value_e;
|
|
char value_s[sizeof(size_t)];
|
|
};
|
|
|
|
for (a = 0, iter.reset(); iter.next(); ++a) {
|
|
const auto ai = iter.getAtomInfo();
|
|
const auto raw_ptr = reinterpret_cast<const char*>(ai) + ap->offset;
|
|
|
|
// numeric values
|
|
switch (ap->Ptype) {
|
|
case cPType_float:
|
|
value[a] = *reinterpret_cast<const float*>(raw_ptr);
|
|
continue;
|
|
case cPType_int:
|
|
case cPType_int_custom_type:
|
|
value[a] = *reinterpret_cast<const int*>(raw_ptr);
|
|
continue;
|
|
case cPType_uint32:
|
|
value[a] = *reinterpret_cast<const uint32_t*>(raw_ptr);
|
|
continue;
|
|
case cPType_schar:
|
|
value[a] = *reinterpret_cast<const signed char*>(raw_ptr);
|
|
continue;
|
|
case cPType_char_as_type:
|
|
value[a] = ai->hetatm;
|
|
continue;
|
|
case cPType_index:
|
|
value[a] = iter.getAtm() + 1.f;
|
|
continue;
|
|
}
|
|
|
|
// enumerated values
|
|
switch (ap->Ptype) {
|
|
case cPType_int_as_string:
|
|
value_e = LexNumeric(*reinterpret_cast<const lexidx_t*>(raw_ptr));
|
|
break;
|
|
case cPType_string:
|
|
// works for small strings
|
|
strncpy(value_s, raw_ptr, sizeof(value_e));
|
|
break;
|
|
case cPType_model:
|
|
value_e = (size_t) iter.obj;
|
|
break;
|
|
default:
|
|
return pymol::make_error("Unsupported Ptype for expr: ", expr);
|
|
}
|
|
|
|
// lookup or insert value
|
|
auto& e = enumerated_values[value_e];
|
|
if (e == 0)
|
|
e = enumerated_values.size();
|
|
value[a] = e - 1.f;
|
|
}
|
|
|
|
if (!quiet && !enumerated_values.empty()) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Spectrum: Expression is non-numeric, enumerating values\n" ENDFB(
|
|
G);
|
|
}
|
|
}
|
|
|
|
if (max < min) {
|
|
max = value[0];
|
|
min = value[0];
|
|
for (a = 1; a < n_atom; a++) {
|
|
if (value[a] < min)
|
|
min = value[a];
|
|
if (value[a] > max)
|
|
max = value[a];
|
|
}
|
|
}
|
|
range = max - min;
|
|
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Spectrum: range (%8.5f to %8.5f).\n", min, max ENDFB(G);
|
|
}
|
|
if (range == 0.0F)
|
|
range = 1.0F;
|
|
ret.first = min;
|
|
ret.second = max;
|
|
|
|
op.code = OMOP_Spectrum;
|
|
op.i1 = n_color - 1;
|
|
op.i2 = n_atom;
|
|
op.i3 = 0;
|
|
op.i4 = byres;
|
|
op.ii1 = color_index.data();
|
|
op.ff1 = value.data();
|
|
op.f1 = min;
|
|
op.f2 = range;
|
|
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
|
|
op.code = OMOP_INVA;
|
|
op.i1 = cRepBitmask;
|
|
op.i2 = cRepInvColor;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int fStrOrderFn(const char* const* array, int l, int r)
|
|
{
|
|
return strcmp(array[l], array[r]) < 0;
|
|
}
|
|
|
|
/**
|
|
* Returns an VLA with pointers into G->Lexicon
|
|
*/
|
|
pymol::Result<std::vector<const char*>> ExecutiveGetChains(
|
|
PyMOLGlobals* G, const char* s1, int state)
|
|
{
|
|
std::set<lexidx_t> chains;
|
|
int c = 0;
|
|
ObjectMoleculeOpRec op;
|
|
|
|
SETUP_SELE_DEFAULT(1);
|
|
|
|
{
|
|
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_GetChains;
|
|
op.ii1 = (int*) (void*) &chains; // pointer pack
|
|
op.i1 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
std::vector<const char*> result(chains.size());
|
|
for (const auto& chain : chains) {
|
|
result[c++] = LexStr(G, chain);
|
|
}
|
|
// sort the array
|
|
UtilSortInPlace(G, result.data(), chains.size(), sizeof(char*),
|
|
(UtilOrderFn*) fStrOrderFn);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
int ExecutiveValidateObjectPtr(
|
|
PyMOLGlobals* G, pymol::CObject* ptr, int object_type)
|
|
{
|
|
/* this routine needs to be sped up significantly... */
|
|
|
|
CExecutive* I = G->Executive;
|
|
int ok = false;
|
|
SpecRec* rec = nullptr;
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->obj == ptr) {
|
|
if (rec->type == cExecObject) {
|
|
if ((!object_type) || (rec->obj->type == object_type)) {
|
|
ok = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
pymol::Result<> ExecutiveRampNew(PyMOLGlobals* G, const char* name,
|
|
const char* src_name, pymol::vla<float> range, pymol::vla<float> color,
|
|
int src_state, const char* sele, float beyond, float within, float sigma,
|
|
int zero, int calc_mode, int quiet)
|
|
{
|
|
ObjectGadgetRamp* obj = nullptr;
|
|
ObjectGadgetRamp* origRamp = nullptr;
|
|
pymol::CObject* src_obj = nullptr;
|
|
pymol::CObject* origObj = ExecutiveFindObjectByName(G, name);
|
|
float* vert_vla = nullptr;
|
|
int rampType = -1;
|
|
|
|
if (origObj && origObj->type == cObjectGadget &&
|
|
((ObjectGadget*) origObj)->GadgetType == cGadgetRamp) {
|
|
origRamp = (ObjectGadgetRamp*) origObj;
|
|
rampType = origRamp->RampType;
|
|
} else if (!range || !(color || calc_mode)) {
|
|
return pymol::make_error("Missing 'range' or 'color' to create new ramp.");
|
|
}
|
|
|
|
if (src_name && src_name[0]) {
|
|
if (WordMatchExact(G, src_name, cKeywordNone, true)) {
|
|
rampType = cRampNone;
|
|
} else {
|
|
src_obj = ExecutiveFindObjectByName(G, src_name);
|
|
if (src_obj) {
|
|
switch (src_obj->type) {
|
|
case cObjectMap:
|
|
rampType = cRampMap;
|
|
break;
|
|
case cObjectMolecule:
|
|
rampType = cRampMol;
|
|
break;
|
|
default:
|
|
pymol::make_error(src_name, " is not a map or molecule.");
|
|
}
|
|
} else {
|
|
return pymol::make_error(src_name, " not found.");
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (rampType) {
|
|
case cRampMap:
|
|
/* mapping this ramp from a selection */
|
|
if (sele && sele[0]) {
|
|
auto tmpsele = SelectorTmp::make(G, sele);
|
|
p_return_if_error(tmpsele);
|
|
sele = tmpsele->getName();
|
|
assert(sele[0]);
|
|
|
|
vert_vla = ExecutiveGetVertexVLA(G, sele, src_state);
|
|
}
|
|
obj = ObjectGadgetRampMapNewAsDefined(G, origRamp, (ObjectMap*) src_obj,
|
|
std::move(range), std::move(color), src_state, vert_vla, beyond, within,
|
|
sigma, zero, calc_mode);
|
|
VLAFreeP(vert_vla);
|
|
break;
|
|
case cRampNone:
|
|
case cRampMol:
|
|
obj =
|
|
ObjectGadgetRampMolNewAsDefined(G, origRamp, (ObjectMolecule*) src_obj,
|
|
std::move(range), std::move(color), src_state, calc_mode);
|
|
break;
|
|
default:
|
|
return pymol::make_error("Missing 'name' to create new ramp.");
|
|
}
|
|
|
|
if (!obj)
|
|
return pymol::make_error("Object not found");
|
|
|
|
if (obj != origRamp) {
|
|
ExecutiveDelete(G, name);
|
|
ObjectSetName(obj, name);
|
|
ColorRegisterExt(G, obj->Name, obj);
|
|
ExecutiveManageObject(G, obj, false, quiet);
|
|
}
|
|
|
|
ExecutiveInvalidateRep(
|
|
G, cKeywordAll, cRepAll, cRepInvColor); /* recolor everything */
|
|
return {};
|
|
}
|
|
|
|
static int ExecutiveSetNamedEntries(
|
|
PyMOLGlobals* G, PyObject* names, int version, int part_rest, int part_sess)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int ok = true;
|
|
int skip = false;
|
|
int a = 0, l = 0, ll = 0;
|
|
PyObject *cur, *el;
|
|
SpecRec* rec = nullptr;
|
|
int extra_int;
|
|
int incomplete = false;
|
|
ObjectNameType new_name;
|
|
|
|
if (ok)
|
|
ok = (names != nullptr);
|
|
if (ok)
|
|
ok = PyList_Check(names);
|
|
if (ok)
|
|
l = PyList_Size(names);
|
|
|
|
while (ok && (a < l)) {
|
|
cur = PyList_GetItem(names, a);
|
|
if (cur != Py_None) { /* skip over None w/o aborting */
|
|
skip = false;
|
|
rec = nullptr;
|
|
ListElemCalloc(G, rec, SpecRec);
|
|
rec->next = nullptr;
|
|
rec->name[0] = 0;
|
|
if (ok)
|
|
ok = PyList_Check(cur);
|
|
if (ok)
|
|
ll = PyList_Size(cur);
|
|
if (ok)
|
|
ok = PConvPyStrToStr(
|
|
PyList_GetItem(cur, 0), rec->name, sizeof(WordType));
|
|
if (ok)
|
|
ok = PConvPyIntToInt(PyList_GetItem(cur, 1), &rec->type);
|
|
if (ok)
|
|
ok = CPythonVal_PConvPyIntToInt_From_List(G, cur, 2, &rec->visible);
|
|
|
|
/* before version 1.8 item 3 was rec reps (repOn) */
|
|
|
|
if (ok)
|
|
ok = PConvPyIntToInt(PyList_GetItem(cur, 4), &extra_int);
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (!ok)
|
|
break;
|
|
|
|
el = PyList_GetItem(cur, 5);
|
|
|
|
switch (extra_int) {
|
|
case cObjectMolecule:
|
|
ok = ObjectMoleculeNewFromPyList(
|
|
G, el, (ObjectMolecule**) (void*) &rec->obj);
|
|
break;
|
|
case cObjectMeasurement:
|
|
ok = ObjectDistNewFromPyList(G, el, (ObjectDist**) (void*) &rec->obj);
|
|
break;
|
|
case cObjectMap:
|
|
ok = ObjectMapNewFromPyList(G, el, (ObjectMap**) (void*) &rec->obj);
|
|
break;
|
|
case cObjectMesh:
|
|
ok = ObjectMeshNewFromPyList(G, el, (ObjectMesh**) (void*) &rec->obj);
|
|
break;
|
|
case cObjectSlice:
|
|
ok = ObjectSliceNewFromPyList(
|
|
G, el, (ObjectSlice**) (void*) &rec->obj);
|
|
break;
|
|
case cObjectSurface:
|
|
ok = ObjectSurfaceNewFromPyList(
|
|
G, el, (ObjectSurface**) (void*) &rec->obj);
|
|
break;
|
|
case cObjectCGO:
|
|
ok = ObjectCGONewFromPyList(
|
|
G, el, (ObjectCGO**) (void*) &rec->obj, version);
|
|
break;
|
|
case cObjectGadget:
|
|
ok = ObjectGadgetNewFromPyList(
|
|
G, el, (ObjectGadget**) (void*) &rec->obj, version);
|
|
break;
|
|
case cObjectAlignment:
|
|
ok = ObjectAlignmentNewFromPyList(
|
|
G, el, (ObjectAlignment**) (void*) &rec->obj, version);
|
|
break;
|
|
case cObjectGroup:
|
|
if (part_rest) {
|
|
// if group already exists, do not create new one
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, rec->name);
|
|
if (obj && obj->type == cObjectGroup) {
|
|
skip = 1;
|
|
break;
|
|
}
|
|
}
|
|
ok = ObjectGroupNewFromPyList(
|
|
G, el, (ObjectGroup**) (void*) &rec->obj, version);
|
|
break;
|
|
case cObjectVolume:
|
|
ok = ObjectVolumeNewFromPyList(
|
|
G, el, (ObjectVolume**) (void*) &rec->obj);
|
|
break;
|
|
#ifndef _PYMOL_NOPY
|
|
case cObjectCallback:
|
|
// skip dummy entries from old sessions and failed-to-pickle sessions
|
|
skip = !ObjectCallbackNewFromPyList(
|
|
G, el, (ObjectCallback**) (void*) &rec->obj);
|
|
break;
|
|
#endif
|
|
case cObjectCurve: {
|
|
rec->obj = new ObjectCurve(G, el);
|
|
} break;
|
|
default:
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" Executive: skipping unrecognized object \"%s\" of type %d.\n",
|
|
rec->name, extra_int ENDFB(G);
|
|
skip = true;
|
|
break;
|
|
}
|
|
|
|
CPythonVal_Free(el);
|
|
|
|
break;
|
|
case cExecSelection: // on the first pass, just create an entry in the rec
|
|
// list
|
|
rec->sele_color = extra_int;
|
|
if (part_rest || part_sess) { // don't attempt to restore selections
|
|
// with partial sessions
|
|
skip = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (ll > 6) {
|
|
if (ok) {
|
|
ok = PConvPyStrToStr(
|
|
PyList_GetItem(cur, 6), rec->group_name, sizeof(WordType));
|
|
}
|
|
}
|
|
|
|
if (PyErr_Occurred()) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetNamedEntries-Error: after object \"%s\".\n",
|
|
rec->name ENDFB(G);
|
|
PyErr_Print();
|
|
}
|
|
|
|
if (ok && !skip) {
|
|
if (part_rest && ExecutiveProcessObjectName(G, rec->name, new_name)) {
|
|
// rename duplicates
|
|
strcpy(rec->obj->Name, new_name);
|
|
strcpy(rec->name, new_name);
|
|
}
|
|
|
|
// replace existing object (unless auto_rename_duplicate_objects=1)
|
|
if (ExecutiveValidName(G, rec->name)) {
|
|
ExecutiveDelete(G, rec->name);
|
|
}
|
|
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->visible) {
|
|
rec->in_scene = SceneObjectAdd(G, rec->obj);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
}
|
|
ExecutiveUpdateObjectSelection(G, rec->obj);
|
|
break;
|
|
}
|
|
|
|
rec->cand_id = TrackerNewCand(I->Tracker, (TrackerRef*) rec);
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_names_list_id, 1);
|
|
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_obj_list_id, 1);
|
|
break;
|
|
case cExecSelection:
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_sel_list_id, 1);
|
|
break;
|
|
}
|
|
ListAppend(I->Spec, rec, next, SpecRec);
|
|
ExecutiveAddKey(I, rec);
|
|
ExecutiveInvalidateGroups(G, false);
|
|
ExecutiveInvalidatePanelList(G);
|
|
} else {
|
|
ListElemFree(rec);
|
|
}
|
|
}
|
|
a++;
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true;
|
|
}
|
|
}
|
|
return (!incomplete);
|
|
}
|
|
|
|
static int ExecutiveSetSelectionsFromPyList(PyMOLGlobals* G, PyObject* names)
|
|
{
|
|
/* must already have objects loaded at this point... */
|
|
|
|
int ok = true;
|
|
int a = 0, l = 0;
|
|
PyObject* cur;
|
|
SpecRec* rec = nullptr;
|
|
int extra;
|
|
int incomplete = false;
|
|
|
|
if (ok)
|
|
ok = (names != nullptr);
|
|
if (ok)
|
|
ok = PyList_Check(names);
|
|
if (ok)
|
|
l = PyList_Size(names);
|
|
while (ok && (a < l)) {
|
|
cur = PyList_GetItem(names, a);
|
|
if (cur != Py_None) { /* skip over None w/o aborting */
|
|
rec = nullptr;
|
|
ListElemCalloc(G, rec, SpecRec);
|
|
rec->next = nullptr;
|
|
|
|
if (ok)
|
|
ok = PyList_Check(cur);
|
|
if (ok)
|
|
ok = PConvPyStrToStr(
|
|
PyList_GetItem(cur, 0), rec->name, sizeof(WordType));
|
|
if (ok)
|
|
ok = PConvPyIntToInt(PyList_GetItem(cur, 1), &rec->type);
|
|
if (ok)
|
|
ok = PConvPyIntToInt(PyList_GetItem(cur, 2), &rec->visible);
|
|
if (ok)
|
|
ok = PConvPyIntToInt(PyList_GetItem(cur, 4), &extra);
|
|
switch (rec->type) {
|
|
case cExecSelection:
|
|
ok = SelectorFromPyList(G, rec->name, PyList_GetItem(cur, 5));
|
|
break;
|
|
}
|
|
ListElemFree(rec);
|
|
}
|
|
a++;
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true;
|
|
}
|
|
}
|
|
return (!incomplete);
|
|
}
|
|
|
|
static PyObject* ExecutiveGetExecObjectAsPyList(PyMOLGlobals* G, SpecRec* rec)
|
|
{
|
|
|
|
PyObject* result = nullptr;
|
|
int recobjtype = rec->obj->type;
|
|
switch (recobjtype) {
|
|
case cObjectMesh: { /* If a mesh no longer has its dependent map, then it gets
|
|
saved as a CGO */
|
|
int allMapsExist = ObjectMeshAllMapsInStatesExist((ObjectMesh*) rec->obj);
|
|
if (!allMapsExist) {
|
|
recobjtype = cObjectCGO;
|
|
}
|
|
}
|
|
}
|
|
result = PyList_New(7);
|
|
PyList_SetItem(result, 0, PyString_FromString(rec->obj->Name));
|
|
PyList_SetItem(result, 1, PyInt_FromLong(cExecObject));
|
|
PyList_SetItem(result, 2, PyInt_FromLong(rec->visible));
|
|
/* before version 1.8 item 3 was rec reps (repOn) */
|
|
PyList_SetItem(result, 3, PConvAutoNone(nullptr));
|
|
PyList_SetItem(result, 4, PyInt_FromLong(recobjtype));
|
|
switch (rec->obj->type) {
|
|
case cObjectGadget:
|
|
PyList_SetItem(result, 5, ObjectGadgetAsPyList((ObjectGadget*) rec->obj));
|
|
break;
|
|
case cObjectMolecule:
|
|
PyList_SetItem(
|
|
result, 5, ObjectMoleculeAsPyList((ObjectMolecule*) rec->obj));
|
|
break;
|
|
case cObjectMeasurement:
|
|
PyList_SetItem(result, 5, ObjectDistAsPyList((ObjectDist*) rec->obj));
|
|
break;
|
|
case cObjectMap:
|
|
PyList_SetItem(result, 5, ObjectMapAsPyList((ObjectMap*) rec->obj));
|
|
break;
|
|
case cObjectMesh:
|
|
PyList_SetItem(result, 5, ObjectMeshAsPyList((ObjectMesh*) rec->obj));
|
|
break;
|
|
case cObjectSlice:
|
|
PyList_SetItem(result, 5, ObjectSliceAsPyList((ObjectSlice*) rec->obj));
|
|
break;
|
|
case cObjectSurface:
|
|
PyList_SetItem(result, 5, ObjectSurfaceAsPyList((ObjectSurface*) rec->obj));
|
|
break;
|
|
case cObjectCGO:
|
|
PyList_SetItem(result, 5, ObjectCGOAsPyList((ObjectCGO*) rec->obj));
|
|
break;
|
|
case cObjectAlignment:
|
|
PyList_SetItem(
|
|
result, 5, ObjectAlignmentAsPyList((ObjectAlignment*) rec->obj));
|
|
break;
|
|
case cObjectGroup:
|
|
PyList_SetItem(result, 5, ObjectGroupAsPyList((ObjectGroup*) rec->obj));
|
|
break;
|
|
case cObjectVolume:
|
|
PyList_SetItem(result, 5, ObjectVolumeAsPyList((ObjectVolume*) rec->obj));
|
|
break;
|
|
case cObjectCallback:
|
|
PyList_SetItem(
|
|
result, 5, ObjectCallbackAsPyList((ObjectCallback*) rec->obj));
|
|
break;
|
|
case cObjectCurve:
|
|
PyList_SetItem(result, 5, static_cast<ObjectCurve*>(rec->obj)->asPyList());
|
|
break;
|
|
default:
|
|
PyList_SetItem(result, 5, PConvAutoNone(nullptr));
|
|
break;
|
|
}
|
|
PyList_SetItem(result, 6, PyString_FromString(rec->group_name));
|
|
|
|
return (result);
|
|
}
|
|
|
|
static PyObject* ExecutiveGetExecSeleAsPyList(PyMOLGlobals* G, SpecRec* rec)
|
|
{
|
|
PyObject* result = nullptr;
|
|
int sele;
|
|
|
|
sele = SelectorIndexByName(G, rec->name);
|
|
if (sele >= 0) {
|
|
result = PyList_New(7);
|
|
PyList_SetItem(result, 0, PyString_FromString(rec->name));
|
|
PyList_SetItem(result, 1, PyInt_FromLong(cExecSelection));
|
|
PyList_SetItem(result, 2, PyInt_FromLong(rec->visible));
|
|
/* before version 1.8 item 3 was rec reps (repOn) */
|
|
PyList_SetItem(result, 3, PConvAutoNone(nullptr));
|
|
PyList_SetItem(result, 4, PyInt_FromLong(-1));
|
|
PyList_SetItem(result, 5, SelectorAsPyList(G, sele));
|
|
PyList_SetItem(result, 6, PyString_FromString(rec->group_name));
|
|
}
|
|
return (PConvAutoNone(result));
|
|
}
|
|
|
|
static PyObject* ExecutiveGetNamedEntries(
|
|
PyMOLGlobals* G, int list_id, int partial)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
PyObject* result = nullptr;
|
|
int count = 0, total_count = 0;
|
|
int iter_id = 0;
|
|
SpecRec *rec = nullptr, *list_rec = nullptr;
|
|
|
|
SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
|
|
|
|
if (list_id) {
|
|
total_count = TrackerGetNCandForList(I_Tracker, list_id);
|
|
iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
} else {
|
|
total_count = ExecutiveCountNames(G);
|
|
}
|
|
result = PyList_New(total_count);
|
|
|
|
/* critical reliance on short-circuit behavior */
|
|
|
|
while ((iter_id && TrackerIterNextCandInList(I_Tracker, iter_id,
|
|
(TrackerRef**) (void*) &list_rec)) ||
|
|
((!iter_id) && ListIterate(I->Spec, rec, next))) {
|
|
|
|
if (list_id)
|
|
rec = list_rec;
|
|
if (count >= total_count)
|
|
break;
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
PyList_SetItem(result, count, ExecutiveGetExecObjectAsPyList(G, rec));
|
|
break;
|
|
case cExecSelection:
|
|
if (!partial) {
|
|
PyList_SetItem(result, count, ExecutiveGetExecSeleAsPyList(G, rec));
|
|
} else {
|
|
/* cannot currently save selections in partial sessions */
|
|
PyList_SetItem(result, count, PConvAutoNone(nullptr));
|
|
}
|
|
break;
|
|
default:
|
|
PyList_SetItem(result, count, PConvAutoNone(nullptr));
|
|
break;
|
|
}
|
|
} else {
|
|
PyList_SetItem(result, count, PConvAutoNone(nullptr));
|
|
}
|
|
count++;
|
|
}
|
|
|
|
while (
|
|
count <
|
|
total_count) { /* insure that all members of outgoing list are defined */
|
|
PyList_SetItem(result, count, PConvAutoNone(nullptr));
|
|
count++;
|
|
}
|
|
|
|
if (iter_id) {
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
return (PConvAutoNone(result));
|
|
}
|
|
|
|
#ifdef PYMOL_EVAL
|
|
#include "ExecutiveEvalMessage.h"
|
|
#endif
|
|
|
|
int ExecutiveGetSession(
|
|
PyMOLGlobals* G, PyObject* dict, const char* names, int partial, int quiet)
|
|
{
|
|
assert(PyGILState_Check());
|
|
|
|
int list_id = 0;
|
|
SceneViewType sv;
|
|
PyObject* tmp;
|
|
|
|
if (names && names[0]) {
|
|
list_id =
|
|
ExecutiveGetNamesListFromPattern(G, names, true, cExecExpandKeepGroups);
|
|
}
|
|
|
|
tmp = MovieScenesAsPyList(G);
|
|
PyDict_SetItemString(dict, "moviescenes", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
tmp = PyInt_FromLong(_PyMOL_VERSION_int);
|
|
PyDict_SetItemString(dict, "version", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
tmp = ExecutiveGetNamedEntries(G, list_id, partial);
|
|
PyDict_SetItemString(dict, "names", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
tmp = ColorAsPyList(G);
|
|
PyDict_SetItemString(dict, "colors", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
tmp = ColorExtAsPyList(G);
|
|
PyDict_SetItemString(dict, "color_ext", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
tmp = SettingUniqueAsPyList(G);
|
|
PyDict_SetItemString(dict, "unique_settings", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
if (partial) { /* mark this as a partial session */
|
|
|
|
PyDict_SetItemString(dict, "partial", PConvAutoNone(Py_None));
|
|
|
|
} else {
|
|
|
|
/* none of the following information is saved in partial sessions */
|
|
|
|
tmp = SelectorSecretsAsPyList(G);
|
|
PyDict_SetItemString(dict, "selector_secrets", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
tmp = SettingGetGlobalsAsPyList(G);
|
|
PyDict_SetItemString(dict, "settings", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
SceneGetView(G, sv);
|
|
tmp = PConvFloatArrayToPyList(sv, cSceneViewSize);
|
|
PyDict_SetItemString(dict, "view", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
tmp = MovieAsPyList(G);
|
|
PyDict_SetItemString(dict, "movie", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
tmp = EditorAsPyList(G);
|
|
PyDict_SetItemString(dict, "editor", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
tmp = MainAsPyList(G);
|
|
PyDict_SetItemString(dict, "main", tmp);
|
|
Py_XDECREF(tmp);
|
|
|
|
#ifdef PYMOL_EVAL
|
|
ExecutiveEvalMessage(G, dict);
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void ExecutiveMigrateSession(PyMOLGlobals* G, int session_version)
|
|
{
|
|
if (session_version < 1700) {
|
|
if (SettingGetGlobal_i(G, cSetting_seq_view_label_color) == 0 /* white */) {
|
|
SettingSetGlobal_i(G, cSetting_seq_view_label_color, cColorFront);
|
|
}
|
|
}
|
|
if (session_version < 100) {
|
|
/* migrate lighting model */
|
|
SettingSetGlobal_f(
|
|
G, cSetting_direct, 1.8 * SettingGetGlobal_f(G, cSetting_direct));
|
|
SettingSetGlobal_f(
|
|
G, cSetting_reflect, 0.5 * SettingGetGlobal_f(G, cSetting_reflect));
|
|
SettingSetGlobal_f(
|
|
G, cSetting_ambient, 1.166 * SettingGetGlobal_f(G, cSetting_ambient));
|
|
SettingSetGlobal_f(
|
|
G, cSetting_gamma, 0.769 * SettingGetGlobal_f(G, cSetting_gamma));
|
|
|
|
/* try best to meet existing expectations with existing sessions */
|
|
SettingSetGlobal_f(G, cSetting_ray_legacy_lighting, 1.0F);
|
|
|
|
/* force use of movie_delay in preference to movie_fps */
|
|
|
|
SettingSetGlobal_f(G, cSetting_movie_fps, 0.0F);
|
|
|
|
/* and labels */
|
|
SettingSetGlobal_i(G, cSetting_label_digits, 2);
|
|
}
|
|
if (session_version < 99) {
|
|
SettingSetGlobal_f(G, cSetting_cartoon_ladder_mode, 0);
|
|
SettingSetGlobal_f(G, cSetting_cartoon_tube_cap, 0);
|
|
SettingSetGlobal_f(G, cSetting_cartoon_nucleic_acid_mode, 1);
|
|
{
|
|
float old_sulfur[3] = {1.0, 0.5, 0.0};
|
|
ColorDef(G, "sulfur", old_sulfur, 0, true);
|
|
}
|
|
}
|
|
if (session_version < 98) {
|
|
/* produce expected rendering quality & performance with old sessions */
|
|
SettingSetGlobal_b(G, cSetting_ray_orthoscopic, 1);
|
|
}
|
|
if (session_version < 96) {
|
|
SettingSetGlobal_f(G, cSetting_ray_transparency_contrast, 1.0F);
|
|
}
|
|
if (session_version < 95) {
|
|
{
|
|
/* adjust fog to reflect current importance of seeing to the Z-slab center
|
|
* w/o fog */
|
|
|
|
float fog_start = SettingGetGlobal_f(G, cSetting_fog_start);
|
|
float ray_trace_fog_start =
|
|
SettingGetGlobal_f(G, cSetting_ray_trace_fog_start);
|
|
if ((fog_start == 0.40F) || (fog_start == 0.35F) ||
|
|
(fog_start == 0.30F)) {
|
|
SettingSetGlobal_f(G, cSetting_fog_start, 0.45F);
|
|
}
|
|
if ((ray_trace_fog_start == 0.45F) || (ray_trace_fog_start == 0.40F) ||
|
|
(ray_trace_fog_start == 0.35F)) {
|
|
SettingSetGlobal_f(G, cSetting_ray_trace_fog_start, 0.50F);
|
|
}
|
|
}
|
|
|
|
{ /* adjust GUI width */
|
|
|
|
int gui_width = SettingGetGlobal_i(G, cSetting_internal_gui_width);
|
|
|
|
if (gui_width == 160) {
|
|
SettingSetGlobal_i(G, cSetting_internal_gui_width, 220);
|
|
}
|
|
}
|
|
|
|
{ /* enable antialiasing */
|
|
|
|
int antialias = SettingGetGlobal_i(G, cSetting_antialias);
|
|
|
|
if (antialias == 0) {
|
|
SettingSetGlobal_i(G, cSetting_antialias, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int ExecutiveSetSessionNoMLock(PyMOLGlobals* G, PyObject* session)
|
|
{
|
|
auto mLocked = MovieLocked(G);
|
|
auto res = ExecutiveSetSession(G, session, false, true);
|
|
MovieSetLock(G, mLocked);
|
|
return res;
|
|
}
|
|
|
|
int ExecutiveSetSession(
|
|
PyMOLGlobals* G, PyObject* session, int partial_restore, int quiet)
|
|
{
|
|
assert(PyGILState_Check());
|
|
|
|
int ok = true;
|
|
int incomplete = false;
|
|
PyObject* tmp;
|
|
SceneViewType sv;
|
|
int version = -1, version_full;
|
|
int migrate_sessions = SettingGetGlobal_b(G, cSetting_session_migration);
|
|
char active[WordLength] = "";
|
|
int have_active = false;
|
|
int partial_session = false;
|
|
|
|
if (!partial_restore) { /* if user has requested partial restore */
|
|
ExecutiveDelete(G, "all");
|
|
ColorReset(G);
|
|
}
|
|
|
|
if (!session || !PyDict_Check(session)) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Error: not a dict\n" ENDFB(G);
|
|
return 0;
|
|
}
|
|
|
|
if (ok) { /* if session is partial, then don't error about missing stuff */
|
|
tmp = PyDict_GetItemString(session, "partial");
|
|
if (tmp) {
|
|
partial_session = true;
|
|
}
|
|
}
|
|
|
|
if (partial_restore) {
|
|
G->SettingUnique->old2new =
|
|
std::make_unique<std::unordered_map<int, int>>();
|
|
}
|
|
|
|
if (ok) {
|
|
tmp = PyDict_GetItemString(session, "version");
|
|
if (tmp) {
|
|
ok = PConvPyIntToInt(tmp, &version);
|
|
if (ok) {
|
|
version_full = version;
|
|
while (version_full <
|
|
210) /* any version less than 2.1 (account for next major version
|
|
2) should be 4 digits, otherwise 3 */
|
|
version_full *= 10;
|
|
float version_float =
|
|
version_full / (version >= 1000000 ? 1000000.f : 1000.f);
|
|
if (version > _PyMOL_VERSION_int) {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Warning: This session was created with a newer version of PyMOL "
|
|
"(%1.6f).\n",
|
|
version_float ENDFB(G);
|
|
if (SettingGetGlobal_i(G, cSetting_session_version_check)) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Error: Please update first -- see http://www.pymol.org\n" ENDFB(
|
|
G);
|
|
ok = false;
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Warning: Some content may not load completely.\n" ENDFB(G);
|
|
}
|
|
}
|
|
} else {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Details)
|
|
" Executive: Loading version %1.6f session...\n",
|
|
version_float ENDFB(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifndef PYMOL_EVAL
|
|
if (ok) {
|
|
tmp = PyDict_GetItemString(session, "eval_nag");
|
|
if (tmp) {
|
|
ok = PyString_Check(tmp);
|
|
if (ok) {
|
|
const char* st = PyString_AsString(tmp);
|
|
if (st) {
|
|
if (Feedback(G, FB_Nag, FB_Warnings)) {
|
|
OrthoAddOutput(G, st);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (ok) { /* colors must be restored before settings and objects */
|
|
tmp = PyDict_GetItemString(session, "colors");
|
|
if (tmp) {
|
|
ok = ColorFromPyList(G, tmp, partial_restore);
|
|
}
|
|
|
|
if (tmp || (!partial_restore)) { /* ignore missing if partial restore */
|
|
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after colors.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
tmp = PyDict_GetItemString(session, "color_ext");
|
|
if (tmp) {
|
|
ok = ColorExtFromPyList(G, tmp, partial_restore);
|
|
}
|
|
|
|
if (tmp || (!partial_session)) { /* ignore missing if partial restore */
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after color_ext.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
}
|
|
if (ok) {
|
|
tmp = PyDict_GetItemString(session, "settings");
|
|
if (tmp && !partial_restore) {
|
|
SettingInitGlobal(G, false, false, /* use_defaults */ true);
|
|
SettingSetGlobalsFromPyList(G, tmp);
|
|
}
|
|
|
|
if (tmp || (!(partial_restore |
|
|
partial_session))) { /* ignore missing if partial restore */
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after settings.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
}
|
|
if (ok) {
|
|
tmp = PyDict_GetItemString(session, "unique_settings");
|
|
if (tmp) {
|
|
ok = SettingUniqueFromPyList(G, tmp, partial_restore);
|
|
}
|
|
|
|
if (tmp || (!partial_session)) { /* ignore missing if partial restore */
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after settings.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
}
|
|
if (ok) {
|
|
tmp = PyDict_GetItemString(session, "names");
|
|
if (tmp) {
|
|
if (ok)
|
|
ok = ExecutiveSetNamedEntries(
|
|
G, tmp, version, partial_restore, partial_session);
|
|
if (!(partial_restore || partial_session)) {
|
|
if (ok)
|
|
ok = ExecutiveSetSelectionsFromPyList(G, tmp);
|
|
if (ok)
|
|
have_active = ExecutiveGetActiveSeleName(G, active, false, false);
|
|
}
|
|
}
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after names.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
if (ok && !(partial_restore)) {
|
|
tmp = PyDict_GetItemString(session, "selector_secrets");
|
|
if (tmp) {
|
|
if (ok)
|
|
ok = SelectorSecretsFromPyList(G, tmp);
|
|
}
|
|
|
|
if (tmp || (!(partial_restore |
|
|
partial_session))) { /* ignore missing if partial restore */
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after selector secrets.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
}
|
|
if (ok && !(partial_restore)) {
|
|
tmp = PyDict_GetItemString(session, "view");
|
|
if (tmp) {
|
|
ok = PConvPyListToFloatArrayInPlace(tmp, sv, cSceneViewSize);
|
|
}
|
|
if (tmp || (!(partial_restore |
|
|
partial_session))) { /* ignore missing if partial restore */
|
|
if (ok)
|
|
SceneSetView(G, sv, true, 0, 0);
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after view.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok && !partial_restore) {
|
|
tmp = CPythonVal_PyDict_GetItemString(G, session, "moviescenes");
|
|
if (tmp) {
|
|
MovieScenesFromPyList(G, tmp);
|
|
CPythonVal_Free(tmp);
|
|
}
|
|
}
|
|
|
|
if (ok && !(partial_restore)) {
|
|
int warning;
|
|
tmp = PyDict_GetItemString(session, "movie");
|
|
if (tmp) {
|
|
ok = MovieFromPyList(G, tmp, &warning);
|
|
}
|
|
if (tmp || (!(partial_restore |
|
|
partial_session))) { /* ignore missing if partial restore */
|
|
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after movie.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok && !(partial_restore)) {
|
|
tmp = PyDict_GetItemString(session, "editor");
|
|
if (tmp) {
|
|
ok = EditorFromPyList(G, tmp);
|
|
}
|
|
if (tmp || (!(partial_restore |
|
|
partial_session))) { /* ignore missing if partial restore */
|
|
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after editor.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
PParse(G, "cmd.mouse(quiet=1)");
|
|
}
|
|
// Do not load viewport size when we have a GUI
|
|
if (ok) {
|
|
tmp = PyDict_GetItemString(session, "main");
|
|
if (tmp) {
|
|
if (!G->HaveGUI &&
|
|
/* PYMOL-775 added suspend_updates check, but does it make sense? */
|
|
!SettingGetGlobal_b(G, cSetting_suspend_updates) &&
|
|
!partial_restore) {
|
|
ok = MainFromPyList(G, tmp);
|
|
#ifndef _PYMOL_NOPY
|
|
} else if (!quiet) {
|
|
int viewport[2];
|
|
PConvPyListToIntArrayInPlace(tmp, viewport, 2);
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Session was saved with: viewport %d, %d\n", viewport[0],
|
|
viewport[1] ENDFB(G);
|
|
#endif
|
|
}
|
|
}
|
|
if (tmp || (!(partial_restore |
|
|
partial_session))) { /* ignore missing if partial restore */
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExectiveSetSession-Error: after main.\n" ENDFB(G);
|
|
}
|
|
if (!ok) {
|
|
incomplete = true;
|
|
ok = true; /* keep trying...don't give up */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok && migrate_sessions) { /* migrate sessions */
|
|
tmp = PyDict_GetItemString(session, "version");
|
|
if (tmp) {
|
|
ok = PConvPyIntToInt(tmp, &version);
|
|
if (ok) {
|
|
ExecutiveMigrateSession(G, version);
|
|
}
|
|
}
|
|
}
|
|
if (ok) {
|
|
if (have_active)
|
|
ExecutiveSetObjVisib(G, active, true, false);
|
|
}
|
|
|
|
G->SettingUnique->old2new.reset();
|
|
|
|
if (incomplete) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"ExectiveSetSession-Warning: restore may be incomplete.\n" ENDFB(G);
|
|
}
|
|
G->ShaderMgr->Set_Reload_Bits(RELOAD_ALL_SHADERS);
|
|
OrthoBackgroundTextureNeedsUpdate(G);
|
|
ExecutiveInvalidateSelectionIndicatorsCGO(G);
|
|
OrthoInvalidateDoDraw(G);
|
|
SceneChanged(G);
|
|
|
|
G->Color->HaveOldSessionColors = false;
|
|
G->Color->HaveOldSessionExtColors = false;
|
|
|
|
return (ok);
|
|
}
|
|
|
|
#define ExecScrollBarMargin DIP2PIXEL(1)
|
|
#define ExecScrollBarWidth DIP2PIXEL(13)
|
|
|
|
/**
|
|
* @param sele object name (or single-object atom selection expression)
|
|
* @param state object state (only for maps, ignored for molecules)
|
|
* @return true if symmetry is defined
|
|
*/
|
|
pymol::Result<bool> ExecutiveGetSymmetry(PyMOLGlobals* G, const char* sele,
|
|
int state, float* a, float* b, float* c, float* alpha, float* beta,
|
|
float* gamma, char* sgroup)
|
|
{
|
|
// try `sele` as an object name
|
|
auto obj = ExecutiveFindObjectByName(G, sele);
|
|
|
|
// odd feature: accept atom selections to select single object
|
|
if (!obj) {
|
|
auto tmpsele1 = SelectorTmp::make(G, sele);
|
|
p_return_if_error(tmpsele1);
|
|
obj = SelectorGetSingleObjectMolecule(G, tmpsele1->getIndex());
|
|
if (!obj) {
|
|
return pymol::make_error("selection must refer to exactly one object");
|
|
}
|
|
}
|
|
|
|
CSymmetry const* symm = obj->getSymmetry(state);
|
|
|
|
if (symm) {
|
|
*a = symm->Crystal.dims()[0];
|
|
*b = symm->Crystal.dims()[1];
|
|
*c = symm->Crystal.dims()[2];
|
|
*alpha = symm->Crystal.angles()[0];
|
|
*beta = symm->Crystal.angles()[1];
|
|
*gamma = symm->Crystal.angles()[2];
|
|
UtilNCopy(sgroup, symm->spaceGroup(), sizeof(WordType));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Set symmetry for one or more objects.
|
|
* @param names Object name pattern
|
|
* @param state Object state (supports all=-1 and current=-2)
|
|
* @return True if symmetry could be set for at least one object
|
|
*/
|
|
static bool ExecutiveSetSymmetry(PyMOLGlobals* G, pymol::zstring_view names,
|
|
int const state, CSymmetry const& symmetry, bool const quiet = false)
|
|
{
|
|
auto objects = ExecutiveGetObjectsFromPattern(G, names);
|
|
bool success = false;
|
|
|
|
for (auto* obj : objects) {
|
|
if (!obj->setSymmetry(symmetry, state)) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
" %s-Warning: Cannot set symmetry for '%s' (type %s)\n", __func__,
|
|
obj->Name, typeid(obj).name() ENDFB(G);
|
|
continue;
|
|
}
|
|
|
|
success = true;
|
|
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Details)
|
|
" %s-Details: Updated symmetry for '%s'\n", __func__, obj->Name ENDFB(G);
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSetSymmetry(PyMOLGlobals* G, const char* sele,
|
|
int state, float a, float b, float c, float alpha, float beta, float gamma,
|
|
const char* sgroup, int quiet)
|
|
{
|
|
/* create a new symmetry object for copying */
|
|
CSymmetry symmetry(G);
|
|
symmetry.Crystal.setDims(a, b, c);
|
|
symmetry.Crystal.setAngles(alpha, beta, gamma);
|
|
symmetry.setSpaceGroup(sgroup);
|
|
|
|
if (!ExecutiveSetSymmetry(G, sele, state, symmetry, quiet)) {
|
|
return pymol::make_error("no object selected");
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSymmetryCopy(PyMOLGlobals* G, const char* source_name,
|
|
const char* target_name, int source_state, int target_state, int quiet)
|
|
{
|
|
|
|
/* Copy the symmetry info from source to target; currently maps can have
|
|
* multiple states for symmetry, but ObjectMolecule cannot */
|
|
|
|
auto const* source_obj = ExecutiveFindObjectByName(G, source_name);
|
|
if (!source_obj) {
|
|
return pymol::make_error("source object not found");
|
|
}
|
|
|
|
auto const* source_symm = source_obj->getSymmetry(source_state);
|
|
if (!source_symm) {
|
|
return pymol::make_error(pymol::string_format(
|
|
"no symmetry in object '%s' state %d", source_name, source_state));
|
|
}
|
|
|
|
if (!ExecutiveSetSymmetry(
|
|
G, target_name, target_state, *source_symm, quiet)) {
|
|
return pymol::make_error("target object not found");
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* Performs a window average of coordinate states (trajectories).
|
|
*
|
|
* @param pbc Consider periodic boundary conditions
|
|
*/
|
|
pymol::Result<> ExecutiveSmooth(PyMOLGlobals* G, const char* selection,
|
|
int cycles, int window, int first, int last, int ends, int quiet,
|
|
float dist_cutoff, bool pbc)
|
|
{
|
|
SETUP_SELE(selection, tmpsele1, sele);
|
|
const char* name = tmpsele1->getName();
|
|
|
|
ObjectMoleculeOpRec op;
|
|
int range, offset;
|
|
int end_skip = 0;
|
|
bool loop = false;
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: entered %s,%d,%d,%d,%d,%d\n", __func__, name, cycles, first, last,
|
|
window, ends ENDFD;
|
|
|
|
/* count the number of states over which to smooth */
|
|
int max_state = ExecutiveCountStates(G, name) - 1;
|
|
if (last < 0)
|
|
last = max_state;
|
|
if (first < 0)
|
|
first = 0;
|
|
if (last < first) {
|
|
std::swap(last, first);
|
|
}
|
|
if (last > max_state)
|
|
last = max_state;
|
|
|
|
int const n_state = last - first + 1;
|
|
|
|
int const backward = window / 2;
|
|
int const forward = window / 2;
|
|
|
|
switch (ends) {
|
|
case 0:
|
|
end_skip = 1;
|
|
break;
|
|
case 1:
|
|
end_skip = 0;
|
|
break;
|
|
case 2:
|
|
end_skip = backward;
|
|
break;
|
|
case 3: /* cyclic averaging */
|
|
end_skip = 0;
|
|
loop = true;
|
|
break;
|
|
default:
|
|
end_skip = 0;
|
|
break;
|
|
}
|
|
|
|
if (ends) {
|
|
range = (last - first) + 1;
|
|
offset = 0;
|
|
} else {
|
|
range = (last - end_skip) - (first + end_skip) + 1;
|
|
offset = end_skip;
|
|
}
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: first %d last %d n_state %d backward %d forward %d range %d\n",
|
|
__func__, first, last, n_state, backward, forward, range ENDFD;
|
|
|
|
auto const window_abs = std::abs(window);
|
|
if (window_abs < 2) {
|
|
return pymol::make_error("window must be at least size 2");
|
|
}
|
|
|
|
if (n_state < window_abs) {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
" %s: Window size (%d) larger than number of frames (%d)\n", __func__,
|
|
window_abs, n_state ENDFB(G);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
float const dist_cutoff_sq =
|
|
dist_cutoff > 0 ? (dist_cutoff * dist_cutoff) : -1;
|
|
|
|
/* determine storage req */
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_CountAtoms;
|
|
op.i1 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
auto const n_atom = size_t(op.i1);
|
|
|
|
// TODO Could we call SelectorCountAtoms instead?
|
|
assert(n_atom == SelectorCountAtoms(G, sele, cStateAll));
|
|
|
|
if (!n_atom) {
|
|
return {};
|
|
}
|
|
|
|
auto pbc_obj = pbc ? SelectorGetFirstObjectMolecule(G, sele) : nullptr;
|
|
if (pbc_obj) {
|
|
ObjectMoleculePBCUnwrap(*pbc_obj);
|
|
}
|
|
|
|
auto const n_index = n_atom * n_state;
|
|
auto coord0 = std::vector<float>(3 * n_index);
|
|
auto flag0 = std::vector<int>(n_index);
|
|
auto flag1 = std::vector<int>(n_index);
|
|
|
|
/* get the data */
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Smooth: copying coordinates to temporary arrays..\n" ENDFB(G);
|
|
}
|
|
op.code = OMOP_CSetIdxGetAndFlag;
|
|
op.i1 = n_atom;
|
|
op.i2 = 0;
|
|
op.cs1 = first;
|
|
op.cs2 = last;
|
|
op.vv1 = coord0.data();
|
|
op.ii1 = flag0.data();
|
|
op.nvv1 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: got %d %d\n", __func__, op.i2, op.nvv1 ENDFD;
|
|
|
|
// make a copy of the input coordinates
|
|
auto coord1 = coord0;
|
|
|
|
for (int a = 0; a < cycles; ++a) {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Smooth: smoothing (pass %d)...\n", a + 1 ENDFB(G);
|
|
}
|
|
|
|
// use coordiantes from previous pass as input
|
|
std::swap(coord0, coord1);
|
|
|
|
// views for friendly indexing
|
|
auto const* coord0x3 = pymol::reshape<3>(coord0.data());
|
|
auto* const coord1x3 = pymol::reshape<3>(coord1.data());
|
|
|
|
#ifdef PYMOL_OPENMP
|
|
#pragma omp parallel for
|
|
#endif
|
|
for (int b = 0; b < range; ++b) {
|
|
int const st_b = b + offset;
|
|
|
|
if (st_b < end_skip || st_b >= (n_state - end_skip)) {
|
|
continue;
|
|
}
|
|
|
|
for (int c = 0; c < n_atom; ++c) {
|
|
auto const index_c = n_atom * st_b + c;
|
|
|
|
if (!flag0[index_c]) {
|
|
continue;
|
|
}
|
|
|
|
float sum[3] = {};
|
|
int cnt = 0;
|
|
int st_prev = 0;
|
|
for (int d = -backward; d <= forward; ++d) {
|
|
int st = st_b + d;
|
|
if (loop) {
|
|
if (st < 0) {
|
|
st = n_state + st;
|
|
} else if (st >= n_state) {
|
|
st = st - n_state;
|
|
}
|
|
} else {
|
|
if (st < 0) {
|
|
st = 0;
|
|
} else if (st >= n_state) {
|
|
st = n_state - 1;
|
|
}
|
|
}
|
|
|
|
auto const index = n_atom * st + c;
|
|
if (!flag0[index]) {
|
|
continue;
|
|
}
|
|
|
|
float const* v0 = coord0x3[index];
|
|
|
|
if (dist_cutoff_sq > 0 && cnt) {
|
|
auto const* v0_prev = coord0x3[n_atom * st_prev + c];
|
|
if (diffsq3f(v0, v0_prev) > dist_cutoff_sq) {
|
|
if (d <= 0) {
|
|
scale3f(v0, cnt, sum);
|
|
} else {
|
|
for (; d <= forward; ++d) {
|
|
add3f(sum, v0_prev, sum);
|
|
++cnt;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* atom's avg position */
|
|
add3f(sum, v0, sum);
|
|
++cnt;
|
|
st_prev = st;
|
|
}
|
|
if (cnt) {
|
|
flag1[index_c] = true;
|
|
scale3f(sum, 1.0F / cnt, coord1x3[index_c]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Smooth: updating coordinates...\n" ENDFB(G);
|
|
}
|
|
|
|
/* set the new coordinates */
|
|
|
|
op.code = OMOP_CSetIdxSetFlagged;
|
|
op.i1 = n_atom;
|
|
op.i2 = 0;
|
|
if (ends) {
|
|
op.cs1 = first;
|
|
op.cs2 = last;
|
|
op.vv1 = coord1.data();
|
|
op.ii1 = flag1.data();
|
|
} else {
|
|
op.cs1 = first + end_skip;
|
|
op.cs2 = last - end_skip;
|
|
op.vv1 = coord1.data() + (end_skip * n_atom * 3);
|
|
op.ii1 = flag1.data() + (end_skip * n_atom);
|
|
}
|
|
op.nvv1 = 0;
|
|
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: put %d %d\n", __func__, op.i2, op.nvv1 ENDFD;
|
|
|
|
if (pbc_obj) {
|
|
ObjectMoleculePBCWrap(*pbc_obj);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveDebug(PyMOLGlobals* G, const char* name)
|
|
{
|
|
ObjectMolecule* obj;
|
|
ObjectMoleculeBPRec bp;
|
|
int a;
|
|
|
|
obj = (ObjectMolecule*) ExecutiveFindObjectByName(G, name);
|
|
if (obj) {
|
|
ObjectMoleculeInitBondPath(obj, &bp);
|
|
ObjectMoleculeGetBondPaths(obj, 0, 10, &bp);
|
|
for (a = 0; a < bp.n_atom; a++) {
|
|
printf("%d %d %d\n", a, bp.list[a], bp.dist[bp.list[a]]);
|
|
}
|
|
|
|
ObjectMoleculePurgeBondPath(obj, &bp);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int*** ExecutiveGetBondPrint(
|
|
PyMOLGlobals* G, const char* name, int max_bond, int max_type, int* dim)
|
|
{
|
|
int*** result = nullptr;
|
|
pymol::CObject* obj;
|
|
ObjectMolecule* objMol;
|
|
|
|
obj = ExecutiveFindObjectByName(G, name);
|
|
if (obj->type == cObjectMolecule) {
|
|
objMol = (ObjectMolecule*) obj;
|
|
result = ObjectMoleculeGetBondPrint(objMol, max_bond, max_type, dim);
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
#define cMapOperatorMinimum 0
|
|
#define cMapOperatorMaximum 1
|
|
#define cMapOperatorSum 2
|
|
#define cMapOperatorAverage 3
|
|
#define cMapOperatorDifference 4
|
|
#define cMapOperatorCopy 5
|
|
#define cMapOperatorUnique 6
|
|
|
|
pymol::Result<> ExecutiveMapSet(PyMOLGlobals* G, const char* name,
|
|
int operator_, const char* operands, int target_state, int source_state,
|
|
int zoom, int quiet)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int isNew = false;
|
|
ObjectMap* target = ExecutiveFindObjectMapByName(G, name);
|
|
ObjectMap* first_operand = nullptr;
|
|
int src_state_start = 0, src_state_stop = 0;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, operands, true, true);
|
|
|
|
if (target_state < 0) /* if we're targeting all states, 0 is the offset */
|
|
target_state = 0;
|
|
|
|
/* first, figure out what the range of input states is */
|
|
|
|
if (source_state < 0) { /* all source states */
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
int max_n_state = 0;
|
|
SpecRec* rec;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type == cObjectMap) {
|
|
ObjectMap* obj = (ObjectMap*) rec->obj;
|
|
if (obj->State.size() > max_n_state)
|
|
max_n_state = obj->State.size(); /* count states */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
src_state_start = 0;
|
|
src_state_stop = max_n_state;
|
|
} else {
|
|
src_state_start = source_state;
|
|
src_state_stop = source_state + 1;
|
|
}
|
|
|
|
{
|
|
/* next, find the first operand */
|
|
|
|
OrthoLineType first_op_st;
|
|
ParseWordCopy(first_op_st, operands,
|
|
sizeof(OrthoLineType) - 1); /* copy the first operand */
|
|
{
|
|
int sub_list_id =
|
|
ExecutiveGetNamesListFromPattern(G, first_op_st, true, true);
|
|
int sub_iter_id = TrackerNewIter(I_Tracker, 0, sub_list_id);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, sub_iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type == cObjectMap) {
|
|
ObjectMap* obj = (ObjectMap*) rec->obj;
|
|
first_operand = obj;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (first_operand)
|
|
break;
|
|
}
|
|
TrackerDelList(I_Tracker, sub_list_id);
|
|
TrackerDelIter(I_Tracker, sub_iter_id);
|
|
}
|
|
}
|
|
|
|
{
|
|
|
|
/* okay, next thing we need to worry about is where we're putting the data.
|
|
|
|
Case 1. If the map already exists, then we'll use the existing map points
|
|
for storing the result.
|
|
|
|
Case 2. If the operation implies a copy of existing map geometry, then
|
|
we'll create that copy first before performing the calulation.
|
|
|
|
Case 3. If the operation implies a new map geometry, then we need to
|
|
compute that geometry and create the map.
|
|
|
|
*/
|
|
|
|
if (!target) { /* target map doesn't exist... */
|
|
int need_union_geometry = false;
|
|
int need_first_geometry = false;
|
|
switch (operator_) {
|
|
case cMapOperatorSum:
|
|
case cMapOperatorAverage:
|
|
case cMapOperatorMinimum:
|
|
case cMapOperatorMaximum:
|
|
case cMapOperatorDifference:
|
|
need_union_geometry = true;
|
|
break;
|
|
case cMapOperatorUnique:
|
|
case cMapOperatorCopy:
|
|
need_first_geometry = true;
|
|
break;
|
|
}
|
|
|
|
if (need_union_geometry) {
|
|
int src_state, trg_state;
|
|
ObjectMapDesc desc;
|
|
target = new ObjectMap(G);
|
|
|
|
ObjectSetName(target, name);
|
|
isNew = true;
|
|
|
|
for (src_state = src_state_start; src_state < src_state_stop;
|
|
src_state++) {
|
|
trg_state = src_state + target_state;
|
|
desc.mode = cObjectMap_OrthoMinMaxGrid; /* Orthorhombic: min, max,
|
|
spacing,
|
|
centered over range */
|
|
desc.init_mode = 0; /* zeros */
|
|
desc.Grid[0] = 1.0F;
|
|
desc.Grid[1] = 1.0F;
|
|
desc.Grid[2] = 1.0F;
|
|
|
|
{
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
float grid_sum[3] = {0.0F, 0.0F, 0.0F};
|
|
int grid_count = 0;
|
|
int first_extent = true;
|
|
|
|
SpecRec* rec;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
/* compute an average grid and get the effective "union" extent
|
|
*/
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type == cObjectMap) {
|
|
|
|
ObjectMap* obj = (ObjectMap*) rec->obj;
|
|
ObjectMapState* ms = &obj->State[src_state];
|
|
if (src_state < obj->State.size()) {
|
|
if (ms->Active) {
|
|
if (first_extent) {
|
|
copy3f(ms->ExtentMin, desc.MinCorner);
|
|
copy3f(ms->ExtentMax, desc.MaxCorner);
|
|
first_extent = false;
|
|
} else {
|
|
int b;
|
|
for (b = 0; b < 3; b++) {
|
|
if (ms->ExtentMin[b] < desc.MinCorner[b])
|
|
desc.MinCorner[b] = ms->ExtentMin[b];
|
|
if (ms->ExtentMax[b] > desc.MaxCorner[b])
|
|
desc.MaxCorner[b] = ms->ExtentMax[b];
|
|
}
|
|
}
|
|
if (!ObjectMapStateValidXtal(ms)) {
|
|
/* other general-purpose maps currently handled */
|
|
int b;
|
|
for (b = 0; b < 3; b++) {
|
|
grid_sum[b] += ms->Grid[b];
|
|
}
|
|
grid_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
if (grid_count) {
|
|
int b;
|
|
for (b = 0; b < 3; b++) {
|
|
desc.Grid[b] = grid_sum[b] / grid_count;
|
|
}
|
|
}
|
|
if (!first_extent) {
|
|
add3f(desc.Grid, desc.MaxCorner, desc.MaxCorner);
|
|
subtract3f(desc.MinCorner, desc.Grid, desc.MinCorner);
|
|
ObjectMapNewStateFromDesc(G, target, &desc, trg_state, quiet);
|
|
target->State[trg_state].Active = true;
|
|
}
|
|
}
|
|
}
|
|
/* need union geometry */
|
|
} else if (need_first_geometry) {
|
|
if (first_operand) {
|
|
if (ObjectMapNewCopy(
|
|
G, first_operand, &target, source_state, target_state)) {
|
|
if (target) {
|
|
ObjectSetName(target, name);
|
|
isNew = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!target) {
|
|
TrackerDelList(I_Tracker, list_id);
|
|
return pymol::make_error("Cannot find or construct target map.");
|
|
}
|
|
|
|
/* now do the actual operation */
|
|
|
|
int src_state;
|
|
for (src_state = src_state_start; src_state < src_state_stop; src_state++) {
|
|
int trg_state = src_state + target_state;
|
|
ObjectMapState* ms;
|
|
VecCheckEmplace(target->State, trg_state, G);
|
|
|
|
ms = &target->State[target_state];
|
|
if (ms->Active) {
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
int n_pnt =
|
|
(ms->Field->points->size() / ms->Field->points->base_size) / 3;
|
|
float* pnt = (float*) ms->Field->points->data.data();
|
|
float* r_value = pymol::malloc<float>(n_pnt);
|
|
float* l_value = pymol::calloc<float>(n_pnt);
|
|
int* present = pymol::calloc<int>(n_pnt);
|
|
std::vector<std::uint8_t> inside(n_pnt);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectMap) {
|
|
ObjectMap* obj = (ObjectMap*) rec->obj;
|
|
if (ObjectMapInterpolate(
|
|
obj, src_state, pnt, r_value, inside.data(), n_pnt)) {
|
|
int a;
|
|
float* rv = r_value;
|
|
float* lv = l_value;
|
|
auto* flg = inside.data();
|
|
int* pre = present;
|
|
|
|
switch (operator_) {
|
|
case cMapOperatorCopy:
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (flg) {
|
|
*lv = *rv;
|
|
}
|
|
rv++;
|
|
lv++;
|
|
flg++;
|
|
}
|
|
break;
|
|
case cMapOperatorMinimum:
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (flg) {
|
|
if (*pre) {
|
|
if (*lv > *rv)
|
|
*lv = *rv;
|
|
} else { /* first map */
|
|
*pre = 1;
|
|
*lv = *rv;
|
|
}
|
|
}
|
|
rv++;
|
|
lv++;
|
|
flg++;
|
|
pre++;
|
|
}
|
|
break;
|
|
case cMapOperatorMaximum:
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (flg) {
|
|
if (*pre) {
|
|
if (*lv < *rv)
|
|
*lv = *rv;
|
|
} else { /* first map */
|
|
*pre = 1;
|
|
*lv = *rv;
|
|
}
|
|
}
|
|
rv++;
|
|
lv++;
|
|
flg++;
|
|
pre++;
|
|
}
|
|
break;
|
|
case cMapOperatorSum:
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (flg) {
|
|
*lv += *rv;
|
|
}
|
|
rv++;
|
|
lv++;
|
|
flg++;
|
|
}
|
|
break;
|
|
case cMapOperatorAverage:
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (flg) {
|
|
*lv += *rv;
|
|
}
|
|
(*pre)++;
|
|
rv++;
|
|
lv++;
|
|
flg++;
|
|
pre++;
|
|
}
|
|
break;
|
|
case cMapOperatorDifference:
|
|
if (obj != first_operand) {
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (flg) {
|
|
*lv -= *rv;
|
|
}
|
|
rv++;
|
|
lv++;
|
|
flg++;
|
|
}
|
|
} else {
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (flg) {
|
|
*lv += *rv;
|
|
}
|
|
rv++;
|
|
lv++;
|
|
flg++;
|
|
}
|
|
}
|
|
break;
|
|
case cMapOperatorUnique:
|
|
if (obj != first_operand) {
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (flg) {
|
|
*lv -= *rv;
|
|
}
|
|
rv++;
|
|
lv++;
|
|
flg++;
|
|
}
|
|
} else {
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (flg) {
|
|
*lv += *rv;
|
|
}
|
|
rv++;
|
|
lv++;
|
|
flg++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
int a;
|
|
float* lv = l_value;
|
|
int* pre = present;
|
|
|
|
switch (operator_) {
|
|
case cMapOperatorUnique:
|
|
lv = l_value;
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (*lv < 0.0F)
|
|
*lv = 0.0F;
|
|
lv++;
|
|
}
|
|
break;
|
|
case cMapOperatorAverage:
|
|
lv = l_value;
|
|
pre = present;
|
|
for (a = 0; a < n_pnt; a++) {
|
|
if (*pre)
|
|
*lv /= *pre;
|
|
lv++;
|
|
pre++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* copy after calculation so that operand can include target */
|
|
|
|
memcpy(ms->Field->data->data.data(), l_value, n_pnt * sizeof(float));
|
|
|
|
FreeP(present);
|
|
FreeP(l_value);
|
|
FreeP(r_value);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
}
|
|
|
|
/* and finally, update */
|
|
|
|
if (target) {
|
|
ObjectMapUpdateExtents(target);
|
|
if (isNew) {
|
|
ExecutiveManageObject(G, target, -1, quiet);
|
|
} else {
|
|
ExecutiveDoZoom(G, target, false, zoom, true);
|
|
}
|
|
SceneChanged(G);
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
|
|
return {};
|
|
}
|
|
|
|
const char* ExecutiveMapGenerate(PyMOLGlobals* G, const char* name,
|
|
const char* reflection_file, const char* tempFile, char* amplitudes,
|
|
const char* phases, const char* weights, double reso_low, double reso_high,
|
|
const char* space_group, double cell[6], int quiet, int zoom)
|
|
{
|
|
/* FIXME: this should be returned in memory!! */
|
|
/* In the meantime, use mkstemp and load in Python */
|
|
int ok;
|
|
|
|
ok = 0;
|
|
|
|
if (weights && (!strncmp(weights, "None", 4)))
|
|
weights = nullptr;
|
|
|
|
/* printf("Passing to primex driver: space_group=%s, cell=[%f %f %f %f %f
|
|
* %f], reso_high=%f, rseo_low=%f, refl_file=%s, ampl=%s, phases=%s,
|
|
* weights=%s, map_file=%s", space_group, cell[0], cell[1], cell[2],
|
|
* cell[3], cell[4], cell[5], reso_high, reso_low, reflection_file,
|
|
* amplitudes, phases, weights, tempFile); */
|
|
|
|
#ifndef NO_MMLIBS
|
|
ok = !(primex_pymol_driver2(space_group, cell, reso_high, reso_low,
|
|
reflection_file, amplitudes, phases, weights, tempFile));
|
|
#else
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" Error: MTZ map loading not supported in this PyMOL build.\n" ENDFB(G);
|
|
#endif
|
|
|
|
if (!ok)
|
|
return nullptr;
|
|
else
|
|
return (const char*) tempFile;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveMapNew(PyMOLGlobals* G, const char* name, int type,
|
|
float grid_spacing, const char* s0, float buffer, const float* minCorner,
|
|
const float* maxCorner, int state, int have_corners, int quiet, int zoom,
|
|
int normalize, float clamp_floor, float clamp_ceiling, float resolution)
|
|
{
|
|
pymol::CObject* origObj = nullptr;
|
|
ObjectMap* objMap;
|
|
ObjectMapState* ms = nullptr;
|
|
int a;
|
|
float v[3];
|
|
ObjectMapDesc _md, *md;
|
|
|
|
SETUP_SELE_DEFAULT(0);
|
|
const char* sele = tmpsele0->getName();
|
|
|
|
int isNew = true;
|
|
int n_state;
|
|
int valid_extent = false;
|
|
int st;
|
|
int st_once_flag = true;
|
|
int n_st;
|
|
int extent_state;
|
|
int clamp_flag = (clamp_floor <= clamp_ceiling);
|
|
|
|
md = &_md;
|
|
|
|
float grid[3] = {grid_spacing, grid_spacing, grid_spacing};
|
|
|
|
if ((state == -2) || (state == -3)) /* TO DO: support per-object states */
|
|
state = SceneGetState(G);
|
|
|
|
/* remove object if it already exists */
|
|
|
|
origObj = ExecutiveFindObjectByName(G, name);
|
|
|
|
if (origObj) {
|
|
if (origObj->type != cObjectMap) {
|
|
ExecutiveDelete(G, origObj->Name);
|
|
} else {
|
|
isNew = false;
|
|
}
|
|
}
|
|
|
|
n_st = ExecutiveCountStates(G, nullptr);
|
|
|
|
for (st = 0; st < n_st; st++) {
|
|
if (state == -1)
|
|
st_once_flag = false; /* each state, separate map, separate extent */
|
|
if (!st_once_flag)
|
|
state = st;
|
|
extent_state = state;
|
|
if (state <= -3)
|
|
extent_state = -1;
|
|
if (strlen(sele) && (!have_corners)) {
|
|
valid_extent = ExecutiveGetExtent(
|
|
G, sele, md->MinCorner, md->MaxCorner, true, extent_state, false);
|
|
/* TODO restrict to state */
|
|
} else {
|
|
valid_extent = 1;
|
|
copy3f(minCorner, md->MinCorner);
|
|
copy3f(maxCorner, md->MaxCorner);
|
|
}
|
|
copy3f(grid, md->Grid);
|
|
|
|
subtract3f(md->MaxCorner, md->MinCorner, v);
|
|
for (a = 0; a < 3; a++) {
|
|
if (v[a] < 0.0)
|
|
std::swap(md->MaxCorner[a], md->MinCorner[a]);
|
|
};
|
|
subtract3f(md->MaxCorner, md->MinCorner, v);
|
|
|
|
if (buffer < -0.5F) { // buffer == -1
|
|
buffer = SettingGetGlobal_f(G, cSetting_gaussian_resolution);
|
|
}
|
|
|
|
if (buffer > 0.0F) {
|
|
for (a = 0; a < 3; a++) {
|
|
md->MinCorner[a] -= buffer;
|
|
md->MaxCorner[a] += buffer;
|
|
}
|
|
}
|
|
md->mode = cObjectMap_OrthoMinMaxGrid;
|
|
md->init_mode = -1; /* no initialization */
|
|
|
|
/* validate grid */
|
|
for (a = 0; a < 3; a++)
|
|
if (md->Grid[a] <= R_SMALL8)
|
|
md->Grid[a] = R_SMALL8;
|
|
|
|
if (isNew)
|
|
objMap = new ObjectMap(G);
|
|
else
|
|
objMap = (ObjectMap*) origObj;
|
|
if (objMap) {
|
|
int once_flag = true;
|
|
n_state = SelectorCountStates(G, sele0);
|
|
if (valid_extent)
|
|
for (a = 0; a < n_state; a++) {
|
|
if (state == -5)
|
|
once_flag = false; /* api: state=-4 = each state, separate map,
|
|
shared extent */
|
|
if (state == -4)
|
|
state = -1; /* api: state=-3 all states, but one map */
|
|
if (!once_flag)
|
|
state = a;
|
|
ms = ObjectMapNewStateFromDesc(G, objMap, md, state, quiet);
|
|
if (!ms) {
|
|
return pymol::make_error("Invalid state ", state);
|
|
}
|
|
|
|
switch (type) {
|
|
case 0: /* vdw */
|
|
SelectorMapMaskVDW(G, sele0, ms, 0.0F, state);
|
|
break;
|
|
case 1: /* coulomb */
|
|
SelectorMapCoulomb(G, sele0, ms, 0.0F, state, false, false, 1.0F);
|
|
break;
|
|
case 2: /* gaussian */
|
|
SelectorMapGaussian(
|
|
G, sele0, ms, 0.0F, state, normalize, false, quiet, resolution);
|
|
break;
|
|
case 3: /* coulomb_neutral */
|
|
SelectorMapCoulomb(G, sele0, ms, 0.0F, state, true, false, 1.0F);
|
|
break;
|
|
case 4: /* coulomb_local */
|
|
SelectorMapCoulomb(G, sele0, ms,
|
|
SettingGetGlobal_f(G, cSetting_coulomb_cutoff), state, false,
|
|
true, 2.0F);
|
|
break;
|
|
case 5: /* gaussian_max */
|
|
SelectorMapGaussian(
|
|
G, sele0, ms, 0.0F, state, normalize, true, quiet, resolution);
|
|
break;
|
|
}
|
|
if (!ms->Active)
|
|
ObjectMapStatePurge(G, ms);
|
|
else if (clamp_flag) {
|
|
ObjectMapStateClamp(ms, clamp_floor, clamp_ceiling);
|
|
}
|
|
|
|
if (once_flag)
|
|
break;
|
|
}
|
|
|
|
ObjectSetName(objMap, name);
|
|
ObjectMapUpdateExtents(objMap);
|
|
if (isNew) {
|
|
ExecutiveManageObject(G, objMap, -1, quiet);
|
|
} else {
|
|
ExecutiveDoZoom(G, objMap, false, zoom, true);
|
|
}
|
|
isNew = false;
|
|
origObj = objMap;
|
|
}
|
|
SceneChanged(G);
|
|
|
|
if (st_once_flag)
|
|
break;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveSculptIterateAll(PyMOLGlobals* G)
|
|
{
|
|
int active = false;
|
|
float center_array[8] = {0.0F, 0.0F, 0.0F, 0.0F};
|
|
float* center = center_array;
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* objMol;
|
|
CGOReset(G->DebugCGO);
|
|
|
|
if (SettingGetGlobal_b(G, cSetting_sculpting)) {
|
|
if (!SettingGetGlobal_b(G, cSetting_sculpt_auto_center))
|
|
center = nullptr;
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
objMol = (ObjectMolecule*) rec->obj;
|
|
if (SettingGet_b(
|
|
G, nullptr, objMol->Setting.get(), cSetting_sculpting)) {
|
|
constexpr int state = -2; // current state
|
|
ObjectMoleculeSculptIterate(objMol, state,
|
|
SettingGet_i(G, nullptr, objMol->Setting.get(),
|
|
cSetting_sculpting_cycles),
|
|
center);
|
|
active = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (center && (center[3] > 1.0F)) {
|
|
float pos[3];
|
|
SceneGetCenter(G, pos);
|
|
center[3] = 1.0F / center[3];
|
|
scale3f(center, center[3], center);
|
|
center[7] = 1.0F / center[7];
|
|
scale3f(center + 4, center[7], center + 4);
|
|
subtract3f(center, center + 4, center);
|
|
add3f(pos, center, center);
|
|
ExecutiveCenter(G, nullptr, -1, true, false, center, true);
|
|
}
|
|
}
|
|
if (active) {
|
|
EditorInvalidateShaderCGO(G);
|
|
}
|
|
return (active);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
float ExecutiveSculptIterate(
|
|
PyMOLGlobals* G, const char* name, int state, int n_cycle)
|
|
{
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, name);
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* objMol;
|
|
float total_strain = 0.0F;
|
|
|
|
if (WordMatchExact(G, name, cKeywordAll, true)) {
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
objMol = (ObjectMolecule*) rec->obj;
|
|
total_strain +=
|
|
ObjectMoleculeSculptIterate(objMol, state, n_cycle, nullptr);
|
|
}
|
|
}
|
|
}
|
|
} else if (!obj) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Executive-Error: object %s not found.\n", name ENDFB(G);
|
|
} else if (obj->type != cObjectMolecule) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Executive-Error: object %s is not a molecular object.\n", name ENDFB(G);
|
|
} else {
|
|
total_strain = ObjectMoleculeSculptIterate(
|
|
(ObjectMolecule*) obj, state, n_cycle, nullptr);
|
|
}
|
|
return (total_strain);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveSculptActivate(PyMOLGlobals* G, const char* name, int state,
|
|
int match_state, int match_by_segment)
|
|
{
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, name);
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* objMol;
|
|
CExecutive* I = G->Executive;
|
|
int ok = true;
|
|
if (state < 0)
|
|
state = SceneGetState(G);
|
|
|
|
if (WordMatchExact(G, name, cKeywordAll, true)) {
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
objMol = (ObjectMolecule*) rec->obj;
|
|
ObjectMoleculeSculptImprint(
|
|
objMol, state, match_state, match_by_segment);
|
|
}
|
|
}
|
|
}
|
|
} else if (!obj) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Executive-Error: object %s not found.\n", name ENDFB(G);
|
|
ok = false;
|
|
} else if (obj->type != cObjectMolecule) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Executive-Error: object %s is not a molecular object.\n", name ENDFB(G);
|
|
ok = false;
|
|
} else {
|
|
ObjectMoleculeSculptImprint(
|
|
(ObjectMolecule*) obj, state, match_state, match_by_segment);
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveSculptDeactivate(PyMOLGlobals* G, const char* name)
|
|
{
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, name);
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* objMol;
|
|
CExecutive* I = G->Executive;
|
|
|
|
int ok = true;
|
|
|
|
if (WordMatchExact(G, name, cKeywordAll, true)) {
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
objMol = (ObjectMolecule*) rec->obj;
|
|
ObjectMoleculeSculptClear(objMol);
|
|
}
|
|
}
|
|
}
|
|
} else if (!obj) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Executive-Error: object %s not found.\n", name ENDFB(G);
|
|
ok = false;
|
|
} else if (obj->type != cObjectMolecule) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Executive-Error: object %s is not a molecular object.\n", name ENDFB(G);
|
|
ok = false;
|
|
} else {
|
|
ObjectMoleculeSculptClear((ObjectMolecule*) obj);
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveSetGeometry(
|
|
PyMOLGlobals* G, const char* s1, int geom, int valence)
|
|
{
|
|
SETUP_SELE_DEFAULT(1);
|
|
|
|
int count = 0;
|
|
auto recs = pymol::make_list_adapter(G->Executive->Spec);
|
|
for (auto& rec : recs) {
|
|
auto obj = ExecutiveIsObjectType(rec, cObjectMolecule) ? rec.obj : nullptr;
|
|
if (obj) {
|
|
count += ObjectMoleculeSetGeometry(
|
|
G, static_cast<ObjectMolecule*>(obj), sele1, geom, valence);
|
|
}
|
|
}
|
|
if (count == 0) {
|
|
return pymol::make_error("Empty selection.");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveMapSetBorder(
|
|
PyMOLGlobals* G, const char* name, float level, int state)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int result = true;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type == cObjectMap) {
|
|
ObjectMap* obj = (ObjectMap*) rec->obj;
|
|
result = ObjectMapSetBorder(obj, level, state);
|
|
|
|
if (result) {
|
|
ExecutiveInvalidateMapDependents(G, obj->Name);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
return result;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveMapDouble(PyMOLGlobals* G, const char* name, int state)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type == cObjectMap) {
|
|
ObjectMap* obj = (ObjectMap*) rec->obj;
|
|
auto result = ObjectMapDouble(obj, state);
|
|
if (result) {
|
|
ExecutiveInvalidateMapDependents(G, obj->Name);
|
|
} else {
|
|
return result;
|
|
}
|
|
if (result && rec->visible)
|
|
SceneChanged(G);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveMapHalve(
|
|
PyMOLGlobals* G, const char* name, int state, int smooth)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type == cObjectMap) {
|
|
ObjectMap* obj = (ObjectMap*) rec->obj;
|
|
auto result = ObjectMapHalve(obj, state, smooth);
|
|
if (result) {
|
|
ExecutiveInvalidateMapDependents(G, obj->Name);
|
|
} else {
|
|
return result;
|
|
}
|
|
if (result && rec->visible)
|
|
SceneChanged(G);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveMapTrim(PyMOLGlobals* G, const char* name,
|
|
const char* sele, float buffer, int map_state, int sele_state, int quiet)
|
|
{
|
|
auto s1 = SelectorTmp2::make(G, sele);
|
|
CExecutive* I = G->Executive;
|
|
float mn[3], mx[3];
|
|
if (ExecutiveGetExtent(G, s1->getName(), mn, mx, true, sele_state, false)) {
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
|
|
{
|
|
int a;
|
|
float t;
|
|
for (a = 0; a < 3; a++) {
|
|
mn[a] -= buffer;
|
|
mx[a] += buffer;
|
|
if (mn[a] > mx[a]) {
|
|
t = mn[a];
|
|
mn[a] = mx[a];
|
|
mx[a] = t;
|
|
}
|
|
}
|
|
}
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type == cObjectMap) {
|
|
ObjectMap* obj = (ObjectMap*) rec->obj;
|
|
auto result = ObjectMapTrim(obj, map_state, mn, mx, quiet);
|
|
if (result) {
|
|
ExecutiveInvalidateMapDependents(G, obj->Name);
|
|
} else {
|
|
return result;
|
|
}
|
|
if (result && rec->visible)
|
|
SceneChanged(G);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<NetSelect> ExecutiveSelectRect(
|
|
PyMOLGlobals* G, BlockRect* rect, int mode)
|
|
{
|
|
NetSelect result;
|
|
pymol::Result<int> selectorResult;
|
|
int beforeSelected = 0;
|
|
Multipick smp;
|
|
char selName[WordLength] = cLeftButSele;
|
|
char prefix[3] = "";
|
|
int log_box = 0;
|
|
const char* sel_mode_kw = "";
|
|
|
|
int logging = SettingGet<int>(G, cSetting_logging);
|
|
if (logging)
|
|
log_box = SettingGetGlobal_b(G, cSetting_log_box_selections);
|
|
/* if(logging==cPLog_pml)
|
|
strcpy(prefix,"_ "); */
|
|
smp.x = rect->left;
|
|
smp.y = rect->bottom;
|
|
smp.w = rect->right - rect->left;
|
|
smp.h = rect->top - rect->bottom;
|
|
SceneMultipick(G, &smp);
|
|
if (!smp.picked.empty()) {
|
|
selectorResult =
|
|
SelectorCreate(G, cTempRectSele, nullptr, nullptr, 1, &smp);
|
|
if (log_box)
|
|
SelectorLogSele(G, cTempRectSele);
|
|
switch (mode) {
|
|
case cButModeRect:
|
|
if (mode == cButModeRect) {
|
|
selectorResult =
|
|
SelectorCreate(G, cLeftButSele, cTempRectSele, nullptr, 1, nullptr);
|
|
if (log_box) {
|
|
auto buf2 =
|
|
pymol::string_format("%scmd.select(\"%s\",\"%s\",enable=1)\n",
|
|
prefix, cLeftButSele, cTempRectSele);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
}
|
|
break;
|
|
case cButModeSeleSetBox:
|
|
case cButModeSeleAddBox:
|
|
case cButModeSeleSubBox:
|
|
ExecutiveGetActiveSeleName(
|
|
G, selName, true, SettingGetGlobal_i(G, cSetting_logging));
|
|
sel_mode_kw = SceneGetSeleModeKeyword(G);
|
|
/* intentional omission of break! */
|
|
case cButModeRectAdd:
|
|
case cButModeRectSub:
|
|
beforeSelected =
|
|
SelectorCountAtoms(G, SelectorTmp{G, selName}.getIndex(), -1);
|
|
if (SelectorIndexByName(G, selName) >= 0) {
|
|
if ((mode == cButModeRectAdd) || (mode == cButModeSeleAddBox)) {
|
|
auto buffer = pymol::string_format(
|
|
"(?%s or %s(%s))", selName, sel_mode_kw, cTempRectSele);
|
|
selectorResult =
|
|
SelectorCreate(G, selName, buffer.c_str(), nullptr, 0, nullptr);
|
|
if (log_box) {
|
|
auto buf2 =
|
|
pymol::string_format("%scmd.select(\"%s\",\"(%s)\",enable=1)\n",
|
|
prefix, selName, buffer);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
} else if ((mode == cButModeRectSub) || (mode == cButModeSeleSubBox)) {
|
|
auto buffer = pymol::string_format("(%s(?%s) and not %s(%s))",
|
|
sel_mode_kw, selName, sel_mode_kw, cTempRectSele);
|
|
selectorResult =
|
|
SelectorCreate(G, selName, buffer.c_str(), nullptr, 0, nullptr);
|
|
if (log_box) {
|
|
auto buf2 =
|
|
pymol::string_format("%scmd.select(\"%s\",\"%s\",enable=1)\n",
|
|
prefix, selName, buffer);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
} else {
|
|
auto buffer =
|
|
pymol::string_format("(%s(?%s))", sel_mode_kw, cTempRectSele);
|
|
selectorResult =
|
|
SelectorCreate(G, selName, buffer.c_str(), nullptr, 0, nullptr);
|
|
if (log_box) {
|
|
auto buf2 =
|
|
pymol::string_format("%scmd.select(\"%s\",\"%s\",enable=1)\n",
|
|
prefix, selName, buffer);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
}
|
|
} else {
|
|
if ((mode == cButModeRectAdd) || (mode == cButModeSeleAddBox)) {
|
|
auto buffer =
|
|
pymol::string_format("%s(?%s)", sel_mode_kw, cTempRectSele);
|
|
selectorResult =
|
|
SelectorCreate(G, selName, buffer.c_str(), nullptr, 0, nullptr);
|
|
if (log_box) {
|
|
auto buf2 =
|
|
pymol::string_format("%scmd.select(\"%s\",\"%s\",enable=1)\n",
|
|
prefix, selName, buffer);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
} else if ((mode == cButModeRectSub) || (mode == cButModeSeleSubBox)) {
|
|
selectorResult =
|
|
SelectorCreate(G, selName, "(none)", nullptr, 0, nullptr);
|
|
if (log_box) {
|
|
auto buf2 = pymol::string_format(
|
|
"%scmd.select(\"%s\",\"(none)\",enable=1)\n", prefix, selName);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
} else {
|
|
auto buffer =
|
|
pymol::string_format("%s(?%s)", sel_mode_kw, cTempRectSele);
|
|
selectorResult =
|
|
SelectorCreate(G, selName, buffer.c_str(), nullptr, 0, nullptr);
|
|
if (log_box) {
|
|
auto buf2 =
|
|
pymol::string_format("%scmd.select(\"%s\",\"%s\",enable=1)\n",
|
|
prefix, selName, buffer);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
}
|
|
}
|
|
if (SettingGetGlobal_b(G, cSetting_auto_show_selections)) {
|
|
ExecutiveSetObjVisib(G, selName, true, false);
|
|
}
|
|
break;
|
|
}
|
|
if (log_box) {
|
|
auto buf2 =
|
|
pymol::string_format("%scmd.delete(\"%s\")\n", prefix, cTempRectSele);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
PLogFlush(G);
|
|
}
|
|
ExecutiveDelete(G, cTempRectSele);
|
|
WizardDoSelect(G, selName);
|
|
} else {
|
|
switch (mode) {
|
|
case cButModeSeleSetBox: {
|
|
ObjectNameType name;
|
|
if (ExecutiveGetActiveSeleName(
|
|
G, name, false, SettingGetGlobal_i(G, cSetting_logging))) {
|
|
ExecutiveSetObjVisib(G, name, 0, false);
|
|
if (SettingGetGlobal_i(G, cSetting_logging)) {
|
|
auto buf2 = pymol::string_format("cmd.disable('%s')\n", name);
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
if (selectorResult) {
|
|
result.currSelected = *selectorResult;
|
|
result.netSelected = *selectorResult - beforeSelected;
|
|
return result;
|
|
} else {
|
|
return selectorResult.error();
|
|
}
|
|
}
|
|
|
|
pymol::Result<> ExecutiveTranslateAtom(PyMOLGlobals* G, const char* sele,
|
|
const float* v, int state, int mode, int log)
|
|
{
|
|
SETUP_SELE(sele, tmpsele0, sele0);
|
|
|
|
{
|
|
auto obj0 = SelectorGetSingleObjectMolecule(G, sele0);
|
|
if (!obj0) {
|
|
return pymol::make_error("Selection isn't a single atom.");
|
|
} else {
|
|
auto i0 = ObjectMoleculeGetAtomIndex(obj0, sele0);
|
|
if (i0 < 0) {
|
|
return pymol::make_error("Selection isn't a single atom.");
|
|
} else {
|
|
ObjectMoleculeMoveAtom(obj0, state, i0, v, mode, log);
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* Performs a TTT-altering function whose transformation is optionally stored as
|
|
* a keyframe
|
|
* @param name name of object or pattern
|
|
* @param store to store transformation as keyframe
|
|
* @param func function to call (ex. ExecutiveSetObjectTTT)
|
|
* @param args arguments to func
|
|
*/
|
|
|
|
template <typename Func, typename... Args>
|
|
void ExecutiveObjectFuncTTT(PyMOLGlobals* G, pymol::zstring_view name,
|
|
int store, Func func, Args... args)
|
|
{
|
|
auto I = G->Executive;
|
|
if (name.empty() || name == cKeywordAll || name == cKeywordSame) {
|
|
for (auto& rec : pymol::make_list_adapter(I->Spec)) {
|
|
switch (rec.type) {
|
|
case cExecObject: {
|
|
pymol::CObject* obj = rec.obj;
|
|
if (ObjectGetSpecLevel(rec.obj, 0) >= 0 || name == cKeywordAll) {
|
|
func(obj, args...);
|
|
obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
if (store && SettingGet<bool>(G, cSetting_movie_auto_interpolate)) {
|
|
ExecutiveMotionReinterpolate(G);
|
|
}
|
|
} else { /* pattern */
|
|
for (auto& rec : ExecutiveGetSpecRecsFromPattern(G, name)) {
|
|
switch (rec.type) {
|
|
case cExecObject: {
|
|
pymol::CObject* obj = rec.obj;
|
|
func(obj, args...);
|
|
obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
} break;
|
|
}
|
|
}
|
|
if (store && SettingGet<bool>(G, cSetting_movie_auto_interpolate)) {
|
|
ExecutiveMotionReinterpolate(G);
|
|
}
|
|
}
|
|
SceneInvalidate(G);
|
|
}
|
|
pymol::Result<> ExecutiveCombineObjectTTT(PyMOLGlobals* G,
|
|
pymol::zstring_view name, const float* ttt, int reverse_order, int store)
|
|
{
|
|
ExecutiveObjectFuncTTT(
|
|
G, name, store, ObjectCombineTTT, ttt, reverse_order, store);
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveTranslateObjectTTT(PyMOLGlobals* G,
|
|
pymol::zstring_view name, const float* trans, int store, int quiet)
|
|
{
|
|
ExecutiveObjectFuncTTT(G, name, store, ObjectTranslateTTT, trans, store);
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSetObjectTTT(PyMOLGlobals* G, pymol::zstring_view name,
|
|
const float* ttt, int state, int quiet, int store)
|
|
{
|
|
ExecutiveObjectFuncTTT(G, name, store, ObjectSetTTT, ttt, state, store);
|
|
return {};
|
|
}
|
|
|
|
int ExecutiveGetObjectTTT(
|
|
PyMOLGlobals* G, const char* name, const float** ttt, int state, int quiet)
|
|
{
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, name);
|
|
int ok = true;
|
|
|
|
if (!obj) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
"Error: object %s not found.\n", name ENDFB(G);
|
|
ok = false;
|
|
} else {
|
|
ObjectGetTTT(obj, ttt, state);
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
pymol::Result<> ExecutiveTransformSelection(PyMOLGlobals* G, int state,
|
|
const char* s1, int log, const float* ttt, int homogenous)
|
|
{
|
|
SETUP_SELE_DEFAULT(1);
|
|
|
|
auto vla = pymol::vla_take_ownership(SelectorGetObjectMoleculeVLA(G, sele1));
|
|
if (!vla)
|
|
return pymol::make_error("Could not find selection");
|
|
for (auto obj : vla) {
|
|
ObjectMoleculeTransformSelection(
|
|
obj, state, sele1, ttt, log, tmpsele1->getName(), homogenous, true);
|
|
}
|
|
SceneInvalidate(G);
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveTransformObjectSelection2(PyMOLGlobals* G,
|
|
pymol::CObject* obj, int state, const char* s1, int log,
|
|
const float* matrix, int homogenous, int global)
|
|
{
|
|
int ok = true;
|
|
|
|
switch (obj->type) {
|
|
case cObjectMolecule: {
|
|
int sele = -1;
|
|
ObjectMolecule* objMol = (ObjectMolecule*) obj;
|
|
|
|
if (s1 && s1[0]) {
|
|
sele = SelectorIndexByName(G, s1);
|
|
if (sele < 0)
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
return pymol::make_error("Selection object ", s1, " not found.");
|
|
} else {
|
|
ObjectMoleculeTransformSelection(
|
|
objMol, state, sele, matrix, log, s1, homogenous, global);
|
|
}
|
|
EditorDihedralInvalid(G, objMol);
|
|
SceneInvalidate(G);
|
|
} break;
|
|
default:
|
|
if (auto* objstate = obj->getObjectState(state)) {
|
|
double matrixd[116];
|
|
if (homogenous) {
|
|
convert44f44d(matrix, matrixd);
|
|
} else {
|
|
convertTTTfR44d(matrix, matrixd);
|
|
}
|
|
ObjectStateTransformMatrix(objstate, matrixd);
|
|
obj->invalidate(cRepNone, cRepInvExtents, state);
|
|
}
|
|
break;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveTransformObjectSelection(PyMOLGlobals* G,
|
|
const char* name, int state, const char* s1, int log, const float* matrix,
|
|
int homogenous, int global)
|
|
{
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, name);
|
|
if (obj) {
|
|
return ExecutiveTransformObjectSelection2(
|
|
G, obj, state, s1, log, matrix, homogenous, global);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
int ExecutiveValidName(PyMOLGlobals* G, const char* name)
|
|
{
|
|
if (!ExecutiveFindSpec(G, name)) {
|
|
int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
|
|
|
|
if (!WordMatchExact(G, name, cKeywordAll, ignore_case))
|
|
if (!WordMatchExact(G, name, cKeywordSame, ignore_case))
|
|
if (!WordMatchExact(G, name, cKeywordCenter, ignore_case))
|
|
if (!WordMatchExact(G, name, cKeywordOrigin, ignore_case))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int ExecutivePhiPsi(PyMOLGlobals* G, const char* s1, ObjectMolecule*** objVLA,
|
|
int** iVLA, float** phiVLA, float** psiVLA, int state)
|
|
{
|
|
SelectorTmp tmpsele1(G, s1);
|
|
int sele1 = tmpsele1.getIndex();
|
|
|
|
int result = false;
|
|
ObjectMoleculeOpRec op1;
|
|
if (sele1 >= 0) {
|
|
ObjectMoleculeOpRecInit(&op1);
|
|
op1.i1 = 0;
|
|
op1.i2 = state;
|
|
op1.obj1VLA = VLAlloc(ObjectMolecule*, 1000);
|
|
op1.i1VLA = VLAlloc(int, 1000);
|
|
op1.f1VLA = VLAlloc(float, 1000);
|
|
op1.f2VLA = VLAlloc(float, 1000);
|
|
op1.code = OMOP_PhiPsi;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op1);
|
|
result = op1.i1;
|
|
VLASize(op1.i1VLA, int, op1.i1);
|
|
VLASize(op1.obj1VLA, ObjectMolecule*, op1.i1);
|
|
VLASize(op1.f1VLA, float, op1.i1);
|
|
VLASize(op1.f2VLA, float, op1.i1);
|
|
*iVLA = op1.i1VLA;
|
|
*objVLA = op1.obj1VLA;
|
|
*phiVLA = op1.f1VLA;
|
|
*psiVLA = op1.f2VLA;
|
|
} else {
|
|
*objVLA = nullptr;
|
|
*iVLA = nullptr;
|
|
*phiVLA = nullptr;
|
|
*psiVLA = nullptr;
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
int ExecutiveAlign(PyMOLGlobals* G, const char* s1, const char* s2,
|
|
const char* mat_file, float gap, float extend, int max_gap, int max_skip,
|
|
float cutoff, int cycles, int quiet, const char* oname, int state1,
|
|
int state2, ExecutiveRMSInfo* rms_info, int transform, int reset,
|
|
float seq_wt, float radius, float scale, float base, float coord_wt,
|
|
float expect, int window, float ante)
|
|
{
|
|
int sele1 = SelectorIndexByName(G, s1);
|
|
int sele2 = SelectorIndexByName(G, s2);
|
|
int* vla1 = nullptr;
|
|
int* vla2 = nullptr;
|
|
int na, nb;
|
|
int c;
|
|
int ok = true;
|
|
int use_sequence = (mat_file && mat_file[0] && (seq_wt != 0.0F));
|
|
int use_structure =
|
|
(seq_wt >= 0.0F); /* negative seq_wt means sequence only! */
|
|
ObjectMolecule* mobile_obj = nullptr;
|
|
CMatch* match = nullptr;
|
|
|
|
if (!use_structure)
|
|
window = 0;
|
|
|
|
if ((scale == 0.0F) && (seq_wt == 0.0F) && (ante < 0.0F) && window)
|
|
ante = window;
|
|
|
|
if (ante < 0.0F)
|
|
ante = 0.0F;
|
|
|
|
if ((sele1 >= 0)) {
|
|
mobile_obj = SelectorGetSingleObjectMolecule(G, sele1);
|
|
if (!mobile_obj) {
|
|
ok = false;
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" ExecutiveAlign: mobile selection must derive from one object "
|
|
"only.\n" ENDFB(G);
|
|
}
|
|
}
|
|
if (ok && (sele1 >= 0) && (sele2 >= 0) && rms_info) {
|
|
vla1 = SelectorGetResidueVLA(G, sele1, use_structure, nullptr);
|
|
vla2 = SelectorGetResidueVLA(G, sele2, use_structure, mobile_obj);
|
|
if (vla1 && vla2) {
|
|
na = VLAGetSize(vla1) / 3;
|
|
nb = VLAGetSize(vla2) / 3;
|
|
if (na && nb) {
|
|
match = MatchNew(G, na, nb, window);
|
|
if (match) {
|
|
if (use_sequence) {
|
|
if (ok)
|
|
ok = MatchResidueToCode(match, vla1, na);
|
|
if (ok)
|
|
ok = MatchResidueToCode(match, vla2, nb);
|
|
if (ok)
|
|
ok = MatchMatrixFromFile(match, mat_file, quiet);
|
|
if (ok)
|
|
ok = MatchPreScore(match, vla1, na, vla2, nb, quiet);
|
|
}
|
|
if (use_structure) {
|
|
/* avoid degenerate alignments */
|
|
ok = ((na > 1) && (nb > 1) && ok);
|
|
if (ok) {
|
|
ok = SelectorResidueVLAsTo3DMatchScores(G, match, vla1, na,
|
|
state1, vla2, nb, state2, seq_wt, radius, scale, base,
|
|
coord_wt, expect);
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" ExecutiveAlign: No alignment found.\n" ENDFB(G);
|
|
}
|
|
}
|
|
if (ok)
|
|
ok = MatchAlign(
|
|
match, gap, extend, max_gap, max_skip, quiet, window, ante);
|
|
if (ok) {
|
|
rms_info->raw_alignment_score = match->score;
|
|
rms_info->n_residues_aligned = match->n_pair;
|
|
if (match->pair) {
|
|
|
|
c = SelectorCreateAlignments(G, match->pair, sele1, vla1, sele2,
|
|
vla2, "_align1", "_align2", false, false);
|
|
|
|
if (c) {
|
|
int mode = 2;
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" %s: %d atoms aligned.\n", __func__, c ENDFB(G);
|
|
}
|
|
if (oname && oname[0] && reset)
|
|
ExecutiveDelete(G, oname);
|
|
if (!transform)
|
|
mode = 1;
|
|
ok = ExecutiveRMS(G, "_align1", "_align2", mode, cutoff, cycles,
|
|
quiet, oname, state1, state2, false, 0, rms_info);
|
|
} else {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" ExecutiveAlign-Error: atomic alignment failed (mismatched "
|
|
"identifiers?).\n" ENDFB(G);
|
|
}
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
MatchFree(match);
|
|
}
|
|
} else {
|
|
ok = false;
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" ExecutiveAlign: invalid selections for alignment.\n" ENDFB(G);
|
|
}
|
|
}
|
|
ExecutiveUpdateCoordDepends(
|
|
G, mobile_obj); // Updates dynamic_measures - see PYMOL-3090
|
|
}
|
|
|
|
VLAFreeP(vla1);
|
|
VLAFreeP(vla2);
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* Implementation of `cmd.find_pairs()`
|
|
*
|
|
* @param s1 atom selection expression
|
|
* @param s2 atom selection expression
|
|
* @param state1 object state
|
|
* @param state2 object state
|
|
* @param mode 0: find all pairs, 1: find hydrogen bonds
|
|
* @param cutoff maximum distance in Angstrom
|
|
* @param h_angle hydrogen bond angle cutoff for mode=1
|
|
* @param[out] indexVLA pairs of atom indices (0-based)
|
|
* @param[out] objVLA pairs of objects
|
|
* @return number of pairs
|
|
*/
|
|
pymol::Result<int> ExecutivePairIndices(PyMOLGlobals* G, const char* s1,
|
|
const char* s2, int state1, int state2, int mode, float cutoff,
|
|
float h_angle, int** indexVLA, ObjectMolecule*** objVLA)
|
|
{
|
|
SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
|
|
SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
|
|
|
|
return SelectorGetPairIndices(
|
|
G, sele1, state1, sele2, state2, mode, cutoff, h_angle, indexVLA, objVLA);
|
|
}
|
|
|
|
pymol::Result<int> ExecutiveCartoon(PyMOLGlobals* G, int type, const char* s1)
|
|
{
|
|
SETUP_SELE_DEFAULT(1);
|
|
|
|
ObjectMoleculeOpRec op1;
|
|
|
|
ObjectMoleculeOpRecInit(&op1);
|
|
op1.i2 = 0;
|
|
{
|
|
op1.code = OMOP_Cartoon;
|
|
op1.i1 = type;
|
|
op1.i2 = 0;
|
|
op1.i3 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op1);
|
|
if (op1.i3 > 0) {
|
|
op1.code = OMOP_INVA;
|
|
op1.i1 = cRepCartoonBit;
|
|
op1.i2 = cRepInvRep;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op1);
|
|
}
|
|
}
|
|
return (op1.i2);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
float* ExecutiveGetVertexVLA(PyMOLGlobals* G, const char* s1, int state)
|
|
{
|
|
/* returns nullptr if none found */
|
|
|
|
float* result = nullptr;
|
|
ObjectMoleculeOpRec op1;
|
|
int sele1;
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
if (sele1 >= 0) {
|
|
ObjectMoleculeOpRecInit(&op1);
|
|
op1.nvv1 = 0;
|
|
op1.vv1 = VLAlloc(float, 1000);
|
|
if (state >= 0) {
|
|
op1.cs1 = state;
|
|
op1.code = OMOP_SingleStateVertices;
|
|
} else {
|
|
op1.code = OMOP_VERT;
|
|
}
|
|
ExecutiveObjMolSeleOp(G, sele1, &op1);
|
|
VLASize(op1.vv1, float, op1.nvv1 * 3);
|
|
result = op1.vv1;
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
#ifndef _PYMOL_NOPY
|
|
/**
|
|
* @pre GIL
|
|
*/
|
|
PyObject* ExecutiveGetSettingOfType(
|
|
PyMOLGlobals* G, int index, const char* object, int state, int type)
|
|
{
|
|
assert(PyGILState_Check());
|
|
|
|
PyObject* result = nullptr;
|
|
pymol::CObject* obj = nullptr;
|
|
CSetting *set_ptr1 = nullptr, *set_ptr2 = nullptr;
|
|
pymol::copyable_ptr<CSetting>* handle = nullptr;
|
|
|
|
if (object && object[0]) {
|
|
obj = ExecutiveFindObjectByName(G, object);
|
|
if (!obj)
|
|
return PyErr_Format(P_CmdException, "object \"%s\" not found", object);
|
|
handle = obj->getSettingHandle(-1);
|
|
if (handle)
|
|
set_ptr1 = handle->get();
|
|
if (state >= 0) {
|
|
handle = obj->getSettingHandle(state);
|
|
if (handle)
|
|
set_ptr2 = handle->get();
|
|
else {
|
|
return PyErr_Format(
|
|
P_CmdException, "object \"%s\" lacks state %d", object, state + 1);
|
|
}
|
|
}
|
|
}
|
|
{
|
|
switch (type) {
|
|
case cSetting_boolean: {
|
|
int value = SettingGet_b(G, set_ptr2, set_ptr1, index);
|
|
result = PyBool_FromLong(value);
|
|
} break;
|
|
case cSetting_int: {
|
|
int value = SettingGet_i(G, set_ptr2, set_ptr1, index);
|
|
result = Py_BuildValue("i", value);
|
|
} break;
|
|
case cSetting_float: {
|
|
float value = SettingGet_f(G, set_ptr2, set_ptr1, index);
|
|
result = PyFloat_FromDouble(pymol::pretty_f2d(value));
|
|
} break;
|
|
case cSetting_float3: {
|
|
const float* value = SettingGet_3fv(G, set_ptr2, set_ptr1, index);
|
|
if (value) {
|
|
result = Py_BuildValue("fff", pymol::pretty_f2d(value[0]),
|
|
pymol::pretty_f2d(value[1]), pymol::pretty_f2d(value[2]));
|
|
} else {
|
|
PyErr_SetNone(PyExc_ValueError);
|
|
}
|
|
} break;
|
|
case cSetting_color: {
|
|
int value = SettingGet_color(G, set_ptr2, set_ptr1, index);
|
|
result = Py_BuildValue("i", value);
|
|
} break;
|
|
case cSetting_string: {
|
|
OrthoLineType buffer = "";
|
|
result = Py_BuildValue(
|
|
"s", SettingGetTextPtr(G, set_ptr2, set_ptr1, index, buffer));
|
|
} break;
|
|
case cSetting_tuple:
|
|
result = SettingGetTuple(G, set_ptr2, set_ptr1, index);
|
|
break;
|
|
default:
|
|
PyErr_Format(PyExc_ValueError, "invalid setting type %d", type);
|
|
break;
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
#endif
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveSetLastObjectEdited(PyMOLGlobals* G, pymol::CObject* o)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
I->LastEdited = o;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::CObject* ExecutiveGetLastObjectEdited(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
return (I->LastEdited);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveSaveUndo(PyMOLGlobals* G, const char* s1, int state)
|
|
{
|
|
int sele1;
|
|
ObjectMoleculeOpRec op1;
|
|
|
|
if (state < 0)
|
|
state = SceneGetState(G);
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
ObjectMoleculeOpRecInit(&op1);
|
|
op1.i2 = 0;
|
|
if (sele1 >= 0) {
|
|
op1.code = OMOP_SaveUndo;
|
|
op1.i1 = state;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op1);
|
|
}
|
|
return (op1.i2);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveSetTitle(
|
|
PyMOLGlobals* G, const char* name, int state, const char* text)
|
|
{
|
|
ObjectMolecule* obj;
|
|
obj = ExecutiveFindObjectMoleculeByName(G, name);
|
|
if (!obj) {
|
|
return pymol::make_error("Object ", name, " not found.");
|
|
} else {
|
|
auto res = ObjectMoleculeSetStateTitle(obj, state, text);
|
|
if (!res) {
|
|
return res;
|
|
}
|
|
}
|
|
SceneDirty(G);
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
const char* ExecutiveGetTitle(PyMOLGlobals* G, const char* name, int state)
|
|
{
|
|
ObjectMolecule* obj;
|
|
obj = ExecutiveFindObjectMoleculeByName(G, name);
|
|
if (!obj) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
"Error: object %s not found.\n", name ENDFB(G);
|
|
return nullptr;
|
|
}
|
|
return ObjectMoleculeGetStateTitle(obj, state);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveHideSelections(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecSelection) {
|
|
if (rec->visible) {
|
|
rec->visible = false;
|
|
SceneInvalidate(G);
|
|
SeqDirty(G);
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExecutiveInvalidateSelectionIndicators(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
ExecutiveInvalidateSelectionIndicatorsCGO(G);
|
|
I->selectorTextureSize = 0;
|
|
}
|
|
|
|
void ExecutiveInvalidateSelectionIndicatorsCGO(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
if (I) {
|
|
if (I->selIndicatorsCGO) {
|
|
CGOFree(I->selIndicatorsCGO);
|
|
I->selIndicatorsCGO = 0;
|
|
}
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
CGOFree(rec->gridSlotSelIndicatorsCGO);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ExecutiveRegenerateTextureForSelector(
|
|
PyMOLGlobals* G, int round_points, int* widths_arg)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
unsigned char* temp_buffer =
|
|
pymol::malloc<unsigned char>(widths_arg[0] * widths_arg[0] * 4);
|
|
int a, b;
|
|
float mid_point, disty, distx, dist, wminusd;
|
|
float widths[] = {
|
|
widths_arg[0] / 2.f, widths_arg[1] / 2.f, widths_arg[2] / 2.f};
|
|
unsigned char* q;
|
|
mid_point = ((widths_arg[0] - 1.f) / 2.f);
|
|
TextureInitTextTexture(G);
|
|
if (I->selectorTextureAllocatedSize < widths_arg[0]) {
|
|
// need to re-allocate a new part of the texture for the selection indicator
|
|
TextureGetPlacementForNewSubtexture(G, widths_arg[0], widths_arg[0],
|
|
&I->selectorTexturePosX, &I->selectorTexturePosY);
|
|
I->selectorTextureAllocatedSize = widths_arg[0];
|
|
}
|
|
q = temp_buffer;
|
|
if (round_points) {
|
|
for (b = 0; b < widths_arg[0]; b++) {
|
|
disty = fabs(mid_point - b);
|
|
for (a = 0; a < widths_arg[0]; a++) {
|
|
distx = fabs(mid_point - a);
|
|
dist = sqrt(distx * distx + disty * disty);
|
|
wminusd = widths[0] - dist;
|
|
if (dist < widths[2]) {
|
|
// white
|
|
q[0] = q[1] = q[2] = q[3] = 255;
|
|
} else if (dist < widths[1]) {
|
|
// black
|
|
q[0] = q[1] = q[2] = 0;
|
|
q[3] = 255;
|
|
} else if (fabs(wminusd) < .5f) {
|
|
// color plus transparency
|
|
q[0] = 255;
|
|
q[1] = 51;
|
|
q[2] = 153;
|
|
q[3] = (unsigned char) ((wminusd + .5) * 255);
|
|
} else if (dist < widths[0]) {
|
|
// color (pink by default 1.f, .2f, .6f)
|
|
q[0] = 255;
|
|
q[1] = 51;
|
|
q[2] = 153;
|
|
q[3] = 255;
|
|
} else {
|
|
// black
|
|
q[0] = q[1] = q[2] = q[3] = 0;
|
|
}
|
|
// printf("%3d ", q[1]);
|
|
q += 4;
|
|
}
|
|
// printf("\n");
|
|
}
|
|
} else {
|
|
for (b = 0; b < widths_arg[0]; b++) {
|
|
dist = disty = fabs(mid_point - b);
|
|
for (a = 0; a < widths_arg[0]; a++) {
|
|
distx = fabs(mid_point - a);
|
|
dist = disty;
|
|
if (distx > dist) {
|
|
dist = distx;
|
|
}
|
|
if (dist < widths[2]) {
|
|
// white
|
|
q[0] = q[1] = q[2] = q[3] = 255;
|
|
} else if (dist < widths[1]) {
|
|
// black
|
|
q[0] = q[1] = q[2] = 0;
|
|
q[3] = 255;
|
|
} else {
|
|
// color (pink by default 1.f, .2f, .6f)
|
|
q[0] = 255;
|
|
q[1] = 51;
|
|
q[2] = 153;
|
|
q[3] = 255;
|
|
}
|
|
// printf("%3d ", q[1]);
|
|
q += 4;
|
|
}
|
|
// printf("\n");
|
|
}
|
|
}
|
|
TextureFillNewSubtexture(G, widths_arg[0], widths_arg[0],
|
|
I->selectorTexturePosX, I->selectorTexturePosY, temp_buffer);
|
|
FreeP(temp_buffer);
|
|
}
|
|
|
|
/**
|
|
* Draws the Selection Indicator CGO
|
|
* @param selIndicatorsCGO CGO to draw
|
|
*/
|
|
static void ExecutiveRenderIndicatorCGOGLRaster(
|
|
PyMOLGlobals* G, CGO* selIndicatorsCGO)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CShaderPrg* shaderPrg;
|
|
float text_texture_dim = TextureGetTextTextureSize(G);
|
|
float textureScale;
|
|
int no_depth = (int) SettingGetGlobal_f(G, cSetting_selection_overlay);
|
|
shaderPrg = G->ShaderMgr->Enable_IndicatorShader();
|
|
if (!shaderPrg)
|
|
return;
|
|
glEnable(GL_POINT_SPRITE);
|
|
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
shaderPrg->SetLightingEnabled(0);
|
|
shaderPrg->SetAttrib4fLocation("a_Color", 1.f, 1.f, 1.f, 1.f);
|
|
shaderPrg->Set1f("g_pointSize", DIP2PIXEL(I->selectorTextureSize));
|
|
shaderPrg->Set2f("textureLookup", I->selectorTexturePosX / text_texture_dim,
|
|
I->selectorTexturePosY / text_texture_dim);
|
|
textureScale = I->selectorTextureSize / text_texture_dim;
|
|
shaderPrg->Set2f("textureScale", textureScale, textureScale);
|
|
int v[4];
|
|
glGetIntegerv(GL_VIEWPORT, v);
|
|
shaderPrg->Set4f("viewport", v[0], v[1], v[2], v[3]);
|
|
if (no_depth)
|
|
glDisable(GL_DEPTH_TEST);
|
|
CGORender(selIndicatorsCGO, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
if (no_depth)
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
glDisable(GL_POINT_SPRITE);
|
|
shaderPrg->Disable();
|
|
}
|
|
|
|
/**
|
|
* Draws the Selection Indicator CGO for a given pass
|
|
* @param rec SpecRec's whose selection color to use
|
|
* @param pass Pass number
|
|
* @param width Width of the selection indicator
|
|
* @param selIndicatorsCGO CGO to draw into
|
|
*/
|
|
static void ExecutiveSetupIndicatorPassMultipassImmediate(
|
|
PyMOLGlobals* G, SpecRec* rec, int pass, int width, CGO* cgo)
|
|
{
|
|
#ifndef PURE_OPENGL_ES_2
|
|
switch (pass) {
|
|
case 0:
|
|
if (rec->sele_color < 0)
|
|
CGOColor(cgo, 1.0F, 0.2F, 0.6F);
|
|
else
|
|
CGOColorv(cgo, ColorGet(G, rec->sele_color));
|
|
CGODotwidth(cgo, static_cast<float>(width));
|
|
break;
|
|
case 1:
|
|
if (width > 2) {
|
|
switch (width) {
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
CGODotwidth(cgo, 1.0F);
|
|
break;
|
|
case 4:
|
|
CGODotwidth(cgo, 2.0F);
|
|
break;
|
|
case 5:
|
|
CGODotwidth(cgo, 3.0F);
|
|
break;
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
CGODotwidth(cgo, 4.0F);
|
|
break;
|
|
default:
|
|
CGODotwidth(cgo, 6.0F);
|
|
break;
|
|
}
|
|
CGOColor(cgo, 0.0F, 0.0F, 0.0F);
|
|
break;
|
|
}
|
|
case 2:
|
|
if (width > 4) {
|
|
if (width > 5) {
|
|
CGODotwidth(cgo, 2.0F);
|
|
} else {
|
|
CGODotwidth(cgo, 1.0F);
|
|
}
|
|
CGOColor(cgo, 1.0F, 1.0F, 1.0F);
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
/**
|
|
* Gets the adjusted selection width
|
|
* @param round_points rounded indicator points
|
|
* @return adjusted selection width
|
|
*/
|
|
static int ExecutiveGetAdjustedSelectionWidth(
|
|
PyMOLGlobals* G, bool round_points)
|
|
{
|
|
int width;
|
|
float width_scale = SettingGet<float>(G, cSetting_selection_width_scale);
|
|
auto min_width =
|
|
static_cast<int>(SettingGet<float>(G, cSetting_selection_width));
|
|
auto max_width =
|
|
static_cast<int>(SettingGet<float>(G, cSetting_selection_width_max));
|
|
|
|
if (width_scale >= 0.0f) {
|
|
width = static_cast<int>(
|
|
width_scale * std::fabs(SettingGetGlobal_f(G, cSetting_stick_radius)) /
|
|
SceneGetScreenVertexScale(G, nullptr));
|
|
width = std::clamp(width, min_width, max_width);
|
|
}
|
|
if (round_points) {
|
|
width = static_cast<int>(width * 1.44f);
|
|
}
|
|
if (width > 6 && width & 0x1) { /* keep it even above 6 */
|
|
width--;
|
|
}
|
|
return width;
|
|
}
|
|
|
|
/**
|
|
* Gets the selection level widths
|
|
* @param width selection width
|
|
* @return level widths
|
|
*/
|
|
static glm::ivec3 ExecutiveGetSelectionLevelWidths(int width)
|
|
{
|
|
glm::ivec3 level_widths(width, 0, 0);
|
|
if (width > 2) {
|
|
switch (width) {
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
level_widths[1] = 1;
|
|
break;
|
|
case 4:
|
|
level_widths[1] = 2;
|
|
break;
|
|
case 5:
|
|
level_widths[1] = 3;
|
|
break;
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
level_widths[1] = 4;
|
|
break;
|
|
default:
|
|
level_widths[1] = 6;
|
|
break;
|
|
}
|
|
}
|
|
if (width > 4) {
|
|
if (width > 5) {
|
|
level_widths[2] = 2;
|
|
} else {
|
|
level_widths[2] = 1;
|
|
}
|
|
}
|
|
return level_widths;
|
|
}
|
|
|
|
/**
|
|
* Renders the selection indicators from the selected targets
|
|
* @param use_shader use shader
|
|
* @param width selection width
|
|
* @param curState current state
|
|
* @param slot grid slot
|
|
* @param grid grid info
|
|
*/
|
|
static void ExecutiveRenderSelectionsFromTargets(PyMOLGlobals* G,
|
|
bool use_shader, int width, int curState, int slot, GridInfo* grid)
|
|
{
|
|
auto I = G->Executive;
|
|
bool vis_only = SettingGet<bool>(G, cSetting_selection_visible_only);
|
|
|
|
for (auto& rec : pymol::make_list_adapter(I->Spec)) {
|
|
if (rec.type != cExecSelection) {
|
|
continue;
|
|
}
|
|
|
|
if (!rec.visible) {
|
|
continue;
|
|
}
|
|
int enabled = true;
|
|
auto* group_rec = rec.group;
|
|
while (enabled && group_rec) {
|
|
if (!group_rec->visible)
|
|
enabled = false;
|
|
else
|
|
group_rec = group_rec->group;
|
|
}
|
|
|
|
if (!enabled) {
|
|
continue;
|
|
}
|
|
|
|
int sele = SelectorIndexByName(G, rec.name); /* TODO: speed this up */
|
|
if (sele < 0) {
|
|
continue;
|
|
}
|
|
int totpass = use_shader ? 1 : 3;
|
|
|
|
if (use_shader) { // there is only one pass anyway
|
|
if (!slot) {
|
|
I->selIndicatorsCGO = CGONew(G);
|
|
CGODotwidth(I->selIndicatorsCGO, width);
|
|
CGOBegin(I->selIndicatorsCGO, GL_POINTS);
|
|
}
|
|
} else {
|
|
I->selIndicatorsCGO = CGONew(G);
|
|
}
|
|
|
|
for (int pass = 0; pass < totpass; pass++) {
|
|
if (!use_shader) {
|
|
ExecutiveSetupIndicatorPassMultipassImmediate(
|
|
G, &rec, pass, width, I->selIndicatorsCGO);
|
|
CGOBegin(I->selIndicatorsCGO, GL_POINTS);
|
|
}
|
|
for (auto& rec1 : pymol::make_list_adapter(I->Spec)) {
|
|
if (rec1.type != cExecObject) {
|
|
continue;
|
|
}
|
|
if (rec1.obj->type != cObjectMolecule) {
|
|
continue;
|
|
}
|
|
auto obj = static_cast<ObjectMolecule*>(rec1.obj);
|
|
if (SceneGetDrawFlagGrid(G, grid, rec1.obj->grid_slot)) {
|
|
if (!use_shader || !slot) {
|
|
ObjectMoleculeRenderSele(
|
|
obj, curState, sele, vis_only, *I->selIndicatorsCGO);
|
|
} else if (!rec1.gridSlotSelIndicatorsCGO) {
|
|
if (!use_shader) {
|
|
ObjectMoleculeRenderSele(
|
|
obj, curState, sele, vis_only, *I->selIndicatorsCGO);
|
|
} else {
|
|
auto gssCGO = std::make_unique<CGO>(G);
|
|
CGODotwidth(gssCGO.get(), width);
|
|
CGOBegin(gssCGO.get(), GL_POINTS);
|
|
ObjectMoleculeRenderSele(obj, curState, sele, vis_only, *gssCGO);
|
|
CGOEnd(gssCGO.get());
|
|
CGOStop(gssCGO.get());
|
|
CGO* optimized = CGOOptimizeToVBONotIndexedNoShader(gssCGO.get());
|
|
rec1.gridSlotSelIndicatorsCGO = optimized;
|
|
assert(rec1.gridSlotSelIndicatorsCGO);
|
|
ExecutiveRenderIndicatorCGOGLRaster(
|
|
G, rec1.gridSlotSelIndicatorsCGO);
|
|
}
|
|
}
|
|
} else {
|
|
CGOFree(rec1.gridSlotSelIndicatorsCGO);
|
|
}
|
|
}
|
|
if (!use_shader) {
|
|
CGOEnd(I->selIndicatorsCGO);
|
|
}
|
|
} // for pass
|
|
if (use_shader) {
|
|
if (!slot) {
|
|
CGOEnd(I->selIndicatorsCGO);
|
|
CGOStop(I->selIndicatorsCGO);
|
|
CGO* optimized =
|
|
CGOOptimizeToVBONotIndexedNoShader(I->selIndicatorsCGO);
|
|
CGOFree(I->selIndicatorsCGO);
|
|
if (I->selIndicatorsCGO = optimized) {
|
|
assert(I->selIndicatorsCGO->use_shader);
|
|
ExecutiveRenderSelections(G, curState, slot, grid);
|
|
}
|
|
return;
|
|
}
|
|
} else {
|
|
CGOStop(I->selIndicatorsCGO);
|
|
ExecutiveRenderSelections(G, curState, slot, grid);
|
|
}
|
|
} // for rec
|
|
}
|
|
|
|
// TODO: This should be incorporated into a CGO or GL Pipeline object
|
|
class ExecutiveRenderSelectionSetTopLevelGLPipeline
|
|
{
|
|
public:
|
|
ExecutiveRenderSelectionSetTopLevelGLPipeline(
|
|
bool round_points, bool no_depth, bool fog)
|
|
: m_round_points{round_points}
|
|
, m_no_depth{no_depth}
|
|
, m_fog{fog}
|
|
{
|
|
#ifndef PURE_OPENGL_ES_2
|
|
// All of this state should probably be restored at the end.
|
|
if (m_round_points) {
|
|
glGetIntegerv(GL_ALPHA_TEST_FUNC, &alpha_func_before);
|
|
glGetFloatv(GL_ALPHA_TEST_REF, &alpha_ref_before);
|
|
glEnable(GL_POINT_SMOOTH);
|
|
glAlphaFunc(GL_GREATER, 0.5F);
|
|
glEnable(GL_ALPHA_TEST);
|
|
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
|
} else {
|
|
glDisable(GL_POINT_SMOOTH);
|
|
glDisable(GL_ALPHA_TEST);
|
|
glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
|
|
}
|
|
#endif
|
|
if (no_depth)
|
|
glDisable(GL_DEPTH_TEST);
|
|
#ifndef PURE_OPENGL_ES_2
|
|
glDisable(GL_FOG);
|
|
#endif
|
|
}
|
|
~ExecutiveRenderSelectionSetTopLevelGLPipeline()
|
|
{
|
|
#ifndef PURE_OPENGL_ES_2
|
|
if (m_fog)
|
|
glEnable(GL_FOG);
|
|
#endif
|
|
if (m_no_depth)
|
|
glEnable(GL_DEPTH_TEST);
|
|
#ifndef PURE_OPENGL_ES_2
|
|
if (m_round_points) {
|
|
glAlphaFunc(alpha_func_before, alpha_ref_before);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
bool m_round_points;
|
|
bool m_no_depth;
|
|
bool m_fog;
|
|
GLint alpha_func_before;
|
|
GLfloat alpha_ref_before;
|
|
};
|
|
|
|
void ExecutiveRenderSelections(
|
|
PyMOLGlobals* G, int curState, int slot, GridInfo* grid)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
bool use_shader = SettingGet<bool>(G, cSetting_use_shaders);
|
|
bool round_points = SettingGet<bool>(G, cSetting_selection_round_points);
|
|
short inv_indicators = false;
|
|
|
|
auto width = ExecutiveGetAdjustedSelectionWidth(G, round_points);
|
|
|
|
if (I->selectorTextureSize != width || round_points != I->selectorIsRound) {
|
|
inv_indicators = true;
|
|
} else {
|
|
if (use_shader) {
|
|
if (slot) {
|
|
for (auto& rec : pymol::make_list_adapter(I->Spec)) {
|
|
if (rec.type == cExecObject) {
|
|
if (SceneGetDrawFlagGrid(G, grid, rec.obj->grid_slot)) {
|
|
if (rec.gridSlotSelIndicatorsCGO) {
|
|
ExecutiveRenderIndicatorCGOGLRaster(
|
|
G, rec.gridSlotSelIndicatorsCGO);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (I->selIndicatorsCGO) {
|
|
ExecutiveRenderIndicatorCGOGLRaster(G, I->selIndicatorsCGO);
|
|
return;
|
|
}
|
|
} else {
|
|
if (I->selIndicatorsCGO) {
|
|
std::optional<ExecutiveRenderSelectionSetTopLevelGLPipeline>
|
|
immediatePipeline;
|
|
auto no_depth =
|
|
static_cast<int>(SettingGet<float>(G, cSetting_selection_overlay));
|
|
[[maybe_unused]] bool fog = SettingGet<bool>(G, cSetting_depth_cue) &&
|
|
SettingGet<float>(G, cSetting_fog);
|
|
immediatePipeline.emplace(round_points, no_depth, fog);
|
|
CGORender(
|
|
I->selIndicatorsCGO, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (inv_indicators) {
|
|
CGOFree(I->selIndicatorsCGO);
|
|
if (slot) {
|
|
for (auto& rec : pymol::make_list_adapter(I->Spec)) {
|
|
if (rec.type == cExecObject) {
|
|
if (SceneGetDrawFlagGrid(G, grid, rec.obj->grid_slot)) {
|
|
CGOFree(rec.gridSlotSelIndicatorsCGO);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto list_adaptor = pymol::make_list_adapter(I->Spec);
|
|
bool any_active = std::any_of(
|
|
std::begin(list_adaptor), std::end(list_adaptor), [](const auto& rec) {
|
|
return rec.type == cExecSelection && rec.visible;
|
|
});
|
|
if (!any_active) {
|
|
return;
|
|
}
|
|
|
|
if (use_shader && inv_indicators) {
|
|
auto level_widths = ExecutiveGetSelectionLevelWidths(width);
|
|
ExecutiveInvalidateSelectionIndicatorsCGO(G);
|
|
ExecutiveRegenerateTextureForSelector(
|
|
G, round_points, glm::value_ptr(level_widths));
|
|
}
|
|
I->selectorTextureSize = width;
|
|
I->selectorIsRound = round_points;
|
|
|
|
ExecutiveRenderSelectionsFromTargets(
|
|
G, use_shader, width, curState, slot, grid);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
/**
|
|
* Macro with error handling to get a single atom vertex and associated temp
|
|
* selection. Assigns `vertexN`, `tmpseleN`, and `resultN`
|
|
*/
|
|
#define SETUP_SINGLE_ATOM_VERTEX(N) \
|
|
auto tmpsele##N = SelectorTmp::make(G, s##N); \
|
|
p_return_if_error_prefixed(tmpsele##N, "Selection " #N ": "); \
|
|
auto result##N = \
|
|
SelectorGetSingleAtomVertex(G, (tmpsele##N)->getIndex(), state); \
|
|
p_return_if_error_prefixed(result##N, "Selection " #N ": "); \
|
|
const float* vertex##N = (result##N).result().data()
|
|
|
|
/**
|
|
* Get the distance between two atoms.
|
|
* Implementation of `cmd.get_distance()`
|
|
*
|
|
* @param s0 single-atom selection expression
|
|
* @param s1 single-atom selection expression
|
|
* @param state object state
|
|
* @return distance in Angstrom
|
|
*/
|
|
pymol::Result<float> ExecutiveGetDistance(
|
|
PyMOLGlobals* G, const char* s1, const char* s2, int state)
|
|
{
|
|
SETUP_SINGLE_ATOM_VERTEX(1);
|
|
SETUP_SINGLE_ATOM_VERTEX(2);
|
|
return static_cast<float>(diff3f(vertex1, vertex2));
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Get the angle between tree atoms s0-s1-s2.
|
|
* Implementation of `cmd.get_angle()`
|
|
*
|
|
* @param s0 single-atom selection expression
|
|
* @param s1 single-atom selection expression
|
|
* @param s2 single-atom selection expression
|
|
* @param state object state
|
|
* @return angle in degrees
|
|
*/
|
|
pymol::Result<float> ExecutiveGetAngle(
|
|
PyMOLGlobals* G, const char* s1, const char* s2, const char* s3, int state)
|
|
{
|
|
SETUP_SINGLE_ATOM_VERTEX(1);
|
|
SETUP_SINGLE_ATOM_VERTEX(2);
|
|
SETUP_SINGLE_ATOM_VERTEX(3);
|
|
|
|
float d1[3], d2[3];
|
|
subtract3f(vertex1, vertex2, d1);
|
|
subtract3f(vertex3, vertex2, d2);
|
|
return rad_to_deg(get_angle3f(d1, d2));
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Get the dihedral angle between four atoms.
|
|
* Implementation of `cmd.get_dihedral()`
|
|
*
|
|
* @param s0 single-atom selection expression
|
|
* @param s1 single-atom selection expression
|
|
* @param s2 single-atom selection expression
|
|
* @param s3 single-atom selection expression
|
|
* @param state object state
|
|
* @return dihedral angle in degrees
|
|
*/
|
|
pymol::Result<float> ExecutiveGetDihe(PyMOLGlobals* G, const char* s1,
|
|
const char* s2, const char* s3, const char* s4, int state)
|
|
{
|
|
SETUP_SINGLE_ATOM_VERTEX(1);
|
|
SETUP_SINGLE_ATOM_VERTEX(2);
|
|
SETUP_SINGLE_ATOM_VERTEX(3);
|
|
SETUP_SINGLE_ATOM_VERTEX(4);
|
|
return rad_to_deg(get_dihedral3f(vertex1, vertex2, vertex3, vertex4));
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveSetDihe(PyMOLGlobals* G, const char* s1,
|
|
const char* s2, const char* s3, const char* s4, float value, int state,
|
|
int quiet)
|
|
{
|
|
SETUP_SINGLE_ATOM_VERTEX(1);
|
|
SETUP_SINGLE_ATOM_VERTEX(2);
|
|
SETUP_SINGLE_ATOM_VERTEX(3);
|
|
SETUP_SINGLE_ATOM_VERTEX(4);
|
|
float current =
|
|
rad_to_deg(get_dihedral3f(vertex1, vertex2, vertex3, vertex4));
|
|
float change = value - current;
|
|
auto save_state = SceneGetState(G);
|
|
SceneSetFrame(G, -1, state); /* KLUDGE ALERT!
|
|
* necessary because the editor
|
|
* can only work on the current state...this
|
|
* needs to be changed.*/
|
|
EditorSelect(
|
|
G, tmpsele3->getName(), tmpsele2->getName(), "", "", false, true, true);
|
|
EditorTorsion(G, change);
|
|
SceneSetFrame(G, -1, save_state);
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Editor, FB_Actions)
|
|
" SetDihedral: adjusted to %5.3f\n", value ENDFB(G);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<float> ExecutiveGetArea(
|
|
PyMOLGlobals* G, const char* sele, int state, bool load_b)
|
|
{
|
|
SETUP_SELE(sele, tmpsele0, sele0);
|
|
|
|
auto obj0 = SelectorGetSingleObjectMolecule(G, sele0);
|
|
if (!obj0) {
|
|
if (SelectorCountAtoms(G, sele0, state) > 0)
|
|
return pymol::make_error("Selection must be within a single object");
|
|
return 0.f;
|
|
}
|
|
|
|
auto cs = obj0->getCoordSet(state);
|
|
if (!cs)
|
|
return pymol::make_error("Invalid state");
|
|
|
|
auto rep = (RepDot*) RepDotDoNew(cs, cRepDotAreaType, state);
|
|
if (!rep)
|
|
return pymol::make_error("Can't get dot representation.");
|
|
|
|
if (load_b) {
|
|
/* zero out B-values within selection */
|
|
ObjectMoleculeOpRec op;
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_SetB;
|
|
op.f1 = 0.0;
|
|
op.i1 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele0, &op);
|
|
}
|
|
|
|
float const* const area = rep->A;
|
|
int const* const ati = rep->Atom;
|
|
|
|
AtomInfoType* ai = nullptr;
|
|
int known_member = -1;
|
|
bool is_member = false;
|
|
float result = 0.f;
|
|
|
|
for (int a = 0; a < rep->N; ++a) {
|
|
if (known_member != ati[a]) {
|
|
known_member = ati[a];
|
|
ai = obj0->AtomInfo + known_member;
|
|
is_member = SelectorIsMember(G, ai->selEntry, sele0);
|
|
}
|
|
|
|
if (is_member) {
|
|
result += area[a];
|
|
if (load_b)
|
|
ai->b += area[a];
|
|
}
|
|
}
|
|
|
|
delete rep;
|
|
return result;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Implementation of `cmd.get_names()`
|
|
*
|
|
* @param mode see modules/pymol/querying.py
|
|
* @param enabled_only only include enabled names
|
|
* @param s0 atom selection expression, limits results to molecular objects and
|
|
* named selections which have at least one atom in the selection.
|
|
*/
|
|
pymol::Result<std::vector<const char*>> ExecutiveGetNames(
|
|
PyMOLGlobals* G, int mode, int enabled_only, const char* s0)
|
|
{
|
|
enum {
|
|
cGetNames_all = 0,
|
|
cGetNames_objects = 1,
|
|
cGetNames_selections = 2,
|
|
cGetNames_public = 3,
|
|
cGetNames_public_objects = 4,
|
|
cGetNames_public_selections = 5,
|
|
cGetNames_public_nongroup_objects = 6,
|
|
cGetNames_public_group_objects = 7,
|
|
cGetNames_nongroup_objects = 8,
|
|
cGetNames_group_objects = 9,
|
|
};
|
|
|
|
bool include_objects = //
|
|
mode == cGetNames_all || mode == cGetNames_objects ||
|
|
mode == cGetNames_public || mode == cGetNames_public_objects;
|
|
bool include_selections =
|
|
mode == cGetNames_all || mode == cGetNames_selections ||
|
|
mode == cGetNames_public || mode == cGetNames_public_selections;
|
|
bool include_group_objects =
|
|
mode == cGetNames_group_objects || mode == cGetNames_public_group_objects;
|
|
bool include_nongroup_objects = //
|
|
mode == cGetNames_nongroup_objects ||
|
|
mode == cGetNames_public_nongroup_objects;
|
|
bool public_only =
|
|
mode >= cGetNames_public && mode <= cGetNames_public_group_objects;
|
|
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
std::vector<const char*> result;
|
|
pymol::Result<SelectorTmp> tmpsele0;
|
|
SelectorID_t sele0 = cSelectionInvalid;
|
|
int incl_flag;
|
|
if (s0[0]) {
|
|
tmpsele0 = SelectorTmp::make(G, s0);
|
|
p_return_if_error(tmpsele0);
|
|
sele0 = tmpsele0->getIndex();
|
|
assert(sele0 != cSelectionInvalid);
|
|
}
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
incl_flag = 0;
|
|
if ((rec->type == cExecObject &&
|
|
(include_objects ||
|
|
(rec->obj->type != cObjectGroup && include_nongroup_objects) ||
|
|
(rec->obj->type == cObjectGroup && include_group_objects))) ||
|
|
(rec->type == cExecSelection && include_selections)) {
|
|
if (!public_only || rec->name[0] != '_') {
|
|
if ((!enabled_only) || (rec->visible)) {
|
|
incl_flag = 0;
|
|
if (sele0 < 0)
|
|
incl_flag = 1;
|
|
else
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
int a;
|
|
ObjectMolecule* obj_mol = (ObjectMolecule*) rec->obj;
|
|
const AtomInfoType* ai = obj_mol->AtomInfo.data();
|
|
for (a = 0; a < obj_mol->NAtom; a++) {
|
|
if (SelectorIsMember(G, ai->selEntry, sele0)) {
|
|
incl_flag = 1;
|
|
break;
|
|
}
|
|
ai++;
|
|
}
|
|
}
|
|
break;
|
|
case cExecSelection:
|
|
if (SelectorCheckIntersection(
|
|
G, sele0, SelectorIndexByName(G, rec->name))) {
|
|
incl_flag = 1;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (incl_flag) {
|
|
result.push_back(rec->name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Return true if `name` is the name of an object molecule or a named selection
|
|
*/
|
|
bool ExecutiveIsMoleculeOrSelection(PyMOLGlobals* G, const char* name)
|
|
{
|
|
if (!strcmp(name, cKeywordAll))
|
|
return true;
|
|
|
|
SpecRec* rec = ExecutiveFindSpec(G, name);
|
|
if (rec && ((rec->type == cExecObject && rec->obj->type == cObjectMolecule) ||
|
|
(rec->type == cExecSelection)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* @param name Name of the object or selection
|
|
* @return Type label like "object:molecule" or "selection"
|
|
*/
|
|
pymol::Result<char const*> ExecutiveGetType(PyMOLGlobals* G, const char* name)
|
|
{
|
|
auto rec = ExecutiveFindSpec(G, name);
|
|
|
|
if (!rec) {
|
|
return pymol::make_error("object not found");
|
|
}
|
|
|
|
if (rec->type == cExecSelection) {
|
|
return "selection";
|
|
}
|
|
|
|
if (rec->type != cExecObject) {
|
|
return "";
|
|
}
|
|
|
|
switch (rec->obj->type) {
|
|
case cObjectMolecule:
|
|
return "object:molecule";
|
|
case cObjectMap:
|
|
return "object:map";
|
|
case cObjectMesh:
|
|
return "object:mesh";
|
|
case cObjectSlice:
|
|
return "object:slice";
|
|
case cObjectSurface:
|
|
return "object:surface";
|
|
case cObjectMeasurement:
|
|
return "object:measurement";
|
|
case cObjectCGO:
|
|
return "object:cgo";
|
|
case cObjectGroup:
|
|
return "object:group";
|
|
case cObjectVolume:
|
|
return "object:volume";
|
|
case cObjectAlignment:
|
|
return "object:alignment";
|
|
case cObjectGadget:
|
|
return "object:ramp";
|
|
default:
|
|
return "object:";
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveUpdateCmd(PyMOLGlobals* G, const char* s0,
|
|
const char* s1, int sta0, int sta1, int method, int quiet)
|
|
{
|
|
SETUP_SELE_DEFAULT_PREFIXED(0, cSelectionInvalid);
|
|
SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
|
|
|
|
return SelectorUpdateCmd(G, sele0, sele1, sta0, sta1, method, quiet);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveRenameObjectAtoms(
|
|
PyMOLGlobals* G, const char* selection, int force, int quiet)
|
|
{
|
|
SETUP_SELE(selection, tmpsele1, sele);
|
|
|
|
{
|
|
ObjectMoleculeOpRec op;
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_RenameAtoms;
|
|
op.i1 = 0;
|
|
op.i2 = force;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Rename: renamed %d atoms.\n", op.i1 ENDFB(G);
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveFuse(PyMOLGlobals* G, const char* s0, const char* s1,
|
|
int mode, int recolor, int move_flag)
|
|
{
|
|
int i0 = -1;
|
|
int i1 = -1;
|
|
SelectorID_t sele2 = cSelectionInvalid;
|
|
ObjectMolecule *obj0, *obj1;
|
|
ObjectMoleculeOpRec op;
|
|
#define tmp_fuse_sele "tmp_fuse_sele"
|
|
|
|
SETUP_SELE_DEFAULT_PREFIXED(0, cSelectionInvalid);
|
|
SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
|
|
|
|
{
|
|
{
|
|
EditorInactivate(G);
|
|
obj0 = SelectorGetSingleObjectMolecule(G, sele0);
|
|
obj1 = SelectorGetSingleObjectMolecule(G, sele1);
|
|
if (obj0)
|
|
i0 = ObjectMoleculeGetAtomIndex(obj0, sele0);
|
|
if (obj1)
|
|
i1 = ObjectMoleculeGetAtomIndex(obj1, sele1);
|
|
if (obj0 && obj1 && (i0 >= 0) && (i1 >= 0) && (obj0 != obj1)) {
|
|
ObjectMoleculeVerifyChemistry(obj0, -1);
|
|
ObjectMoleculeVerifyChemistry(obj1, -1);
|
|
|
|
SelectorCreate(G, tmp_fuse_sele, nullptr, obj0, 1, nullptr);
|
|
sele2 = SelectorIndexByName(G, tmp_fuse_sele);
|
|
if (mode) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_PrepareFromTemplate;
|
|
op.ai = obj1->AtomInfo + i1;
|
|
op.i1 = mode;
|
|
op.i2 = 0;
|
|
op.i3 = recolor;
|
|
if (recolor)
|
|
op.i4 = obj1->Color;
|
|
ExecutiveObjMolSeleOp(G, sele2, &op);
|
|
}
|
|
SelectorDelete(G, tmp_fuse_sele);
|
|
|
|
p_return_if_error(
|
|
ObjectMoleculeFuse(obj1, i1, obj0, i0, mode != 3, move_flag));
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveSpheroid(
|
|
PyMOLGlobals* G, const char* name, int average)
|
|
{ /* EXPERIMENTAL */
|
|
CExecutive* I = G->Executive;
|
|
pymol::CObject* os = nullptr;
|
|
|
|
if (strlen(name)) {
|
|
os = ExecutiveFindObjectByName(G, name);
|
|
if (!os)
|
|
return pymol::make_error("Object not found.");
|
|
else if (os->type != cObjectMolecule) {
|
|
return pymol::make_error("Bad object type.");
|
|
}
|
|
}
|
|
|
|
if (os || (!strlen(name))) { /* sort one or all */
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject)
|
|
if (rec->obj->type == cObjectMolecule)
|
|
if ((!os) || (rec->obj == os)) {
|
|
auto obj = (ObjectMolecule*) rec->obj;
|
|
ObjectMoleculeCreateSpheroid(obj, average);
|
|
obj->invalidate(cRepAll, cRepInvRep, -1);
|
|
}
|
|
}
|
|
SceneChanged(G);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveRebuildAll(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveRebuildAll: entered.\n" ENDFD;
|
|
auto defer_builds_mode = SettingGet<bool>(G, cSetting_defer_builds_mode);
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
auto level = cRepInvAll;
|
|
switch (rec->obj->type) {
|
|
case cObjectMeasurement:
|
|
ObjectDistInvalidateRep((ObjectDist*) rec->obj, cRepAll);
|
|
break;
|
|
case cObjectMolecule:
|
|
level = defer_builds_mode ? cRepInvPurge : cRepInvRep;
|
|
case cObjectSurface:
|
|
case cObjectMesh:
|
|
case cObjectSlice:
|
|
case cObjectAlignment:
|
|
case cObjectCGO:
|
|
rec->obj->invalidate(cRepAll, level, -1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
SeqChanged(G);
|
|
SceneChanged(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveRebuildAllObjectDist(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectMeasurement) {
|
|
ObjectDistInvalidateRep((ObjectDist*) rec->obj, cRepAll);
|
|
}
|
|
}
|
|
}
|
|
SceneInvalidate(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveUndo(PyMOLGlobals* G, int dir)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
ObjectMolecule *obj = nullptr, *compObj;
|
|
SpecRec* rec = nullptr;
|
|
|
|
auto o = ExecutiveGetLastObjectEdited(G);
|
|
PRINTFB(G, FB_Executive, FB_Debugging)
|
|
" ExecutiveUndo: last object %p\n", (void*) o ENDFB(G);
|
|
if (o)
|
|
if (o->type == cObjectMolecule)
|
|
obj = (ObjectMolecule*) o;
|
|
/* make sure this is still a real object */
|
|
if (obj) {
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject)
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
compObj = (ObjectMolecule*) rec->obj;
|
|
if (obj == compObj) {
|
|
ObjectMoleculeUndo(obj, dir);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveSort(PyMOLGlobals* G, const char* name)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
ObjectMolecule* obj;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMoleculeOpRec op;
|
|
int sele;
|
|
int ok = true;
|
|
if ((!name) || (!name[0]))
|
|
name = cKeywordAll;
|
|
|
|
{
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
int changed = false;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if ((rec->type == cExecObject) &&
|
|
(rec->obj->type == cObjectMolecule)) {
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
if (ok)
|
|
ok &= ObjectMoleculeSort(obj);
|
|
if (ok) {
|
|
changed = true;
|
|
sele = SelectorIndexByName(G, rec->name);
|
|
if (sele >= 0) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_INVA;
|
|
op.i1 = cRepCartoonBit | cRepRibbonBit;
|
|
op.i2 = cRepInvRep;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecSelection:
|
|
sele = SelectorIndexByName(G, rec->name);
|
|
if (sele >= 0) {
|
|
op.code = OMOP_Sort;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_INVA;
|
|
op.i1 = cRepCartoonBit | cRepRibbonBit;
|
|
op.i2 = cRepInvRep;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
ObjectMoleculeOpRecInit(&op);
|
|
}
|
|
break;
|
|
case cExecObject:
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
if (ok)
|
|
ok &= ObjectMoleculeSort(obj);
|
|
changed = true;
|
|
sele = SelectorIndexByName(G, rec->name);
|
|
if (sele >= 0) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_INVA;
|
|
op.i1 = cRepCartoonBit | cRepRibbonBit;
|
|
op.i2 = cRepInvRep;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
if (changed)
|
|
SceneChanged(G);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveRemoveAtoms(PyMOLGlobals* G, const char* s1, int quiet)
|
|
{
|
|
SETUP_SELE_DEFAULT(1);
|
|
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
ObjectMoleculeOpRec op;
|
|
|
|
{
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_Remove;
|
|
op.i1 = 0;
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
ObjectMoleculeVerifyChemistry(
|
|
obj, -1); /* remember chemistry for later */
|
|
ObjectMoleculeSeleOp(obj, sele1, &op);
|
|
if (op.i1) {
|
|
if (!quiet) {
|
|
PRINTFD(G, FB_Editor)
|
|
" ExecutiveRemove-Debug: purging %i of %i atoms in %s\n", op.i1,
|
|
obj->NAtom, obj->Name ENDFD;
|
|
}
|
|
ObjectMoleculePurge(obj);
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Editor, FB_Actions)
|
|
" Remove: eliminated %d atoms in model \"%s\".\n", op.i1,
|
|
obj->Name ENDFB(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EditorRemoveStale(G);
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveAddHydrogens(
|
|
PyMOLGlobals* G, const char* s1, int quiet, int state, bool legacy)
|
|
{
|
|
ObjectMoleculeOpRec op;
|
|
|
|
if (legacy) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
" %s-Warning: legacy mode was removed\n", __FUNCTION__ ENDFB(G);
|
|
}
|
|
|
|
SETUP_SELE_DEFAULT(1);
|
|
|
|
{
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_AddHydrogens;
|
|
op.i1 = state;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveFixHydrogens(PyMOLGlobals* G, const char* s1, int quiet)
|
|
{
|
|
int sele1;
|
|
ObjectMoleculeOpRec op;
|
|
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
if (sele1 >= 0) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_FixHydrogens;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveFlag(
|
|
PyMOLGlobals* G, int flag, const char* sele, int action, int quiet)
|
|
{
|
|
enum {
|
|
cFlagActionReset = 0,
|
|
cFlagActionSet = 1,
|
|
cFlagActionClear = 2,
|
|
};
|
|
|
|
if (flag < 0 || flag > 31) {
|
|
return pymol::make_error("flag ", flag, " out of range [0, 31]");
|
|
}
|
|
|
|
ObjectMoleculeOpRec op;
|
|
ObjectMoleculeOpRecInit(&op);
|
|
switch (action) {
|
|
case cFlagActionReset:
|
|
op.code = OMOP_Flag;
|
|
break;
|
|
case cFlagActionSet:
|
|
op.code = OMOP_FlagSet;
|
|
break;
|
|
case cFlagActionClear:
|
|
op.code = OMOP_FlagClear;
|
|
break;
|
|
default:
|
|
return pymol::make_error("invalid action ", action);
|
|
}
|
|
|
|
op.i1 = 1u << flag;
|
|
op.i2 = ~op.i1;
|
|
op.i3 = 0;
|
|
op.i4 = 0;
|
|
|
|
if ((op.i1 & cAtomFlag_exfoliate) && action != cFlagActionClear) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"The 'exfoliate' flag is deprecated. "
|
|
"Use 'hide surface, (%s)' instead.\n",
|
|
sele ENDFB(G);
|
|
}
|
|
|
|
SETUP_SELE(sele, s1, sele1);
|
|
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
if (Feedback(G, FB_Executive, FB_Actions)) {
|
|
if (!quiet) {
|
|
switch (action) {
|
|
case cFlagActionReset:
|
|
if (op.i3) {
|
|
PRINTF " Flag: flag %d is set in %d of %d atoms.\n", flag, op.i3,
|
|
op.i4 ENDF(G);
|
|
} else {
|
|
PRINTF " Flag: flag %d cleared on all atoms.\n", flag ENDF(G);
|
|
}
|
|
break;
|
|
case cFlagActionSet:
|
|
PRINTF " Flag: flag %d set on %d atoms.\n", flag, op.i3 ENDF(G);
|
|
break;
|
|
case cFlagActionClear:
|
|
PRINTF " Flag: flag %d cleared on %d atoms.\n", flag, op.i3 ENDF(G);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (SettingGetGlobal_b(G, cSetting_auto_indicate_flags)) {
|
|
auto buffer = pymol::string_format("(flag %d)", flag);
|
|
SelectorCreate(G, cIndicateSele, buffer.c_str(), nullptr, true, nullptr);
|
|
ExecutiveSetObjVisib(G, cIndicateSele, true, false);
|
|
SceneInvalidate(G);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
float ExecutiveOverlap(PyMOLGlobals* G, const char* s1, int state1,
|
|
const char* s2, int state2, float adjust)
|
|
{
|
|
SelectorTmp tmpsele1(G, s1);
|
|
SelectorTmp tmpsele2(G, s2);
|
|
int sele1 = tmpsele1.getIndex();
|
|
int sele2 = tmpsele2.getIndex();
|
|
|
|
float result = 0.0;
|
|
|
|
if (state1 < 0)
|
|
state1 = 0;
|
|
if (state2 < 0)
|
|
state2 = 0;
|
|
|
|
if ((sele1 >= 0) && (sele2 >= 0))
|
|
result = SelectorSumVDWOverlap(G, sele1, state1, sele2, state2, adjust);
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveProtect(
|
|
PyMOLGlobals* G, const char* s1, int mode, int quiet)
|
|
{
|
|
ObjectMoleculeOpRec op;
|
|
|
|
SETUP_SELE_DEFAULT(1);
|
|
|
|
{
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_Protect;
|
|
op.i1 = mode;
|
|
op.i2 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
if (!quiet) {
|
|
if (Feedback(G, FB_Executive, FB_Actions)) {
|
|
if (op.i2) {
|
|
if (mode) {
|
|
PRINTF " Protect: %d atoms protected from movement.\n",
|
|
op.i2 ENDF(G);
|
|
} else {
|
|
PRINTF " Protect: %d atoms deprotected.\n", op.i2 ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveMask(
|
|
PyMOLGlobals* G, const char* s1, int mode, int quiet)
|
|
{
|
|
ObjectMoleculeOpRec op;
|
|
|
|
SETUP_SELE_DEFAULT(1);
|
|
|
|
{
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_Mask;
|
|
op.i1 = mode;
|
|
op.i2 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
if (!quiet) {
|
|
if (Feedback(G, FB_Executive, FB_Actions)) {
|
|
if (op.i2) {
|
|
if (mode) {
|
|
PRINTF " Mask: %d atoms masked (cannot be picked or selected).\n",
|
|
op.i2 ENDF(G);
|
|
} else {
|
|
PRINTF " Mask: %d atoms unmasked.\n", op.i2 ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
op.code = OMOP_INVA; /* need to invalidate all pickable representations */
|
|
op.i1 = cRepsAtomMask;
|
|
op.i2 = cRepInvPick;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* flag > 0: Set stereo_mode and turn stereo on
|
|
* flag = 0: Turn off stereo
|
|
* flag = -1: Swap eyes (stereo_shift *= -1)
|
|
* flag = -2: Turn on stereo with current stereo_mode
|
|
* flag = -3: Turn on chromadepth and turn off stereo
|
|
*/
|
|
pymol::Result<> ExecutiveStereo(PyMOLGlobals* G, int flag)
|
|
{
|
|
switch (flag) {
|
|
case -3:
|
|
SettingSet(G, cSetting_chromadepth, 1);
|
|
SceneSetStereo(G, 0);
|
|
break;
|
|
case -1:
|
|
SettingSetGlobal_f(G, cSetting_stereo_shift,
|
|
-SettingGetGlobal_f(G, cSetting_stereo_shift));
|
|
break;
|
|
default:
|
|
SettingSet(G, cSetting_chromadepth, 0);
|
|
|
|
if (flag == cStereo_quadbuffer && !G->StereoCapable) {
|
|
return pymol::make_error(
|
|
"no 'quadbuffer' support detected (force with 'pymol -S')");
|
|
}
|
|
|
|
if (flag == cStereo_openvr) {
|
|
#ifdef _PYMOL_OPENVR
|
|
OpenVRInit(G);
|
|
OpenVRFeedback(G);
|
|
#else
|
|
return pymol::make_error(
|
|
"'openvr' stereo mode not available in this build");
|
|
#endif
|
|
}
|
|
|
|
if (flag > 0) {
|
|
SettingSet(G, cSetting_stereo_mode, flag);
|
|
}
|
|
|
|
SceneSetStereo(G, flag != 0);
|
|
}
|
|
|
|
// for chromadepth
|
|
G->ShaderMgr->Set_Reload_Bits(RELOAD_VARIABLES);
|
|
|
|
SceneDirty(G);
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveRevalence(PyMOLGlobals* G, const char* s1,
|
|
const char* s2, const char* src, int target_state, int source_state,
|
|
int reset, int quiet)
|
|
{
|
|
SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
|
|
SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
|
|
|
|
{
|
|
if (src && src[0]) {
|
|
SETUP_SELE(src, tmpsele3, sele3);
|
|
|
|
{
|
|
ObjectMolecule* obj3 = SelectorGetSingleObjectMolecule(G, sele3);
|
|
if (!obj3) {
|
|
return pymol::make_error(
|
|
"Revalence can only source a single object at a time.");
|
|
} else {
|
|
ObjectMoleculeOpRec op;
|
|
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_RevalenceFromSource;
|
|
op.i1 = sele1;
|
|
op.i2 = sele2;
|
|
op.i3 = target_state;
|
|
op.obj3 = obj3;
|
|
op.i4 = sele3;
|
|
op.i5 = source_state;
|
|
op.i6 = quiet;
|
|
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
|
|
/*
|
|
if(ObjectMoleculeXferValences(obj1,sele1,sele2,target_state,obj3,sele3,source_state,quiet))
|
|
{ ObjectMoleculeVerifyChemistry(obj1,target_state);
|
|
ObjectMoleculeInvalidate(obj1,cRepAll,cRepInvBonds,target_state);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
} else { /* guess valences */
|
|
ObjectMoleculeOpRec op;
|
|
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_RevalenceByGuessing;
|
|
op.i1 = sele1;
|
|
op.i2 = sele2;
|
|
op.i3 = target_state;
|
|
op.i4 = reset;
|
|
op.i6 = quiet;
|
|
|
|
ExecutiveObjMolSeleOp(G, sele1, &op);
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveBond(PyMOLGlobals* G, const char* s1, const char* s2,
|
|
int order, int mode, int quiet, pymol::zstring_view symop)
|
|
{
|
|
int cnt;
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
int flag = false;
|
|
|
|
SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
|
|
SETUP_SELE_DEFAULT_PREFIXED(2, cSelectionInvalid);
|
|
|
|
{
|
|
ObjectMolecule* obj1 = SelectorGetSingleObjectMolecule(G, sele1);
|
|
ObjectMolecule* obj2 = SelectorGetSingleObjectMolecule(G, sele2);
|
|
if ((!obj1) || (!obj2) || (obj1 != obj2)) {
|
|
if ((!quiet) && (mode == 1)) {
|
|
PRINTFB(G, FB_Editor, FB_Warnings)
|
|
"Editor-Warning: bonds cannot be created between objects, only "
|
|
"within.\n" ENDFB(G);
|
|
}
|
|
}
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
switch (mode) {
|
|
case 1: /* add */
|
|
cnt = ObjectMoleculeAddBond(
|
|
(ObjectMolecule*) rec->obj, sele1, sele2, order, symop);
|
|
if (cnt) {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Editor, FB_Actions)
|
|
" Bond: %d bonds added to model \"%s\".\n", cnt,
|
|
rec->obj->Name ENDFB(G);
|
|
flag = true;
|
|
}
|
|
}
|
|
break;
|
|
case 2: /* adjust */
|
|
cnt = ObjectMoleculeAdjustBonds(
|
|
(ObjectMolecule*) rec->obj, sele1, sele2, 1, order, symop);
|
|
if (cnt) {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Editor, FB_Actions)
|
|
" Valence: %d bond valences adjusted in model \"%s\".\n", cnt,
|
|
rec->obj->Name ENDFB(G);
|
|
flag = true;
|
|
}
|
|
}
|
|
break;
|
|
case 0: /* remove */
|
|
default:
|
|
cnt = ObjectMoleculeRemoveBonds(
|
|
(ObjectMolecule*) rec->obj, sele1, sele2);
|
|
if (cnt) {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Editor, FB_Actions)
|
|
" Unbond: %d bonds removed from model \"%s\".\n", cnt,
|
|
rec->obj->Name ENDFB(G);
|
|
}
|
|
flag = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!flag) {
|
|
if (!quiet) {
|
|
switch (mode) {
|
|
case 1:
|
|
PRINTFB(G, FB_Editor, FB_Warnings)
|
|
"Bond-Warning: no bonds added." ENDFB(G);
|
|
break;
|
|
case 2:
|
|
PRINTFB(G, FB_Editor, FB_Warnings)
|
|
"Valence-Warning: no bond valences changed." ENDFB(G);
|
|
break;
|
|
case 0:
|
|
default:
|
|
PRINTFB(G, FB_Editor, FB_Warnings)
|
|
"Unbond-Warning: no bonds removed." ENDFB(G);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* brief Returns an error if quiet flag is not set
|
|
* @param quiet quiet flag
|
|
* @param msgs error messages
|
|
* @return pymol::make_error with conditionally filled message
|
|
*/
|
|
|
|
template <typename... Ts>
|
|
static pymol::Error ErrorMsgIfQuiet(bool quiet, Ts&&... msgs)
|
|
{
|
|
if (!quiet) {
|
|
return pymol::make_error(std::forward<Ts>(msgs)...);
|
|
} else {
|
|
return pymol::make_error("");
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<float> ExecutiveAngle(PyMOLGlobals* G, const char* nam,
|
|
const char* s1, const char* s2, const char* s3, int mode, int labels,
|
|
int reset, int zoom, int quiet, int state, int state1, int state2,
|
|
int state3)
|
|
{
|
|
SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
|
|
SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
|
|
SETUP_SELE_DEFAULT_PREFIXED(3, sele2);
|
|
|
|
auto obj = ExecutiveFindOrDeleteObject<ObjectDist>(G, nam);
|
|
auto need_manage = !obj;
|
|
float result = -1.0F;
|
|
|
|
obj = ObjectDistNewFromAngleSele(G, obj, sele1, sele2, sele3, mode, labels,
|
|
&result, reset, state, state1, state2, state3);
|
|
|
|
// ObjectDistNewFrom... always succeeds
|
|
assert(obj);
|
|
|
|
if (need_manage) {
|
|
ObjectSetName(obj, nam);
|
|
ExecutiveManageObject(G, obj, zoom, quiet);
|
|
if (!labels)
|
|
ExecutiveSetRepVisib(G, nam, cRepLabel, 0);
|
|
}
|
|
|
|
return rad_to_deg(result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<float> ExecutiveDihedral(PyMOLGlobals* G, const char* nam,
|
|
const char* s1, const char* s2, const char* s3, const char* s4, int mode,
|
|
int labels, int reset, int zoom, int quiet, int state)
|
|
{
|
|
SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
|
|
SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
|
|
SETUP_SELE_DEFAULT_PREFIXED(3, sele2);
|
|
SETUP_SELE_DEFAULT_PREFIXED(4, sele3);
|
|
|
|
auto obj = ExecutiveFindOrDeleteObject<ObjectDist>(G, nam);
|
|
auto need_manage = !obj;
|
|
float result = -1.0F;
|
|
|
|
obj = ObjectDistNewFromDihedralSele(
|
|
G, obj, sele1, sele2, sele3, sele4, mode, labels, &result, reset, state);
|
|
|
|
// ObjectDistNewFrom... always succeeds
|
|
assert(obj);
|
|
|
|
if (need_manage) {
|
|
ObjectSetName(obj, nam);
|
|
ExecutiveManageObject(G, obj, zoom, quiet);
|
|
if (!labels)
|
|
ExecutiveSetRepVisib(G, nam, cRepLabel, 0);
|
|
}
|
|
|
|
return rad_to_deg(result);
|
|
}
|
|
|
|
/**
|
|
* Create a distance measurement object
|
|
*
|
|
* @param nam: name of measurement object to create or add to
|
|
* @param s1: selection expression
|
|
* @param s2: selection expression or "same" keyword (shortcut for s1 = s2)
|
|
* @param mode: 0 (any), 1 (bonds), 2 (hbonds), 3 (distance_exclusion),
|
|
* 4 (centroids), 5 (pi-*), 6 (pi-pi), 7 (pi-cat), 8 (vdw-dist-ratio)
|
|
* @param cutoff: Distance cutoff in Angstrom, or vdw-distance ratio cutoff
|
|
* (mode 8)
|
|
* @param labels: Boolean flag to show distance labels
|
|
* @param reset: Boolean flag to clear a distance object if it already exists
|
|
* @return average of measured distances in Angstrom
|
|
*/
|
|
pymol::Result<float> ExecutiveDistance(PyMOLGlobals* G, const char* nam,
|
|
const char* s1, const char* s2, int mode, float cutoff, int labels,
|
|
int quiet, int reset, int state, int zoom, int state1, int state2)
|
|
{
|
|
if (strcmp(s1, s2) == 0) {
|
|
s2 = cKeywordSame;
|
|
}
|
|
|
|
SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
|
|
SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
|
|
|
|
auto obj = ExecutiveFindOrDeleteObject<ObjectDist>(G, nam);
|
|
auto need_manage = !obj;
|
|
float result = -1.0F;
|
|
|
|
/* create a new distance from the two selections */
|
|
obj = ObjectDistNewFromSele(G, obj, sele1, sele2, mode, cutoff, labels, reset,
|
|
&result, state, state1, state2);
|
|
|
|
// ObjectDistNewFrom... always succeeds
|
|
assert(obj);
|
|
|
|
if (need_manage) {
|
|
switch (mode) {
|
|
case 6: // pi-pi
|
|
SettingSet(cSetting_dash_color, "0x06c5ff" /* light blue */, obj);
|
|
break;
|
|
case 7: // pi-cat
|
|
SettingSet(cSetting_dash_color, "0x468c00" /* dark green */, obj);
|
|
break;
|
|
case 8: // "clashes"
|
|
SettingSet(cSetting_dash_color, "0xff8800" /* light red */, obj);
|
|
break;
|
|
case 9: // "halogen-bonds"
|
|
SettingSet(cSetting_dash_color, "0xaa00ff" /* dark magenta */, obj);
|
|
break;
|
|
case 10: // "salted-bridge"
|
|
SettingSet(cSetting_dash_color, "0xff2eff" /* light magenta */, obj);
|
|
break;
|
|
}
|
|
|
|
ObjectSetName(obj, nam);
|
|
ExecutiveManageObject(G, obj, zoom, quiet);
|
|
if (!labels)
|
|
ExecutiveSetRepVisib(G, nam, cRepLabel, 0);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
char* ExecutiveNameToSeqAlignStrVLA(
|
|
PyMOLGlobals* G, const char* name, int state, int format, int quiet)
|
|
{
|
|
char* result = nullptr;
|
|
if ((!name) || (!name[0]) || (strcmp(name, "(all)") == 0)) {
|
|
/* use current alignment as the default */
|
|
name = SettingGetGlobal_s(G, cSetting_seq_view_alignment);
|
|
if (name[0] == 0) {
|
|
SpecRec* rec = nullptr;
|
|
CExecutive* I = G->Executive;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->visible) {
|
|
if (rec->type == cExecObject)
|
|
if (rec->obj->type == cObjectAlignment) {
|
|
name = rec->obj->Name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!name) {
|
|
ErrMessage(G, " Executive", "invalid alignment object name.");
|
|
} else {
|
|
pymol::CObject* obj = ExecutiveFindObjectByName(G, name);
|
|
|
|
if (!obj) {
|
|
ErrMessage(G, " Executive", "alignment object not found.");
|
|
} else if (obj->type != cObjectAlignment) {
|
|
ErrMessage(G, " Executive", "invalid object type.");
|
|
} else {
|
|
ObjectAlignmentAsStrVLA(
|
|
G, (ObjectAlignment*) obj, state, format, &result);
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveSeleToObject(PyMOLGlobals* G, const char* name,
|
|
const char* s1, int source, int target, int discrete, int zoom, int quiet,
|
|
int singletons, int copy_properties)
|
|
{
|
|
SelectorTmp tmpsele1(G, s1);
|
|
int sele1 = tmpsele1.getIndex();
|
|
|
|
int ok = false;
|
|
ObjectNameType valid_name;
|
|
|
|
UtilNCopy(valid_name, name, sizeof(valid_name));
|
|
if (SettingGetGlobal_b(G, cSetting_validate_object_names)) {
|
|
ObjectMakeValidName(G, valid_name);
|
|
name = valid_name;
|
|
}
|
|
{
|
|
int exists = (ExecutiveFindObjectMoleculeByName(G, name) != nullptr);
|
|
|
|
if (sele1 >= 0) {
|
|
ok = SelectorCreateObjectMolecule(G, sele1, name, target, source,
|
|
discrete, false, quiet, singletons, copy_properties);
|
|
if (ok) {
|
|
int sele2 = SelectorIndexByName(G, name);
|
|
ObjectMolecule *old_obj, *new_obj;
|
|
old_obj = SelectorGetFirstObjectMolecule(
|
|
G, sele1); /* get at least one object */
|
|
new_obj = SelectorGetSingleObjectMolecule(G, sele2);
|
|
|
|
/* first we need to make sure that the object being moved
|
|
matches the target with respect to both the TTT and the
|
|
object's state matrix (if any) */
|
|
|
|
if (old_obj && new_obj) {
|
|
ExecutiveMatrixCopy(G, old_obj->Name, new_obj->Name, 1,
|
|
1, /* TTT mode */
|
|
source, target, false, 0, quiet);
|
|
|
|
ExecutiveMatrixCopy(G, old_obj->Name, new_obj->Name, 2,
|
|
2, /* Object state mode */
|
|
source, target, false, 0, quiet);
|
|
|
|
if (copy_properties) {
|
|
// old_obj to new_obj
|
|
if (old_obj->NCSet == new_obj->NCSet) {
|
|
// only if the objects have the same number of CoordSets (for now)
|
|
int state;
|
|
for (state = 0; state < new_obj->NCSet; state++) {
|
|
CoordSet *new_cs = new_obj->CSet[state],
|
|
*old_cs = old_obj->CSet[state];
|
|
if (old_cs->prop_id) {
|
|
PropertyCheckUniqueID(G, new_cs);
|
|
PropertyCopyProperties(G, old_cs->prop_id, new_cs->prop_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExecutiveDoZoom(G, new_obj, !exists, zoom, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (ok) {
|
|
return {};
|
|
} else {
|
|
return pymol::make_error("Failed to Create Object");
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveCopy(
|
|
PyMOLGlobals* G, const char* src, const char* dst, int zoom)
|
|
{
|
|
const pymol::CObject* os = ExecutiveFindObjectByName(G, src);
|
|
if (!os) {
|
|
return pymol::make_error("Object not found.");
|
|
}
|
|
|
|
pymol::CObject* oDst = os->clone();
|
|
|
|
if (!oDst) {
|
|
return pymol::make_error("Failed to create copy");
|
|
}
|
|
|
|
strcpy(oDst->Name, dst);
|
|
ExecutiveManageObject(G, oDst, zoom, false);
|
|
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Executive: object %s created.\n", oDst->Name ENDFB(G);
|
|
SceneChanged(G);
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveOrient(PyMOLGlobals* G, const char* sele, int state,
|
|
float animate, int complete, float buffer, int quiet)
|
|
{
|
|
double egval[3], egvali[3];
|
|
double evect[3][3];
|
|
float m[4][4], mt[4][4];
|
|
float t[3];
|
|
const float _0 = 0.0F;
|
|
int a, b;
|
|
|
|
double mi[16];
|
|
|
|
SelectorTmp tmpsele(G, sele);
|
|
sele = tmpsele.getName();
|
|
|
|
if (!ExecutiveGetMoment(G, sele, mi, state)) {
|
|
return {};
|
|
}
|
|
|
|
if (!MatrixEigensolveC33d(G, mi, egval, egvali, (double*) (void*) evect)) {
|
|
|
|
normalize3d(evect[0]);
|
|
normalize3d(evect[1]);
|
|
normalize3d(evect[2]);
|
|
|
|
for (a = 0; a < 3; a++) {
|
|
for (b = 0; b < 3; b++) {
|
|
m[a][b] = (float) evect[b][a]; /* fill columns */
|
|
}
|
|
}
|
|
|
|
for (a = 0; a < 3; a++) { /* expand to 4x4 */
|
|
m[3][a] = 0;
|
|
m[a][3] = 0;
|
|
}
|
|
m[3][3] = 1.0;
|
|
|
|
normalize3f(m[0]); /* cross normalization (probably unnec.) */
|
|
normalize3f(m[1]);
|
|
normalize3f(m[2]);
|
|
|
|
for (a = 0; a < 3; a++) /* convert to row-major */
|
|
for (b = 0; b < 3; b++)
|
|
mt[a][b] = m[b][a];
|
|
|
|
cross_product3f(mt[0], mt[1], t); /* insure right-handed matrix */
|
|
if (dot_product3f(t, mt[2]) < 0.0) {
|
|
mt[2][0] = -mt[2][0];
|
|
mt[2][1] = -mt[2][1];
|
|
mt[2][2] = -mt[2][2];
|
|
}
|
|
|
|
for (a = 0; a < 3; a++) /* convert back to column major */
|
|
for (b = 0; b < 3; b++)
|
|
m[a][b] = mt[b][a];
|
|
|
|
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);
|
|
|
|
{
|
|
float old_mat[16];
|
|
float new_mat[16];
|
|
float x, y, z;
|
|
copy44f(SceneGetMatrix(G), old_mat);
|
|
|
|
SceneSetMatrix(G, m[0]); /* load matrix */
|
|
|
|
/* there must be a more elegant to get the PC on X and the SC
|
|
* on Y then what is shown below, but I couldn't get it to work.
|
|
* I tried swapping the eigen-columns around but either that is
|
|
* a bogus approach (?) or my code was buggy. Hence the following...*/
|
|
|
|
if ((egval[0] < egval[2]) && (egval[2] < egval[1])) { /* X < Z < Y */
|
|
SceneRotate(G, 90, 1, 0, 0); /*1<-->2 */
|
|
} else if ((egval[1] < egval[0]) &&
|
|
(egval[0] < egval[2])) { /* Y < X < Z */
|
|
SceneRotate(G, 90, 0, 0, 1); /*0<-->1 */
|
|
} else if ((egval[1] < egval[2]) &&
|
|
(egval[2] < egval[0])) { /* Y < Z < X */
|
|
SceneRotate(G, 90, 0, 1, 0); /*1<-->2 */
|
|
SceneRotate(G, 90, 0, 0, 1); /*0<-->1 */
|
|
} else if ((egval[2] < egval[1]) &&
|
|
(egval[1] < egval[0])) { /* Z < Y < X */
|
|
SceneRotate(G, 90, 0, 1, 0); /*0<-->2 */
|
|
} else if ((egval[2] < egval[0]) &&
|
|
(egval[0] < egval[1])) { /* Z < X < Y */
|
|
SceneRotate(G, 90, 0, 1, 0); /*0<-->2 */
|
|
SceneRotate(G, 90, 1, 0, 0); /*0<-->1 */
|
|
}
|
|
|
|
/* now choose orientation that has the least perturbation from the
|
|
* starting matrix */
|
|
|
|
copy44f(SceneGetMatrix(G), new_mat);
|
|
|
|
x = old_mat[0] * new_mat[0] + old_mat[4] * new_mat[4] +
|
|
old_mat[8] * new_mat[8];
|
|
y = old_mat[1] * new_mat[1] + old_mat[5] * new_mat[5] +
|
|
old_mat[9] * new_mat[9];
|
|
z = old_mat[2] * new_mat[2] + old_mat[6] * new_mat[6] +
|
|
old_mat[10] * new_mat[10];
|
|
|
|
if ((x > _0) && (y < _0) && (z < _0)) {
|
|
SceneRotate(G, 180, 1, 0, 0);
|
|
} else if ((x < _0) && (y > _0) && (z < _0)) {
|
|
SceneRotate(G, 180, 0, 1, 0);
|
|
} else if ((x < _0) && (y < _0) && (z > _0)) {
|
|
SceneRotate(G, 180, 0, 0, 1);
|
|
}
|
|
}
|
|
|
|
/* X < Y < Z - do nothing - that's what we want */
|
|
|
|
ExecutiveWindowZoom(G, sele, buffer, state, complete, false, quiet);
|
|
if (animate != 0.0F)
|
|
SceneLoadAnimation(G, animate, 0);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveMove(
|
|
PyMOLGlobals* G, pymol::zstring_view axis, float dist)
|
|
{
|
|
switch (axis[0]) {
|
|
case 'x':
|
|
SceneTranslate(G, dist, 0.0, 0.0);
|
|
break;
|
|
case 'y':
|
|
SceneTranslate(G, 0.0, dist, 0.0);
|
|
break;
|
|
case 'z':
|
|
SceneTranslate(G, 0.0, 0.0, dist);
|
|
break;
|
|
default:
|
|
return pymol::make_error("Axis must be x, y, or z");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveLabel(PyMOLGlobals* G, const char* str1,
|
|
const char* expr, int quiet, int eval_mode)
|
|
{
|
|
int sele1;
|
|
ObjectMoleculeOpRec op1;
|
|
int cnt;
|
|
|
|
SelectorTmp s1(G, str1);
|
|
sele1 = s1.getIndex();
|
|
if (sele1 >= 0) {
|
|
ObjectMoleculeOpRecInit(&op1);
|
|
op1.code = OMOP_LABL;
|
|
op1.s1 = expr;
|
|
op1.i1 = 0;
|
|
op1.i2 = eval_mode;
|
|
|
|
#ifndef _PYMOL_NOPY
|
|
pymol::pautoblock gil(G);
|
|
#endif
|
|
|
|
if (!ExecutiveObjMolSeleOp(G, sele1, &op1)) {
|
|
return pymol::Error();
|
|
}
|
|
|
|
cnt = op1.i1;
|
|
op1.code = OMOP_VISI;
|
|
op1.i1 = cRepLabelBit;
|
|
op1.i2 = cVis_SHOW;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op1);
|
|
op1.code = OMOP_INVA;
|
|
op1.i2 = cRepInvVisib;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op1);
|
|
|
|
if (!quiet) {
|
|
{
|
|
const char* unlabelledstr = "";
|
|
if (cnt < 0) { /* if negative, say unlabelled */
|
|
cnt = -cnt;
|
|
unlabelledstr = "un";
|
|
}
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Label: %slabelled %i atoms.\n", unlabelledstr, cnt ENDFB(G);
|
|
}
|
|
}
|
|
} else {
|
|
return pymol::make_error("No atoms selected");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
#ifdef _WEBGL
|
|
#else
|
|
pymol::Result<int> ExecutiveIterate(PyMOLGlobals* G, const char* str1,
|
|
const char* expr, int read_only, int quiet, PyObject* space)
|
|
#endif
|
|
{
|
|
ObjectMoleculeOpRec op1;
|
|
ObjectMoleculeOpRecInit(&op1);
|
|
#ifdef _WEBGL
|
|
#endif
|
|
SelectorTmp tmpsele1(G, str1);
|
|
int sele1 = tmpsele1.getIndex();
|
|
op1.i1 = 0;
|
|
if (sele1 >= 0) {
|
|
op1.code = OMOP_ALTR;
|
|
op1.i1 = 0;
|
|
op1.i2 = read_only;
|
|
#ifdef _WEBGL
|
|
#else
|
|
op1.s1 = expr;
|
|
op1.py_ob1 = space;
|
|
#endif
|
|
|
|
if (!ExecutiveObjMolSeleOp(G, sele1, &op1)) {
|
|
return pymol::Error();
|
|
}
|
|
|
|
if (!quiet) {
|
|
if (!read_only) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Alter: modified %i atoms.\n", op1.i1 ENDFB(G);
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Iterate: iterated over %i atoms.\n", op1.i1 ENDFB(G);
|
|
}
|
|
}
|
|
if (!read_only) {
|
|
SeqChanged(G);
|
|
}
|
|
} else {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
" %s: No atoms selected.\n", __func__ ENDFB(G);
|
|
}
|
|
}
|
|
return (op1.i1);
|
|
}
|
|
|
|
SelectArgs ExecutiveSelectPrepareArgs(
|
|
PyMOLGlobals* G, pymol::zstring_view sname, pymol::zstring_view sele)
|
|
{
|
|
SelectArgs args;
|
|
args.sname = sname.c_str();
|
|
args.sele = sele.c_str();
|
|
|
|
// selection in first argument (cmd.select("expr"))
|
|
if (args.sele.empty()) {
|
|
args.sele = sname.c_str();
|
|
args.sname =
|
|
SettingGet<bool>(G, cSetting_auto_number_selections) ? "" : "sele";
|
|
}
|
|
|
|
if (args.sname.empty()) {
|
|
auto sel_num = SettingGet<int>(G, cSetting_sel_counter) + 1;
|
|
SettingSet<int>(G, cSetting_sel_counter, sel_num);
|
|
args.sname =
|
|
pymol::string_format("sel%02u", static_cast<unsigned>(sel_num));
|
|
}
|
|
return args;
|
|
}
|
|
|
|
/**
|
|
* cmd.select() implementation
|
|
*
|
|
* @param name Name of selection to create, or selection expression if `sele` is
|
|
* empty. If `sele` is empty, `name` will become "sele" or "sel01", depending on
|
|
* `auto_number_selections` and `sel_counter`. If `name` is empty, it will
|
|
* become "sel01", depending on `sel_counter` but independent of
|
|
* `auto_number_selections`.
|
|
* @param sele Selection expression
|
|
* @param enable Enable the named selection's SpecRec if 1, disable if 0, keep
|
|
* unchanged (if exists, otherwise enable) if -1.
|
|
* @param merge Discard existing named selection if 0. Merge with existing named
|
|
* selection if 1, merge only if existing one is enabled if 2.
|
|
* @param state Object state for state (coordinate) dependent expressions
|
|
* @param domain Existing selection name, same as selecting `(sele) and
|
|
* (domain)`
|
|
* @return Number of selected atoms
|
|
*/
|
|
pymol::Result<int> ExecutiveSelect(PyMOLGlobals* G, const SelectArgs& sargs,
|
|
int enable, int quiet, int merge, int state, const char* domain)
|
|
{
|
|
const char* name = sargs.sname.c_str();
|
|
const char* sele = sargs.sele.c_str();
|
|
// bail if name not available
|
|
if (ExecutiveFindObjectByName(G, name)) {
|
|
return pymol::make_error("name conflicts with an object");
|
|
}
|
|
|
|
// merge with existing selection
|
|
std::string selebuf;
|
|
if (merge) {
|
|
if (merge == 2) {
|
|
// merge if exists and active
|
|
selebuf = pymol::join_to_string("(", sele, ") or ??", name);
|
|
} else {
|
|
// merge if exists
|
|
selebuf = pymol::join_to_string("(", sele, ") or ?", name);
|
|
}
|
|
sele = selebuf.c_str();
|
|
}
|
|
|
|
auto res = SelectorCreateWithStateDomain(
|
|
G, name, sele, nullptr, quiet, nullptr, state, domain);
|
|
|
|
p_return_if_error(res);
|
|
|
|
if (enable == 1) {
|
|
ExecutiveSetObjVisib(G, name, 1, 0);
|
|
} else if (enable == 0) {
|
|
ExecutiveSetObjVisib(G, name, 0, 0);
|
|
}
|
|
|
|
SceneInvalidate(G);
|
|
SeqDirty(G);
|
|
|
|
return res.result();
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<int> ExecutiveSelectList(PyMOLGlobals* G, const char* sele_name,
|
|
const char* oname, const int* list, size_t list_len, int state, int mode,
|
|
int quiet)
|
|
{
|
|
enum {
|
|
MODE_INDEX = 0,
|
|
MODE_ID = 1,
|
|
MODE_RANK = 2,
|
|
};
|
|
|
|
auto obj = ExecutiveFindObject<ObjectMolecule>(G, oname);
|
|
if (!obj) {
|
|
return pymol::make_error("object not found");
|
|
}
|
|
|
|
std::vector<int> idx_list;
|
|
idx_list.reserve(list_len);
|
|
|
|
if (mode == MODE_INDEX) {
|
|
for (size_t i = 0; i != list_len; ++i) {
|
|
// convert 1-based indices to 0-based array offsets
|
|
idx_list.push_back(list[i] - 1);
|
|
}
|
|
} else if (mode == MODE_ID || mode == MODE_RANK) {
|
|
CoordSet const* cs = obj->getCoordSet(state);
|
|
std::set<int> const list_set(list, list + list_len);
|
|
|
|
for (int atm = 0; atm < obj->NAtom; ++atm) {
|
|
auto const& ai = obj->AtomInfo[atm];
|
|
int const index = (mode == MODE_ID) ? ai.id : ai.rank;
|
|
|
|
if (list_set.count(index)) {
|
|
if (!cs || cs->atmToIdx(atm) >= 0) {
|
|
idx_list.push_back(atm);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return pymol::make_error("invalid mode");
|
|
}
|
|
|
|
return SelectorCreateOrderedFromObjectIndices(
|
|
G, sele_name, obj, idx_list.data(), idx_list.size());
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<int> ExecutiveIterateList(PyMOLGlobals* G, const char* str1,
|
|
PyObject* list, int read_only, int quiet, PyObject* space)
|
|
{
|
|
#ifdef _PYMOL_NOPY
|
|
return pymol::make_error("Iterate List not available.");
|
|
#else
|
|
assert(PyGILState_Check());
|
|
|
|
int ok = true;
|
|
int n_eval = 0;
|
|
SelectorTmp s1(G, str1);
|
|
int sele0 = s1.getIndex();
|
|
PyObject* entry = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
if (sele0 >= 0)
|
|
obj = SelectorGetSingleObjectMolecule(G, sele0);
|
|
if (obj) {
|
|
int n_atom = obj->NAtom;
|
|
int list_len = 0;
|
|
int a;
|
|
int index = 0;
|
|
const char* expr = nullptr;
|
|
if (ok)
|
|
ok = PyList_Check(list);
|
|
if (ok) {
|
|
list_len = PyList_Size(list);
|
|
for (a = 0; a < list_len; a++) {
|
|
if (ok)
|
|
entry = PyList_GetItem(list, a);
|
|
if (ok)
|
|
ok = PyList_Check(entry);
|
|
if (ok)
|
|
ok = (PyList_Size(entry) == 2);
|
|
if (ok)
|
|
ok = PConvPyIntToInt(PyList_GetItem(entry, 0), &index);
|
|
if (ok)
|
|
ok = PConvPyStrToStrPtr(PyList_GetItem(entry, 1), &expr);
|
|
if (ok)
|
|
ok = ((index <= n_atom) && (index > 0));
|
|
if (ok) {
|
|
CoordSet* cs = nullptr;
|
|
if (obj->DiscreteFlag && obj->DiscreteCSet) {
|
|
cs = obj->DiscreteCSet[index - 1];
|
|
} else if (obj->NCSet == 1) {
|
|
cs = obj->CSet[0];
|
|
}
|
|
auto expr_co =
|
|
unique_PyObject_ptr(Py_CompileString(expr, "", Py_single_input));
|
|
ok = expr_co && PAlterAtom(G, obj, cs, expr_co.get(), read_only,
|
|
index - 1, space);
|
|
}
|
|
if (ok)
|
|
n_eval++;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
return pymol::make_error("Selection cannot span more than one object.");
|
|
}
|
|
if (ok) {
|
|
if (!quiet) {
|
|
if (!read_only) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" AlterList: modified %i atoms.\n", n_eval ENDFB(G);
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" IterateList: iterated over %i atoms.\n", n_eval ENDFB(G);
|
|
}
|
|
}
|
|
if (!read_only) {
|
|
SeqChanged(G);
|
|
}
|
|
}
|
|
if (!ok)
|
|
return pymol::make_error("An error occurred.");
|
|
else
|
|
return n_eval;
|
|
#endif
|
|
}
|
|
|
|
/*========================================================================*/
|
|
#ifdef _WEBGL
|
|
#else
|
|
pymol::Result<int> ExecutiveIterateState(PyMOLGlobals* G, int state,
|
|
const char* str1, const char* expr, int read_only, int quiet,
|
|
PyObject* space)
|
|
#endif
|
|
{
|
|
#ifdef _WEBGL
|
|
#endif
|
|
SelectorTmp tmpsele1(G, str1);
|
|
int sele1 = tmpsele1.getIndex();
|
|
if (sele1 >= 0) {
|
|
int start_state = 0, stop_state = 0;
|
|
ObjectMoleculeOpRec op1;
|
|
|
|
if (state >= 0) {
|
|
start_state = state;
|
|
stop_state = state + 1;
|
|
} else {
|
|
if ((state == -2) ||
|
|
(state == -3)) { /* current state, TO DO: effective object state */
|
|
state = SceneGetState(G);
|
|
start_state = state;
|
|
stop_state = state + 1;
|
|
} else if (state == -1) { /* all states (for the selection) */
|
|
start_state = 0;
|
|
stop_state = SelectorCountStates(G, sele1);
|
|
}
|
|
}
|
|
ObjectMoleculeOpRecInit(&op1);
|
|
op1.i1 = 0;
|
|
|
|
for (state = start_state; state < stop_state; state++) {
|
|
op1.code = OMOP_AlterState;
|
|
#ifdef _WEBGL
|
|
#else
|
|
op1.s1 = expr;
|
|
op1.py_ob1 = space;
|
|
#endif
|
|
op1.i2 = state;
|
|
op1.i3 = read_only;
|
|
if (!ExecutiveObjMolSeleOp(G, sele1, &op1)) {
|
|
return pymol::Error();
|
|
}
|
|
}
|
|
if (!read_only) {
|
|
// for dynamic_measures
|
|
ExecutiveUpdateCoordDepends(G, nullptr);
|
|
SeqChanged(G);
|
|
}
|
|
if (!quiet) {
|
|
if (!read_only) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" AlterState: modified %i atom coordinate states.\n", op1.i1 ENDFB(G);
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" IterateState: iterated over %i atom coordinate states.\n",
|
|
op1.i1 ENDFB(G);
|
|
}
|
|
}
|
|
return op1.i1;
|
|
} else {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"ExecutiveIterateState: No atoms selected.\n" ENDFB(G);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
typedef struct {
|
|
int priority;
|
|
float vertex[3];
|
|
AtomInfoType* ai;
|
|
} FitVertexRec;
|
|
|
|
static int fVertexOrdered(const FitVertexRec* array, int l, int r)
|
|
{
|
|
return (array[l].priority <= array[r].priority);
|
|
}
|
|
|
|
static int fAtomOrdered(
|
|
PyMOLGlobals* G, const AtomInfoType* const* array, int l, int r)
|
|
{
|
|
return (AtomInfoCompare(G, array[l], array[r]));
|
|
}
|
|
|
|
static int fAtomIDOrdered(const AtomInfoType* const* array, int l, int r)
|
|
{
|
|
return (array[l]->id <= array[r]->id);
|
|
}
|
|
|
|
static int fAtomRankOrdered(const AtomInfoType* const* array, int l, int r)
|
|
{
|
|
return (array[l]->rank <= array[r]->rank);
|
|
}
|
|
|
|
static int fAtomTemp1Ordered(const AtomInfoType* const* array, int l, int r)
|
|
{
|
|
return (array[l]->temp1 <= array[r]->temp1);
|
|
}
|
|
|
|
static void PackSortedIndices(int n, int* x, int rec_size, void* data)
|
|
{
|
|
int a;
|
|
for (a = 0; a < n; a++) {
|
|
if (a != x[a]) {
|
|
memcpy(((char*) data) + (a * rec_size),
|
|
((char*) data) + (x[a] * rec_size), rec_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveRMS(PyMOLGlobals* G, const char* s1, const char* s2, int mode,
|
|
float refine, int max_cyc, int quiet, const char* oname, int state1,
|
|
int state2, int ordered_selections, int matchmaker,
|
|
ExecutiveRMSInfo* rms_info)
|
|
{
|
|
/* mode 0: measure rms without superposition
|
|
mode 1: measure rms with trial superposition
|
|
mode 2: measure rms with actual superposition */
|
|
|
|
int sele1, sele2;
|
|
float rms = -1.0;
|
|
int a, b;
|
|
float inv, *f, *f1, *f2;
|
|
ObjectMoleculeOpRec op1;
|
|
ObjectMoleculeOpRec op2;
|
|
OrthoLineType buffer;
|
|
int* flag;
|
|
int ok = true;
|
|
int repeat;
|
|
float v1[3], *v2;
|
|
ObjectAlignment* align_to_update = nullptr;
|
|
|
|
bool ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
|
|
bool ignore_case_chain = SettingGetGlobal_b(G, cSetting_ignore_case_chain);
|
|
|
|
int matrix_mode = SettingGetGlobal_i(G, cSetting_matrix_mode);
|
|
if (matrix_mode < 0)
|
|
matrix_mode = 0; /* for now */
|
|
|
|
if (matchmaker == -1) {
|
|
/* matchmaker -1 is the same as matchmaker 0 except that the
|
|
selections are not pre-matched prior to calling of this routine */
|
|
matchmaker = 0;
|
|
}
|
|
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
sele2 = SelectorIndexByName(G, s2);
|
|
|
|
/* this function operates on stored coordinates -- thus transformation
|
|
matrices will need to be applied to the resulting atoms */
|
|
|
|
// get coordinates
|
|
{
|
|
auto sele = sele1;
|
|
auto state = state1;
|
|
auto op = &op1;
|
|
|
|
// for both selections
|
|
do {
|
|
ObjectMoleculeOpRecInit(op);
|
|
|
|
if (sele >= 0) {
|
|
if (state < 0) {
|
|
op->code = OMOP_AVRT;
|
|
} else {
|
|
op->code = OMOP_StateVRT;
|
|
op->i1 = state;
|
|
}
|
|
|
|
op->nvv1 = 0; // length of vc1 (number of atoms with coordinates)
|
|
op->vc1 = VLACalloc(int, 1000); // number of states per atom
|
|
op->vv1 = VLACalloc(float, 1000); // coordinates (sum over states)
|
|
|
|
if (mode == 0)
|
|
op->i2 = true; /* if measuring current coordinates, then get global
|
|
txfd values */
|
|
|
|
if (matchmaker || (oname && oname[0]))
|
|
op->ai1VLA = VLACalloc(AtomInfoType*, 1000);
|
|
|
|
if (ordered_selections)
|
|
op->vp1 = VLAlloc(
|
|
int, 1000); // selection member "priority"? (MemberType::tag)
|
|
|
|
ExecutiveObjMolSeleOp(G, sele, op);
|
|
|
|
for (a = 0; a < op->nvv1; a++) {
|
|
inv = (float) op->vc1[a]; /* average over coordinate sets */
|
|
if (inv) {
|
|
f = op->vv1 + (a * 3);
|
|
scale3f(f, 1.F / inv, f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// second iteration done
|
|
if (sele == sele2)
|
|
break;
|
|
|
|
sele = sele2;
|
|
state = state2;
|
|
op = &op2;
|
|
|
|
} while (true);
|
|
}
|
|
|
|
if (op1.vv1 && op2.vv1) {
|
|
if (op1.nvv1 && op2.nvv1) {
|
|
ObjectMolecule* mobile_obj = nullptr;
|
|
|
|
int n_pair = 0;
|
|
|
|
if (!(mobile_obj = SelectorGetSingleObjectMolecule(G, sele1))) {
|
|
if (mode != 2) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"Executive-Warning: Mobile selection spans more than one "
|
|
"object.\n" ENDFB(G);
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"Executive-Error: Mobile selection spans more than one object. "
|
|
"Aborting.\n" ENDFB(G);
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (ok && op1.nvv1 && op2.nvv1 && (matchmaker > 0)) {
|
|
/* matchmaker 0 is the default... by internal atom ordering only */
|
|
int* idx1 = pymol::malloc<int>(op1.nvv1);
|
|
int* idx2 = pymol::malloc<int>(op2.nvv1);
|
|
int sort_flag = false;
|
|
if (!(idx1 && idx2))
|
|
ok = false;
|
|
else {
|
|
switch (matchmaker) {
|
|
case 1: /* by atom info-based ordering */
|
|
UtilSortIndexGlobals(G, op1.nvv1, op1.ai1VLA, idx1,
|
|
(UtilOrderFnGlobals*) fAtomOrdered);
|
|
UtilSortIndexGlobals(G, op2.nvv1, op2.ai1VLA, idx2,
|
|
(UtilOrderFnGlobals*) fAtomOrdered);
|
|
sort_flag = true;
|
|
break;
|
|
case 2: /* by matching atom identifiers */
|
|
UtilSortIndex(
|
|
op1.nvv1, op1.ai1VLA, idx1, (UtilOrderFn*) fAtomIDOrdered);
|
|
UtilSortIndex(
|
|
op2.nvv1, op2.ai1VLA, idx2, (UtilOrderFn*) fAtomIDOrdered);
|
|
sort_flag = true;
|
|
break;
|
|
case 3: /* by matching atom ranks */
|
|
UtilSortIndex(
|
|
op1.nvv1, op1.ai1VLA, idx1, (UtilOrderFn*) fAtomRankOrdered);
|
|
UtilSortIndex(
|
|
op2.nvv1, op2.ai1VLA, idx2, (UtilOrderFn*) fAtomRankOrdered);
|
|
sort_flag = true;
|
|
break;
|
|
case 4: /* by internal atom indexes (stored in temp1 kludge field) */
|
|
UtilSortIndex(
|
|
op1.nvv1, op1.ai1VLA, idx1, (UtilOrderFn*) fAtomTemp1Ordered);
|
|
UtilSortIndex(
|
|
op2.nvv1, op2.ai1VLA, idx2, (UtilOrderFn*) fAtomTemp1Ordered);
|
|
sort_flag = true;
|
|
break;
|
|
}
|
|
if (sort_flag) {
|
|
/* GOD this is SO ugly! */
|
|
|
|
if (op1.vv1) {
|
|
float* tmp = VLAlloc(float, op1.nvv1 * 3);
|
|
if (!tmp)
|
|
ok = false;
|
|
else {
|
|
UtilApplySortedIndices(
|
|
op1.nvv1, idx1, 3 * sizeof(float), op1.vv1, tmp);
|
|
VLAFreeP(op1.vv1);
|
|
op1.vv1 = tmp;
|
|
}
|
|
}
|
|
if (op1.vc1) {
|
|
int* tmp = VLAlloc(int, op1.nvv1);
|
|
if (!tmp)
|
|
ok = false;
|
|
else {
|
|
UtilApplySortedIndices(
|
|
op1.nvv1, idx1, sizeof(int), op1.vc1, tmp);
|
|
VLAFreeP(op1.vc1);
|
|
op1.vc1 = tmp;
|
|
}
|
|
}
|
|
if (op1.vp1) {
|
|
int* tmp = VLAlloc(int, op1.nvv1);
|
|
if (!tmp)
|
|
ok = false;
|
|
else {
|
|
UtilApplySortedIndices(
|
|
op1.nvv1, idx1, sizeof(int), op1.vp1, tmp);
|
|
VLAFreeP(op1.vp1);
|
|
op1.vp1 = tmp;
|
|
}
|
|
}
|
|
if (op1.ai1VLA) {
|
|
AtomInfoType** tmp = VLACalloc(AtomInfoType*, op1.nvv1);
|
|
if (!tmp)
|
|
ok = false;
|
|
else {
|
|
UtilApplySortedIndices(
|
|
op1.nvv1, idx1, sizeof(AtomInfoType*), op1.ai1VLA, tmp);
|
|
VLAFreeP(op1.ai1VLA);
|
|
op1.ai1VLA = tmp;
|
|
}
|
|
}
|
|
|
|
if (op2.vv1) {
|
|
float* tmp = VLAlloc(float, op2.nvv1 * 3);
|
|
if (!tmp)
|
|
ok = false;
|
|
else {
|
|
UtilApplySortedIndices(
|
|
op2.nvv1, idx2, 3 * sizeof(float), op2.vv1, tmp);
|
|
VLAFreeP(op2.vv1);
|
|
op2.vv1 = tmp;
|
|
}
|
|
}
|
|
if (op2.vc1) {
|
|
int* tmp = VLAlloc(int, op2.nvv1);
|
|
if (!tmp)
|
|
ok = false;
|
|
else {
|
|
UtilApplySortedIndices(
|
|
op2.nvv1, idx2, sizeof(int), op2.vc1, tmp);
|
|
VLAFreeP(op2.vc1);
|
|
op2.vc1 = tmp;
|
|
}
|
|
}
|
|
if (op2.vp1) {
|
|
int* tmp = VLAlloc(int, op2.nvv1);
|
|
if (!tmp)
|
|
ok = false;
|
|
else {
|
|
UtilApplySortedIndices(
|
|
op2.nvv1, idx2, sizeof(int), op2.vp1, tmp);
|
|
VLAFreeP(op2.vp1);
|
|
op2.vp1 = tmp;
|
|
}
|
|
}
|
|
if (op2.ai1VLA) {
|
|
AtomInfoType** tmp = VLACalloc(AtomInfoType*, op2.nvv1);
|
|
if (!tmp)
|
|
ok = false;
|
|
else {
|
|
UtilApplySortedIndices(
|
|
op2.nvv1, idx2, sizeof(AtomInfoType*), op2.ai1VLA, tmp);
|
|
VLAFreeP(op2.ai1VLA);
|
|
op2.ai1VLA = tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (matchmaker != 0) {
|
|
int n1 = 0, n2 = 0, c1 = 0, c2 = 0;
|
|
int cmp;
|
|
|
|
while ((n1 < op1.nvv1) && (n2 < op2.nvv1)) {
|
|
cmp = 0;
|
|
switch (matchmaker) {
|
|
case 1: /* insure that AtomInfoType matches */
|
|
if (AtomInfoMatch(G, op1.ai1VLA[n1], op2.ai1VLA[n2], ignore_case,
|
|
ignore_case_chain))
|
|
cmp = 0;
|
|
else
|
|
cmp = AtomInfoCompare(G, op1.ai1VLA[n1], op2.ai1VLA[n2]);
|
|
printf("%d-%d %d-%d: %d\n", c1, n1, c2, n2, cmp);
|
|
break;
|
|
case 2: /* ID */
|
|
case 3: /* rank */
|
|
{
|
|
int val1;
|
|
int val2;
|
|
|
|
switch (matchmaker) {
|
|
case 2: /* ID */
|
|
val1 = op1.ai1VLA[n1]->id;
|
|
val2 = op2.ai1VLA[n2]->id;
|
|
break;
|
|
case 3: /* rank */
|
|
val1 = op1.ai1VLA[n1]->rank;
|
|
val2 = op2.ai1VLA[n2]->rank;
|
|
break;
|
|
case 4: /* index (via temp1) */
|
|
val1 = op1.ai1VLA[n1]->temp1;
|
|
val2 = op2.ai1VLA[n2]->temp1;
|
|
break;
|
|
default:
|
|
val1 = 0;
|
|
val2 = 0;
|
|
break;
|
|
}
|
|
if (val1 == val2)
|
|
cmp = 0;
|
|
else if (val1 < val2)
|
|
cmp = -1;
|
|
else
|
|
cmp = 1;
|
|
} break;
|
|
}
|
|
if (!cmp) { /* match found */
|
|
idx1[c1++] = n1++;
|
|
idx2[c2++] = n2++;
|
|
n_pair++;
|
|
} else if (cmp < 0) { /* op1 below op2 */
|
|
n1++;
|
|
} else { /* op2 below op1 */
|
|
n2++;
|
|
}
|
|
}
|
|
|
|
if (n_pair) {
|
|
if (op1.vv1)
|
|
PackSortedIndices(n_pair, idx1, 3 * sizeof(float), op1.vv1);
|
|
if (op1.vc1)
|
|
PackSortedIndices(n_pair, idx1, sizeof(int), op1.vc1);
|
|
if (op1.vp1)
|
|
PackSortedIndices(n_pair, idx1, sizeof(int), op1.vp1);
|
|
if (op1.ai1VLA)
|
|
PackSortedIndices(
|
|
n_pair, idx1, sizeof(AtomInfoType*), op1.ai1VLA);
|
|
|
|
if (op2.vv1)
|
|
PackSortedIndices(n_pair, idx2, 3 * sizeof(float), op2.vv1);
|
|
if (op2.vc1)
|
|
PackSortedIndices(n_pair, idx2, sizeof(int), op2.vc1);
|
|
if (op2.vp1)
|
|
PackSortedIndices(n_pair, idx2, sizeof(int), op2.vp1);
|
|
if (op2.ai1VLA)
|
|
PackSortedIndices(
|
|
n_pair, idx2, sizeof(AtomInfoType*), op2.ai1VLA);
|
|
}
|
|
}
|
|
FreeP(idx1);
|
|
FreeP(idx2);
|
|
} else if (op1.nvv1 != op2.nvv1) {
|
|
sprintf(buffer, "Atom counts between selections don't match (%d vs %d)",
|
|
op1.nvv1, op2.nvv1);
|
|
ErrMessage(G, "ExecutiveRMS", buffer);
|
|
n_pair = 0;
|
|
ok = false;
|
|
} else {
|
|
n_pair = 0;
|
|
for (a = 0; a < op1.nvv1; ++a) { // for atoms in selection
|
|
if (op1.vc1[a] && op2.vc1[a]) { // check state counts
|
|
if (n_pair < a) { // copy over if necessary
|
|
copy3(op1.vv1 + 3 * a, op1.vv1 + 3 * n_pair);
|
|
copy3(op2.vv1 + 3 * a, op2.vv1 + 3 * n_pair);
|
|
if (op1.ai1VLA)
|
|
op1.ai1VLA[n_pair] = op1.ai1VLA[a];
|
|
if (op2.ai1VLA)
|
|
op2.ai1VLA[n_pair] = op2.ai1VLA[a];
|
|
if (op1.vp1)
|
|
op1.vp1[n_pair] = op1.vp1[a];
|
|
if (op2.vp1)
|
|
op2.vp1[n_pair] = op2.vp1[a];
|
|
op1.vc1[n_pair] = op1.vc1[a];
|
|
op2.vc1[n_pair] = op2.vc1[a];
|
|
}
|
|
++n_pair;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (n_pair) {
|
|
/* okay -- we're on track to do an alignment */
|
|
|
|
if (ordered_selections && op1.vp1 && op2.vp1) {
|
|
/* if we expected ordered selections and have priorities,
|
|
then we may need to sort vertices */
|
|
|
|
int sort_flag1 = false, sort_flag2 = false;
|
|
int well_defined1 = true, well_defined2 = true;
|
|
|
|
for (a = 0; a < (n_pair - 1); a++) {
|
|
/* printf("op1 vertex %d priority %d\n",a,op1.vp1[a]);
|
|
printf("op2 vertex %d priority %d\n",a,op2.vp1[a]); */
|
|
|
|
if (op1.vp1[a] > op1.vp1[a + 1])
|
|
sort_flag1 = true;
|
|
else if (op1.vp1[a] == op1.vp1[a + 1])
|
|
well_defined1 = false;
|
|
if (op2.vp1[a] > op2.vp1[a + 1])
|
|
sort_flag2 = true;
|
|
else if (op2.vp1[a] == op2.vp1[a + 1])
|
|
well_defined2 = false;
|
|
}
|
|
|
|
if (sort_flag1 || sort_flag2) {
|
|
if (!(well_defined1 || well_defined2)) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"Executive-Warning: Ordering requested but not well "
|
|
"defined.\n" ENDFB(G);
|
|
} else {
|
|
FitVertexRec* vert = pymol::malloc<FitVertexRec>(n_pair);
|
|
|
|
if (sort_flag1) {
|
|
float *src, *dst;
|
|
src = op1.vv1;
|
|
for (a = 0; a < n_pair; a++) {
|
|
vert[a].priority = op1.vp1[a];
|
|
dst = vert[a].vertex;
|
|
copy3f(src, dst);
|
|
src += 3;
|
|
}
|
|
UtilSortInPlace(G, vert, n_pair, sizeof(FitVertexRec),
|
|
(UtilOrderFn*) fVertexOrdered);
|
|
dst = op1.vv1;
|
|
for (a = 0; a < n_pair; a++) {
|
|
src = vert[a].vertex;
|
|
copy3f(src, dst);
|
|
dst += 3;
|
|
}
|
|
}
|
|
|
|
if (sort_flag2) {
|
|
float *src, *dst;
|
|
src = op2.vv1;
|
|
for (a = 0; a < n_pair; a++) {
|
|
vert[a].priority = op2.vp1[a];
|
|
dst = vert[a].vertex;
|
|
copy3f(src, dst);
|
|
src += 3;
|
|
}
|
|
UtilSortInPlace(G, vert, n_pair, sizeof(FitVertexRec),
|
|
(UtilOrderFn*) fVertexOrdered);
|
|
dst = op2.vv1;
|
|
for (a = 0; a < n_pair; a++) {
|
|
src = vert[a].vertex;
|
|
copy3f(src, dst);
|
|
dst += 3;
|
|
}
|
|
}
|
|
|
|
FreeP(vert);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rms_info) {
|
|
rms_info->initial_n_atom = n_pair;
|
|
rms_info->n_cycles_run = 0;
|
|
rms_info->final_n_atom = n_pair; /* in case there is no refinement */
|
|
}
|
|
|
|
if (mode != 0) {
|
|
rms = MatrixFitRMSTTTf(G, n_pair, op1.vv1, op2.vv1, nullptr, op2.ttt);
|
|
if (rms_info) {
|
|
rms_info->initial_rms = rms;
|
|
rms_info->final_rms = rms;
|
|
}
|
|
repeat = true;
|
|
b = 0;
|
|
while (repeat) {
|
|
repeat = false;
|
|
b++;
|
|
if (b > max_cyc)
|
|
break;
|
|
if ((refine > R_SMALL4) && (rms > R_SMALL4)) {
|
|
int n_next = n_pair;
|
|
AtomInfoType **ai1, **ai2;
|
|
|
|
flag = pymol::malloc<int>(n_pair);
|
|
|
|
if (flag) {
|
|
for (a = 0; a < n_pair; a++) {
|
|
MatrixTransformTTTfN3f(1, v1, op2.ttt, op1.vv1 + (a * 3));
|
|
v2 = op2.vv1 + (a * 3);
|
|
if ((diff3f(v1, v2) / rms) > refine) {
|
|
flag[a] = false;
|
|
repeat = true;
|
|
} else
|
|
flag[a] = true;
|
|
}
|
|
f1 = op1.vv1;
|
|
f2 = op2.vv1;
|
|
ai1 = op1.ai1VLA;
|
|
ai2 = op2.ai1VLA;
|
|
for (a = 0; a < n_pair; a++) {
|
|
if (!flag[a]) {
|
|
n_next--;
|
|
} else {
|
|
copy3f(op1.vv1 + (3 * a), f1);
|
|
copy3f(op2.vv1 + (3 * a), f2);
|
|
f1 += 3;
|
|
f2 += 3;
|
|
if (ai1 && ai2) { /* make sure we keep track of which atoms
|
|
are aligned */
|
|
*(ai1++) = op1.ai1VLA[a];
|
|
*(ai2++) = op2.ai1VLA[a];
|
|
}
|
|
}
|
|
}
|
|
if (!quiet && (n_next != n_pair)) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" %s: %d atoms rejected during cycle %d (RMSD=%0.2f).\n",
|
|
__func__, n_pair - n_next, b, rms ENDFB(G);
|
|
}
|
|
n_pair = n_next;
|
|
FreeP(flag);
|
|
if (n_pair) {
|
|
rms = MatrixFitRMSTTTf(
|
|
G, n_pair, op1.vv1, op2.vv1, nullptr, op2.ttt);
|
|
if (rms_info) {
|
|
rms_info->n_cycles_run = b;
|
|
rms_info->final_n_atom = n_pair;
|
|
rms_info->final_rms = rms;
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else { /* mode == 0 -- simple RMS, with no coordinate movement */
|
|
rms = MatrixGetRMS(G, n_pair, op1.vv1, op2.vv1, nullptr);
|
|
if (rms_info) {
|
|
rms_info->initial_rms = rms;
|
|
rms_info->final_rms = rms;
|
|
}
|
|
}
|
|
}
|
|
if (!n_pair) {
|
|
PRINTFB(G, FB_Executive, FB_Results)
|
|
" Executive: Error -- no atoms left after refinement!\n" ENDFB(G);
|
|
ok = false;
|
|
}
|
|
|
|
if (ok) {
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Results)
|
|
" Executive: RMSD = %8.3f (%d to %d atoms)\n", rms, n_pair,
|
|
n_pair ENDFB(G);
|
|
}
|
|
if (oname && oname[0]) {
|
|
int align_state = state2;
|
|
ObjectMolecule* trg_obj = SelectorGetSingleObjectMolecule(G, sele2);
|
|
|
|
if (align_state < 0) {
|
|
align_state = SceneGetState(G);
|
|
}
|
|
|
|
/* we're going to create/update an alignment object */
|
|
|
|
{
|
|
/* Get unique ids and construct the alignment vla */
|
|
pymol::vla<int> align_vla(n_pair * 3);
|
|
|
|
{
|
|
int* id_p = align_vla.data();
|
|
int i;
|
|
for (i = 0; i < n_pair; i++) {
|
|
id_p[0] = AtomInfoCheckUniqueID(G, op2.ai1VLA[i]); /* target */
|
|
id_p[1] = AtomInfoCheckUniqueID(G, op1.ai1VLA[i]);
|
|
id_p[2] = 0;
|
|
id_p += 3;
|
|
}
|
|
VLASize(align_vla, int, n_pair * 3);
|
|
}
|
|
{
|
|
ObjectAlignment* obj = nullptr;
|
|
|
|
/* does object already exist? */
|
|
{
|
|
pymol::CObject* execObj = ExecutiveFindObjectByName(G, oname);
|
|
if (execObj && (execObj->type != cObjectAlignment))
|
|
ExecutiveDelete(G, oname);
|
|
else
|
|
obj = (ObjectAlignment*) execObj;
|
|
}
|
|
obj = ObjectAlignmentDefine(
|
|
G, obj, align_vla, align_state, true, trg_obj, mobile_obj);
|
|
obj->Color = ColorGetIndex(G, "yellow");
|
|
ObjectSetName(obj, oname);
|
|
ExecutiveManageObject(G, obj, 0, quiet);
|
|
align_to_update = obj;
|
|
SceneInvalidate(G);
|
|
}
|
|
}
|
|
}
|
|
if (ok && mode == 2) {
|
|
if (matrix_mode > 0) {
|
|
|
|
ObjectMolecule *src_obj, *trg_obj;
|
|
src_obj = SelectorGetFirstObjectMolecule(
|
|
G, sele1); /* get at least one object */
|
|
trg_obj = SelectorGetSingleObjectMolecule(G, sele2);
|
|
|
|
/* first we need to make sure that the object being moved
|
|
matches the target with respect to both the TTT and the
|
|
object's state matrix (if any) */
|
|
|
|
if (src_obj && trg_obj) {
|
|
ExecutiveMatrixCopy(G, trg_obj->Name, src_obj->Name, 1,
|
|
1, /* TTT mode */
|
|
state2, state1, false, 0, quiet);
|
|
|
|
ExecutiveMatrixCopy(G, trg_obj->Name, src_obj->Name, 2,
|
|
2, /* Object state mode */
|
|
state2, state1, false, 0, quiet);
|
|
|
|
switch (matrix_mode) {
|
|
case 1: /* TTTs */
|
|
ExecutiveCombineObjectTTT(G, src_obj->Name, op2.ttt, true, -1);
|
|
break;
|
|
case 2: {
|
|
double homo[16], *src_homo;
|
|
convertTTTfR44d(op2.ttt, homo);
|
|
if (ExecutiveGetObjectMatrix(
|
|
G, src_obj->Name, state1, &src_homo, false)) {
|
|
left_multiply44d44d(src_homo, homo);
|
|
ExecutiveSetObjectMatrix(G, src_obj->Name, state1, homo);
|
|
}
|
|
} break;
|
|
}
|
|
/* next we need to update the object's TTT matrix to reflect
|
|
the transformation */
|
|
}
|
|
} else { /* matrix_mode is zero -- legacy behavior */
|
|
/* this will transform the actual coordinates */
|
|
op2.code = OMOP_TTTF;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op2);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ErrMessage(G, __func__, "No atoms selected.");
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (align_to_update) {
|
|
align_to_update->update();
|
|
}
|
|
|
|
VLAFreeP(op1.vv1);
|
|
VLAFreeP(op2.vv1);
|
|
VLAFreeP(op1.vc1);
|
|
VLAFreeP(op2.vc1);
|
|
VLAFreeP(op1.vp1);
|
|
VLAFreeP(op2.vp1);
|
|
VLAFreeP(op1.ai1VLA);
|
|
VLAFreeP(op2.ai1VLA);
|
|
return (ok);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Implementation of `cmd.identify()`
|
|
*
|
|
* @param s1 atom selection expression
|
|
* @param mode 0 for index only, 1 for (obj, index)
|
|
* @param[out] indexVLA
|
|
* @param[out] objVLA
|
|
* @return number of atoms or -1 on selection error
|
|
*/
|
|
int ExecutiveIdentifyObjects(PyMOLGlobals* G, const char* s1, int mode,
|
|
int** indexVLA, ObjectMolecule*** objVLA)
|
|
{
|
|
SelectorTmp tmpsele1(G, s1);
|
|
int sele1 = tmpsele1.getIndex();
|
|
ObjectMoleculeOpRec op2;
|
|
if (sele1 >= 0) {
|
|
ObjectMoleculeOpRecInit(&op2);
|
|
op2.code = OMOP_IdentifyObjects;
|
|
if (mode != 0) {
|
|
op2.obj1VLA = VLAlloc(ObjectMolecule*, 1000);
|
|
}
|
|
op2.i1VLA = VLAlloc(int, 1000);
|
|
op2.i1 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op2);
|
|
VLASize(op2.i1VLA, int, op2.i1);
|
|
if (mode != 0) {
|
|
VLASize(op2.obj1VLA, ObjectMolecule*, op2.i1);
|
|
}
|
|
(*indexVLA) = op2.i1VLA;
|
|
(*objVLA) = op2.obj1VLA;
|
|
} else {
|
|
return -1;
|
|
}
|
|
return (op2.i1);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveIndex(PyMOLGlobals* G, const char* s1, int mode, int** indexVLA,
|
|
ObjectMolecule*** objVLA)
|
|
{
|
|
ObjectMoleculeOpRec op2;
|
|
|
|
SelectorTmp tmpsele1(G, s1);
|
|
int sele1 = tmpsele1.getIndex();
|
|
|
|
if (sele1 >= 0) {
|
|
ObjectMoleculeOpRecInit(&op2);
|
|
op2.code = OMOP_Index;
|
|
op2.obj1VLA = VLAlloc(ObjectMolecule*, 1000);
|
|
op2.i1VLA = VLAlloc(int, 1000);
|
|
op2.i1 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op2);
|
|
VLASize(op2.i1VLA, int, op2.i1);
|
|
VLASize(op2.obj1VLA, ObjectMolecule*, op2.i1);
|
|
(*indexVLA) = op2.i1VLA;
|
|
(*objVLA) = op2.obj1VLA;
|
|
} else {
|
|
return -1; // invalid selection
|
|
}
|
|
return (op2.i1);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Fit states or calculate ensemble RMSD
|
|
*
|
|
* @param s1 atom selection expression
|
|
* @param target reference state
|
|
* @param mode 2=intra_fit, 1=intra_rms, 0=intra_rms_cur
|
|
* @param mix intra_fit only, average the prior target coordinates
|
|
* @param pbc Consider periodic boundary conditions
|
|
*/
|
|
pymol::Result<pymol::vla<float>> ExecutiveRMSStates(PyMOLGlobals* G,
|
|
const char* s1, int target, int mode, int quiet, int mix, bool pbc)
|
|
{
|
|
SelectorTmp tmpsele1(G, s1);
|
|
int sele1 = tmpsele1.getIndex();
|
|
|
|
ObjectMoleculeOpRec op1;
|
|
ObjectMoleculeOpRec op2;
|
|
float* result = nullptr;
|
|
int ok = true;
|
|
|
|
ObjectMoleculeOpRecInit(&op1);
|
|
ObjectMoleculeOpRecInit(&op2);
|
|
op1.vv1 = nullptr;
|
|
op2.vv1 = nullptr;
|
|
|
|
ObjectMolecule* obj = SelectorGetSingleObjectMolecule(G, sele1);
|
|
|
|
if (!obj) {
|
|
if (mode != 2) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"Executive-Warning: Mobile selection spans more than one object.\n" ENDFB(
|
|
G);
|
|
} else {
|
|
return pymol::make_error("Mobile selection spans more than one object.");
|
|
}
|
|
}
|
|
|
|
if (target == cStateCurrent) {
|
|
target = obj ? obj->getCurrentState() : SceneGetState(G);
|
|
}
|
|
|
|
if (target < 0) {
|
|
target = 0;
|
|
}
|
|
|
|
if (mode != 2) {
|
|
pbc = false;
|
|
}
|
|
|
|
if (ok && sele1 >= 0) {
|
|
op1.code = OMOP_SVRT;
|
|
op1.nvv1 = 0;
|
|
op1.i1 = target;
|
|
op1.vv1 = (float*) VLAMalloc(1000, sizeof(float), 5, 0);
|
|
op1.i1VLA = VLAlloc(int, 1000);
|
|
ExecutiveObjMolSeleOp(G, sele1, &op1);
|
|
|
|
if (pbc) {
|
|
ObjectMoleculePBCUnwrap(*obj);
|
|
}
|
|
|
|
op2.vv2 = op1.vv1;
|
|
op2.nvv2 = op1.nvv1;
|
|
op2.i1VLA = op1.i1VLA;
|
|
op2.i2 = target;
|
|
op2.i1 = mode;
|
|
op2.i3 = mix;
|
|
op2.f1VLA = VLAlloc(float, 10);
|
|
VLASize(op2.f1VLA, float, 0); /* failsafe */
|
|
op2.vv1 = (float*) VLAMalloc(1000, sizeof(float), 5, 0);
|
|
op2.code = OMOP_SFIT;
|
|
op2.nvv1 = 0;
|
|
ExecutiveObjMolSeleOp(G, sele1, &op2);
|
|
result = op2.f1VLA;
|
|
VLAFreeP(op1.i1VLA);
|
|
VLAFreeP(op2.vv1);
|
|
|
|
if (pbc) {
|
|
float center[3];
|
|
pymol::meanNx3(op1.vv1, op1.nvv1, center);
|
|
ObjectMoleculePBCWrap(*obj, center);
|
|
}
|
|
|
|
VLAFreeP(op1.vv1);
|
|
|
|
if (mode == 2) {
|
|
ExecutiveUpdateCoordDepends(G, obj);
|
|
}
|
|
}
|
|
return pymol::vla_take_ownership(result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
float ExecutiveRMSPairs(
|
|
PyMOLGlobals* G, const std::vector<SelectorTmp>& sele, int mode, bool quiet)
|
|
{
|
|
int sele1, sele2;
|
|
int a, c;
|
|
float rms = -1.0, inv, *f;
|
|
OrthoLineType buffer;
|
|
|
|
ObjectMoleculeOpRec op1;
|
|
ObjectMoleculeOpRec op2;
|
|
OrthoLineType combi, s1;
|
|
|
|
ObjectMoleculeOpRecInit(&op1);
|
|
ObjectMoleculeOpRecInit(&op2);
|
|
op1.nvv1 = 0;
|
|
op1.vc1 = (int*) VLAMalloc(1000, sizeof(int), 5, 1);
|
|
op1.vv1 = (float*) VLAMalloc(1000, sizeof(float), 5, 1); /* auto-zero */
|
|
op1.code = OMOP_AVRT;
|
|
|
|
op2.nvv1 = 0;
|
|
op2.vc1 = (int*) VLAMalloc(1000, sizeof(int), 5, 1);
|
|
op2.vv1 = (float*) VLAMalloc(1000, sizeof(float), 5, 1); /* auto-zero */
|
|
op2.code = OMOP_AVRT;
|
|
|
|
strcpy(combi, "(");
|
|
c = 0;
|
|
auto pairs = sele.size() / 2;
|
|
for (a = 0; a < pairs; a++) {
|
|
sele1 = sele[c].getIndex();
|
|
if (sele1 >= 0)
|
|
ExecutiveObjMolSeleOp(G, sele1, &op1);
|
|
strcat(combi, sele[c].getName());
|
|
if (a < (pairs - 1))
|
|
strcat(combi, " or ");
|
|
c++;
|
|
sele2 = sele[c].getIndex();
|
|
if (sele2 >= 0)
|
|
ExecutiveObjMolSeleOp(G, sele2, &op2);
|
|
c++;
|
|
}
|
|
strcat(combi, ")");
|
|
for (a = 0; a < op1.nvv1; a++) {
|
|
inv = (float) op1.vc1[a];
|
|
if (inv) {
|
|
f = op1.vv1 + (a * 3);
|
|
inv = 1.0F / inv;
|
|
*(f++) *= inv;
|
|
*(f++) *= inv;
|
|
*(f++) *= inv;
|
|
}
|
|
}
|
|
for (a = 0; a < op2.nvv1; a++) {
|
|
inv = (float) op2.vc1[a];
|
|
if (inv) {
|
|
f = op2.vv1 + (a * 3);
|
|
inv = 1.0F / inv;
|
|
*(f++) *= inv;
|
|
*(f++) *= inv;
|
|
*(f++) *= inv;
|
|
}
|
|
}
|
|
if (op1.vv1 && op2.vv1) {
|
|
if (op1.nvv1 != op2.nvv1) {
|
|
sprintf(buffer,
|
|
"Atom counts between selection sets don't match (%d != %d).",
|
|
op1.nvv1, op2.nvv1);
|
|
ErrMessage(G, __func__, buffer);
|
|
} else if (op1.nvv1) {
|
|
if (mode != 0)
|
|
rms = MatrixFitRMSTTTf(G, op1.nvv1, op1.vv1, op2.vv1, nullptr, op2.ttt);
|
|
else
|
|
rms = MatrixGetRMS(G, op1.nvv1, op1.vv1, op2.vv1, nullptr);
|
|
|
|
if (!quiet)
|
|
PRINTFB(G, FB_Executive, FB_Results)
|
|
" %s: RMSD = %8.3f (%d to %d atoms)\n", __func__, rms, op1.nvv1,
|
|
op2.nvv1 ENDFB(G);
|
|
|
|
op2.code = OMOP_TTTF;
|
|
SelectorGetTmp(G, combi, s1);
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
ExecutiveObjMolSeleOp(G, sele1, &op2);
|
|
SelectorFreeTmp(G, s1);
|
|
} else {
|
|
ErrMessage(G, __func__, "No atoms selected.");
|
|
}
|
|
}
|
|
VLAFreeP(op1.vv1);
|
|
VLAFreeP(op2.vv1);
|
|
VLAFreeP(op1.vc1);
|
|
VLAFreeP(op2.vc1);
|
|
return (rms);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveUpdateObjectSelection(PyMOLGlobals* G, pymol::CObject* obj)
|
|
{
|
|
if (obj->type == cObjectMolecule) {
|
|
SelectorUpdateObjectSele(G, (ObjectMolecule*) obj);
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Reset camera view or object TTT matrix. Stores key frames for modified
|
|
* objects if `movie_auto_store=on`.
|
|
*
|
|
* @param name Empty, "all", "same", or object name pattern
|
|
*
|
|
* - empty name: Reset camera view
|
|
* - "all": Reset TTT matrices and store key frames for all objects
|
|
* - "same": Reset TTT matrices and store key frames for objects which currently
|
|
* have any key frames
|
|
* - pattern: Reset TTT matrices and store key frames for objects which match
|
|
* the pattern
|
|
*/
|
|
pymol::Result<> ExecutiveReset(PyMOLGlobals* G, pymol::zstring_view name)
|
|
{
|
|
if (name.empty()) {
|
|
SceneResetMatrix(G);
|
|
ExecutiveWindowZoom(
|
|
G, cKeywordAll, 0.0, -1, 0, 0, true); /* reset does all states */
|
|
return {};
|
|
}
|
|
|
|
bool do_reset_all = name == cKeywordAll;
|
|
auto store = SettingGet<bool>(G, cSetting_movie_auto_store);
|
|
|
|
/**
|
|
* @param any_spec_level If false, then filter for objects with spec level >=
|
|
* 0
|
|
*/
|
|
auto reset_rec = [&](SpecRec& rec, bool any_spec_level = true) {
|
|
pymol::CObject* obj = rec.obj;
|
|
if (rec.type == cExecObject &&
|
|
(any_spec_level || ObjectGetSpecLevel(obj, 0) >= 0)) {
|
|
ObjectResetTTT(obj, store);
|
|
obj->invalidate(cRepNone, cRepInvExtents, -1);
|
|
}
|
|
};
|
|
|
|
if (do_reset_all || name == cKeywordSame) {
|
|
for (auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
|
|
reset_rec(rec, do_reset_all);
|
|
}
|
|
} else {
|
|
for (auto& rec : ExecutiveGetSpecRecsFromPattern(G, name)) {
|
|
reset_rec(rec);
|
|
}
|
|
}
|
|
|
|
if (store && SettingGet<bool>(G, cSetting_movie_auto_interpolate)) {
|
|
ExecutiveMotionReinterpolate(G);
|
|
}
|
|
|
|
SceneInvalidate(G);
|
|
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveDrawNow(PyMOLGlobals* G, ExecutiveDrawInfo execDrawInfo)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
OrthoDrawInfo drawInfo{};
|
|
drawInfo.offscreenRender = execDrawInfo.offscreen;
|
|
drawInfo.clearTarget = execDrawInfo.clearTarget;
|
|
|
|
if (PyMOL_GetIdleAndReady(G->PyMOL) &&
|
|
!SettingGetGlobal_b(G, cSetting_suspend_deferred))
|
|
OrthoExecDeferred(G);
|
|
if (!SettingGetGlobal_b(G, cSetting_suspend_updates)) {
|
|
int stereo_mode = SettingGetGlobal_i(G, cSetting_stereo_mode);
|
|
int stereo = SettingGetGlobal_i(G, cSetting_stereo);
|
|
if (G->HaveGUI && G->ValidContext) {
|
|
glMatrixMode(GL_MODELVIEW); /* why is this necessary? is it? */
|
|
}
|
|
|
|
ExecutiveUpdateSceneMembers(G);
|
|
SceneUpdate(G, false);
|
|
if (WizardUpdate(G))
|
|
SceneUpdate(G, false);
|
|
if (stereo) {
|
|
switch (stereo_mode) {
|
|
case cStereo_geowall: {
|
|
int width = G->Option->winX;
|
|
int height = G->Option->winY;
|
|
SceneSetViewport(G, 0, 0, width / 2, height);
|
|
drawInfo.renderMode = OrthoRenderMode::GeoWallLeft;
|
|
OrthoDoDraw(G, drawInfo);
|
|
drawInfo.renderMode = OrthoRenderMode::GeoWallRight;
|
|
OrthoDoDraw(G, drawInfo);
|
|
SceneSetViewport(G, 0, 0, width, height);
|
|
} break;
|
|
#ifdef _PYMOL_OPENVR
|
|
case cStereo_openvr: {
|
|
Block* scene_block = SceneGetBlock(G);
|
|
int scene_width = scene_block->rect.right - scene_block->rect.left;
|
|
int scene_height = scene_block->rect.top - scene_block->rect.bottom;
|
|
OpenVRFrameStart(G);
|
|
float matrix[16];
|
|
SceneGetModel2WorldMatrix(G, matrix);
|
|
OpenVRHandleInput(G, scene_block->rect.left, scene_block->rect.bottom,
|
|
scene_width, scene_height, matrix);
|
|
drawInfo.renderMode = OrthoRenderMode::VR;
|
|
OrthoDoDraw(G, drawInfo);
|
|
if (SettingGetGlobal_b(G, cSetting_openvr_cut_laser) &&
|
|
OpenVRIsScenePickerActive(G)) {
|
|
int x = scene_block->rect.left + scene_width / 2;
|
|
int y = scene_block->rect.bottom + scene_height / 2;
|
|
float atomWorldPos[3];
|
|
ScenePickAtomInWorld(G, x, y, atomWorldPos);
|
|
OpenVRUpdateScenePickerLength(G, atomWorldPos);
|
|
}
|
|
OpenVRFrameFinish(G);
|
|
PyMOL_NeedRedisplay(G->PyMOL);
|
|
} break;
|
|
#endif
|
|
default:
|
|
drawInfo.renderMode = OrthoRenderMode::Main;
|
|
OrthoDoDraw(G, drawInfo);
|
|
break;
|
|
}
|
|
} else {
|
|
drawInfo.renderMode = OrthoRenderMode::Main;
|
|
OrthoDoDraw(G, drawInfo);
|
|
}
|
|
|
|
if (G->HaveGUI && G->ValidContext) {
|
|
if (I->CaptureFlag) {
|
|
I->CaptureFlag = false;
|
|
SceneCaptureWindow(G);
|
|
}
|
|
}
|
|
PyMOL_NeedSwap(G->PyMOL);
|
|
}
|
|
|
|
// PRINTFD(G, FB_Executive)
|
|
// " ExecutiveDrawNow: leaving.\n" ENDFD;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveCountStates(PyMOLGlobals* G, const char* s1)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int sele1;
|
|
int result = 0;
|
|
int n_state;
|
|
SpecRec* list_rec = nullptr;
|
|
if ((!s1) || (!s1[0]))
|
|
s1 = cKeywordAll;
|
|
for (const auto& rec : ExecutiveGetSpecRecsFromPattern(G, s1)) {
|
|
switch (rec.type) {
|
|
case cExecAll:
|
|
while (ListIterate(I->Spec, list_rec, next)) {
|
|
if (list_rec->type == cExecObject) {
|
|
n_state = list_rec->obj->getNFrame();
|
|
if (result < n_state)
|
|
result = n_state;
|
|
}
|
|
}
|
|
break;
|
|
case cExecSelection:
|
|
sele1 = SelectorIndexByName(G, rec.name);
|
|
if (sele1 >= 0) {
|
|
SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
|
|
n_state = SelectorGetSeleNCSet(G, sele1);
|
|
if (result < n_state)
|
|
result = n_state;
|
|
}
|
|
break;
|
|
case cExecObject:
|
|
n_state = rec.obj->getNFrame();
|
|
if (result < n_state)
|
|
result = n_state;
|
|
break;
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveRay(PyMOLGlobals* G, int width, int height, int mode, float angle,
|
|
float shift, int quiet, int defer, int antialias)
|
|
{
|
|
if ((mode == 0) && G->HaveGUI &&
|
|
SettingGetGlobal_b(G, cSetting_auto_copy_images)) {
|
|
/* force deferred behavior if copying image to clipboard */
|
|
defer = 1;
|
|
}
|
|
|
|
ExecutiveUpdateSceneMembers(G);
|
|
|
|
if (defer && (mode == 0)) {
|
|
SceneDeferRay(G, width, height, mode, angle, shift, quiet, true, antialias);
|
|
} else {
|
|
SceneRay(G, width, height, mode, nullptr, nullptr, angle, shift, quiet,
|
|
nullptr, true, antialias);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int* ExecutiveGetG3d(PyMOLGlobals* G)
|
|
{
|
|
int* result = nullptr;
|
|
SceneRay(G, 0, 0, 3, nullptr, nullptr, 0.0F, 0.0F, true,
|
|
(G3dPrimitive**) (void*) &result, false, -1);
|
|
return result;
|
|
}
|
|
|
|
int ExecutiveSetBondSettingFromString(PyMOLGlobals* G, int index,
|
|
const char* value, const char* s1, const char* s2, int state, int quiet,
|
|
int updates)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
int sele1, sele2;
|
|
SettingName name;
|
|
int ok = true;
|
|
int side_effects = false;
|
|
int value_storage[3], *value_ptr;
|
|
float float_storage[3];
|
|
int value_type = 0;
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: entered. '%s' '%s'\n", __func__, s1, s2 ENDFD;
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
sele2 = SelectorIndexByName(G, s2);
|
|
value_ptr = &value_storage[0];
|
|
if ((sele1 >= 0) && (sele2 >= 0)) {
|
|
int have_value = false;
|
|
int type = SettingGetType(G, index);
|
|
switch (type) {
|
|
case cSetting_boolean: {
|
|
if ((!*value) || (*value == '0') || (*value == 'F') ||
|
|
WordMatchExact(G, value, "on", true) ||
|
|
WordMatchExact(G, value, "false", true))
|
|
*(value_ptr) = 0;
|
|
else
|
|
*(value_ptr) = 1;
|
|
value_type = cSetting_boolean;
|
|
have_value = true;
|
|
} break;
|
|
case cSetting_int: {
|
|
if (sscanf(value, "%d", value_ptr) == 1) {
|
|
value_type = cSetting_int;
|
|
have_value = true;
|
|
} else {
|
|
ok = false;
|
|
}
|
|
} break;
|
|
case cSetting_float: {
|
|
if (sscanf(value, "%f", &float_storage[0]) == 1) {
|
|
value_ptr = (int*) (void*) &float_storage[0];
|
|
value_type = cSetting_float;
|
|
have_value = true;
|
|
} else {
|
|
ok = false;
|
|
}
|
|
} break;
|
|
case cSetting_float3:
|
|
if (sscanf(value, "%f%f%f", &float_storage[0], &float_storage[1],
|
|
&float_storage[2]) == 3) {
|
|
value_ptr = (int*) (void*) &float_storage[0];
|
|
value_type = cSetting_float3;
|
|
have_value = true;
|
|
} else {
|
|
ok = false;
|
|
}
|
|
break;
|
|
case cSetting_color: {
|
|
int color_index = ColorGetIndex(G, value);
|
|
if ((color_index < 0) && (color_index > cColorExtCutoff))
|
|
color_index = 0;
|
|
*(value_ptr) = color_index;
|
|
value_type = cSetting_color;
|
|
have_value = true;
|
|
} break;
|
|
/* cSetting_string? */
|
|
default:
|
|
ok = false;
|
|
break;
|
|
}
|
|
|
|
if (ok && have_value) {
|
|
rec = nullptr;
|
|
while ((ListIterate(I->Spec, rec, next))) {
|
|
if ((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
{
|
|
int a, nBond = obj->NBond;
|
|
int nSet = 0;
|
|
BondType* bi = obj->Bond.data();
|
|
const AtomInfoType *ai1, *ai2, *ai = obj->AtomInfo.data();
|
|
for (a = 0; a < nBond; a++) {
|
|
ai1 = ai + bi->index[0];
|
|
ai2 = ai + bi->index[1];
|
|
if ((SelectorIsMember(G, ai1->selEntry, sele1) &&
|
|
SelectorIsMember(G, ai2->selEntry, sele2)) ||
|
|
(SelectorIsMember(G, ai2->selEntry, sele1) &&
|
|
SelectorIsMember(G, ai1->selEntry, sele2))) {
|
|
|
|
int uid = AtomInfoCheckUniqueBondID(G, bi);
|
|
int isset;
|
|
bi->has_setting = true;
|
|
isset = SettingUniqueSetTypedValue(
|
|
G, uid, index, value_type, value_ptr);
|
|
if (updates && isset)
|
|
side_effects = true;
|
|
nSet++;
|
|
}
|
|
bi++;
|
|
}
|
|
if (nSet && !quiet) {
|
|
SettingGetName(G, index, name);
|
|
PRINTF
|
|
" Setting: %s set for %d bonds in object \"%s\".\n", name, nSet,
|
|
obj->Name ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (side_effects) {
|
|
SettingGenerateSideEffects(
|
|
G, index, s1, state, quiet); /* not strickly correct */
|
|
/* SettingGenerateSideEffects(G,index,s2,state); */
|
|
}
|
|
return (ok);
|
|
}
|
|
/*========================================================================*/
|
|
/**
|
|
* @pre GIL
|
|
*/
|
|
PyObject* ExecutiveGetBondSetting(PyMOLGlobals* G, int index, char* s1,
|
|
const char* s2, int state, int quiet, int updates)
|
|
{
|
|
#ifdef _PYMOL_NOPY
|
|
return 0;
|
|
#else
|
|
assert(PyGILState_Check());
|
|
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
int sele1, sele2;
|
|
SettingName name;
|
|
PyObject* result = PyList_New(0);
|
|
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
sele2 = SelectorIndexByName(G, s2);
|
|
|
|
if ((sele1 >= 0) && (sele2 >= 0)) {
|
|
while ((ListIterate(I->Spec, rec, next))) {
|
|
if ((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
{
|
|
int a, nBond = obj->NBond;
|
|
int nSet = 0;
|
|
const BondType* bi = obj->Bond.data();
|
|
const AtomInfoType *ai1, *ai2, *ai = obj->AtomInfo.data();
|
|
|
|
PyObject* pyObjList = nullptr;
|
|
PyObject* pyBondList = nullptr;
|
|
|
|
for (a = 0; a < nBond; a++) {
|
|
ai1 = ai + bi->index[0];
|
|
ai2 = ai + bi->index[1];
|
|
if ((SelectorIsMember(G, ai1->selEntry, sele1) &&
|
|
SelectorIsMember(G, ai2->selEntry, sele2)) ||
|
|
(SelectorIsMember(G, ai2->selEntry, sele1) &&
|
|
SelectorIsMember(G, ai1->selEntry, sele2))) {
|
|
PyObject* pyBondInfo = PyList_New(3);
|
|
PyObject* bond_setting_value = nullptr;
|
|
if (!pyObjList) {
|
|
pyObjList = PyList_New(2);
|
|
pyBondList = PyList_New(0);
|
|
PyList_SetItem(pyObjList, 0, PyString_FromString(obj->Name));
|
|
PyList_SetItem(pyObjList, 1, pyBondList);
|
|
PyList_Append(result, pyObjList);
|
|
Py_DECREF(pyObjList);
|
|
}
|
|
PyList_SetItem(
|
|
pyBondInfo, 0, PyInt_FromLong((long) bi->index[0] + 1));
|
|
PyList_SetItem(
|
|
pyBondInfo, 1, PyInt_FromLong((long) bi->index[1] + 1));
|
|
if (bi->has_setting) {
|
|
bond_setting_value =
|
|
SettingUniqueGetPyObject(G, bi->unique_id, index);
|
|
}
|
|
PyList_SetItem(pyBondInfo, 2, PConvAutoNone(bond_setting_value));
|
|
PyList_Append(pyBondList, pyBondInfo);
|
|
Py_DECREF(pyBondInfo);
|
|
nSet++;
|
|
}
|
|
bi++;
|
|
}
|
|
if (nSet && !quiet) {
|
|
SettingGetName(G, index, name);
|
|
PRINTF
|
|
" Getting: %s for %d bonds in object \"%s\".\n", name, nSet,
|
|
obj->Name ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
#endif
|
|
}
|
|
/*========================================================================*/
|
|
int ExecutiveSetBondSetting(PyMOLGlobals* G, int index, PyObject* tuple,
|
|
const char* s1, const char* s2, int state, int quiet, int updates)
|
|
{
|
|
#ifdef _PYMOL_NOPY
|
|
return 0;
|
|
#else
|
|
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
int sele1, sele2;
|
|
SettingName name = "";
|
|
int unblock;
|
|
int ok = true;
|
|
int side_effects = false;
|
|
union {
|
|
int value_ptr[1];
|
|
float float_storage[1];
|
|
};
|
|
int value_type = 0;
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: entered. '%s' '%s'\n", __func__, s1, s2 ENDFD;
|
|
unblock = PAutoBlock(G);
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
sele2 = SelectorIndexByName(G, s2);
|
|
if ((sele1 >= 0) && (sele2 >= 0)) {
|
|
int have_value = false;
|
|
int type = PyInt_AsLong(PyTuple_GetItem(tuple, 0));
|
|
PyObject* value = PyTuple_GetItem(tuple, 1);
|
|
if (value) {
|
|
switch (type) {
|
|
case cSetting_boolean:
|
|
*(value_ptr) = PyInt_AsLong(value);
|
|
value_type = cSetting_boolean;
|
|
have_value = true;
|
|
break;
|
|
case cSetting_int:
|
|
*(value_ptr) = PyInt_AsLong(value);
|
|
value_type = cSetting_int;
|
|
have_value = true;
|
|
break;
|
|
case cSetting_float:
|
|
float_storage[0] = PyFloat_AsDouble(value);
|
|
value_type = cSetting_float;
|
|
have_value = true;
|
|
break;
|
|
case cSetting_color: {
|
|
int color_index = ColorGetIndex(G, PyString_AsString(value));
|
|
if ((color_index < 0) && (color_index > cColorExtCutoff))
|
|
color_index = 0;
|
|
*(value_ptr) = color_index;
|
|
value_type = cSetting_color;
|
|
have_value = true;
|
|
} break;
|
|
}
|
|
if (have_value) {
|
|
rec = nullptr;
|
|
while ((ListIterate(I->Spec, rec, next))) {
|
|
if ((rec->type == cExecObject) &&
|
|
(rec->obj->type == cObjectMolecule)) {
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
{
|
|
int a, nBond = obj->NBond;
|
|
int nSet = 0;
|
|
BondType* bi = obj->Bond.data();
|
|
const AtomInfoType *ai1, *ai2, *ai = obj->AtomInfo.data();
|
|
for (a = 0; a < nBond; a++) {
|
|
ai1 = ai + bi->index[0];
|
|
ai2 = ai + bi->index[1];
|
|
if ((SelectorIsMember(G, ai1->selEntry, sele1) &&
|
|
SelectorIsMember(G, ai2->selEntry, sele2)) ||
|
|
(SelectorIsMember(G, ai2->selEntry, sele1) &&
|
|
SelectorIsMember(G, ai1->selEntry, sele2))) {
|
|
|
|
int uid = AtomInfoCheckUniqueBondID(G, bi);
|
|
bi->has_setting = true;
|
|
SettingUniqueSetTypedValue(
|
|
G, uid, index, value_type, value_ptr);
|
|
if (updates)
|
|
side_effects = true;
|
|
nSet++;
|
|
}
|
|
bi++;
|
|
}
|
|
if (nSet && !quiet) {
|
|
SettingGetName(G, index, name);
|
|
PRINTF
|
|
" Setting: %s set for %d bonds in object \"%s\".\n", name, nSet,
|
|
obj->Name ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (side_effects) {
|
|
SettingGenerateSideEffects(
|
|
G, index, s1, state, quiet); /* not strictly correct */
|
|
/* SettingGenerateSideEffects(G,index,s2,state); */
|
|
}
|
|
|
|
if (!SettingLevelCheck(G, index, cSettingLevel_bond)) {
|
|
if (!name[0])
|
|
SettingGetName(G, index, name);
|
|
|
|
PRINTFB(G, FB_Setting, FB_Warnings)
|
|
" Setting-Warning: '%s' is not a bond-level setting\n", name ENDFB(G);
|
|
}
|
|
|
|
PAutoUnblock(G, unblock);
|
|
return (ok);
|
|
#endif
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* @return Always true
|
|
*/
|
|
int ExecutiveUnsetBondSetting(PyMOLGlobals* G, int index, const char* s1,
|
|
const char* s2, int state, int quiet, int updates)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
SettingName name;
|
|
/* int unblock; */
|
|
int ok = true;
|
|
int side_effects = false;
|
|
int sele1, sele2;
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: entered. sele '%s' '%s'\n", __func__, s1, s2 ENDFD;
|
|
/* unblock = PAutoBlock(G); */
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
sele2 = SelectorIndexByName(G, s2);
|
|
if ((sele1 >= 0) && (sele2 >= 0)) {
|
|
rec = nullptr;
|
|
while ((ListIterate(I->Spec, rec, next))) {
|
|
if ((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
{
|
|
int nSet = 0;
|
|
BondType* bi = obj->Bond.data();
|
|
BondType* bi_end = bi + obj->NBond;
|
|
AtomInfoType *ai1, *ai2, *ai = obj->AtomInfo.data();
|
|
for (; bi != bi_end; ++bi) {
|
|
if (!bi->has_setting)
|
|
continue;
|
|
ai1 = ai + bi->index[0];
|
|
ai2 = ai + bi->index[1];
|
|
if ((SelectorIsMember(G, ai1->selEntry, sele1) &&
|
|
SelectorIsMember(G, ai2->selEntry, sele2)) ||
|
|
(SelectorIsMember(G, ai2->selEntry, sele1) &&
|
|
SelectorIsMember(G, ai1->selEntry, sele2))) {
|
|
int uid = AtomInfoCheckUniqueBondID(G, bi);
|
|
if (!SettingUniqueUnset(G, uid, index))
|
|
continue;
|
|
if (updates)
|
|
side_effects = true;
|
|
nSet++;
|
|
}
|
|
}
|
|
if (nSet && !quiet) {
|
|
SettingGetName(G, index, name);
|
|
PRINTF
|
|
" Setting: %s unset for %d bonds in object \"%s\".\n", name, nSet,
|
|
rec->obj->Name ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (side_effects) {
|
|
SettingGenerateSideEffects(G, index, s1, state, quiet);
|
|
/* SettingGenerateSideEffects(G,index,s2,state); */
|
|
}
|
|
/* PAutoUnblock(G, unblock); */
|
|
return (ok);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveSetSetting(PyMOLGlobals* G, int index, PyObject* tuple,
|
|
pymol::zstring_view preSele, int state, int quiet, int updates)
|
|
{
|
|
#ifdef _PYMOL_NOPY
|
|
return pymol::make_error("Python not available.");
|
|
#else
|
|
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
int sele1;
|
|
ObjectMoleculeOpRec op;
|
|
OrthoLineType value;
|
|
pymol::copyable_ptr<CSetting>* handle = nullptr;
|
|
SettingName name = "";
|
|
int nObj = 0;
|
|
const char* sele = preSele.c_str();
|
|
int ok = true;
|
|
|
|
pymol::Result<SelectorTmp2> s1;
|
|
|
|
if (!preSele.empty()) {
|
|
s1 = SelectorTmp2::make(G, preSele.c_str());
|
|
p_return_if_error(s1);
|
|
sele = s1->getName();
|
|
}
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: entered. sele \"%s\" updates=%d index=%d\n", __func__, sele, updates,
|
|
index ENDFD;
|
|
|
|
if (!quiet) {
|
|
SettingGetName(G, index, name);
|
|
}
|
|
|
|
pymol::pautoblock unblock(G);
|
|
|
|
if ((!sele) || (sele[0] == 0)) { /* global setting */
|
|
ok = SettingSetFromTuple(G, nullptr, index, tuple);
|
|
if (ok) {
|
|
if (!quiet) {
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
SettingGetTextValue(G, nullptr, nullptr, index, value);
|
|
PRINTF " Setting: %s set to %s.\n", name, value ENDF(G);
|
|
}
|
|
}
|
|
if (updates) {
|
|
SettingGenerateSideEffects(G, index, nullptr, state, quiet);
|
|
}
|
|
}
|
|
} else {
|
|
unsigned char levelmask = 0;
|
|
int side_effects = false;
|
|
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, sele, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
levelmask |= SettingLevelInfo[state < 0 ? cSettingLevel_object
|
|
: cSettingLevel_ostate]
|
|
.mask;
|
|
rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
{
|
|
handle = rec->obj->getSettingHandle(state);
|
|
if (handle) {
|
|
SettingCheckHandle(G, *handle);
|
|
ok = SettingSetFromTuple(G, handle->get(), index, tuple);
|
|
if (updates)
|
|
side_effects = true;
|
|
nObj++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
if (nObj && handle) {
|
|
SettingGetTextValue(G, handle->get(), nullptr, index, value);
|
|
if (!quiet) {
|
|
if (state < 0) {
|
|
PRINTF
|
|
" Setting: %s set to %s in %d objects.\n", name, value,
|
|
nObj ENDF(G);
|
|
} else {
|
|
PRINTF
|
|
" Setting: %s set to %s in %d objects, state %d.\n", name,
|
|
value, nObj, state + 1 ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecSelection:
|
|
if (SettingLevelCheckMask(
|
|
G, index, SettingLevelInfo[cSettingLevel_bond].mask)) {
|
|
// handle bond-level settings (PYMOL-2726)
|
|
ok = ExecutiveSetBondSetting(
|
|
G, index, tuple, sele, sele, state, quiet, false);
|
|
if (updates)
|
|
side_effects = true;
|
|
sele1 = -1;
|
|
} else {
|
|
levelmask |= SettingLevelInfo[cSettingLevel_atom].mask;
|
|
sele1 = SelectorIndexByName(G, rec->name);
|
|
}
|
|
|
|
if (sele1 >= 0) {
|
|
int have_atomic_value = false;
|
|
int type = PyInt_AsLong(PyTuple_GetItem(tuple, 0));
|
|
PyObject* value = PyTuple_GetItem(tuple, 1);
|
|
if (value) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_SetAtomicSetting;
|
|
op.i1 = index;
|
|
op.ii1 = &op.i3;
|
|
switch (type) {
|
|
case cSetting_boolean:
|
|
*(op.ii1) = PyInt_AsLong(value);
|
|
op.i2 = cSetting_boolean;
|
|
have_atomic_value = true;
|
|
break;
|
|
case cSetting_int:
|
|
*(op.ii1) = PyInt_AsLong(value);
|
|
op.i2 = cSetting_int;
|
|
have_atomic_value = true;
|
|
break;
|
|
case cSetting_float:
|
|
*(float*) op.ii1 = (float) PyFloat_AsDouble(value);
|
|
op.i2 = cSetting_float;
|
|
have_atomic_value = true;
|
|
break;
|
|
case cSetting_float3: {
|
|
PConvPyListOrTupleToFloatArrayInPlace(value, op.ttt, 3);
|
|
op.mat1 = op.ttt; // for passing (float**)
|
|
op.ii1 = (int*) &op.mat1;
|
|
op.i2 = cSetting_float3;
|
|
have_atomic_value = true;
|
|
} break;
|
|
case cSetting_color: {
|
|
int color_index = ColorGetIndex(G, PyString_AsString(value));
|
|
if ((color_index < 0) && (color_index > cColorExtCutoff)) {
|
|
switch (color_index) {
|
|
case cColorAtomic:
|
|
color_index = -1;
|
|
break;
|
|
case cColorFront:
|
|
case cColorBack:
|
|
case cColorDefault:
|
|
break;
|
|
default:
|
|
color_index = 0;
|
|
break;
|
|
}
|
|
}
|
|
*(op.ii1) = color_index;
|
|
op.i2 = cSetting_color;
|
|
have_atomic_value = true;
|
|
} break;
|
|
}
|
|
if (have_atomic_value) {
|
|
rec = nullptr;
|
|
while ((ListIterate(I->Spec, rec, next))) {
|
|
if ((rec->type == cExecObject) &&
|
|
(rec->obj->type == cObjectMolecule)) {
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
op.i4 = 0;
|
|
ObjectMoleculeSeleOp(obj, sele1, &op);
|
|
if (op.i4) {
|
|
if (updates)
|
|
side_effects = true;
|
|
if (!quiet) {
|
|
PRINTF
|
|
" Setting: %s set for %d atoms in object \"%s\".\n",
|
|
name, op.i4, rec->obj->Name ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecObject:
|
|
levelmask |= SettingLevelInfo[state < 0 ? cSettingLevel_object
|
|
: cSettingLevel_ostate]
|
|
.mask;
|
|
{
|
|
handle = rec->obj->getSettingHandle(state);
|
|
if (handle) {
|
|
SettingCheckHandle(G, *handle);
|
|
ok = SettingSetFromTuple(G, handle->get(), index, tuple);
|
|
if (ok) {
|
|
if (updates)
|
|
side_effects = true;
|
|
if (!quiet) {
|
|
if (state < 0) { /* object-specific */
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
SettingGetTextValue(
|
|
G, handle->get(), nullptr, index, value);
|
|
PRINTF
|
|
" Setting: %s set to %s in object \"%s\".\n", name, value,
|
|
rec->obj->Name ENDF(G);
|
|
}
|
|
} else { /* state-specific */
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
SettingGetTextValue(
|
|
G, handle->get(), nullptr, index, value);
|
|
PRINTF
|
|
" Setting: %s set to %s in object \"%s\", state %d.\n",
|
|
name, value, rec->obj->Name, state + 1 ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
|
|
if (side_effects)
|
|
SettingGenerateSideEffects(G, index, sele, state, quiet);
|
|
|
|
if (!SettingLevelCheckMask(G, index, levelmask)) {
|
|
if (!name[0])
|
|
SettingGetName(G, index, name);
|
|
|
|
PRINTFB(G, FB_Setting, FB_Warnings)
|
|
" Setting-Warning: '%s' is a %s-level setting\n", name,
|
|
SettingLevelGetName(index) ENDFB(G);
|
|
}
|
|
}
|
|
|
|
if (!ok) {
|
|
return pymol::make_error("Error");
|
|
}
|
|
return {};
|
|
#endif
|
|
}
|
|
|
|
int ExecutiveGetSettingFromString(PyMOLGlobals* G, PyMOLreturn_value* result,
|
|
int index, const char* sele, int state, int quiet)
|
|
{
|
|
pymol::CObject* obj = nullptr;
|
|
CSetting *set_ptr1 = nullptr, *set_ptr2 = nullptr;
|
|
pymol::copyable_ptr<CSetting>* handle = nullptr;
|
|
int ok = true;
|
|
int type;
|
|
type = SettingGetType(G, index);
|
|
if (sele)
|
|
if (sele[0]) {
|
|
obj = ExecutiveFindObjectByName(G, sele);
|
|
if (!obj)
|
|
ok = false;
|
|
}
|
|
if (!ok) {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" %s-Error: sele \"%s\" not found.\n", __func__, sele ENDFB(G);
|
|
ok = false;
|
|
} else if (obj) {
|
|
handle = obj->getSettingHandle(-1);
|
|
if (handle)
|
|
set_ptr1 = handle->get();
|
|
if (state >= 0) {
|
|
handle = obj->getSettingHandle(state);
|
|
if (handle)
|
|
set_ptr2 = handle->get();
|
|
else {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
" %s-Error: sele \"%s\" lacks state %d.\n", __func__, sele,
|
|
state + 1 ENDFB(G);
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
if (ok) {
|
|
switch (type) {
|
|
case cSetting_boolean: {
|
|
int value = SettingGet_b(G, set_ptr2, set_ptr1, index);
|
|
result->type = PYMOL_RETURN_VALUE_IS_INT;
|
|
result->int_value = value;
|
|
} break;
|
|
case cSetting_int: {
|
|
int value = SettingGet_i(G, set_ptr2, set_ptr1, index);
|
|
result->type = PYMOL_RETURN_VALUE_IS_INT;
|
|
result->int_value = value;
|
|
} break;
|
|
case cSetting_float: {
|
|
float value = SettingGet_f(G, set_ptr2, set_ptr1, index);
|
|
result->type = PYMOL_RETURN_VALUE_IS_FLOAT;
|
|
result->float_value = value;
|
|
} break;
|
|
case cSetting_float3: {
|
|
result->type = PYMOL_RETURN_VALUE_IS_FLOAT_ARRAY;
|
|
result->float_array = VLAlloc(float, 3);
|
|
result->array_length = 3;
|
|
copy3f(SettingGet<const float*>(G, set_ptr2, set_ptr1, index),
|
|
result->float_array);
|
|
} break;
|
|
case cSetting_color: {
|
|
int value = SettingGet_color(G, set_ptr2, set_ptr1, index);
|
|
result->type = PYMOL_RETURN_VALUE_IS_INT;
|
|
result->int_value = value;
|
|
} break;
|
|
case cSetting_string: {
|
|
OrthoLineType buffer = "";
|
|
result->type = PYMOL_RETURN_VALUE_IS_STRING;
|
|
result->string =
|
|
mstrdup(SettingGetTextPtr(G, set_ptr2, set_ptr1, index, buffer));
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
int ExecutiveSetSettingFromString(PyMOLGlobals* G, int index, const char* value,
|
|
const char* sele, int state, int quiet, int updates)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
int sele1;
|
|
ObjectMoleculeOpRec op;
|
|
OrthoLineType value2;
|
|
pymol::copyable_ptr<CSetting>* handle = nullptr;
|
|
SettingName name;
|
|
int nObj = 0;
|
|
int ok = true;
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: entered. sele \"%s\"\n", __func__, sele ENDFD;
|
|
if (sele[0] == 0) { /* global setting */
|
|
ok = SettingSetFromString(G, nullptr, index, value);
|
|
if (ok) {
|
|
if (!quiet) {
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
SettingGetTextValue(G, nullptr, nullptr, index, value2);
|
|
SettingGetName(G, index, name);
|
|
PRINTF " Setting: %s set to %s.\n", name, value2 ENDF(G);
|
|
}
|
|
}
|
|
if (updates)
|
|
SettingGenerateSideEffects(G, index, sele, state, quiet);
|
|
}
|
|
} else {
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, sele, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
{
|
|
handle = rec->obj->getSettingHandle(state);
|
|
if (handle) {
|
|
SettingCheckHandle(G, *handle);
|
|
ok = SettingSetFromString(G, handle->get(), index, value);
|
|
if (updates)
|
|
SettingGenerateSideEffects(
|
|
G, index, rec->name, state, quiet);
|
|
nObj++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
if (nObj && handle) {
|
|
SettingGetTextValue(G, handle->get(), nullptr, index, value2);
|
|
SettingGetName(G, index, name);
|
|
if (!quiet) {
|
|
if (state < 0) {
|
|
PRINTF
|
|
" Setting: %s set to %s in %d objects.\n", name, value2,
|
|
nObj ENDF(G);
|
|
} else {
|
|
PRINTF
|
|
" Setting: %s set to %s in %d objects, state %d.\n", name,
|
|
value2, nObj, state + 1 ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecSelection:
|
|
/* this code has not yet been tested... */
|
|
|
|
sele1 = SelectorIndexByName(G, rec->name);
|
|
if (sele1 >= 0) {
|
|
int type;
|
|
int value_store;
|
|
if (SettingStringToTypedValue(
|
|
G, index, value, &type, &value_store)) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_SetAtomicSetting;
|
|
op.i1 = index;
|
|
op.i2 = type;
|
|
op.ii1 = &value_store;
|
|
rec = nullptr;
|
|
while ((ListIterate(I->Spec, rec, next))) {
|
|
if ((rec->type == cExecObject) &&
|
|
(rec->obj->type == cObjectMolecule)) {
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
op.i4 = 0;
|
|
ObjectMoleculeSeleOp(obj, sele1, &op);
|
|
if (op.i4) {
|
|
if (updates)
|
|
SettingGenerateSideEffects(
|
|
G, index, rec->name, state, quiet);
|
|
if (!quiet) {
|
|
SettingGetName(G, index, name);
|
|
PRINTF
|
|
" Setting: %s set for %d atoms in object \"%s\".\n", name,
|
|
op.i4, rec->obj->Name ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecObject: {
|
|
handle = rec->obj->getSettingHandle(state);
|
|
if (handle) {
|
|
SettingCheckHandle(G, *handle);
|
|
ok = SettingSetFromString(G, handle->get(), index, value);
|
|
if (ok) {
|
|
if (updates)
|
|
SettingGenerateSideEffects(G, index, sele, state, quiet);
|
|
if (!quiet) {
|
|
if (state < 0) { /* object-specific */
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
SettingGetTextValue(
|
|
G, handle->get(), nullptr, index, value2);
|
|
SettingGetName(G, index, name);
|
|
PRINTF
|
|
" Setting: %s set to %s in object \"%s\".\n", name, value2,
|
|
rec->obj->Name ENDF(G);
|
|
}
|
|
} else { /* state-specific */
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
SettingGetTextValue(
|
|
G, handle->get(), nullptr, index, value2);
|
|
SettingGetName(G, index, name);
|
|
PRINTF
|
|
" Setting: %s set to %s in object \"%s\", state %d.\n",
|
|
name, value2, rec->obj->Name, state + 1 ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
int ExecutiveSetObjSettingFromString(PyMOLGlobals* G, int index,
|
|
const char* value, pymol::CObject* obj, int state, int quiet, int updates)
|
|
{
|
|
OrthoLineType value2;
|
|
pymol::copyable_ptr<CSetting>* handle = nullptr;
|
|
SettingName name;
|
|
int ok = true;
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveSetObjSettingFromString: entered \n" ENDFD;
|
|
if (!obj) { /* global */
|
|
ok = SettingSetFromString(G, nullptr, index, value);
|
|
if (ok) {
|
|
if (!quiet) {
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
SettingGetTextValue(G, nullptr, nullptr, index, value2);
|
|
SettingGetName(G, index, name);
|
|
PRINTF " Setting: %s set to %s.\n", name, value2 ENDF(G);
|
|
}
|
|
}
|
|
if (updates)
|
|
SettingGenerateSideEffects(G, index, obj->Name, state, quiet);
|
|
}
|
|
} else { /* based on a single object */
|
|
{
|
|
handle = obj->getSettingHandle(state);
|
|
if (handle) {
|
|
SettingCheckHandle(G, *handle);
|
|
ok = SettingSetFromString(G, handle->get(), index, value);
|
|
if (ok) {
|
|
if (updates)
|
|
SettingGenerateSideEffects(G, index, obj->Name, state, quiet);
|
|
if (!quiet) {
|
|
if (state < 0) { /* object-specific */
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
SettingGetTextValue(G, handle->get(), nullptr, index, value2);
|
|
SettingGetName(G, index, name);
|
|
PRINTF
|
|
" Setting: %s set to %s in object \"%s\".\n", name, value2,
|
|
obj->Name ENDF(G);
|
|
}
|
|
} else { /* state-specific */
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
SettingGetTextValue(G, handle->get(), nullptr, index, value2);
|
|
SettingGetName(G, index, name);
|
|
PRINTF
|
|
" Setting: %s set to %s in object \"%s\", state %d.\n", name,
|
|
value2, obj->Name, state + 1 ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Restore setting to default value.
|
|
*
|
|
* The effective value will be the value at the next higher level (e.g. unset
|
|
* state-level -> use object-level) or the value stored in `G->Default` for
|
|
* global settings.
|
|
*
|
|
* @return Always true
|
|
*/
|
|
pymol::Result<> ExecutiveUnsetSetting(PyMOLGlobals* G, int index,
|
|
pymol::zstring_view preSele, int state, int quiet, int updates)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
int sele1;
|
|
ObjectMoleculeOpRec op;
|
|
pymol::copyable_ptr<CSetting>* handle = nullptr;
|
|
const char* name = SettingGetName(index);
|
|
int nObj = 0;
|
|
int ok = true;
|
|
const char* sele = preSele.c_str();
|
|
|
|
pymol::Result<SelectorTmp2> s1;
|
|
|
|
if (!preSele.empty()) {
|
|
s1 = SelectorTmp2::make(G, preSele.c_str());
|
|
p_return_if_error(s1);
|
|
sele = s1->getName();
|
|
}
|
|
|
|
if (sele[0] == 0) {
|
|
SettingRestoreDefault(G->Setting, index, G->Default);
|
|
if (!quiet && Feedback(G, FB_Executive, FB_Actions)) {
|
|
OrthoLineType value = "";
|
|
SettingGetTextValue(G, nullptr, nullptr, index, value);
|
|
PRINTF " Setting: %s restored to default (%s)\n", name, value ENDF(G);
|
|
}
|
|
} else {
|
|
// Undefine per-object, per-state, or per-atom settings.
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, sele, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
{
|
|
handle = rec->obj->getSettingHandle(state);
|
|
if (handle && *handle && SettingUnset(handle->get(), index)) {
|
|
nObj++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
if (nObj && handle) {
|
|
if (!quiet) {
|
|
if (state < 0) {
|
|
PRINTF " Setting: %s unset in %d objects.\n", name,
|
|
nObj ENDF(G);
|
|
} else {
|
|
PRINTF
|
|
" Setting: %s unset in %d objects, state %d.\n", name, nObj,
|
|
state + 1 ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecSelection:
|
|
if (SettingLevelCheckMask(
|
|
G, index, SettingLevelInfo[cSettingLevel_bond].mask)) {
|
|
// handle bond-level settings (PYMOL-2726)
|
|
ok = ExecutiveUnsetBondSetting(
|
|
G, index, sele, sele, state, quiet, false);
|
|
sele1 = -1;
|
|
} else {
|
|
sele1 = SelectorIndexByName(G, rec->name);
|
|
}
|
|
|
|
if (sele1 >= 0) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_SetAtomicSetting;
|
|
op.i1 = index;
|
|
op.i2 = cSetting_blank;
|
|
op.ii1 = nullptr;
|
|
|
|
rec = nullptr;
|
|
while ((ListIterate(I->Spec, rec, next))) {
|
|
if ((rec->type == cExecObject) &&
|
|
(rec->obj->type == cObjectMolecule)) {
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
op.i4 = 0;
|
|
ObjectMoleculeSeleOp(obj, sele1, &op);
|
|
if (op.i4) {
|
|
if (!quiet) {
|
|
PRINTF
|
|
" Setting: %s unset for %d atoms in object \"%s\".\n", name,
|
|
op.i4, rec->obj->Name ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecObject: {
|
|
handle = rec->obj->getSettingHandle(state);
|
|
if (handle && *handle && SettingUnset(handle->get(), index)) {
|
|
if (!quiet) {
|
|
if (state < 0) { /* object-specific */
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
PRINTF
|
|
" Setting: %s unset in object \"%s\".\n", name,
|
|
rec->obj->Name ENDF(G);
|
|
}
|
|
} else { /* state-specific */
|
|
if (Feedback(G, FB_Setting, FB_Actions)) {
|
|
PRINTF
|
|
" Setting: %s unset in object \"%s\", state %d.\n", name,
|
|
rec->obj->Name, state + 1 ENDF(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
if (updates)
|
|
SettingGenerateSideEffects(G, index, sele, state, quiet);
|
|
if (!ok) {
|
|
return pymol::make_error("Error");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveColorFromSele(
|
|
PyMOLGlobals* G, const char* sele, const char* color, int flags, int quiet)
|
|
{
|
|
auto s1 = SelectorTmp2::make(G, sele);
|
|
p_return_if_error(s1);
|
|
return ExecutiveColor(G, s1->getName(), color, flags, quiet);
|
|
}
|
|
|
|
pymol::Result<> ExecutiveColor(
|
|
PyMOLGlobals* G, const char* name, const char* color, int flags, int quiet)
|
|
{
|
|
/* flags:
|
|
0x1 -- ignore or suppress selection name matches
|
|
*/
|
|
|
|
CExecutive* I = G->Executive;
|
|
int col_ind;
|
|
int ok = false;
|
|
col_ind = ColorGetIndex(G, color);
|
|
if ((!name) || (!name[0]))
|
|
name = cKeywordAll;
|
|
if (col_ind == -1) {
|
|
return pymol::make_error("Unknown color.");
|
|
} else {
|
|
CTracker* I_Tracker = I->Tracker;
|
|
SpecRec* rec = nullptr;
|
|
int n_atm = 0;
|
|
int n_obj = 0;
|
|
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecSelection:
|
|
case cExecObject:
|
|
case cExecAll:
|
|
if ((rec->type == cExecSelection) || /* coloring a selection */
|
|
(rec->type == cExecAll) || /* coloring all */
|
|
((rec->type == cExecObject) && /* coloring object and its backing
|
|
selection */
|
|
(rec->obj->type == cObjectMolecule))) {
|
|
if (!(flags & 0x1)) {
|
|
int sele = SelectorIndexByName(G, rec->name);
|
|
ObjectMoleculeOpRec op;
|
|
if (sele >= 0) {
|
|
ok = true;
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_COLR;
|
|
op.i1 = col_ind;
|
|
op.i2 = n_atm;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
n_atm = op.i2;
|
|
op.code = OMOP_INVA;
|
|
op.i1 = cRepBitmask;
|
|
op.i2 = cRepInvColor;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (rec->type) { /* sets object color */
|
|
case cExecObject:
|
|
rec->obj->Color = col_ind;
|
|
rec->obj->invalidate(cRepAll, cRepInvColor, -1);
|
|
n_obj++;
|
|
ok = true;
|
|
SceneInvalidate(G);
|
|
break;
|
|
case cExecAll:
|
|
rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
rec->obj->Color = col_ind;
|
|
rec->obj->invalidate(cRepAll, cRepInvColor, -1);
|
|
n_obj++;
|
|
ok = true;
|
|
SceneInvalidate(G);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
|
|
if (n_obj || n_atm) {
|
|
char atms[] = "s";
|
|
char objs[] = "s";
|
|
if (n_obj < 2)
|
|
objs[0] = 0;
|
|
if (n_atm < 2)
|
|
atms[0] = 0;
|
|
if (!quiet) {
|
|
|
|
if (n_obj && n_atm) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Executive: Colored %d atom%s and %d object%s.\n", n_atm, atms,
|
|
n_obj, objs ENDFB(G);
|
|
} else if (n_obj) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Executive: Colored %d object%s.\n", n_obj, objs ENDFB(G);
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Executive: Colored %d atom%s.\n", n_atm, atms ENDFB(G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
const char* ExecutiveFindBestNameMatch(PyMOLGlobals* G, const char* name)
|
|
{
|
|
const char* result;
|
|
CExecutive* I = G->Executive;
|
|
SpecRec *rec = nullptr, *best_rec = nullptr;
|
|
int best;
|
|
int wm;
|
|
auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
|
|
|
|
best = 0;
|
|
result = name;
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
wm = WordMatch(G, name, rec->name, ignore_case);
|
|
if (wm < 0) {
|
|
best_rec = rec;
|
|
best = wm;
|
|
break;
|
|
} else if ((best > 0) && (best < wm)) {
|
|
best_rec = rec;
|
|
best = wm;
|
|
}
|
|
}
|
|
if (best_rec)
|
|
result = best_rec->name;
|
|
return (result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
static int count_objects(PyMOLGlobals* G, int public_only)
|
|
{
|
|
int count = 0;
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (!public_only)
|
|
count++;
|
|
else if (rec->obj->Name[0] != '_')
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
SpecRec* ExecutiveFindSpec(PyMOLGlobals* G, pymol::zstring_view name_view)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
const char* name = name_view.c_str();
|
|
// ignore % prefix
|
|
if (name[0] && name[0] == '%')
|
|
name++;
|
|
{ /* first, try for perfect, case-specific match */
|
|
OVreturn_word result;
|
|
if (OVreturn_IS_OK((result = OVLexicon_BorrowFromCString(I->Lex, name)))) {
|
|
auto keyRes = I->Key.find(result.word);
|
|
if (keyRes != I->Key.end()) {
|
|
if (!TrackerGetCandRef(
|
|
I->Tracker, keyRes->second, (TrackerRef**) (void*) &rec)) {
|
|
rec = nullptr;
|
|
}
|
|
}
|
|
}
|
|
if (!rec) { /* otherwise try partial/case-nonspecific match */
|
|
rec = ExecutiveAnyCaseNameMatch(G, name);
|
|
}
|
|
}
|
|
return (rec);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
bool ExecutiveObjMolSeleOp(PyMOLGlobals* G, int sele, ObjectMoleculeOpRec* op)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
int update_table = true;
|
|
|
|
/* if we're given a valid selection */
|
|
if (sele >= 0) {
|
|
/* iterate over all the objects in the global list */
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->obj->type == cObjectMolecule) {
|
|
/* if the objects are valid molecules, then perform the operation in
|
|
* op_code */
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
|
|
switch (op->code) {
|
|
case OMOP_RenameAtoms: {
|
|
int result =
|
|
SelectorRenameObjectAtoms(G, obj, sele, op->i2, update_table);
|
|
if (result > 0)
|
|
op->i1 += result;
|
|
update_table = false;
|
|
} break;
|
|
default:
|
|
/* all other cases, perform the operation on obj */
|
|
if (!ObjectMoleculeSeleOp(obj, sele, op)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveGetCameraExtent(PyMOLGlobals* G, const char* name, float* mn,
|
|
float* mx, int transformed, int state)
|
|
{
|
|
int sele;
|
|
ObjectMoleculeOpRec op;
|
|
int flag = false;
|
|
|
|
if ((state == -2) || (state == -3)) /* TO DO: support per-object states */
|
|
state = SceneGetState(G);
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: name %s state %d\n", __func__, name, state ENDFD;
|
|
|
|
sele = SelectorIndexByName(G, name);
|
|
|
|
if (sele >= 0) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
if (state < 0) {
|
|
op.code = OMOP_CameraMinMax;
|
|
} else {
|
|
op.code = OMOP_CSetCameraMinMax;
|
|
op.cs1 = state;
|
|
}
|
|
op.v1[0] = FLT_MAX;
|
|
op.v1[1] = FLT_MAX;
|
|
op.v1[2] = FLT_MAX;
|
|
op.v2[0] = -FLT_MAX;
|
|
op.v2[1] = -FLT_MAX;
|
|
op.v2[2] = -FLT_MAX;
|
|
op.i1 = 0;
|
|
op.i2 = transformed;
|
|
op.mat1 = SceneGetMatrix(G);
|
|
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: minmax over %d vertices\n", __func__, op.i1 ENDFD;
|
|
if (op.i1)
|
|
flag = true;
|
|
}
|
|
copy3f(op.v1, mn);
|
|
copy3f(op.v2, mx);
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: returning %d\n", __func__, flag ENDFD;
|
|
|
|
return (flag);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveGetExtent(PyMOLGlobals* G, const char* name, float* mn, float* mx,
|
|
int transformed, int state, int weighted)
|
|
{
|
|
int sele;
|
|
ObjectMoleculeOpRec op, op2;
|
|
CExecutive* I = G->Executive;
|
|
pymol::CObject* obj;
|
|
int result = false;
|
|
float f1, f2, fmx;
|
|
int a;
|
|
|
|
if (WordMatchExact(G, cKeywordCenter, name, true)) {
|
|
SceneGetCenter(G, mn);
|
|
copy3f(mn, mx);
|
|
return 1;
|
|
}
|
|
if (WordMatchExact(G, cKeywordOrigin, name, true)) {
|
|
SceneOriginGet(G, mn);
|
|
copy3f(mn, mx);
|
|
return 1;
|
|
}
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: name %s state %d\n", __func__, name, state ENDFD;
|
|
|
|
ObjectMoleculeOpRecInit(&op);
|
|
ObjectMoleculeOpRecInit(&op2);
|
|
|
|
if ((state == -2) ||
|
|
(state == -3)) { /* we want the currently displayed state */
|
|
state = SceneGetState(G);
|
|
op.include_static_singletons =
|
|
true; /* make sure we get the static singletons too */
|
|
op2.include_static_singletons = true;
|
|
}
|
|
|
|
op2.i1 = 0;
|
|
op2.v1[0] = -1.0;
|
|
op2.v1[1] = -1.0;
|
|
op2.v1[2] = -1.0;
|
|
op2.v2[0] = 1.0;
|
|
op2.v2[1] = 1.0;
|
|
op2.v2[2] = 1.0;
|
|
|
|
{
|
|
auto matched_recs = ExecutiveGetSpecRecsFromPattern(G, name);
|
|
int have_atoms_flag = false;
|
|
int have_extent_flag = false;
|
|
|
|
/* first, compute atomic extents */
|
|
|
|
if (weighted) {
|
|
op2.i1 = 0;
|
|
|
|
op2.v1[0] = 0.0F;
|
|
op2.v1[1] = 0.0F;
|
|
op2.v1[2] = 0.0F;
|
|
|
|
op.i1 = 0;
|
|
|
|
op.v1[0] = FLT_MAX;
|
|
op.v1[1] = FLT_MAX;
|
|
op.v1[2] = FLT_MAX;
|
|
|
|
op.v2[0] = -FLT_MAX;
|
|
op.v2[1] = -FLT_MAX;
|
|
op.v2[2] = -FLT_MAX;
|
|
}
|
|
|
|
/* first, handle molecular objects */
|
|
|
|
for (auto& recref : matched_recs) {
|
|
auto* rec = &recref;
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type != cObjectMolecule &&
|
|
rec->obj->type != cObjectAlignment)
|
|
break;
|
|
case cExecSelection:
|
|
case cExecAll:
|
|
if (rec->type == cExecAll)
|
|
sele = SelectorIndexByName(G, cKeywordAll);
|
|
else
|
|
sele = SelectorIndexByName(G, rec->name);
|
|
if (sele >= 0) {
|
|
if (state < 0) {
|
|
op.code = OMOP_MNMX;
|
|
} else {
|
|
op.code = OMOP_CSetMinMax;
|
|
op.cs1 = state;
|
|
}
|
|
op.i2 = transformed;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
if (op.i1) {
|
|
have_atoms_flag = true;
|
|
}
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: minmax over %d vertices\n", __func__, op.i1 ENDFD;
|
|
}
|
|
|
|
if (weighted) {
|
|
if (state < 0)
|
|
op2.code = OMOP_SUMC;
|
|
else {
|
|
op2.code = OMOP_CSetSumVertices;
|
|
op2.cs1 = state;
|
|
}
|
|
op2.i2 = transformed;
|
|
ExecutiveObjMolSeleOp(G, sele, &op2);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (have_atoms_flag)
|
|
have_extent_flag = true;
|
|
|
|
/* now handle nonmolecular objects */
|
|
|
|
for (auto& recref : matched_recs) {
|
|
auto* rec = &recref;
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
obj = rec->obj;
|
|
if (!obj->ExtentFlag) {
|
|
switch (obj->type) {
|
|
case cObjectMap:
|
|
case cObjectMesh:
|
|
case cObjectSurface:
|
|
if (!rec->obj->ExtentFlag) {
|
|
/* allow object to update extents, if necessary */
|
|
rec->obj->update();
|
|
}
|
|
}
|
|
}
|
|
if (obj->ExtentFlag)
|
|
switch (obj->type) {
|
|
case cObjectMolecule:
|
|
break;
|
|
/* intentional fall-through */
|
|
default:
|
|
if (!have_extent_flag) {
|
|
copy3f(obj->ExtentMin, op.v1);
|
|
copy3f(obj->ExtentMax, op.v2);
|
|
have_extent_flag = true;
|
|
} else {
|
|
min3f(obj->ExtentMin, op.v1, op.v1);
|
|
max3f(obj->ExtentMax, op.v2, op.v2);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecObject:
|
|
obj = rec->obj;
|
|
if (!obj->ExtentFlag) {
|
|
switch (obj->type) {
|
|
case cObjectMap:
|
|
case cObjectMesh:
|
|
case cObjectSurface:
|
|
if (!rec->obj->ExtentFlag) {
|
|
/* allow object to update extents, if necessary */
|
|
rec->obj->update();
|
|
}
|
|
}
|
|
}
|
|
if (obj->ExtentFlag)
|
|
switch (obj->type) {
|
|
case cObjectMolecule: /* will have been handled above... */
|
|
break;
|
|
default:
|
|
if (!have_extent_flag) {
|
|
copy3f(obj->ExtentMin, op.v1);
|
|
copy3f(obj->ExtentMax, op.v2);
|
|
have_extent_flag = true;
|
|
} else {
|
|
min3f(obj->ExtentMin, op.v1, op.v1);
|
|
max3f(obj->ExtentMax, op.v2, op.v2);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (have_atoms_flag && weighted) {
|
|
if (op2.i1) {
|
|
op2.v1[0] /= op2.i1; /* compute average */
|
|
op2.v1[1] /= op2.i1;
|
|
op2.v1[2] /= op2.i1;
|
|
|
|
for (a = 0; a < 3; a++) { /* this puts origin at the weighted center */
|
|
f1 = op2.v1[a] - op.v1[a];
|
|
f2 = op.v2[a] - op2.v1[a];
|
|
if (f1 > f2)
|
|
fmx = f1;
|
|
else
|
|
fmx = f2;
|
|
op.v1[a] = op2.v1[a] - fmx;
|
|
op.v2[a] = op2.v1[a] + fmx;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (have_extent_flag) {
|
|
copy3f(op.v1, mn);
|
|
copy3f(op.v2, mx);
|
|
} else {
|
|
zero3f(mn);
|
|
zero3f(mx);
|
|
}
|
|
|
|
result = have_extent_flag;
|
|
}
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: returning %d\n", __func__, result ENDFD;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
static int ExecutiveGetMaxDistance(PyMOLGlobals* G, const char* name,
|
|
float* pos, float* dev, int transformed, int state)
|
|
{
|
|
int sele;
|
|
ObjectMoleculeOpRec op, op2;
|
|
CExecutive* I = G->Executive;
|
|
pymol::CObject* obj;
|
|
int flag = false;
|
|
float f1, fmx = 0.0F;
|
|
|
|
if ((state == -2) || (state == -3)) /* TO DO: support per-object states */
|
|
state = SceneGetState(G);
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: name %s state %d\n", __func__, name, state ENDFD;
|
|
|
|
ObjectMoleculeOpRecInit(&op);
|
|
ObjectMoleculeOpRecInit(&op2);
|
|
|
|
{
|
|
auto matched_recs = ExecutiveGetSpecRecsFromPattern(G, name);
|
|
|
|
op2.i1 = 0;
|
|
op2.v1[0] = -1.0;
|
|
op2.v1[1] = -1.0;
|
|
op2.v1[2] = -1.0;
|
|
op2.v2[0] = 1.0;
|
|
op2.v2[1] = 1.0;
|
|
op2.v2[2] = 1.0;
|
|
|
|
{
|
|
/* first handle molecular objects */
|
|
|
|
for (auto& recref : matched_recs) {
|
|
auto* rec = &recref;
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
case cExecSelection:
|
|
case cExecAll:
|
|
if (rec->type == cExecAll)
|
|
sele = SelectorIndexByName(G, cKeywordAll);
|
|
else
|
|
sele = SelectorIndexByName(G, rec->name);
|
|
if (sele >= 0) {
|
|
if (state < 0) {
|
|
op.code = OMOP_MaxDistToPt;
|
|
} else {
|
|
op.code = OMOP_CSetMaxDistToPt;
|
|
op.cs1 = state;
|
|
}
|
|
op.v1[0] = pos[0];
|
|
op.v1[1] = pos[1];
|
|
op.v1[2] = pos[2];
|
|
op.i1 = 0;
|
|
op.f1 = 0.0F;
|
|
op.i2 = transformed;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
fmx = op.f1;
|
|
|
|
if (op.i1)
|
|
flag = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
/* now handle nonmolecular objects */
|
|
|
|
for (auto& recref : matched_recs) {
|
|
auto* rec = &recref;
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
obj = rec->obj;
|
|
if (obj->ExtentFlag) {
|
|
switch (obj->type) {
|
|
case cObjectMolecule:
|
|
break;
|
|
default:
|
|
if (obj->ExtentFlag) {
|
|
f1 = (float) diff3f(obj->ExtentMin, pos);
|
|
if (fmx < f1)
|
|
fmx = f1;
|
|
f1 = (float) diff3f(obj->ExtentMax, pos);
|
|
if (fmx < f1)
|
|
fmx = f1;
|
|
flag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cExecObject:
|
|
obj = rec->obj;
|
|
switch (rec->obj->type) {
|
|
case cObjectMolecule:
|
|
break;
|
|
default:
|
|
if (obj->ExtentFlag) {
|
|
f1 = (float) diff3f(obj->ExtentMin, pos);
|
|
if (fmx < f1)
|
|
fmx = f1;
|
|
f1 = (float) diff3f(obj->ExtentMax, pos);
|
|
if (fmx < f1)
|
|
fmx = f1;
|
|
flag = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*dev = fmx;
|
|
return (flag);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveWindowZoom(PyMOLGlobals* G, const char* name,
|
|
float buffer, int state, int inclusive, float animate, int quiet)
|
|
{
|
|
float center[3], radius;
|
|
float mn[3], mx[3], df[3];
|
|
int sele0;
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveWindowZoom-DEBUG: entered\n" ENDFD;
|
|
if (ExecutiveGetExtent(G, name, mn, mx, true, state, true)) {
|
|
if (buffer != 0.0F) {
|
|
mx[0] += buffer;
|
|
mx[1] += buffer;
|
|
mx[2] += buffer;
|
|
mn[0] -= buffer;
|
|
mn[1] -= buffer;
|
|
mn[2] -= buffer;
|
|
}
|
|
subtract3f(mx, mn, df);
|
|
average3f(mn, mx, center);
|
|
if (inclusive) {
|
|
if (!ExecutiveGetMaxDistance(G, name, center, &radius, true, state))
|
|
radius = 0.0;
|
|
radius += buffer;
|
|
} else {
|
|
radius = df[0];
|
|
if (radius < df[1])
|
|
radius = df[1];
|
|
if (radius < df[2])
|
|
radius = df[2];
|
|
radius = radius / 2.0F;
|
|
}
|
|
if (radius < MAX_VDW)
|
|
radius = MAX_VDW;
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: zooming with radius %8.3f...state %d\n", __func__, radius,
|
|
state ENDFD;
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: on center %8.3f %8.3f %8.3f...\n", __func__, center[0], center[1],
|
|
center[2] ENDFD;
|
|
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);
|
|
SceneOriginSet(G, center, false);
|
|
SceneWindowSphere(G, center, radius);
|
|
if (animate != 0.0F)
|
|
SceneLoadAnimation(G, animate, 0);
|
|
else
|
|
SceneAbortAnimation(G);
|
|
SceneInvalidate(G);
|
|
} else {
|
|
|
|
sele0 = SelectorIndexByName(G, name);
|
|
if (sele0 > 0) { /* any valid selection except "all" */
|
|
/* no longer an error to zoom on an empty selection -- just has no effect
|
|
*/
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"ExecutiveWindowZoom-Warning: selection doesn't specify any "
|
|
"coordinates.\n" ENDFB(G);
|
|
}
|
|
} else if (ExecutiveValidName(G, name)) {
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveWindowZoom-DEBUG: name valid, but no extents -- using default "
|
|
"view\n" ENDFD;
|
|
SceneSetDefaultView(G);
|
|
SceneInvalidate(G);
|
|
} else {
|
|
return pymol::make_error(__func__, "selection or object unknown.");
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveCenter(PyMOLGlobals* G, const char* name, int state,
|
|
int origin, float animate, float* pos, int quiet)
|
|
{
|
|
float center[3];
|
|
float mn[3], mx[3];
|
|
int sele0;
|
|
int have_center = false;
|
|
|
|
if (name && ExecutiveGetExtent(G, name, mn, mx, true, state, true)) {
|
|
average3f(mn, mx, center);
|
|
have_center = true;
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: centering state %d\n", __func__, state ENDFD;
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: on center %8.3f %8.3f %8.3f...\n", __func__, center[0], center[1],
|
|
center[2] ENDFD;
|
|
} else if (pos) {
|
|
have_center = true;
|
|
copy3f(pos, center);
|
|
}
|
|
if (have_center) {
|
|
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);
|
|
if (origin)
|
|
SceneOriginSet(G, center, false);
|
|
SceneRelocate(G, center);
|
|
SceneInvalidate(G);
|
|
if (animate != 0.0F)
|
|
SceneLoadAnimation(G, animate, 0);
|
|
} else {
|
|
sele0 = SelectorIndexByName(G, name);
|
|
if (sele0 >= 0) { /* any valid selection except "all" */
|
|
if (!quiet) {
|
|
/* no longer an error to center on an empty selection -- just have no
|
|
* effect */
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"ExecutiveCenter-Warning: selection doesn't specify any "
|
|
"coordinates.\n" ENDFB(G);
|
|
}
|
|
} else if (ExecutiveValidName(G, name)) {
|
|
SceneSetDefaultView(G);
|
|
SceneInvalidate(G);
|
|
} else {
|
|
return pymol::make_error("Selection or object unknown.");
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveOrigin(PyMOLGlobals* G, const char* sele, int preserve,
|
|
const char* oname, const float* pos, int state)
|
|
{
|
|
|
|
float center[3];
|
|
float mn[3], mx[3];
|
|
pymol::CObject* obj = nullptr;
|
|
int have_center = false;
|
|
if (oname && oname[0]) {
|
|
obj = ExecutiveFindObjectByName(G, oname);
|
|
if (!obj)
|
|
return pymol::make_error("Object ", oname, " not found.");
|
|
}
|
|
if (sele && sele[0]) {
|
|
auto s1 = SelectorTmp2::make(G, sele);
|
|
auto has_extent =
|
|
ExecutiveGetExtent(G, s1->getName(), mn, mx, true, state, true);
|
|
if (!has_extent) {
|
|
return pymol::make_error("Could not determine extent of selection.");
|
|
}
|
|
average3f(mn, mx, center);
|
|
have_center = true;
|
|
} else if (pos) {
|
|
copy3f(pos, center);
|
|
have_center = true;
|
|
}
|
|
|
|
if (!have_center) {
|
|
return pymol::make_error("Center could not be determined.");
|
|
}
|
|
|
|
if (obj) {
|
|
ObjectSetTTTOrigin(obj, center);
|
|
PRINTFB(G, FB_Executive, FB_Blather)
|
|
" %s: origin for %s set to %8.3f %8.3f %8.3f\n", __func__, oname, center[0],
|
|
center[1], center[2] ENDFB(G);
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Blather)
|
|
" %s: scene origin set to %8.3f %8.3f %8.3f\n", __func__, center[0],
|
|
center[1], center[2] ENDFB(G);
|
|
SceneOriginSet(G, center, preserve);
|
|
}
|
|
SceneInvalidate(G);
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveGetMoment(PyMOLGlobals* G, const char* name, double* mi, int state)
|
|
{
|
|
int sele;
|
|
ObjectMoleculeOpRec op;
|
|
int a, b;
|
|
int c = 0;
|
|
|
|
if ((state == -2) || (state == -3)) /* TO DO: support per-object states */
|
|
state = SceneGetState(G);
|
|
|
|
sele = SelectorIndexByName(G, name);
|
|
if (sele >= 0) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
if (state < 0) {
|
|
op.code = OMOP_SUMC;
|
|
} else {
|
|
op.code = OMOP_CSetSumVertices;
|
|
op.cs1 = state;
|
|
}
|
|
|
|
op.v1[0] = 0.0;
|
|
op.v1[1] = 0.0;
|
|
op.v1[2] = 0.0;
|
|
op.i1 = 0;
|
|
op.i2 = 0; /* untransformed...is this right? */
|
|
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
|
|
if (op.i1) { /* any vertices? */
|
|
c += op.i1;
|
|
scale3f(op.v1, 1.0F / op.i1, op.v1); /* compute raw average */
|
|
if (state < 0) {
|
|
op.code = OMOP_MOME;
|
|
} else {
|
|
op.code = OMOP_CSetMoment;
|
|
op.cs1 = state;
|
|
}
|
|
for (a = 0; a < 3; a++)
|
|
for (b = 0; b < 3; b++)
|
|
op.d[a][b] = 0.0;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
{
|
|
double* p = mi;
|
|
for (a = 0; a < 3; a++)
|
|
for (b = 0; b < 3; b++)
|
|
*(p++) = op.d[a][b];
|
|
}
|
|
}
|
|
} else {
|
|
identity33d(mi);
|
|
}
|
|
|
|
return (c);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<bool> ExecutiveSetObjVisib(
|
|
PyMOLGlobals* G, pymol::zstring_view name, int onoff, int parents)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
bool changed = false;
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveSetObjVisib: entered.\n" ENDFD;
|
|
{
|
|
CTracker* I_Tracker = I->Tracker;
|
|
SpecRec* rec;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name.data(), true, false);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
|
|
if (rec) {
|
|
if (!changed && rec->visible != onoff) {
|
|
changed = true;
|
|
}
|
|
switch (rec->type) {
|
|
case cExecAll: {
|
|
bool const suppress_hidden =
|
|
SettingGet<bool>(G, cSetting_suppress_hidden);
|
|
bool const hide_underscore =
|
|
SettingGet<bool>(G, cSetting_hide_underscore_names);
|
|
if (suppress_hidden && hide_underscore)
|
|
ExecutiveUpdateGroups(G, false);
|
|
SpecRec* tRec = nullptr;
|
|
while (ListIterate(I->Spec, tRec, next)) {
|
|
if (onoff != tRec->visible) {
|
|
if (tRec->type == cExecObject) {
|
|
if (tRec->visible) {
|
|
tRec->in_scene = SceneObjectDel(G, tRec->obj, true);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
tRec->visible = !tRec->visible;
|
|
ReportEnabledChange(G, rec);
|
|
} else {
|
|
if (!(suppress_hidden && tRec->isHidden(hide_underscore))) {
|
|
tRec->in_scene = SceneObjectAdd(G, tRec->obj);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
tRec->visible = !tRec->visible;
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
}
|
|
} else if ((tRec->type != cExecSelection) ||
|
|
(!onoff)) { /* hide all selections, but show all */
|
|
tRec->visible = !tRec->visible;
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
case cExecObject:
|
|
/*
|
|
if(rec->visible!=onoff) {
|
|
if(rec->visible) {
|
|
rec->in_scene = SceneObjectDel(G,rec->obj);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
} else {
|
|
rec->in_scene = SceneObjectAdd(G,rec->obj);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
}
|
|
rec->visible=!rec->visible;
|
|
}
|
|
*/
|
|
if (onoff) { /* enable */
|
|
ExecutiveSpecEnable(G, rec, parents, false);
|
|
} else { /* disable */
|
|
if (rec->visible) {
|
|
if (rec->in_scene)
|
|
rec->in_scene = SceneObjectDel(G, rec->obj, true);
|
|
rec->visible = false;
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
SceneChanged(G);
|
|
}
|
|
break;
|
|
case cExecSelection:
|
|
if (rec->visible != onoff) {
|
|
int previousVisible = rec->visible;
|
|
rec->visible = !rec->visible;
|
|
if (rec->visible)
|
|
if (SettingGetGlobal_b(G, cSetting_active_selections)) {
|
|
ExecutiveHideSelections(G);
|
|
rec->visible = true;
|
|
}
|
|
SceneInvalidate(G);
|
|
SeqDirty(G);
|
|
if (previousVisible != rec->visible) {
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveSetObjVisib: leaving...\n" ENDFD;
|
|
return changed;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Full screen state fallback in case we can't get get the state from
|
|
* the window manager.
|
|
*/
|
|
static bool _is_full_screen = false;
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Get the fullscreen state from the window manager or return -1 if
|
|
* not available.
|
|
*/
|
|
bool ExecutiveIsFullScreen(PyMOLGlobals* G)
|
|
{
|
|
if (!G->HaveGUI || !G->ValidContext)
|
|
return false;
|
|
|
|
int flag = -1;
|
|
|
|
#if defined(GLUT_FULL_SCREEN)
|
|
flag = glutGet(GLUT_FULL_SCREEN);
|
|
#endif
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" %s: flag=%d fallback=%d.\n", __func__, flag, _is_full_screen ENDFD;
|
|
|
|
if (flag > -1)
|
|
return flag;
|
|
return _is_full_screen;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveFullScreen(PyMOLGlobals* G, int flag)
|
|
{
|
|
if (!G->HaveGUI)
|
|
return;
|
|
|
|
int wm_flag = ExecutiveIsFullScreen(G);
|
|
|
|
if (flag < 0) {
|
|
flag = !wm_flag;
|
|
}
|
|
|
|
_is_full_screen = (flag != 0);
|
|
|
|
#ifndef _PYMOL_NO_GLUT
|
|
if (G->HaveGUI && G->ValidContext) {
|
|
if (flag) {
|
|
#ifndef GLUT_FULL_SCREEN
|
|
if (wm_flag < 1) {
|
|
CExecutive* I = G->Executive;
|
|
I->oldPX = p_glutGet(P_GLUT_WINDOW_X);
|
|
I->oldPY = p_glutGet(P_GLUT_WINDOW_Y);
|
|
I->oldWidth = p_glutGet(P_GLUT_WINDOW_WIDTH);
|
|
I->oldHeight = p_glutGet(P_GLUT_WINDOW_HEIGHT);
|
|
}
|
|
#endif
|
|
|
|
p_glutFullScreen();
|
|
} else {
|
|
#ifndef GLUT_FULL_SCREEN
|
|
// freeglut < 2.6
|
|
CExecutive* I = G->Executive;
|
|
p_glutReshapeWindow(I->oldWidth, I->oldHeight);
|
|
p_glutPositionWindow(I->oldPX, I->oldPY);
|
|
#elif !defined(GLUT_HAS_MULTI)
|
|
// freeglut < 2.8
|
|
if (wm_flag)
|
|
glutFullScreenToggle();
|
|
#else
|
|
glutLeaveFullScreen();
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
PyMOL_NeedReshape(G->PyMOL, flag, 0, 0, 0, 0);
|
|
SceneChanged(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
static void fInvalidateRepMask(
|
|
pymol::CObject* obj, cRepBitmask_t repmask, int state = -1)
|
|
{
|
|
for (auto a = cRep_t(0); a < cRepCnt; ++a) {
|
|
if ((1 << a) & repmask)
|
|
obj->invalidate(a, cRepInvVisib, state);
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveToggleRepVisib(
|
|
PyMOLGlobals* G, const char* name, int rep)
|
|
{
|
|
int sele = -1;
|
|
SpecRec* tRec;
|
|
ObjectMoleculeOpRec op;
|
|
OrthoLineType tmpname;
|
|
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveToggleRepVisib: entered.\n" ENDFD;
|
|
|
|
tRec = ExecutiveFindSpec(G, name);
|
|
|
|
if (rep == -2) {
|
|
// special case: toggle object visibility (should that be in this function?)
|
|
if (tRec) {
|
|
ExecutiveSetObjVisib(G, name, !tRec->visible, 0);
|
|
} else {
|
|
return pymol::make_error(name, " not found.");
|
|
}
|
|
} else if (tRec && tRec->type == cExecObject &&
|
|
tRec->obj->type != cObjectMolecule) {
|
|
// non-atom object
|
|
tRec->obj->visRep ^= rep;
|
|
fInvalidateRepMask(tRec->obj, rep, 0);
|
|
SceneChanged(G);
|
|
} else if (SelectorGetTmp(G, name, tmpname) >= 0) {
|
|
// atom selection
|
|
sele = SelectorIndexByName(G, tmpname);
|
|
if (sele >= 0) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
|
|
op.code = OMOP_CheckVis;
|
|
op.i1 = rep;
|
|
op.i2 = false;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
op.i2 = !op.i2;
|
|
|
|
op.code = OMOP_VISI;
|
|
op.i1 = rep;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
op.code = OMOP_INVA;
|
|
op.i2 = cRepInvVisib;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
}
|
|
SelectorFreeTmp(G, tmpname);
|
|
}
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveToggleRepVisib: leaving...\n" ENDFD;
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveSetRepVisib(
|
|
PyMOLGlobals* G, pymol::zstring_view name, int rep, int state)
|
|
{
|
|
int repmask = (rep == cRepAll) ? cRepBitmask : (1 << rep);
|
|
return ExecutiveSetRepVisMask(G, name, repmask, state);
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSetRepVisMaskFromSele(
|
|
PyMOLGlobals* G, pymol::zstring_view sele, int repmask, int state)
|
|
{
|
|
if (sele[0] == '@') {
|
|
// DEPRECATED
|
|
sele = cKeywordAll;
|
|
repmask = cRepBitmask;
|
|
}
|
|
auto s1 = SelectorTmp2::make(G, sele.c_str());
|
|
p_return_if_error(s1);
|
|
return ExecutiveSetRepVisMask(G, s1->getName(), repmask, state);
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSetRepVisMask(
|
|
PyMOLGlobals* G, pymol::zstring_view name, int repmask, int state)
|
|
{
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveSetRepVisib: entered.\n" ENDFD;
|
|
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
SpecRec* rec = nullptr;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name.data(), true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
/* per-atom */
|
|
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (rec->obj->type != cObjectMolecule &&
|
|
rec->obj->type != cObjectAlignment)
|
|
break;
|
|
case cExecSelection: {
|
|
int sele = SelectorIndexByName(G, rec->name);
|
|
if (sele >= 0) {
|
|
ObjectMoleculeOpRec op;
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_VISI;
|
|
op.i1 = repmask;
|
|
op.i2 = state;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
op.code = OMOP_INVA;
|
|
if (state == cVis_AS)
|
|
op.i1 = cRepBitmask;
|
|
op.i2 = cRepInvVisib;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
/* per-object/name */
|
|
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
ObjectSetRepVisMask(rec->obj, repmask, state);
|
|
fInvalidateRepMask(rec->obj, repmask, 0);
|
|
SceneChanged(G);
|
|
break;
|
|
case cExecAll:
|
|
ExecutiveSetAllRepVisMask(G, repmask, state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveSetRepVisib: leaving...\n" ENDFD;
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveSetOnOffBySele(
|
|
PyMOLGlobals* G, pymol::zstring_view sname, int onoff)
|
|
{
|
|
SelectorTmp2 tmp{G, sname.data()};
|
|
const char* name = tmp.getName();
|
|
|
|
auto tRec = ExecutiveFindSpec(G, name);
|
|
if (!tRec && sname == cKeywordAll) {
|
|
ExecutiveSetObjVisib(G, name, onoff, false);
|
|
}
|
|
if (tRec) {
|
|
int sele = tmp.getIndex();
|
|
if (sele >= 0) {
|
|
ObjectMoleculeOpRec op;
|
|
ObjectMoleculeOpRecInit(&op);
|
|
|
|
op.code = OMOP_OnOff;
|
|
op.i1 = onoff;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* @param repmask rep bit mask
|
|
* @param state 0 (hide), 1 (show), 2 (as)
|
|
*/
|
|
static void ExecutiveSetAllRepVisMask(PyMOLGlobals* G, int repmask, int state)
|
|
{
|
|
ObjectMoleculeOpRec op;
|
|
ObjectMolecule* obj;
|
|
int sele;
|
|
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveSetAllRepVisib: entered.\n" ENDFD;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (rec->type == cExecObject) {
|
|
switch (rec->obj->type) {
|
|
case cObjectMolecule:
|
|
obj = (ObjectMolecule*) rec->obj;
|
|
sele = SelectorIndexByName(G, obj->Name);
|
|
ObjectMoleculeOpRecInit(&op);
|
|
|
|
op.code = OMOP_VISI;
|
|
op.i1 = repmask;
|
|
op.i2 = state;
|
|
ObjectMoleculeSeleOp(obj, sele, &op);
|
|
op.code = OMOP_INVA;
|
|
if (state == cVis_AS)
|
|
op.i1 = cRepBitmask;
|
|
op.i2 = cRepInvVisib;
|
|
ObjectMoleculeSeleOp(obj, sele, &op);
|
|
break;
|
|
default:
|
|
ObjectSetRepVisMask(rec->obj, repmask, state);
|
|
fInvalidateRepMask(rec->obj, repmask, -1);
|
|
SceneInvalidate(G);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PRINTFD(G, FB_Executive)
|
|
" ExecutiveSetAllRepVisib: leaving...\n" ENDFD;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveInvalidateRep(
|
|
PyMOLGlobals* G, const char* str1, cRep_t rep, cRepInv_t level)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
ObjectMoleculeOpRec op;
|
|
SpecRec* rec = nullptr;
|
|
const char* name = "";
|
|
|
|
SelectorTmp2 s1;
|
|
if (str1 && !WordMatchExact(G, str1, "all", true)) {
|
|
s1 = SelectorTmp2(G, str1);
|
|
name = s1.getName();
|
|
} else {
|
|
name = str1;
|
|
}
|
|
|
|
if ((!name) || (!name[0]))
|
|
name = cKeywordAll;
|
|
{
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecSelection:
|
|
case cExecObject: {
|
|
int sele = SelectorIndexByName(G, rec->name);
|
|
if (sele >= 0) {
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_INVA;
|
|
op.i1 = (rep == cRepAll) ? cRepBitmask : (1 << rep);
|
|
op.i2 = level;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
} else {
|
|
rec->obj->invalidate(rep, level, -1);
|
|
}
|
|
} break;
|
|
case cExecAll:
|
|
rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
rec->obj->invalidate(rep, level, -1);
|
|
}
|
|
}
|
|
SceneInvalidate(G);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
int ExecutiveCheckGroupMembership(
|
|
PyMOLGlobals* G, int list_id, pymol::CObject* obj)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int result = false;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
if (iter_id) {
|
|
SpecRec* rec = nullptr;
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec && (rec->type == cExecObject) && (rec->obj == obj)) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int ExecutiveGetExpandedGroupListFromPattern(PyMOLGlobals* G, const char* name)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int result = 0;
|
|
CWordMatcher* matcher;
|
|
CWordMatchOptions options;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
const char* wildcard = SettingGetGlobal_s(G, cSetting_wildcard);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, I->all_names_list_id);
|
|
int cand_id;
|
|
SpecRec* rec;
|
|
|
|
WordMatchOptionsConfigNameList(
|
|
&options, *wildcard, SettingGetGlobal_b(G, cSetting_ignore_case));
|
|
matcher = WordMatcherNew(G, name, &options, false);
|
|
if (matcher) {
|
|
if (iter_id) {
|
|
while ((cand_id = TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec))) {
|
|
if (rec && !(rec->type == cExecAll)) {
|
|
if (WordMatcherMatchAlpha(matcher, rec->name)) {
|
|
if ((rec->type == cExecObject) &&
|
|
(rec->obj->type == cObjectGroup)) {
|
|
if (!result)
|
|
result = TrackerNewList(I_Tracker, nullptr);
|
|
if (result) {
|
|
TrackerLink(I_Tracker, cand_id, result, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if ((rec = ExecutiveFindSpec(G, name))) { /* only one name in list */
|
|
if ((rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
|
|
result = TrackerNewList(I_Tracker, nullptr);
|
|
TrackerLink(I_Tracker, rec->cand_id, result, 1);
|
|
}
|
|
} else if ((rec = ExecutiveUnambiguousNameMatch(G, name))) {
|
|
if ((rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
|
|
result = TrackerNewList(I_Tracker, nullptr);
|
|
TrackerLink(I_Tracker, rec->cand_id, result, 1);
|
|
}
|
|
}
|
|
if (matcher)
|
|
WordMatcherFree(matcher);
|
|
if (iter_id)
|
|
TrackerDelIter(I->Tracker, iter_id);
|
|
if (result)
|
|
ExecutiveExpandGroupsInList(G, result, cExecExpandGroups);
|
|
return result;
|
|
}
|
|
|
|
int ExecutiveGetExpandedGroupList(PyMOLGlobals* G, const char* name)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int result = 0;
|
|
int list_id = 0;
|
|
|
|
SpecRec* rec = ExecutiveFindSpec(G, name);
|
|
ExecutiveUpdateGroups(G, false);
|
|
if (rec && (rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
|
|
list_id = rec->group_member_list_id;
|
|
}
|
|
if (list_id) {
|
|
result = TrackerNewListCopy(I->Tracker, list_id, nullptr);
|
|
ExecutiveExpandGroupsInList(G, result, cExecExpandGroups);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ExecutiveFreeGroupList(PyMOLGlobals* G, int list_id)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
TrackerDelList(I->Tracker, list_id);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::CObject* ExecutiveFindObjectByName(
|
|
PyMOLGlobals* G, pymol::zstring_view name)
|
|
{
|
|
pymol::CObject* obj = nullptr;
|
|
SpecRec* rec = ExecutiveFindSpec(G, name.c_str());
|
|
if (rec && (rec->type == cExecObject)) {
|
|
obj = rec->obj;
|
|
}
|
|
return (obj);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* returns nullptr if none found */
|
|
pymol::CObject** ExecutiveFindObjectsByType(PyMOLGlobals* G, int objType)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
int n = 0;
|
|
pymol::CObject** rVal = VLAlloc(pymol::CObject*, 1);
|
|
|
|
/* loop over all known objects */
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
/* make sure it exists and is the right type */
|
|
if (rec->obj && rec->type == cExecObject) {
|
|
if (rec->obj->type == objType) {
|
|
/* this could be optimized */
|
|
VLACheck(rVal, pymol::CObject*, n);
|
|
*(rVal + n) = rec->obj;
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
VLASize(rVal, pymol::CObject*, n);
|
|
if (n == 0) {
|
|
VLAFree(rVal);
|
|
return nullptr;
|
|
} else
|
|
return rVal;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
Block* ExecutiveGetBlock(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
return (I);
|
|
}
|
|
|
|
/**
|
|
* Makes a custom symmetry operation label.
|
|
*/
|
|
static std::string make_symexp_segi_label(int a, int x, int y, int z)
|
|
{
|
|
SegIdent seg = "_000";
|
|
|
|
if (a > 61) {
|
|
// beyond what can be encoded with a single alphanumeric
|
|
// character (PYMOL-2475)
|
|
} else if (a > 35) {
|
|
seg[0] = 'a' + (a - 36);
|
|
} else if (a > 25) {
|
|
seg[0] = '0' + (a - 26);
|
|
} else {
|
|
seg[0] = 'A' + a;
|
|
}
|
|
|
|
if (x > 0) {
|
|
seg[1] = 'A' + x - 1;
|
|
} else if (x < 0) {
|
|
seg[1] = 'Z' + x + 1;
|
|
}
|
|
|
|
if (y > 0) {
|
|
seg[2] = 'A' + y - 1;
|
|
} else if (y < 0) {
|
|
seg[2] = 'Z' + y + 1;
|
|
}
|
|
|
|
if (z > 0) {
|
|
seg[3] = 'A' + z - 1;
|
|
} else if (z < 0) {
|
|
seg[3] = 'Z' + z + 1;
|
|
}
|
|
|
|
return seg;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Generates symmetry mates of object @a name as new objects, with all states.
|
|
*
|
|
* Only includes mates within @a cutoff of selection @a s1, and only in a range
|
|
* of +/-1 unit cells. The cutoff applies across states, so a symmetry atom will
|
|
* be included if it's within any atom of @a s1 in any state.
|
|
*
|
|
* @param name Prefix for new objects
|
|
* @param oname Object to replicate
|
|
* @param s1 Atom selection (for cutoff)
|
|
* @param cutoff Distance cutoff from selection
|
|
* @param segi (bool) If true, write symmetry operation code to segment
|
|
* identifier
|
|
*/
|
|
void ExecutiveSymExp(PyMOLGlobals* G, const char* name, const char* oname,
|
|
const char* s1, float cutoff, int segi, int quiet)
|
|
{ /* TODO state */
|
|
SelectorTmp tmpsele1(G, s1);
|
|
auto sele = tmpsele1.getIndex();
|
|
|
|
/* object to expand */
|
|
auto const* const obj = ExecutiveFindObject<ObjectMolecule>(G, oname);
|
|
|
|
if (!(obj && sele)) {
|
|
ErrMessage(G, __func__, "Invalid object");
|
|
return;
|
|
}
|
|
|
|
// Get symmetry from first coordset
|
|
const CSymmetry* sym = nullptr;
|
|
for (int b = 0; b < obj->NCSet && !sym; ++b) {
|
|
if (auto const* cs = obj->CSet[b]) {
|
|
sym = cs->getSymmetry();
|
|
}
|
|
}
|
|
|
|
if (!sym) {
|
|
ErrMessage(G, __func__, "No symmetry loaded!");
|
|
return;
|
|
}
|
|
|
|
int const nsymmat = sym->getNSymMat();
|
|
|
|
if (nsymmat < 1) {
|
|
ErrMessage(G, __func__, "unknown space group");
|
|
return;
|
|
}
|
|
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" ExecutiveSymExp: Generating symmetry mates...\n" ENDFB(G);
|
|
}
|
|
|
|
bool const matrix_mode = SettingGet<int>(G, cSetting_matrix_mode) > 0;
|
|
|
|
// Get the center of mass for this object/selection (over all states!)
|
|
ObjectMoleculeOpRec op;
|
|
ObjectMoleculeOpRecInit(&op);
|
|
op.code = OMOP_SUMC;
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
auto tc_vec = glm::make_vec3(op.v1) / float(std::max(1, op.i1));
|
|
auto tc = glm::value_ptr(tc_vec);
|
|
|
|
/* Transformation: RealToFrac (3x3) * tc (3x1) = (3x1) */
|
|
transform33f3f(sym->Crystal.realToFrac(), tc, tc);
|
|
|
|
/* 2. Copy the coordinates for the atoms in this selection into op */
|
|
op.code = OMOP_VERT;
|
|
op.nvv1 = 0;
|
|
op.vv1 = VLAlloc(float, 10000);
|
|
ExecutiveObjMolSeleOp(G, sele, &op);
|
|
auto const vv1 = pymol::vla_take_ownership(op.vv1);
|
|
|
|
/* op.nvv1 is the number of atom coordinates we copied in the previous step */
|
|
if (!op.nvv1) {
|
|
ErrMessage(G, __func__, "No atoms indicated!");
|
|
return;
|
|
}
|
|
|
|
auto map = std::unique_ptr<MapType>(
|
|
new MapType(G, -cutoff, vv1.data(), op.nvv1, nullptr));
|
|
if (!map) {
|
|
ErrMessage(G, __func__, "map is nullptr");
|
|
return;
|
|
}
|
|
|
|
std::vector<glm::vec3> cs_centers(obj->NCSet);
|
|
for (int b = 0; b < obj->NCSet; ++b) {
|
|
if (auto const* cs = obj->CSet[b]) {
|
|
CoordSetGetAverage(cs, glm::value_ptr(cs_centers[b]));
|
|
}
|
|
}
|
|
|
|
/* go out no more than one lattice step in each direction: -1, 0, +1 */
|
|
for (int x = -1; x < 2; ++x) {
|
|
for (int y = -1; y < 2; ++y) {
|
|
for (int z = -1; z < 2; ++z) {
|
|
for (int a = 0; a < nsymmat; a++) {
|
|
/* make a copy of the original */
|
|
auto new_obj = ObjectMoleculeCopy(obj);
|
|
bool keepFlag = false;
|
|
|
|
for (int b = 0; b < new_obj->NCSet; ++b) {
|
|
auto* cs = new_obj->CSet[b];
|
|
if (!cs) {
|
|
continue;
|
|
}
|
|
|
|
float mat[16];
|
|
copy33f44f(sym->Crystal.realToFrac(), mat);
|
|
left_multiply44f44f(sym->getSymMat(a), mat);
|
|
|
|
float ts[3];
|
|
transform44f3f(mat, glm::value_ptr(cs_centers[b]), ts);
|
|
|
|
/* compute the effective translation resulting
|
|
from application of the symmetry operator so
|
|
that we can shift it into the cell of the
|
|
target selection */
|
|
for (int c = 0; c < 3; c++) {
|
|
ts[c] = std::round(tc[c] - ts[c]);
|
|
}
|
|
float const shift[3] = {ts[0] + x, ts[1] + y, ts[2] + z};
|
|
float m[16];
|
|
identity44f(m);
|
|
m[3] = shift[0];
|
|
m[7] = shift[1];
|
|
m[11] = shift[2];
|
|
|
|
left_multiply44f44f(const_cast<float const*>(m), mat);
|
|
copy33f44f(sym->Crystal.fracToReal(), m);
|
|
left_multiply44f44f(const_cast<float const*>(m), mat);
|
|
|
|
if (is_identityf(4, mat)) {
|
|
continue;
|
|
}
|
|
|
|
double mat_d[16];
|
|
copy44f44d(mat, mat_d);
|
|
ObjectStateLeftCombineMatrixR44d(cs, mat_d);
|
|
|
|
if (!matrix_mode) {
|
|
CoordSetTransform44f(cs, mat);
|
|
}
|
|
|
|
if (keepFlag) {
|
|
continue;
|
|
}
|
|
|
|
/* for each coordinate in this coordinate set */
|
|
for (unsigned idx = 0; idx < cs->NIndex; ++idx) {
|
|
const auto* v2 = cs->coordPtr(idx);
|
|
|
|
if (matrix_mode) {
|
|
transform44f3f(mat, v2, ts);
|
|
v2 = ts;
|
|
}
|
|
|
|
if (MapAnyWithin(*map, vv1.data(), v2, cutoff)) {
|
|
keepFlag = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// CIF-style symop label (e.g. "1_555")
|
|
cs->setTitle(pymol::string_format("%d_%.0f%.0f%.0f", a + 1,
|
|
shift[0] + 5, shift[1] + 5, shift[2] + 5));
|
|
}
|
|
|
|
if (!keepFlag) {
|
|
DeleteP(new_obj);
|
|
continue;
|
|
}
|
|
|
|
if (segi) {
|
|
auto seg = make_symexp_segi_label(a, x, y, z);
|
|
lexidx_t segi = LexIdx(G, seg.c_str());
|
|
for (unsigned atm = 0; atm < new_obj->NAtom; ++atm) {
|
|
LexAssign(G, new_obj->AtomInfo[atm].segi, segi);
|
|
}
|
|
LexDec(G, segi);
|
|
}
|
|
|
|
// manage the new object
|
|
|
|
auto const new_name =
|
|
pymol::string_format("%s%02d%02d%02d%02d", name, a, x, y, z);
|
|
|
|
PRINTFB(G, FB_Executive, FB_Blather)
|
|
"Making new object: %s from name=%s, a=%d, x=%d, y=%d, z=%d\n",
|
|
new_name.c_str(), name, a, x, y, z ENDFB(G);
|
|
|
|
ObjectSetName(new_obj, new_name.c_str());
|
|
ExecutiveDelete(G, new_obj->Name);
|
|
ExecutiveManageObject(G, new_obj, false, quiet);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExecutivePurgeSpec(PyMOLGlobals* G, SpecRec* rec, bool save)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
|
|
if (!save) {
|
|
CGOFree(rec->gridSlotSelIndicatorsCGO);
|
|
}
|
|
|
|
ExecutiveInvalidateGroups(G, false);
|
|
ExecutiveInvalidatePanelList(G);
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
if (I->LastEdited == rec->obj)
|
|
I->LastEdited = nullptr;
|
|
if (rec->obj->type == cObjectMolecule)
|
|
if (EditorIsAnActiveObject(G, (ObjectMolecule*) rec->obj))
|
|
EditorInactivate(G);
|
|
SeqChanged(G);
|
|
if (rec->visible) {
|
|
SceneObjectDel(G, rec->obj, false);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
}
|
|
ExecutiveDelKey(I, rec);
|
|
SelectorDelete(G, rec->name);
|
|
if (!save) {
|
|
DeleteP(rec->obj);
|
|
}
|
|
TrackerDelCand(I->Tracker, rec->cand_id);
|
|
break;
|
|
case cExecSelection:
|
|
if (rec->visible) {
|
|
SceneInvalidate(G);
|
|
SeqDirty(G);
|
|
}
|
|
ExecutiveDelKey(I, rec);
|
|
SelectorDelete(G, rec->name);
|
|
TrackerDelCand(I->Tracker, rec->cand_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<std::vector<DiscardedRec>> ExecutiveDelete(
|
|
PyMOLGlobals* G, pymol::zstring_view nameView, bool save)
|
|
{
|
|
std::vector<DiscardedRec> discardedRecs;
|
|
CExecutive* I = G->Executive;
|
|
auto name = nameView.c_str();
|
|
std::vector<OrderRec> specPositions;
|
|
if (save) {
|
|
specPositions = ExecutiveGetOrderOf(G, nameView);
|
|
}
|
|
|
|
auto getRecPos = [&](SpecRec* rec) {
|
|
auto it = std::find_if(specPositions.begin(), specPositions.end(),
|
|
[&](OrderRec& oRec) { return oRec.name == rec->name; });
|
|
return it == specPositions.end() ? -1 : it->pos;
|
|
};
|
|
auto deleteObjRec = [&](SpecRec* rec) {
|
|
if (save) {
|
|
if (rec->obj->type == cObjectGroup) {
|
|
ExecutiveGroupPurge(G, rec, &discardedRecs);
|
|
}
|
|
ExecutivePurgeSpec(G, rec, save);
|
|
auto rec_pos = getRecPos(rec);
|
|
auto rec_out = ListDetachT(I->Spec, rec);
|
|
SceneObjectRemove(G, rec->obj);
|
|
assert(rec_pos);
|
|
discardedRecs.emplace_back(rec_out, rec_pos);
|
|
} else {
|
|
if (rec->obj->type == cObjectGroup) {
|
|
/* remove members of the group */
|
|
ExecutiveGroup(G, rec->name, "", cExecutiveGroupPurge, true);
|
|
}
|
|
ExecutivePurgeSpec(G, rec, save);
|
|
ListDelete(I->Spec, rec, next,
|
|
SpecRec); /* NOTE: order N in list length! TO FIX */
|
|
}
|
|
};
|
|
|
|
auto deleteSeleRec = [&](SpecRec* rec) {
|
|
ExecutivePurgeSpec(G, rec, save);
|
|
if (save) {
|
|
auto rec_pos = getRecPos(rec);
|
|
auto rec_out = ListDetachT(I->Spec, rec);
|
|
assert(rec_pos);
|
|
discardedRecs.emplace_back(rec_out, rec_pos);
|
|
} else {
|
|
ListDelete(I->Spec, rec, next,
|
|
SpecRec); /* NOTE: order N in list length! TO FIX */
|
|
}
|
|
};
|
|
|
|
SpecRec* rec = nullptr;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, false, false);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecSelection:
|
|
deleteSeleRec(rec);
|
|
break;
|
|
case cExecObject:
|
|
deleteObjRec(rec);
|
|
break;
|
|
case cExecAll:
|
|
rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
break;
|
|
case cExecObject:
|
|
deleteObjRec(rec);
|
|
rec = nullptr;
|
|
break;
|
|
case cExecSelection:
|
|
deleteSeleRec(rec);
|
|
rec = nullptr;
|
|
break;
|
|
}
|
|
}
|
|
SelectorDefragment(G);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
|
|
// fix for PYMOL-757 - Note: This should probably go somewhere else, but we
|
|
// couldn't figure out the correct place so far
|
|
ExecutiveUpdateGroups(G, false);
|
|
return discardedRecs;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveDeleteStates(
|
|
PyMOLGlobals* G, std::string_view name, const std::vector<int>& states)
|
|
{
|
|
for (auto& rec : ExecutiveGetSpecRecsFromPattern(G, name.data())) {
|
|
if (rec.type != cExecObject) {
|
|
continue;
|
|
}
|
|
auto* obj = rec.obj;
|
|
if (obj->type != cObjectMolecule) {
|
|
continue;
|
|
}
|
|
auto* mol = static_cast<ObjectMolecule*>(obj);
|
|
if (mol->DiscreteFlag) {
|
|
G->Feedback->addColored(
|
|
"Error: Cannot delete states from discrete objects.\n", FB_Warnings);
|
|
continue;
|
|
}
|
|
ObjectMoleculeDeleteStates(mol, states);
|
|
}
|
|
SceneChanged(G);
|
|
ExecutiveInvalidatePanelList(G);
|
|
return {};
|
|
}
|
|
|
|
void ExecutiveReAddSpec(PyMOLGlobals* G, std::vector<DiscardedRec>& specs)
|
|
{
|
|
auto I = G->Executive;
|
|
for (auto& spec : specs) {
|
|
auto rec = spec.rec;
|
|
auto pos = spec.pos;
|
|
rec->cand_id = TrackerNewCand(I->Tracker, (TrackerRef*) rec);
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_names_list_id, 1);
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_obj_list_id, 1);
|
|
ListInsertAt(I->Spec, rec, pos);
|
|
ExecutiveAddKey(I, rec);
|
|
ExecutiveInvalidatePanelList(G);
|
|
if (rec->type == cExecObject) {
|
|
rec->in_scene = SceneObjectAdd(G, rec->obj);
|
|
}
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
ExecutiveUpdateGroups(G, true);
|
|
}
|
|
specs.clear();
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveDump(
|
|
PyMOLGlobals* G, const char* fname, const char* obj, int state, int quiet)
|
|
{
|
|
SpecRec* rec = nullptr;
|
|
CExecutive* I = G->Executive;
|
|
|
|
SceneUpdate(G, false);
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (strcmp(rec->obj->Name, obj) == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (rec) {
|
|
if (rec->obj->type == cObjectMesh) {
|
|
ObjectMeshDump((ObjectMesh*) rec->obj, fname, state, quiet);
|
|
} else if (rec->obj->type == cObjectSurface) {
|
|
ObjectSurfaceDump((ObjectSurface*) rec->obj, fname, state, quiet);
|
|
} else if (rec->obj->type == cObjectMap) {
|
|
ObjectMapDump((ObjectMap*) rec->obj, fname, state, quiet);
|
|
} else {
|
|
ErrMessage(G, __func__, "Invalid object type for this operation.");
|
|
}
|
|
} else {
|
|
ErrMessage(G, __func__, "Object not found.");
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveMemoryDump(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
fprintf(stderr, " Executive: %d candidate(s) %d list(s) %d link(s).\n",
|
|
TrackerGetNCand(I->Tracker), TrackerGetNList(I->Tracker),
|
|
TrackerGetNLink(I->Tracker));
|
|
}
|
|
|
|
/**
|
|
* The `zoom` argument takes the following values:
|
|
*
|
|
* -1 = use `auto_zoom` setting
|
|
* 0 = do nothing
|
|
* 1 = zoom `obj` if `is_new` is ture, otherwise do nothing
|
|
* 2 = zoom `obj`
|
|
* 3 = zoom current state of `obj`
|
|
* 4 = zoom all
|
|
* 5 = zoom `obj` if it is the only object
|
|
*
|
|
*/
|
|
void ExecutiveDoZoom(
|
|
PyMOLGlobals* G, pymol::CObject* obj, int is_new, int zoom, int quiet)
|
|
{
|
|
if (zoom) {
|
|
if (zoom < 0) {
|
|
zoom = SettingGetGlobal_i(G, cSetting_auto_zoom);
|
|
if (zoom < 0) {
|
|
zoom = 1;
|
|
}
|
|
}
|
|
switch (zoom) {
|
|
case 1: /* zoom when new */
|
|
if (is_new)
|
|
ExecutiveWindowZoom(G, obj->Name, 0.0, -1, 0, 0, quiet);
|
|
/* (all states) */
|
|
break;
|
|
case 2: /* zoom always */
|
|
ExecutiveWindowZoom(G, obj->Name, 0.0, -1, 0, 0, quiet);
|
|
/* (all states) */
|
|
break;
|
|
case 3: /* always zoom current state */
|
|
ExecutiveWindowZoom(
|
|
G, obj->Name, 0.0, ObjectGetCurrentState(obj, false), 0, 0, quiet);
|
|
break;
|
|
case 4: /* zoom all objects */
|
|
ExecutiveWindowZoom(G, cKeywordAll, 0.0, -1, 0, 0, quiet);
|
|
break;
|
|
case 5: /* zoom first object only */
|
|
if (count_objects(G, true) == 1)
|
|
ExecutiveWindowZoom(
|
|
G, obj->Name, 0.0, -1, 0, 0, quiet); /* (all states) */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ExecutiveDoAutoGroup(PyMOLGlobals* G, SpecRec* rec)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
int auto_mode = SettingGetGlobal_i(G, cSetting_group_auto_mode);
|
|
if (auto_mode && (rec->name[0] != '_')) {
|
|
auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
|
|
char* period = rec->name + strlen(rec->name);
|
|
SpecRec* found_group = nullptr;
|
|
WordType seek_group_name;
|
|
UtilNCopy(seek_group_name, rec->name, sizeof(WordType));
|
|
|
|
while ((period > rec->name) && (!found_group)) {
|
|
period--;
|
|
if (*period == '.') {
|
|
seek_group_name[period - rec->name] = 0;
|
|
{
|
|
SpecRec* group_rec = nullptr;
|
|
while (ListIterate(I->Spec, group_rec, next)) {
|
|
if ((group_rec->type == cExecObject) &&
|
|
(group_rec->obj->type == cObjectGroup)) {
|
|
if (WordMatchExact(
|
|
G, group_rec->name, seek_group_name, ignore_case)) {
|
|
found_group = group_rec;
|
|
strcpy(rec->group_name, seek_group_name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((!found_group) && (auto_mode == 2)) {
|
|
auto obj = new ObjectGroup(G);
|
|
if (obj) {
|
|
ObjectSetName(obj, seek_group_name);
|
|
strcpy(rec->group_name, obj->Name);
|
|
ExecutiveManageObject(G, obj, false, true);
|
|
ExecutiveInvalidateGroups(G, false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (found_group)
|
|
ExecutiveInvalidateGroups(G, false);
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Manages an object. Adds it to the list of spec records and related trackers,
|
|
* and to the scene for rendering.
|
|
*
|
|
* Also handles:
|
|
* - auto_dss
|
|
* - group_auto_mode
|
|
* - auto_defer_builds
|
|
*
|
|
* If the object is already managed, then only do `auto_dss` and
|
|
* `auto_defer_builds`.
|
|
*
|
|
* If an object with the same name exists, then delete it and re-use the
|
|
* existing spec rec to manage the new object.
|
|
*
|
|
* @param obj Object to manage. Executive takes ownership.
|
|
* @param zoom Zoom the camera, see ExecutiveDoZoom for valid values.
|
|
*/
|
|
void ExecutiveManageObject(
|
|
PyMOLGlobals* G, pymol::CObject* obj, int zoom, int quiet)
|
|
{
|
|
SpecRec* rec = nullptr;
|
|
CExecutive* I = G->Executive;
|
|
int exists = false;
|
|
int previousVisible;
|
|
int previousObjType = 0;
|
|
|
|
if (SettingGetGlobal_b(G, cSetting_auto_hide_selections))
|
|
ExecutiveHideSelections(G);
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->obj == obj) {
|
|
exists = true;
|
|
}
|
|
}
|
|
if (!exists) {
|
|
if (WordMatchExact(G, cKeywordAll, obj->Name, true)) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
" Executive: object name \"%s\" is illegal -- renamed to 'all_'.\n",
|
|
obj->Name ENDFB(G);
|
|
strcat(obj->Name, "_"); /* don't allow object named "all" */
|
|
} else if (SelectorNameIsKeyword(G, obj->Name)) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
" Executive-Warning: name \"%s\" collides with a selection language "
|
|
"keyword.\n",
|
|
obj->Name ENDFB(G);
|
|
}
|
|
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject) {
|
|
if (strcmp(rec->obj->Name, obj->Name) == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (rec) { /* another object of this type already exists */
|
|
/* purge it */
|
|
SceneObjectDel(G, rec->obj, false);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
previousObjType = rec->obj->type;
|
|
DeleteP(rec->obj);
|
|
} else {
|
|
if (!quiet)
|
|
if (obj->Name[0] != '_') { /* suppress internal objects */
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" Executive: object \"%s\" created.\n", obj->Name ENDFB(G);
|
|
}
|
|
}
|
|
if (!rec)
|
|
ListElemCalloc(G, rec, SpecRec);
|
|
|
|
strcpy(rec->name, obj->Name);
|
|
rec->type = cExecObject;
|
|
rec->obj = obj;
|
|
previousVisible = rec->visible;
|
|
if (previousObjType == rec->obj->type) {
|
|
// skip
|
|
} else if (rec->obj->type == cObjectMap) {
|
|
rec->visible = 0;
|
|
} else {
|
|
rec->visible = 1;
|
|
/* SceneObjectAdd(G,obj); */
|
|
}
|
|
if (previousVisible != rec->visible) {
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
|
|
// Is this a new rec?
|
|
if (!rec->cand_id) {
|
|
rec->cand_id = TrackerNewCand(I->Tracker, (TrackerRef*) (void*) rec);
|
|
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_names_list_id, 1);
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_obj_list_id, 1);
|
|
ListAppend(I->Spec, rec, next, SpecRec);
|
|
ExecutiveAddKey(I, rec);
|
|
ExecutiveInvalidatePanelList(G);
|
|
|
|
ExecutiveDoAutoGroup(G, rec);
|
|
}
|
|
|
|
if (rec->visible) {
|
|
rec->in_scene = SceneObjectAdd(G, obj);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
}
|
|
}
|
|
|
|
ExecutiveUpdateObjectSelection(G, obj);
|
|
|
|
if (SettingGetGlobal_b(G, cSetting_auto_dss)) {
|
|
if (obj->type == cObjectMolecule) {
|
|
ObjectMolecule* objMol = (ObjectMolecule*) obj;
|
|
if (objMol->NCSet == 1) {
|
|
ExecutiveAssignSS(G, obj->Name, 0, nullptr, true, objMol, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
int n_state = obj->getNFrame();
|
|
int defer_limit = SettingGetGlobal_i(G, cSetting_auto_defer_builds);
|
|
if ((defer_limit >= 0) && (n_state >= defer_limit)) {
|
|
int defer_builds = SettingGetGlobal_b(G, cSetting_defer_builds_mode);
|
|
if (!defer_builds)
|
|
SettingSetGlobal_b(G, cSetting_defer_builds_mode, 3);
|
|
}
|
|
}
|
|
|
|
ExecutiveDoZoom(G, obj, !exists, zoom, true);
|
|
|
|
SeqChanged(G);
|
|
OrthoInvalidateDoDraw(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveManageSelection(PyMOLGlobals* G, const char* name)
|
|
{
|
|
|
|
SpecRec* rec = nullptr;
|
|
CExecutive* I = G->Executive;
|
|
bool hidden_name = name[0] == '_';
|
|
bool hide_all =
|
|
!hidden_name && (SettingGet<bool>(G, cSetting_active_selections) ||
|
|
SettingGet<bool>(G, cSetting_auto_hide_selections));
|
|
|
|
for (auto& rec_ : pymol::make_list_adapter(I->Spec)) {
|
|
if (rec_.type == cExecSelection) {
|
|
if (!rec && pymol::zstring_view(rec_.name) == name) {
|
|
rec = &rec_;
|
|
} else if (hide_all) {
|
|
rec_.setEnabled(G, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rec) {
|
|
ListElemCalloc(G, rec, SpecRec);
|
|
strcpy(rec->name, name);
|
|
rec->type = cExecSelection;
|
|
rec->next = nullptr;
|
|
rec->sele_color = -1;
|
|
assert(!rec->visible);
|
|
rec->cand_id = TrackerNewCand(I->Tracker, (TrackerRef*) (void*) rec);
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_names_list_id, 1);
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_sel_list_id, 1);
|
|
ListAppend(I->Spec, rec, next, SpecRec);
|
|
ExecutiveAddKey(I, rec);
|
|
ExecutiveInvalidatePanelList(G);
|
|
}
|
|
assert(rec);
|
|
if (!hidden_name) {
|
|
if (SettingGetGlobal_b(G, cSetting_auto_show_selections) && !rec->visible) {
|
|
rec->visible = true;
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
}
|
|
if (rec->visible)
|
|
SceneInvalidate(G);
|
|
ExecutiveDoAutoGroup(G, rec);
|
|
SeqDirty(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int CExecutive::click(int button, int x, int y, int mod)
|
|
{
|
|
PyMOLGlobals* G = m_G;
|
|
CExecutive* I = G->Executive;
|
|
int n, a;
|
|
SpecRec* rec = nullptr;
|
|
int t, xx;
|
|
int pass = false;
|
|
int skip;
|
|
int ExecLineHeight =
|
|
DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_control_size));
|
|
#ifndef NDEBUG
|
|
int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
|
|
#endif
|
|
int op_cnt = get_op_cnt(G);
|
|
|
|
if (y < I->HowFarDown) {
|
|
if (SettingGet<InternalGUIMode>(cSetting_internal_gui_mode, G->Setting) !=
|
|
InternalGUIMode::Default)
|
|
return SceneGetBlock(G)->click(button, x, y, mod);
|
|
}
|
|
|
|
switch (button) {
|
|
case P_GLUT_BUTTON_SCROLL_FORWARD:
|
|
I->m_ScrollBar.moveBy(-1);
|
|
return 1;
|
|
case P_GLUT_BUTTON_SCROLL_BACKWARD:
|
|
I->m_ScrollBar.moveBy(1);
|
|
return 1;
|
|
}
|
|
|
|
n = ((I->rect.top - y) - (ExecTopMargin + ExecClickMargin)) / ExecLineHeight;
|
|
a = n;
|
|
xx = (x - rect.left);
|
|
if (I->ScrollBarActive) {
|
|
if ((x - rect.left) <
|
|
(ExecScrollBarWidth + ExecScrollBarMargin + ExecToggleMargin)) {
|
|
pass = 1;
|
|
I->m_ScrollBar.click(button, x, y, mod);
|
|
}
|
|
xx -= (ExecScrollBarWidth + ExecScrollBarMargin);
|
|
}
|
|
skip = I->NSkip;
|
|
if (!pass) {
|
|
I->RecoverPressed = nullptr;
|
|
/* while(ListIterate(I->Spec,rec,next)) { */
|
|
for (auto& panelitem : I->Panel) {
|
|
auto* const panel = &panelitem;
|
|
rec = panel->spec;
|
|
|
|
assert(rec->name[0] != '_' || !hide_underscore);
|
|
{
|
|
if (skip) {
|
|
skip--;
|
|
} else {
|
|
if (!a) {
|
|
t = ((rect.right - ExecRightMargin) - x - 1) / ExecToggleWidth;
|
|
if (t < op_cnt) {
|
|
int my = rect.top - (ExecTopMargin + n * ExecLineHeight) - 3;
|
|
int mx = rect.right - (ExecRightMargin + t * ExecToggleWidth);
|
|
|
|
#if 0
|
|
// prefix name with % as guarantee to not match a selection keyword
|
|
WordType namesele = "%";
|
|
UtilNCopy(namesele + 1, (rec->type == cExecObject) ?
|
|
rec->obj->Name : rec->name, WordLength - 1);
|
|
#else
|
|
char* namesele =
|
|
(rec->type == cExecObject) ? rec->obj->Name : rec->name;
|
|
#endif
|
|
|
|
t = (op_cnt - t) - 1;
|
|
switch (t) {
|
|
case 0: /* action */
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
MenuActivate(G, mx, my, x, y, false, "all_action", namesele);
|
|
break;
|
|
case cExecSelection:
|
|
MenuActivate(G, mx, my, x, y, false, "sele_action", namesele);
|
|
break;
|
|
case cExecObject:
|
|
switch (rec->obj->type) {
|
|
case cObjectGroup:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "group_action", namesele);
|
|
break;
|
|
case cObjectMolecule:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "mol_action", namesele);
|
|
break;
|
|
case cObjectMap:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "map_action", namesele);
|
|
break;
|
|
case cObjectSurface:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "surface_action", namesele);
|
|
break;
|
|
case cObjectMesh:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "mesh_action", namesele);
|
|
break;
|
|
case cObjectMeasurement:
|
|
case cObjectCGO:
|
|
case cObjectCallback:
|
|
case cObjectAlignment:
|
|
case cObjectVolume:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "simple_action", namesele);
|
|
break;
|
|
case cObjectSlice:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "slice_action", namesele);
|
|
break;
|
|
case cObjectGadget:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "ramp_action", namesele);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_show", cKeywordAll);
|
|
break;
|
|
case cExecSelection:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_show", namesele);
|
|
break;
|
|
case cExecObject:
|
|
switch (rec->obj->type) {
|
|
case cObjectGroup:
|
|
case cObjectMolecule:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_show", namesele);
|
|
break;
|
|
case cObjectCGO:
|
|
case cObjectAlignment:
|
|
MenuActivate(G, mx, my, x, y, false, "cgo_show", namesele);
|
|
break;
|
|
case cObjectMeasurement:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "measurement_show", namesele);
|
|
break;
|
|
case cObjectMap:
|
|
MenuActivate(G, mx, my, x, y, false, "map_show", namesele);
|
|
break;
|
|
case cObjectMesh:
|
|
MenuActivate(G, mx, my, x, y, false, "mesh_show", namesele);
|
|
break;
|
|
case cObjectSurface:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "surface_show", namesele);
|
|
break;
|
|
case cObjectSlice:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "slice_show", namesele);
|
|
break;
|
|
case cObjectVolume:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "volume_show", namesele);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_hide", cKeywordAll);
|
|
break;
|
|
case cExecSelection:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_hide", namesele);
|
|
break;
|
|
case cExecObject:
|
|
switch (rec->obj->type) {
|
|
case cObjectGroup:
|
|
case cObjectMolecule:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_hide", namesele);
|
|
break;
|
|
case cObjectCGO:
|
|
case cObjectAlignment:
|
|
MenuActivate(G, mx, my, x, y, false, "cgo_hide", namesele);
|
|
break;
|
|
case cObjectMeasurement:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "measurement_hide", namesele);
|
|
break;
|
|
case cObjectMap:
|
|
MenuActivate(G, mx, my, x, y, false, "map_hide", namesele);
|
|
break;
|
|
case cObjectMesh:
|
|
MenuActivate(G, mx, my, x, y, false, "mesh_hide", namesele);
|
|
break;
|
|
case cObjectSurface:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "surface_hide", namesele);
|
|
break;
|
|
case cObjectSlice:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "slice_hide", namesele);
|
|
break;
|
|
case cObjectVolume:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "volume_hide", namesele);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_labels", "(all)");
|
|
break;
|
|
case cExecSelection:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_labels", namesele);
|
|
break;
|
|
case cExecObject:
|
|
switch (rec->obj->type) {
|
|
case cObjectGroup:
|
|
case cObjectMolecule:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "mol_labels", namesele);
|
|
break;
|
|
case cObjectMeasurement:
|
|
break;
|
|
case cObjectMap:
|
|
case cObjectSurface:
|
|
case cObjectMesh:
|
|
case cObjectSlice:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 4:
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
case cExecSelection:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_color", namesele);
|
|
break;
|
|
case cExecObject:
|
|
switch (rec->obj->type) {
|
|
case cObjectGroup:
|
|
case cObjectMolecule:
|
|
MenuActivate(G, mx, my, x, y, false, "mol_color", namesele);
|
|
break;
|
|
case cObjectMap:
|
|
case cObjectCGO:
|
|
case cObjectAlignment:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "general_color", namesele);
|
|
break;
|
|
case cObjectMesh:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "mesh_color", namesele);
|
|
break;
|
|
case cObjectSurface:
|
|
MenuActivate2Arg(G, mx, my, x, y, false, "mesh_color",
|
|
namesele, "surface");
|
|
break;
|
|
case cObjectMeasurement:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "measurement_color", namesele);
|
|
break;
|
|
case cObjectSlice:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "slice_color", namesele);
|
|
break;
|
|
case cObjectVolume:
|
|
MenuActivate(G, mx, my, x, y, false, "vol_color", namesele);
|
|
break;
|
|
case cObjectGadget:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "ramp_color", namesele);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 5:
|
|
switch (rec->type) {
|
|
case cExecAll:
|
|
MenuActivate0Arg(G, mx, my, x, y, false, "camera_motion");
|
|
break;
|
|
case cExecSelection: /* not relevant at present */
|
|
break;
|
|
case cExecObject: /* this kinds of objects can be animated */
|
|
switch (rec->obj->type) {
|
|
case cObjectGroup: /* however, this just groups motions for a
|
|
set of objects */
|
|
case cObjectMolecule:
|
|
case cObjectMeasurement:
|
|
case cObjectMap:
|
|
case cObjectSurface:
|
|
case cObjectCGO:
|
|
case cObjectMesh:
|
|
MenuActivate(
|
|
G, mx, my, x, y, false, "obj_motion", namesele);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
} else { /* clicked in variable area */
|
|
|
|
if (((panel->is_group) &&
|
|
(((xx) -1) / DIP2PIXEL(8)) > (panel->nest_level + 1)) ||
|
|
((!panel->is_group) &&
|
|
(((xx) -1) / DIP2PIXEL(8)) > panel->nest_level)) {
|
|
/* clicked on name */
|
|
|
|
rec->hilight = 1;
|
|
switch (button) {
|
|
case P_GLUT_LEFT_BUTTON:
|
|
if (I->DragMode != ExecutiveDragMode::Off) {
|
|
break;
|
|
}
|
|
I->Pressed = n;
|
|
I->OldVisibility = rec->visible;
|
|
I->Over = n;
|
|
I->DragMode = ExecutiveDragMode::Visibility;
|
|
I->ToggleMode = ExecutiveToggleMode::DeferVisibility;
|
|
I->LastChanged = nullptr;
|
|
I->LastZoomed = nullptr;
|
|
if (mod == (cOrthoSHIFT | cOrthoCTRL)) {
|
|
I->ToggleMode = ExecutiveToggleMode::HoverActivate;
|
|
if (!rec->visible) {
|
|
ExecutiveSpecSetVisibility(
|
|
G, rec, !I->OldVisibility, mod, false);
|
|
}
|
|
if (rec != I->LastZoomed)
|
|
ExecutiveWindowZoom(
|
|
G, rec->name, 0.0F, -1, false, -1.0F, true);
|
|
I->LastZoomed = rec;
|
|
I->LastChanged = rec;
|
|
} else if (mod & cOrthoSHIFT) {
|
|
ExecutiveSpecSetVisibility(
|
|
G, rec, !I->OldVisibility, mod, false);
|
|
I->ToggleMode = ExecutiveToggleMode::ImmediateVisibility;
|
|
} else if (mod & cOrthoCTRL) {
|
|
I->ToggleMode = ExecutiveToggleMode::HoverActivate;
|
|
if (!rec->visible) {
|
|
ExecutiveSpecSetVisibility(
|
|
G, rec, !I->OldVisibility, mod, false);
|
|
}
|
|
I->LastChanged = rec;
|
|
}
|
|
I->PressedWhat = 1;
|
|
I->OverWhat = 1;
|
|
break;
|
|
case P_GLUT_MIDDLE_BUTTON:
|
|
if (I->DragMode != ExecutiveDragMode::Off) {
|
|
break;
|
|
}
|
|
I->Pressed = n;
|
|
I->OldVisibility = rec->visible;
|
|
I->Over = n;
|
|
I->LastOver = I->Over;
|
|
I->DragMode = ExecutiveDragMode::VisibilityWithCamera;
|
|
I->ToggleMode = ExecutiveToggleMode::DeferVisibility;
|
|
I->LastChanged = rec;
|
|
I->LastZoomed = nullptr;
|
|
if (mod & cOrthoCTRL) {
|
|
I->ToggleMode =
|
|
ExecutiveToggleMode::ZoomActivateDeactivatePrevious;
|
|
ExecutiveWindowZoom(
|
|
G, rec->name, 0.0F, -1, false, -1.0F, true);
|
|
I->LastZoomed = rec;
|
|
if (mod & cOrthoSHIFT) { /* exclusive */
|
|
I->ToggleMode =
|
|
ExecutiveToggleMode::ZoomExclusiveActivate;
|
|
ExecutiveSetObjVisib(G, cKeywordAll, false, false);
|
|
/* need to log this */
|
|
if (!rec->visible)
|
|
ExecutiveSpecSetVisibility(G, rec, true, 0, true);
|
|
}
|
|
} else {
|
|
I->ToggleMode =
|
|
ExecutiveToggleMode::CenterActivateDeactivatePrevious;
|
|
ExecutiveCenter(
|
|
G, rec->name, -1, true, -1.0F, nullptr, true);
|
|
}
|
|
if (!rec->visible) {
|
|
ExecutiveSpecSetVisibility(
|
|
G, rec, !rec->visible, mod, false);
|
|
I->LastChanged = rec;
|
|
}
|
|
I->PressedWhat = 1;
|
|
I->OverWhat = 1;
|
|
break;
|
|
case P_GLUT_RIGHT_BUTTON:
|
|
if (I->DragMode != ExecutiveDragMode::Off) {
|
|
break;
|
|
}
|
|
I->DragMode = ExecutiveDragMode::Reorder; /* reorder */
|
|
I->Pressed = n;
|
|
I->Over = n;
|
|
I->LastOver = I->Over;
|
|
I->PressedWhat = 1;
|
|
I->OverWhat = 1;
|
|
break;
|
|
}
|
|
OrthoGrab(G, this);
|
|
OrthoDirty(G);
|
|
} else if (panel->is_group) {
|
|
/* clicked on group control */
|
|
rec->hilight = 2;
|
|
I->DragMode = ExecutiveDragMode::Visibility;
|
|
I->Pressed = n;
|
|
I->Over = n;
|
|
I->LastOver = I->Over;
|
|
I->PressedWhat = 2;
|
|
I->OverWhat = 2;
|
|
|
|
OrthoGrab(G, this);
|
|
OrthoDirty(G);
|
|
}
|
|
}
|
|
}
|
|
a--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PyMOL_NeedRedisplay(G->PyMOL);
|
|
return (1);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
static void ExecutiveSpecEnable(
|
|
PyMOLGlobals* G, SpecRec* rec, int parents, int log)
|
|
{
|
|
if (log && SettingGetGlobal_b(G, cSetting_logging)) {
|
|
OrthoLineType buffer = "";
|
|
sprintf(buffer, "cmd.enable('%s',%d)", rec->obj->Name, parents);
|
|
PLog(G, buffer, cPLog_pym);
|
|
}
|
|
|
|
if (!rec->visible) {
|
|
rec->visible = true;
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
if (!rec->in_scene) {
|
|
rec->in_scene = SceneObjectAdd(G, rec->obj);
|
|
}
|
|
|
|
if (parents) {
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetObjectParentList(G, rec);
|
|
if (list_id) {
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* parent_rec = nullptr;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &parent_rec)) {
|
|
if (rec) {
|
|
switch (parent_rec->type) {
|
|
case cExecObject:
|
|
if (!parent_rec->in_scene) {
|
|
parent_rec->in_scene = SceneObjectAdd(G, parent_rec->obj);
|
|
}
|
|
if (!parent_rec->visible) {
|
|
parent_rec->visible = true;
|
|
ReportEnabledChange(G, parent_rec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
}
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
}
|
|
|
|
void ExecutiveSpecSetVisibility(
|
|
PyMOLGlobals* G, SpecRec* rec, int new_vis, int mod, int parents)
|
|
{
|
|
std::string buffer;
|
|
int logging = SettingGetGlobal_i(G, cSetting_logging);
|
|
if (rec->type == cExecObject) {
|
|
if (rec->visible && !new_vis) {
|
|
if (logging)
|
|
buffer = pymol::string_format("cmd.disable('%s')", rec->obj->Name);
|
|
SceneObjectDel(G, rec->obj, true);
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
if (rec->visible != new_vis) {
|
|
rec->visible = new_vis;
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
} else if ((!rec->visible) && new_vis) {
|
|
ExecutiveSpecEnable(G, rec, parents, logging);
|
|
/*
|
|
if(logging)
|
|
sprintf(buffer,"cmd.enable('%s')",rec->obj->Name);
|
|
rec->in_scene = SceneObjectAdd(G,rec->obj);
|
|
rec->visible=new_vis;
|
|
ExecutiveInvalidateSceneMembers(G);
|
|
*/
|
|
}
|
|
SceneChanged(G);
|
|
if (logging && buffer[0]) {
|
|
PLog(G, buffer, cPLog_pym);
|
|
}
|
|
} else if (rec->type == cExecAll) {
|
|
if (SettingGetGlobal_i(G, cSetting_logging)) {
|
|
if (rec->visible)
|
|
buffer = "cmd.disable('all')";
|
|
else
|
|
buffer = "cmd.enable('all')";
|
|
PLog(G, buffer, cPLog_pym);
|
|
}
|
|
ExecutiveSetObjVisib(G, cKeywordAll, !rec->visible, false);
|
|
} else if (rec->type == cExecSelection) {
|
|
if (mod & cOrthoCTRL) {
|
|
buffer = pymol::string_format("cmd.enable('%s')", rec->name);
|
|
PLog(G, buffer, cPLog_pym);
|
|
if (!rec->visible) {
|
|
rec->visible = true;
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
} else {
|
|
|
|
if (rec->visible && !new_vis) {
|
|
if (SettingGetGlobal_i(G, cSetting_logging))
|
|
buffer = pymol::string_format("cmd.disable('%s')", rec->name);
|
|
} else if ((!rec->visible) && new_vis) {
|
|
buffer = pymol::string_format("cmd.enable('%s')", rec->name);
|
|
}
|
|
if (new_vis && SettingGetGlobal_b(G, cSetting_active_selections)) {
|
|
ExecutiveHideSelections(G);
|
|
}
|
|
if (SettingGetGlobal_i(G, cSetting_logging)) {
|
|
PLog(G, buffer, cPLog_pym);
|
|
}
|
|
if (rec->visible != new_vis) {
|
|
rec->visible = new_vis;
|
|
ReportEnabledChange(G, rec);
|
|
}
|
|
}
|
|
SceneChanged(G);
|
|
}
|
|
}
|
|
|
|
int ExecutiveAssignAtomTypes(
|
|
PyMOLGlobals* G, const char* s1, int format, int state, int quiet)
|
|
{
|
|
int result = 0;
|
|
int sele1;
|
|
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
if (state < 0)
|
|
state = 0;
|
|
{
|
|
if (sele1 >= 0) {
|
|
result = SelectorAssignAtomTypes(G, sele1, state, quiet, format);
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
int CExecutive::release(int button, int x, int y, int mod)
|
|
{
|
|
PyMOLGlobals* G = m_G;
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
int pass = false;
|
|
int skip;
|
|
int xx;
|
|
#ifndef NDEBUG
|
|
int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
|
|
#endif
|
|
if (y < I->HowFarDown) {
|
|
if (SettingGet<InternalGUIMode>(cSetting_internal_gui_mode, G->Setting) !=
|
|
InternalGUIMode::Default)
|
|
return SceneGetBlock(G)->release(button, x, y, mod);
|
|
}
|
|
|
|
xx = (x - rect.left);
|
|
if (I->ScrollBarActive) {
|
|
if ((x - rect.left) <
|
|
(ExecScrollBarWidth + ExecScrollBarMargin + ExecToggleMargin)) {
|
|
pass = 1;
|
|
I->m_ScrollBar.release(button, x, y, mod);
|
|
OrthoUngrab(G);
|
|
}
|
|
xx -= (ExecScrollBarWidth + ExecScrollBarMargin);
|
|
}
|
|
|
|
skip = I->NSkip;
|
|
|
|
if (!pass) {
|
|
I->drag(x, y, mod); /* incorporate final changes in cursor position */
|
|
switch (I->DragMode) {
|
|
case ExecutiveDragMode::Visibility:
|
|
|
|
/*while(ListIterate(I->Spec,rec,next)) { */
|
|
for (auto& panelitem : I->Panel) {
|
|
auto* const panel = &panelitem;
|
|
rec = panel->spec;
|
|
|
|
assert(rec->name[0] != '_' || !hide_underscore);
|
|
{
|
|
if (skip) {
|
|
skip--;
|
|
} else {
|
|
if ((I->PressedWhat == 1) &&
|
|
(((panel->is_group) &&
|
|
((xx - 1) / DIP2PIXEL(8)) > (panel->nest_level + 1)) ||
|
|
((!panel->is_group) &&
|
|
((xx - 1) / DIP2PIXEL(8)) > panel->nest_level))) {
|
|
/* over name */
|
|
if (rec->hilight == 1) {
|
|
if (rec->type == cExecSelection) {
|
|
ExecutiveSpecSetVisibility(
|
|
G, rec, !I->OldVisibility, 0, false);
|
|
} else {
|
|
ExecutiveSpecSetVisibility(
|
|
G, rec, !I->OldVisibility, mod, true);
|
|
}
|
|
}
|
|
} else if ((I->PressedWhat == 2) && (panel->is_group)) {
|
|
if (rec->hilight == 2) {
|
|
ObjectGroup* obj = (ObjectGroup*) rec->obj;
|
|
OrthoLineType buf2;
|
|
sprintf(buf2, "cmd.group(\"%s\",action='%s')\n", rec->obj->Name,
|
|
obj->OpenOrClosed ? "close" : "open");
|
|
PLog(G, buf2, cPLog_no_flush);
|
|
ExecutiveGroup(G, rec->obj->Name, "", 5, 1);
|
|
}
|
|
/* over group control */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ExecutiveDragMode::Reorder:
|
|
if (I->ReorderFlag) {
|
|
I->ReorderFlag = false;
|
|
PLog(G, I->ReorderLog, cPLog_no_flush);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
{
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
rec->hilight = 0;
|
|
}
|
|
}
|
|
|
|
I->Over = -1;
|
|
I->Pressed = -1;
|
|
I->DragMode = ExecutiveDragMode::Off;
|
|
I->PressedWhat = 0;
|
|
OrthoUngrab(G);
|
|
PyMOL_NeedRedisplay(G->PyMOL);
|
|
return (1);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int CExecutive::drag(int x, int y, int mod)
|
|
{
|
|
PyMOLGlobals* G = m_G;
|
|
CExecutive* I = G->Executive;
|
|
int xx, t;
|
|
int ExecLineHeight =
|
|
DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_control_size));
|
|
#ifndef NDEBUG
|
|
int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
|
|
#endif
|
|
int op_cnt = get_op_cnt(G);
|
|
|
|
ExecutiveUpdateGroups(G, false);
|
|
ExecutiveUpdatePanelList(G);
|
|
if (y < I->HowFarDown) {
|
|
if (SettingGet<InternalGUIMode>(cSetting_internal_gui_mode, G->Setting) !=
|
|
InternalGUIMode::Default)
|
|
return SceneGetBlock(G)->drag(x, y, mod);
|
|
}
|
|
|
|
if (I->DragMode != ExecutiveDragMode::Off) {
|
|
xx = (x - rect.left);
|
|
t = ((rect.right - ExecRightMargin) - x) / ExecToggleWidth;
|
|
if (I->ScrollBarActive) {
|
|
xx -= (ExecScrollBarWidth + ExecScrollBarMargin);
|
|
}
|
|
|
|
{
|
|
int row_offset;
|
|
if ((xx >= 0) && (t >= op_cnt)) {
|
|
row_offset = ((rect.top - y) - (ExecTopMargin + ExecClickMargin)) /
|
|
ExecLineHeight;
|
|
I->Over = row_offset;
|
|
} else {
|
|
I->Over = -1;
|
|
row_offset = -1;
|
|
{
|
|
SpecRec* rec = nullptr;
|
|
while (ListIterate(I->Spec, rec, next))
|
|
rec->hilight = 0;
|
|
}
|
|
}
|
|
|
|
if (I->RecoverPressed) {
|
|
SpecRec* rec = nullptr;
|
|
int skip = I->NSkip;
|
|
int row = 0;
|
|
for (auto& panelitem : I->Panel) {
|
|
auto* const panel = &panelitem;
|
|
rec = panel->spec;
|
|
|
|
assert(rec->name[0] != '_' || !hide_underscore);
|
|
{
|
|
if (skip) {
|
|
skip--;
|
|
} else {
|
|
if (rec == I->RecoverPressed) {
|
|
I->Pressed = row;
|
|
I->RecoverPressed = nullptr;
|
|
}
|
|
row++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (I->PressedWhat == 2) {
|
|
I->OverWhat = 0;
|
|
}
|
|
if (I->Over >= 0) {
|
|
SpecRec* rec = nullptr;
|
|
int skip = I->NSkip;
|
|
int row = 0;
|
|
switch (I->DragMode) {
|
|
case ExecutiveDragMode::Visibility:
|
|
|
|
for (auto& panelitem : I->Panel) {
|
|
auto* const panel = &panelitem;
|
|
rec = panel->spec;
|
|
|
|
assert(rec->name[0] != '_' || !hide_underscore);
|
|
{
|
|
if (skip) {
|
|
skip--;
|
|
} else {
|
|
|
|
rec->hilight = 0;
|
|
if ((I->PressedWhat == 1) && /* name button */
|
|
(((row >= I->Over) && (row <= I->Pressed)) ||
|
|
((row >= I->Pressed) && (row <= I->Over)))) {
|
|
|
|
if (((panel->is_group) && ((xx - 1) / DIP2PIXEL(8)) >
|
|
(panel->nest_level + 1)) ||
|
|
((!panel->is_group) &&
|
|
((xx - 1) / DIP2PIXEL(8)) > panel->nest_level)) {
|
|
/* dragged over name */
|
|
|
|
I->OverWhat = 1;
|
|
|
|
switch (I->ToggleMode) {
|
|
case ExecutiveToggleMode::DeferVisibility:
|
|
if (row || (row == I->Pressed))
|
|
rec->hilight = 1;
|
|
break;
|
|
case ExecutiveToggleMode::ImmediateVisibility:
|
|
if (row)
|
|
ExecutiveSpecSetVisibility(
|
|
G, rec, !I->OldVisibility, mod, false);
|
|
break;
|
|
case ExecutiveToggleMode::HoverActivate:
|
|
if ((row == I->Over) && row) {
|
|
if (I->LastChanged != rec) {
|
|
ExecutiveSpecSetVisibility(
|
|
G, I->LastChanged, false, mod, false);
|
|
}
|
|
if (!rec->visible) {
|
|
ExecutiveSpecSetVisibility(G, rec, true, mod, false);
|
|
I->LastChanged = rec;
|
|
}
|
|
if (mod == (cOrthoSHIFT | cOrthoCTRL)) {
|
|
if (rec != I->LastZoomed)
|
|
ExecutiveWindowZoom(
|
|
G, rec->name, 0.0F, -1, false, -1.0F, true);
|
|
I->LastZoomed = rec;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else if ((row == I->Pressed) && (I->PressedWhat == 2)) {
|
|
if (!((panel->is_group) && ((xx - 1) / DIP2PIXEL(8)) >
|
|
(panel->nest_level + 1))) {
|
|
|
|
/* on group control */
|
|
|
|
I->OverWhat = 2;
|
|
if (I->PressedWhat == I->OverWhat) {
|
|
rec->hilight = 2;
|
|
}
|
|
} else {
|
|
I->OverWhat = 0;
|
|
}
|
|
}
|
|
row++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ExecutiveDragMode::Reorder: /* right buttonBROKEN */
|
|
{
|
|
if ((I->Over != I->Pressed) && (I->LastOver != I->Over)) {
|
|
SpecRec* new_rec = nullptr;
|
|
SpecRec* mov_rec = nullptr;
|
|
|
|
for (auto& panelitem : I->Panel) {
|
|
rec = panelitem.spec;
|
|
assert(rec->name[0] != '_' || !hide_underscore);
|
|
{
|
|
{
|
|
if (skip) {
|
|
skip--;
|
|
} else {
|
|
if (row == I->Pressed) {
|
|
mov_rec = rec;
|
|
}
|
|
if (row == I->Over) {
|
|
new_rec = rec;
|
|
}
|
|
row++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
{
|
|
int group_flag = false;
|
|
int order_flag = false;
|
|
int location = 0;
|
|
char *first = nullptr, *second = nullptr;
|
|
int is_child = false;
|
|
|
|
if (mov_rec && (!new_rec) && (I->Over > I->Pressed) &&
|
|
mov_rec->group) {
|
|
first = mov_rec->group->name;
|
|
second = mov_rec->name;
|
|
order_flag = true;
|
|
strcpy(mov_rec->group_name, mov_rec->group->group_name);
|
|
group_flag = true;
|
|
} else if (mov_rec && new_rec) {
|
|
if (new_rec->isChildOf(mov_rec)) {
|
|
/* do nothing when a group is dragged over one of its members
|
|
*/
|
|
} else {
|
|
|
|
if (I->Pressed < I->Over) { /* downward */
|
|
first = new_rec->name;
|
|
second = mov_rec->name;
|
|
order_flag = true;
|
|
} else { /* upward */
|
|
first = mov_rec->name;
|
|
second = new_rec->name;
|
|
order_flag = true;
|
|
location = -2; /* upper */
|
|
}
|
|
|
|
if (mov_rec->group ==
|
|
new_rec->group) { /* reordering within a group level */
|
|
if ((new_rec->type == cExecObject) &&
|
|
(new_rec->obj->type == cObjectGroup)) {
|
|
ObjectGroup* group = (ObjectGroup*) new_rec->obj;
|
|
if (group->OpenOrClosed && !is_child) {
|
|
/* put inside an open group */
|
|
strcpy(mov_rec->group_name, new_rec->name);
|
|
order_flag = false;
|
|
group_flag = true;
|
|
}
|
|
}
|
|
} else if ((mov_rec->group != new_rec) &&
|
|
(new_rec->group != mov_rec)) {
|
|
|
|
if ((new_rec->type == cExecObject) &&
|
|
(new_rec->obj->type == cObjectGroup)) {
|
|
ObjectGroup* group = (ObjectGroup*) new_rec->obj;
|
|
if (group->OpenOrClosed && !is_child) {
|
|
/* put inside group */
|
|
strcpy(mov_rec->group_name, new_rec->name);
|
|
} else {
|
|
strcpy(mov_rec->group_name, new_rec->group_name);
|
|
}
|
|
|
|
/* WLD TEST */
|
|
|
|
if (I->Pressed < I->Over) { /* downward */
|
|
first = new_rec->name;
|
|
second = mov_rec->name;
|
|
order_flag = true;
|
|
} else { /* upward */
|
|
first = mov_rec->name;
|
|
second = new_rec->name;
|
|
order_flag = true;
|
|
}
|
|
|
|
/* WLD END */
|
|
|
|
} else
|
|
strcpy(mov_rec->group_name, new_rec->group_name);
|
|
group_flag = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (group_flag) {
|
|
OrthoLineType buf;
|
|
if (mov_rec->group_name[0]) {
|
|
sprintf(buf, "group %s, %s\n", mov_rec->group_name,
|
|
mov_rec->name);
|
|
} else {
|
|
sprintf(buf, "ungroup %s\n", mov_rec->name);
|
|
}
|
|
PLog(G, buf, cPLog_no_flush);
|
|
ExecutiveInvalidateGroups(G, false);
|
|
I->RecoverPressed = mov_rec;
|
|
I->Pressed = 0;
|
|
}
|
|
if (order_flag && first && second) {
|
|
OrthoLineType order_input;
|
|
sprintf(order_input, "%s %s", first, second);
|
|
ExecutiveOrder(G, order_input, false, location);
|
|
sprintf(I->ReorderLog, "cmd.order(\"%s\",location=\"%s\")\n",
|
|
order_input, (location == -2) ? "upper" : "current");
|
|
PLog(G, I->ReorderLog, cPLog_no_flush);
|
|
I->RecoverPressed = mov_rec;
|
|
I->Pressed = 0;
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
case ExecutiveDragMode::VisibilityWithCamera: /* middle button */
|
|
for (auto& panelitem : I->Panel) {
|
|
rec = panelitem.spec;
|
|
assert(rec->name[0] != '_' || !hide_underscore);
|
|
{
|
|
if (skip) {
|
|
skip--;
|
|
} else {
|
|
rec->hilight = 0;
|
|
if (((row >= I->Over) && (row <= I->Pressed)) ||
|
|
((row >= I->Pressed) && (row <= I->Over))) {
|
|
switch (I->ToggleMode) {
|
|
case ExecutiveToggleMode::
|
|
CenterActivateDeactivatePrevious: /* center and activate,
|
|
while deactivating
|
|
previous */
|
|
if ((row == I->Over) && row) {
|
|
if (I->LastChanged != rec) {
|
|
ExecutiveSpecSetVisibility(
|
|
G, I->LastChanged, false, mod, false);
|
|
ExecutiveCenter(
|
|
G, rec->name, -1, true, -1.0F, nullptr, true);
|
|
if (!rec->visible)
|
|
ExecutiveSpecSetVisibility(G, rec, true, mod, false);
|
|
I->LastChanged = rec;
|
|
}
|
|
rec->hilight = 0;
|
|
}
|
|
break;
|
|
case ExecutiveToggleMode::
|
|
ZoomActivateDeactivatePrevious: /* zoom and activate,
|
|
while deactivating
|
|
previous */
|
|
if ((row == I->Over) && row) {
|
|
if (I->LastChanged != rec) {
|
|
ExecutiveSpecSetVisibility(
|
|
G, I->LastChanged, false, mod, false);
|
|
ExecutiveWindowZoom(
|
|
G, rec->name, 0.0F, -1, false, -1.0F, true);
|
|
if (!rec->visible)
|
|
ExecutiveSpecSetVisibility(G, rec, true, mod, false);
|
|
I->LastChanged = rec;
|
|
}
|
|
rec->hilight = 1;
|
|
}
|
|
break;
|
|
case ExecutiveToggleMode::
|
|
ZoomExclusiveActivate: /* zoom and make only object
|
|
enabled */
|
|
if ((row == I->Over) && row) {
|
|
if (rec != I->LastZoomed) {
|
|
ExecutiveSpecSetVisibility(
|
|
G, I->LastZoomed, false, mod, false);
|
|
ExecutiveWindowZoom(
|
|
G, rec->name, 0.0F, -1, false, -1.0F, true);
|
|
I->LastZoomed = rec;
|
|
ExecutiveSpecSetVisibility(G, rec, true, 0, false);
|
|
}
|
|
rec->hilight = 1;
|
|
}
|
|
}
|
|
}
|
|
row++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
I->LastOver = I->Over;
|
|
|
|
} else if (I->LastChanged)
|
|
ExecutiveSpecSetVisibility(G, I->LastChanged, false, mod, false);
|
|
OrthoDirty(G);
|
|
}
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
static void draw_button(int x2, int y2, int w, int h, const float* light,
|
|
const float* dark, const float* inside, CGO* orthoCGO)
|
|
{
|
|
if (orthoCGO) {
|
|
CGOColorv(orthoCGO, light);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, x2, y2, 0.f);
|
|
CGOVertex(orthoCGO, x2, y2 + h, 0.f);
|
|
CGOVertex(orthoCGO, x2 + w, y2, 0.f);
|
|
CGOVertex(orthoCGO, x2 + w, y2 + h, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
} else {
|
|
glColor3fv(light);
|
|
glBegin(GL_POLYGON);
|
|
glVertex2i(x2, y2);
|
|
glVertex2i(x2, y2 + h);
|
|
glVertex2i(x2 + w, y2 + h);
|
|
glVertex2i(x2 + w, y2);
|
|
glEnd();
|
|
}
|
|
|
|
if (orthoCGO) {
|
|
CGOColorv(orthoCGO, dark);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, x2 + 1, y2, 0.f);
|
|
CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, 0.f);
|
|
CGOVertex(orthoCGO, x2 + w, y2, 0.f);
|
|
CGOVertex(orthoCGO, x2 + w, y2 + h - 1, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
} else {
|
|
glColor3fv(dark);
|
|
glBegin(GL_POLYGON);
|
|
glVertex2i(x2 + 1, y2);
|
|
glVertex2i(x2 + 1, y2 + h - 1);
|
|
glVertex2i(x2 + w, y2 + h - 1);
|
|
glVertex2i(x2 + w, y2);
|
|
glEnd();
|
|
}
|
|
|
|
if (inside) {
|
|
if (orthoCGO) {
|
|
CGOColorv(orthoCGO, inside);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, x2 + 1, y2 + 1, 0.f);
|
|
CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, 0.f);
|
|
CGOVertex(orthoCGO, x2 + w - 1, y2 + 1, 0.f);
|
|
CGOVertex(orthoCGO, x2 + w - 1, y2 + h - 1, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
} else {
|
|
glColor3fv(inside);
|
|
glBegin(GL_POLYGON);
|
|
glVertex2i(x2 + 1, y2 + 1);
|
|
glVertex2i(x2 + 1, y2 + h - 1);
|
|
glVertex2i(x2 + w - 1, y2 + h - 1);
|
|
glVertex2i(x2 + w - 1, y2 + 1);
|
|
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, 0.f);
|
|
CGOColor(orthoCGO, 1.0F, 1.0F, 0.1F); // yellow
|
|
CGOVertex(orthoCGO, x2 + w - 1, y2 + h - 1, 0.f);
|
|
CGOColor(orthoCGO, 1.f, 0.1f, 0.1f); // red
|
|
CGOVertex(orthoCGO, x2 + 1, y2 + 1, 0.f);
|
|
CGOColor(orthoCGO, 0.1F, 0.1F, 1.0F); // blue
|
|
CGOVertex(orthoCGO, x2 + w - 1, y2 + 1, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
} else {
|
|
glBegin(GL_POLYGON);
|
|
glColor3f(1.0F, 0.1F, 0.1F);
|
|
glVertex2i(x2 + 1, y2 + 1);
|
|
glColor3f(0.1F, 1.0F, 0.1F);
|
|
glVertex2i(x2 + 1, y2 + h - 1);
|
|
glColor3f(1.0F, 1.0F, 0.1F);
|
|
glVertex2i(x2 + w - 1, y2 + h - 1);
|
|
glColor3f(0.1F, 0.1F, 1.0F);
|
|
glVertex2i(x2 + w - 1, y2 + 1);
|
|
glEnd();
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef _PYMOL_NOPY
|
|
static void draw_button_char(
|
|
PyMOLGlobals* G, int x2, int y2, char ch, CGO* orthoCGO)
|
|
{
|
|
TextSetColor3f(G, 0.0F, 0.0F, 0.0F);
|
|
TextSetPos2i(G, x2 + ExecToggleTextShift, y2);
|
|
TextDrawChar(G, ch, orthoCGO);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Get the text color for the object name.
|
|
* @param obj Object pointer, may be nullptr
|
|
* @param mode 0: default, 1: first carbon atom color, 2: object color
|
|
* @param bg_rgb Background color
|
|
* @param default_rgb Default text color
|
|
* @return pointer to borrowed RGB array
|
|
*/
|
|
static const float* getNameColor(const pymol::CObject* obj, int mode,
|
|
const float* bg_rgb, const float* default_rgb)
|
|
{
|
|
enum {
|
|
cNameColorMode_default = 0,
|
|
cNameColorMode_carbon = 1,
|
|
cNameColorMode_object = 2,
|
|
};
|
|
|
|
if (mode == cNameColorMode_default || !obj) {
|
|
return default_rgb;
|
|
}
|
|
|
|
int color = cColorDefault;
|
|
|
|
switch (mode) {
|
|
case cNameColorMode_carbon:
|
|
// First carbon atom color
|
|
// Assuming that there is typically a carbon atom within the first few
|
|
// atoms, this procedure should be cheap (O(1)).
|
|
if (obj->type == cObjectMolecule) {
|
|
auto objmol = static_cast<const ObjectMolecule*>(obj);
|
|
for (auto ai = objmol->AtomInfo.data(), ai_end = ai + objmol->NAtom;
|
|
ai != ai_end; ++ai) {
|
|
if (ai->protons == cAN_C) {
|
|
color = ai->color;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cNameColorMode_object:
|
|
// Object color
|
|
color = obj->Color;
|
|
break;
|
|
}
|
|
|
|
if (color != cColorDefault) {
|
|
const float* rgb = ColorGet(obj->G, color);
|
|
|
|
// only use if it's different from the background color
|
|
if (!within3f(bg_rgb, rgb, 0.1f)) {
|
|
return rgb;
|
|
}
|
|
}
|
|
|
|
return default_rgb;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void CExecutive::draw(CGO* orthoCGO)
|
|
{
|
|
PyMOLGlobals* G = m_G;
|
|
int x, y, xx, x2, y2;
|
|
WordType ch;
|
|
char* c = nullptr;
|
|
float enabledColor[3] = {0.5F, 0.5F, 0.5F};
|
|
float cloakedColor[3] = {0.35F, 0.35F, 0.35F};
|
|
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};
|
|
float captionColor[3] = {0.3F, 0.9F, 0.3F};
|
|
|
|
#ifndef _PYMOL_NOPY
|
|
float activeColor[3] = {0.9F, 0.9F, 1.0F};
|
|
float toggleColor3[3] = {0.6F, 0.6F, 0.8F};
|
|
#endif
|
|
|
|
SpecRec* rec = nullptr;
|
|
CExecutive* I = G->Executive;
|
|
int n_ent;
|
|
int n_disp;
|
|
int skip = 0;
|
|
int row = -1;
|
|
int ExecLineHeight =
|
|
DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_control_size));
|
|
int text_lift = (ExecLineHeight / 2) - DIP2PIXEL(5);
|
|
int op_cnt = get_op_cnt(G);
|
|
int full_names = SettingGetGlobal_b(G, cSetting_group_full_member_names);
|
|
int arrows = SettingGetGlobal_b(G, cSetting_group_arrow_prefix);
|
|
auto name_color_mode =
|
|
SettingGet<int>(G, cSetting_internal_gui_name_color_mode);
|
|
|
|
ExecutiveUpdatePanelList(G);
|
|
|
|
/* if we're running with a GUI and have a valid panel */
|
|
if (G->HaveGUI && G->ValidContext && ((rect.right - rect.left) > 6) &&
|
|
!I->Panel.empty()) {
|
|
int max_char;
|
|
int nChar;
|
|
|
|
/* count entries
|
|
* do we have enough structures to warrant a scroll bar? */
|
|
n_ent = 0;
|
|
for (auto& panelitem : I->Panel) {
|
|
rec = panelitem.spec;
|
|
assert(rec && (rec->name[0] != '_' ||
|
|
!SettingGet<bool>(G, cSetting_hide_underscore_names)));
|
|
n_ent++;
|
|
}
|
|
|
|
n_disp = ((rect.top - rect.bottom) - (ExecTopMargin)) / ExecLineHeight;
|
|
if (n_disp < 1)
|
|
n_disp = 1;
|
|
|
|
/* we need a scrollbar */
|
|
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;
|
|
}
|
|
|
|
/* determination of longest string based on internal_gui_size, etc... */
|
|
max_char =
|
|
(((rect.right - rect.left) - (ExecLeftMargin + ExecRightMargin + 4)) -
|
|
(op_cnt * ExecToggleWidth));
|
|
if (I->ScrollBarActive) {
|
|
max_char -= (ExecScrollBarMargin + ExecScrollBarWidth);
|
|
}
|
|
max_char /= DIP2PIXEL(8);
|
|
|
|
/* fill and outline the entire block */
|
|
if (SettingGet<InternalGUIMode>(cSetting_internal_gui_mode, G->Setting) ==
|
|
InternalGUIMode::Default) {
|
|
if (orthoCGO)
|
|
CGOColorv(orthoCGO, BackColor);
|
|
#ifndef PURE_OPENGL_ES_2
|
|
else
|
|
glColor3fv(BackColor);
|
|
#endif
|
|
fill(orthoCGO);
|
|
drawLeftEdge(orthoCGO);
|
|
}
|
|
|
|
/* draw the scroll bar */
|
|
if (I->ScrollBarActive) {
|
|
I->m_ScrollBar.setBox(rect.top - ExecScrollBarMargin,
|
|
rect.left + ExecScrollBarMargin, rect.bottom + 2,
|
|
rect.left + ExecScrollBarMargin + ExecScrollBarWidth);
|
|
I->m_ScrollBar.draw(orthoCGO);
|
|
}
|
|
|
|
x = rect.left + ExecLeftMargin;
|
|
y = (rect.top - ExecLineHeight) - ExecTopMargin;
|
|
/* xx = rect.right-ExecRightMargin-ExecToggleWidth*(cRepCnt+op_cnt); */
|
|
#ifndef _PYMOL_NOPY
|
|
xx = rect.right - ExecRightMargin - ExecToggleWidth * (op_cnt);
|
|
#else
|
|
xx = rect.right - ExecRightMargin;
|
|
#endif
|
|
|
|
if (I->ScrollBarActive) {
|
|
x += ExecScrollBarWidth + ExecScrollBarMargin;
|
|
}
|
|
skip = I->NSkip;
|
|
|
|
/* for each object in the Panel... */
|
|
for (auto& panelitem : I->Panel) {
|
|
auto* const panel = &panelitem;
|
|
rec = panel->spec;
|
|
{
|
|
if (skip) {
|
|
skip--;
|
|
} else {
|
|
/* setup the X,Y offsets for this entry */
|
|
row++;
|
|
x2 = xx;
|
|
y2 = y;
|
|
nChar = max_char;
|
|
|
|
if ((x - ExecToggleMargin) - (xx - ExecToggleMargin) > -10) {
|
|
x2 = x + 10;
|
|
}
|
|
#ifndef _PYMOL_NOPY
|
|
/*
|
|
* The ASHLC menus; these access Python so,
|
|
* protect this block from non-python instances
|
|
*/
|
|
{
|
|
int a;
|
|
float toggleColor[3] = {0.5F, 0.5F, 1.0F};
|
|
float toggleColor2[3] = {0.4F, 0.4F, 0.6F};
|
|
float toggleDarkEdge[3] = {0.3F, 0.3F, 0.5F};
|
|
float toggleLightEdge[3] = {0.7F, 0.7F, 0.9F};
|
|
|
|
glColor3fv(toggleColor);
|
|
for (a = 0; a < op_cnt; a++) {
|
|
switch (a) {
|
|
case 0:
|
|
/*
|
|
glColor3fv(toggleColor);
|
|
glBegin(GL_POLYGON);
|
|
glVertex2i(x2,y2+(ExecToggleSize)/2);
|
|
glVertex2i(x2+(ExecToggleSize)/2,y2);
|
|
glVertex2i(x2+ExecToggleSize,y2+(ExecToggleSize)/2);
|
|
glVertex2i(x2+(ExecToggleSize)/2,y2+ExecToggleSize);
|
|
glEnd();
|
|
*/
|
|
|
|
/* the infamous ASHLC! */
|
|
|
|
draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
|
|
toggleLightEdge, toggleDarkEdge, toggleColor, orthoCGO);
|
|
|
|
draw_button_char(G, x2, y2 + text_lift, 'A', orthoCGO);
|
|
break;
|
|
case 1:
|
|
draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
|
|
toggleLightEdge, toggleDarkEdge, toggleColor3, orthoCGO);
|
|
|
|
draw_button_char(G, x2, y2 + text_lift, 'S', orthoCGO);
|
|
break;
|
|
case 2:
|
|
draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
|
|
toggleLightEdge, toggleDarkEdge, toggleColor2, orthoCGO);
|
|
draw_button_char(G, x2, y2 + text_lift, 'H', orthoCGO);
|
|
break;
|
|
case 3:
|
|
draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
|
|
toggleLightEdge, toggleDarkEdge, toggleColor, orthoCGO);
|
|
draw_button_char(G, x2, y2 + text_lift, 'L', orthoCGO);
|
|
break;
|
|
case 4:
|
|
draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
|
|
toggleLightEdge, toggleDarkEdge, nullptr, orthoCGO);
|
|
draw_button_char(G, x2, y2 + text_lift, 'C', orthoCGO);
|
|
break;
|
|
case 5: {
|
|
float* button_color = toggleColor2;
|
|
int spec_level = 0;
|
|
/* choose color / brightness based on specification level */
|
|
|
|
if (rec->type == cExecAll) {
|
|
spec_level = MovieGetSpecLevel(G, SceneGetFrame(G));
|
|
} else if (rec->type == cExecObject) {
|
|
spec_level = ObjectGetSpecLevel(rec->obj, SceneGetFrame(G));
|
|
}
|
|
switch (spec_level) {
|
|
case 1:
|
|
button_color = toggleColor3;
|
|
break;
|
|
case 2:
|
|
button_color = activeColor;
|
|
break;
|
|
}
|
|
draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
|
|
toggleLightEdge, toggleDarkEdge, button_color, orthoCGO);
|
|
}
|
|
|
|
draw_button_char(G, x2, y2 + text_lift, 'M', orthoCGO);
|
|
break;
|
|
}
|
|
x2 += ExecToggleWidth;
|
|
}
|
|
}
|
|
/* end ASHLC */
|
|
#endif
|
|
|
|
{
|
|
int x3 = x;
|
|
int hidden_prefix = false;
|
|
|
|
TextSetColor(G, TextColor);
|
|
TextSetPos2i(G, x3 + DIP2PIXEL(2), y2 + text_lift);
|
|
|
|
if ((rec->type == cExecObject) || (rec->type == cExecAll) ||
|
|
(rec->type == cExecSelection)) {
|
|
|
|
y2 = y;
|
|
x2 = xx;
|
|
if ((x - ExecToggleMargin) - (xx - ExecToggleMargin) >
|
|
DIP2PIXEL(-10)) {
|
|
x2 = x + DIP2PIXEL(10);
|
|
}
|
|
x3 += panel->nest_level * DIP2PIXEL(8);
|
|
TextSetPos2i(G, x3 + DIP2PIXEL(2), y2 + text_lift);
|
|
nChar -= panel->nest_level;
|
|
|
|
const float* but_color = disabledColor;
|
|
{
|
|
int but_width = (x2 - x3) - 1;
|
|
|
|
/* drawing a group +/- NAME */
|
|
if (panel->is_group) {
|
|
const int button_width = DIP2PIXEL(15);
|
|
if ((rec->hilight == 2) && (I->Over == I->Pressed)) {
|
|
draw_button(x3, y2, button_width, (ExecLineHeight - 1),
|
|
lightEdge, darkEdge, pressedColor, orthoCGO);
|
|
} else if (panel->is_open) {
|
|
draw_button(x3, y2, button_width, (ExecLineHeight - 1),
|
|
lightEdge, darkEdge, disabledColor, orthoCGO);
|
|
} else {
|
|
draw_button(x3, y2, button_width, (ExecLineHeight - 1),
|
|
lightEdge, darkEdge, disabledColor, orthoCGO);
|
|
}
|
|
TextSetPos2i(G, x3 + DIP2PIXEL(4), y2 + text_lift);
|
|
if (panel->is_open) {
|
|
TextDrawChar(G, '-', orthoCGO);
|
|
} else {
|
|
TextDrawChar(G, '+', orthoCGO);
|
|
}
|
|
|
|
but_width -= DIP2PIXEL(16);
|
|
x3 += DIP2PIXEL(16);
|
|
nChar -= 2;
|
|
|
|
TextSetPos2i(G, x3 + DIP2PIXEL(2), y2 + text_lift);
|
|
}
|
|
|
|
if ((rec->hilight == 1) ||
|
|
((row == I->Over) && (I->OverWhat == 1))) {
|
|
/* button hull */
|
|
but_color = pressedColor;
|
|
} else if (rec->visible) {
|
|
int enabled = true;
|
|
SpecRec* group_rec = rec->group;
|
|
while (enabled && group_rec) {
|
|
if (!group_rec->visible)
|
|
enabled = false;
|
|
else
|
|
group_rec = group_rec->group;
|
|
}
|
|
|
|
if (enabled) {
|
|
but_color = enabledColor;
|
|
} else {
|
|
but_color = cloakedColor;
|
|
}
|
|
}
|
|
|
|
draw_button(x3, y2, but_width, (ExecLineHeight - 1), lightEdge,
|
|
darkEdge, but_color, orthoCGO);
|
|
}
|
|
|
|
TextSetColor(G, getNameColor(rec->obj, name_color_mode, but_color,
|
|
TextColor));
|
|
|
|
/* object name */
|
|
c = rec->name;
|
|
|
|
/* parse out the prefix if group.foo */
|
|
if (!full_names) {
|
|
if (rec->group) { /* if prefix matches group name, then truncate
|
|
*/
|
|
char *p = c, *q = rec->group->name;
|
|
while ((*p == *q) && (*q)) {
|
|
p++;
|
|
q++;
|
|
}
|
|
if ((*p) && (!*q) && (*p == '.')) {
|
|
hidden_prefix = true;
|
|
c = p;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* wrap selection names with "(" and ")" */
|
|
if (rec->type == cExecSelection)
|
|
if ((nChar--) > 0) {
|
|
TextDrawChar(G, '(', orthoCGO);
|
|
}
|
|
}
|
|
|
|
if (c) {
|
|
if (hidden_prefix) {
|
|
/* ^.name */
|
|
if (arrows && ((nChar--) > 0)) {
|
|
TextDrawChar(G, '^', orthoCGO);
|
|
TextSetPos2i(G, x3 + DIP2PIXEL(2), y2 + text_lift);
|
|
TextDrawChar(G, '|', orthoCGO);
|
|
}
|
|
}
|
|
|
|
/* draw the object name, char by char */
|
|
while (*c) {
|
|
if ((nChar--) > 0) {
|
|
TextDrawChar(G, *(c++), orthoCGO);
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* SELECTIONS: wrap selection names with "(" and ")" */
|
|
if (rec->type == cExecSelection) {
|
|
if ((nChar--) > 0) {
|
|
TextDrawChar(G, ')', orthoCGO);
|
|
}
|
|
}
|
|
|
|
/* OBJECTS: output any label captions, like state number, state
|
|
* title */
|
|
if (rec->type == cExecObject) {
|
|
/* get this object's "caption" that goes on its title line,
|
|
* currently, this is "state-title [curState/nState]" */
|
|
c = rec->obj->getCaption(ch, WordLength);
|
|
/* now print the caption */
|
|
if (c && c[0] && nChar > 1) {
|
|
TextSetColor(G, captionColor);
|
|
TextSetPos2i(G,
|
|
x + DIP2PIXEL(2) + DIP2PIXEL(8) * (max_char - nChar),
|
|
y2 + text_lift);
|
|
if ((nChar--) > 0)
|
|
TextDrawChar(G, ' ', orthoCGO);
|
|
while (*c && nChar > 0) {
|
|
/* allow color encoding for names */
|
|
if (TextSetColorFromCode(G, c, captionColor)) {
|
|
c += 4;
|
|
} else {
|
|
TextDrawChar(G, *(c++), orthoCGO);
|
|
--nChar;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
y -= ExecLineHeight;
|
|
if (y < (rect.bottom))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
I->HowFarDown = y;
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveIterateObject(PyMOLGlobals* G, pymol::CObject** obj, void** hidden)
|
|
{
|
|
SpecRec **rec = (SpecRec**) hidden, *I_Spec = G->Executive->Spec;
|
|
while (ListIterate(I_Spec, (*rec), next)) {
|
|
if ((*rec)->type == cExecObject)
|
|
break;
|
|
}
|
|
if (*rec)
|
|
(*obj) = (*rec)->obj;
|
|
else
|
|
(*obj) = nullptr;
|
|
return ((*rec) != nullptr);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveIterateObjectMolecule(
|
|
PyMOLGlobals* G, ObjectMolecule** obj, void** hidden)
|
|
{
|
|
SpecRec **rec = (SpecRec**) hidden, *I_Spec = G->Executive->Spec;
|
|
while (ListIterate(I_Spec, (*rec), next)) {
|
|
if (((*rec)->type == cExecObject) && ((*rec)->obj->type == cObjectMolecule))
|
|
break;
|
|
}
|
|
if (*rec)
|
|
(*obj) = (ObjectMolecule*) (*rec)->obj;
|
|
else
|
|
(*obj) = nullptr;
|
|
return ((*rec) != nullptr);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void CExecutive::reshape(int width, int height)
|
|
{
|
|
PyMOLGlobals* G = m_G;
|
|
CExecutive* I = G->Executive;
|
|
|
|
Block::reshape(width, height);
|
|
|
|
I->Width = rect.right - rect.left + 1;
|
|
I->Height = rect.top - rect.bottom + 1;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ExecutiveReinitialize(
|
|
PyMOLGlobals* G, int what, pymol::zstring_view inPattern)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
#ifndef _PYMOL_NOPY
|
|
int blocked = false;
|
|
#endif
|
|
/* reinitialize PyMOL */
|
|
const char* pattern = inPattern.data();
|
|
if (what == 2)
|
|
pattern = nullptr;
|
|
|
|
if (pattern && (!pattern[0]))
|
|
pattern = nullptr;
|
|
if (!pattern) {
|
|
|
|
switch (what) {
|
|
case 0: /* everything */
|
|
ExecutiveDelete(G, cKeywordAll);
|
|
ColorReset(G);
|
|
SettingInitGlobal(G, false, false, true);
|
|
ColorUpdateFrontFromSettings(G);
|
|
MovieReset(G);
|
|
EditorInactivate(G);
|
|
ControlRock(G, 0);
|
|
OrthoReshape(G, -1, -1, false);
|
|
MovieScenesInit(G);
|
|
|
|
#ifndef _PYMOL_NOPY
|
|
blocked = PAutoBlock(G);
|
|
PRunStringInstance(G, "cmd.view('*','clear')");
|
|
PRunStringInstance(G, "cmd.config_mouse(\"three_button\")");
|
|
WizardSet(G, nullptr, false);
|
|
PAutoUnblock(G, blocked);
|
|
#endif
|
|
|
|
SculptCachePurge(G);
|
|
SceneReinitialize(G);
|
|
SelectorReinit(G);
|
|
SeqChanged(G);
|
|
break;
|
|
case 1: /* settings */
|
|
SettingInitGlobal(G, false, false, true);
|
|
ExecutiveRebuildAll(G);
|
|
break;
|
|
case 2: /* store_defaults */
|
|
SettingStoreDefault(G);
|
|
break;
|
|
case 3: /* original_settings */
|
|
SettingInitGlobal(G, false, false, false);
|
|
ExecutiveRebuildAll(G);
|
|
break;
|
|
case 4: /* purge_defaults */
|
|
SettingPurgeDefault(G);
|
|
break;
|
|
/* reinitialize is called with what + 5 to reset internal_gui if necessary
|
|
* (PYMOL-1425) */
|
|
case 5:
|
|
case 6:
|
|
if (G->Default) {
|
|
SettingSetGlobal_i(G, cSetting_internal_gui,
|
|
SettingGet_i(G, G->Default, nullptr, cSetting_internal_gui));
|
|
SettingGenerateSideEffects(G, cSetting_internal_gui, nullptr, -1, 0);
|
|
}
|
|
break;
|
|
}
|
|
SceneUpdateStereo(G);
|
|
} else {
|
|
{
|
|
CTracker* I_Tracker = I->Tracker;
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, pattern, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if (rec) {
|
|
switch (rec->type) {
|
|
case cExecObject:
|
|
switch (what) {
|
|
case 0:
|
|
case 1:
|
|
if (rec->obj->Setting.get()) {
|
|
ObjectPurgeSettings(rec->obj);
|
|
rec->obj->invalidate(cRepAll, cRepInvAll, -1);
|
|
SceneInvalidate(G);
|
|
SeqChanged(G);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TrackerDelList(I_Tracker, list_id);
|
|
TrackerDelIter(I_Tracker, iter_id);
|
|
}
|
|
|
|
/* to do */
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ExecutiveInit(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = nullptr;
|
|
if ((I = (G->Executive = new CExecutive(G)))) {
|
|
|
|
SpecRec* rec = nullptr;
|
|
|
|
ListInit(I->Spec);
|
|
I->Tracker = TrackerNew(G);
|
|
I->all_names_list_id = TrackerNewList(I->Tracker, nullptr);
|
|
I->all_obj_list_id = TrackerNewList(I->Tracker, nullptr);
|
|
I->all_sel_list_id = TrackerNewList(I->Tracker, nullptr);
|
|
I->active = true;
|
|
OrthoAttach(G, I, cOrthoTool);
|
|
#ifndef GLUT_FULL_SCREEN
|
|
I->oldWidth = 640;
|
|
I->oldHeight = 480;
|
|
#endif
|
|
|
|
I->Lex = OVLexicon_New(G->Context->heap);
|
|
|
|
/* create "all" entry */
|
|
|
|
ListElemCalloc(G, rec, SpecRec);
|
|
|
|
strcpy(rec->name, cKeywordAll);
|
|
rec->type = cExecAll;
|
|
rec->visible = true;
|
|
rec->next = nullptr;
|
|
rec->cand_id = TrackerNewCand(I->Tracker, (TrackerRef*) (void*) rec);
|
|
TrackerLink(I->Tracker, rec->cand_id, I->all_names_list_id, 1);
|
|
ListAppend(I->Spec, rec, next, SpecRec);
|
|
ExecutiveAddKey(I, rec);
|
|
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ExecutiveFree(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
SpecRec* rec = nullptr;
|
|
CGOFree(I->selIndicatorsCGO);
|
|
while (ListIterate(I->Spec, rec, next)) {
|
|
if (rec->type == cExecObject)
|
|
DeleteP(rec->obj);
|
|
}
|
|
ListFree(I->Spec, next, SpecRec);
|
|
if (I->Tracker)
|
|
TrackerFree(I->Tracker);
|
|
OVLexicon_DEL_AUTO_NULL(I->Lex);
|
|
|
|
ExecutiveUniqueIDAtomDictInvalidate(G);
|
|
|
|
DeleteP(G->Executive);
|
|
}
|
|
|
|
#ifdef _undefined
|
|
|
|
matrix checking code...
|
|
|
|
double mt[3][3],
|
|
mt2[3][3], pr[3][3], im[3][3], em[3][3];
|
|
printf("normalized matrix \n");
|
|
for (a = 0; a < 3; a++) {
|
|
for (b = 0; b < 3; b++) {
|
|
printf("%12.3f ", evect[a][b]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
printf("tensor \n");
|
|
for (a = 0; a < 3; a++) {
|
|
for (b = 0; b < 3; b++) {
|
|
printf("%12.3f ", mi[a][b]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
for (a = 0; a < 3; a++) {
|
|
for (b = 0; b < 3; b++) {
|
|
mt[a][b] = evect[a][b];
|
|
}
|
|
}
|
|
|
|
for (a = 0; a < 3; a++) {
|
|
for (b = 0; b < 3; b++) {
|
|
mt2[a][b] = evect[b][a];
|
|
}
|
|
}
|
|
|
|
matrix_multiply33d33d(mt, mt2, pr);
|
|
printf("self product 1 \n");
|
|
for (a = 0; a < 3; a++) {
|
|
for (b = 0; b < 3; b++) {
|
|
printf("%8.3f ", pr[a][b]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
matrix_multiply33d33d(mt, mi, im);
|
|
matrix_multiply33d33d(im, mt2, pr);
|
|
printf("diagonal product 1 \n");
|
|
for (a = 0; a < 3; a++) {
|
|
for (b = 0; b < 3; b++) {
|
|
printf("%8.3f ", pr[a][b]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
for (a = 0; a < 3; a++) {
|
|
for (b = 0; b < 3; b++) {
|
|
em[a][b] = 0.0;
|
|
}
|
|
em[a][a] = egval[a];
|
|
}
|
|
|
|
matrix_multiply33d33d(mt2, em, im);
|
|
matrix_multiply33d33d(im, mt, pr);
|
|
printf("diagonal product 4 \n");
|
|
for (a = 0; a < 3; a++) {
|
|
for (b = 0; b < 3; b++) {
|
|
printf("%8.3f ", pr[a][b]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n");
|
|
#endif
|
|
|
|
PyObject* ExecutiveCEAlign(PyMOLGlobals* G, PyObject* listA, PyObject* listB,
|
|
int lenA, int lenB, float d0, float d1, int windowSize, int gapMax)
|
|
{
|
|
#ifdef _PYMOL_NOPY
|
|
return nullptr;
|
|
#else
|
|
int i = 0;
|
|
int smaller;
|
|
double **dmA, **dmB, **S;
|
|
int bufferSize;
|
|
pcePoint coordsA, coordsB;
|
|
pathCache paths = nullptr;
|
|
PyObject* result;
|
|
|
|
smaller = lenA < lenB ? lenA : lenB;
|
|
|
|
/* get the coodinates from the Python objects */
|
|
coordsA = (pcePoint) getCoords(listA, lenA);
|
|
coordsB = (pcePoint) getCoords(listB, lenB);
|
|
|
|
/* calculate the distance matrix for each protein */
|
|
dmA = (double**) calcDM(coordsA, lenA);
|
|
dmB = (double**) calcDM(coordsB, lenB);
|
|
|
|
/* calculate the CE Similarity matrix */
|
|
S = (double**) calcS(dmA, dmB, lenA, lenB, windowSize);
|
|
|
|
/* find the best path through the CE Sim. matrix */
|
|
bufferSize = 0;
|
|
|
|
/* the following line HANGS PyMOL */
|
|
paths = (pathCache) findPath(
|
|
S, dmA, dmB, lenA, lenB, d0, d1, windowSize, gapMax, &bufferSize);
|
|
|
|
/* Get the optimal superposition here... */
|
|
result = (PyObject*) findBest(
|
|
coordsA, coordsB, paths, bufferSize, smaller, windowSize);
|
|
|
|
/* release memory */
|
|
free(coordsA);
|
|
free(coordsB);
|
|
for (i = 0; i < bufferSize; ++i)
|
|
free(paths[i]);
|
|
free(paths);
|
|
|
|
/* distance matrices */
|
|
for (i = 0; i < lenA; i++)
|
|
free(dmA[i]);
|
|
free(dmA);
|
|
|
|
for (i = 0; i < lenB; i++)
|
|
free(dmB[i]);
|
|
free(dmB);
|
|
|
|
/* similarity matrix */
|
|
for (i = 0; i < lenA; i++)
|
|
free(S[i]);
|
|
free(S);
|
|
|
|
return (PyObject*) result;
|
|
#endif
|
|
}
|
|
|
|
char* ExecutiveGetObjectNames(
|
|
PyMOLGlobals* G, int mode, const char* name, int enabled_only, int* numstrs)
|
|
{
|
|
char* res;
|
|
int size = 0, stlen;
|
|
CExecutive* I = G->Executive;
|
|
CTracker* I_Tracker = I->Tracker;
|
|
*numstrs = 0;
|
|
{
|
|
int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
|
|
int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
|
|
SpecRec* rec;
|
|
res = VLAlloc(char, 1000);
|
|
while (TrackerIterNextCandInList(
|
|
I_Tracker, iter_id, (TrackerRef**) (void*) &rec)) {
|
|
if ((rec->type == cExecObject &&
|
|
(((!mode) || (mode == cObjectTypeObjects) ||
|
|
(mode == cObjectTypePublic) ||
|
|
(mode == cObjectTypePublicObjects)) ||
|
|
((rec->obj->type != cObjectGroup) &&
|
|
((mode == cObjectTypePublicNonGroupObjects) ||
|
|
(mode == cObjectTypeNonGroupObjects))) ||
|
|
((rec->obj->type == cObjectGroup) &&
|
|
((mode == cObjectTypePublicGroupObjects) ||
|
|
(mode == cObjectTypeGroupObjects))))) ||
|
|
(rec->type == cExecSelection &&
|
|
((!mode) || (mode == cObjectTypeSelections) ||
|
|
(mode == cObjectTypePublic) ||
|
|
(mode == cObjectTypePublicSelections)))) {
|
|
if ((mode < 3) || (mode > 7) || (mode == cObjectTypeGroupObjects) ||
|
|
(rec->name[0] != '_')) {
|
|
if ((!enabled_only) || (rec->visible)) {
|
|
stlen = strlen(rec->name);
|
|
VLACheck(res, char, size + stlen + 1);
|
|
strcpy(res + size, rec->name);
|
|
size += stlen + 1;
|
|
*numstrs += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!size) {
|
|
VLAFreeP(res);
|
|
res = nullptr;
|
|
} else {
|
|
VLASize(res, char, size);
|
|
}
|
|
return (res);
|
|
}
|
|
|
|
/**
|
|
* Get the coord set for the given object name and state index. If "omp" is
|
|
* not nullptr, then also store a pointer to the object molecule.
|
|
*/
|
|
CoordSet* ExecutiveGetCoordSet(
|
|
PyMOLGlobals* G, const char* name, int state, ObjectMolecule** omp)
|
|
{
|
|
ObjectMolecule* om = nullptr;
|
|
CoordSet* cs = nullptr;
|
|
|
|
ok_assert(1, om = ExecutiveFindObject<ObjectMolecule>(G, name));
|
|
ok_assert(1, cs = om->getCoordSet(state));
|
|
|
|
ok_except1:
|
|
if (omp != nullptr)
|
|
*omp = om;
|
|
return cs;
|
|
}
|
|
|
|
/**
|
|
* Like ExecutiveGetProperty() but also return the type code.
|
|
*
|
|
* @return A [type, value] pair, or nullptr if there is no such property
|
|
*/
|
|
CPythonVal* ExecutiveGetPropertyForObject(PyMOLGlobals* G, const char* propname,
|
|
const char* objname, int state, int quiet)
|
|
{
|
|
CPythonVal* result = nullptr;
|
|
ObjectMolecule* obj = nullptr;
|
|
obj = ExecutiveFindObjectMoleculeByName(G, objname);
|
|
if (obj) {
|
|
result = ObjectMoleculeGetProperty(obj, propname, state, quiet);
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Errors)
|
|
"ExecutiveGetProperty: objname='%s' not found\n", objname ENDFB(G);
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/**
|
|
* @return The property value, or nullptr if there is no such property
|
|
*/
|
|
CPythonVal* ExecutiveGetProperty(PyMOLGlobals* G, const char* propname,
|
|
const char* objname, int state, int quiet)
|
|
{
|
|
CPythonVal* result = nullptr;
|
|
auto* withtype =
|
|
ExecutiveGetPropertyForObject(G, propname, objname, state, quiet);
|
|
if (withtype) {
|
|
result = PyList_GetItem(withtype, 1);
|
|
CPythonVal_Free(withtype);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSetPropertyForObject(PyMOLGlobals* G,
|
|
const char* propname, CPythonVal* value, const char* objname, int state,
|
|
int proptype, int quiet)
|
|
{
|
|
ObjectMolecule* obj = nullptr;
|
|
int ok = true;
|
|
|
|
auto vla = ExecutiveGetObjectMoleculeVLA(G, objname);
|
|
if (!vla)
|
|
return pymol::make_error("No object(s) found");
|
|
if (ok) {
|
|
int nObj = VLAGetSize(vla);
|
|
for (int a = 0; a < nObj; a++) {
|
|
obj = vla[a];
|
|
ok &= ObjectMoleculeSetProperty((ObjectMolecule*) obj, propname, value,
|
|
static_cast<PropertyType>(proptype), state, quiet);
|
|
}
|
|
}
|
|
if (!ok) {
|
|
return pymol::make_error("Error while setting properties...");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
int ExecutiveSetAtomPropertyForSelection(PyMOLGlobals* G, const char* propname,
|
|
CPythonVal* value, const char* s1, int state, int proptype, int quiet)
|
|
{
|
|
int sele1, result, ok = true;
|
|
sele1 = SelectorIndexByName(G, s1);
|
|
|
|
if (state < 0)
|
|
state = 0;
|
|
|
|
{
|
|
int unblock = PAutoBlock(G); /* PBlock(G); PBlockAndUnlockAPI(); */
|
|
if (sele1 >= 0) {
|
|
result = SelectorSetAtomPropertyForSelection(G, sele1, propname, value,
|
|
static_cast<PropertyType>(proptype), state, quiet);
|
|
if (!quiet && !result) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"ExecutiveSetAtomPropertyForSelection: no atoms set\n" ENDFB(G);
|
|
}
|
|
}
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
ok = false;
|
|
}
|
|
PAutoUnblock(G, unblock); /* PUnblock(G); PLockAPIAndUnblock(); */
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
void ExecutiveUniqueIDAtomDictInvalidate(PyMOLGlobals* G)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
I->m_id2eoo.clear();
|
|
I->m_eoo.clear();
|
|
}
|
|
|
|
const ExecutiveObjectOffset* ExecutiveUniqueIDAtomDictGet(
|
|
PyMOLGlobals* G, int i)
|
|
{
|
|
CExecutive* I = G->Executive;
|
|
|
|
if (I->m_eoo.empty()) {
|
|
ExecutiveGetUniqueIDAtomVLADict(G);
|
|
}
|
|
|
|
auto offsetIt = I->m_id2eoo.find(i);
|
|
return offsetIt == I->m_id2eoo.end() ? nullptr : &I->m_eoo[offsetIt->second];
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSetFeedbackMask(
|
|
PyMOLGlobals* G, int action, unsigned int sysmod, unsigned char mask)
|
|
{
|
|
switch (action) {
|
|
case 0:
|
|
G->Feedback->setMask(sysmod, mask);
|
|
break;
|
|
case 1:
|
|
G->Feedback->enable(sysmod, mask);
|
|
break;
|
|
case 2:
|
|
G->Feedback->disable(sysmod, mask);
|
|
break;
|
|
case 3:
|
|
G->Feedback->push();
|
|
break;
|
|
case 4:
|
|
G->Feedback->pop();
|
|
break;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveSliceNew(PyMOLGlobals* G, const char* slice_name,
|
|
const char* map_name, int state, int map_state)
|
|
{
|
|
int multi = false;
|
|
pymol::CObject *obj = nullptr, *mObj, *origObj;
|
|
ObjectMap* mapObj;
|
|
ObjectMapState* ms;
|
|
|
|
origObj = ExecutiveFindObjectByName(G, slice_name);
|
|
if (origObj) {
|
|
if (origObj->type != cObjectSlice) {
|
|
return pymol::make_error(
|
|
"Object ", slice_name, " is not an ObjectSlice.");
|
|
}
|
|
}
|
|
|
|
mObj = ExecutiveFindObjectByName(G, map_name);
|
|
if (mObj) {
|
|
if (mObj->type != cObjectMap)
|
|
mObj = nullptr;
|
|
}
|
|
if (mObj) {
|
|
mapObj = (ObjectMap*) mObj;
|
|
if (state == -1) {
|
|
multi = true;
|
|
state = 0;
|
|
map_state = 0;
|
|
} else if (state == -2) {
|
|
state = SceneGetState(G);
|
|
if (map_state < 0)
|
|
map_state = state;
|
|
} else if (state == -3) { /* append mode */
|
|
state = 0;
|
|
if (origObj)
|
|
state = origObj->getNFrame();
|
|
} else {
|
|
if (map_state == -1) {
|
|
map_state = 0;
|
|
multi = true;
|
|
} else {
|
|
multi = false;
|
|
}
|
|
}
|
|
while (1) {
|
|
if (map_state == -2)
|
|
map_state = SceneGetState(G);
|
|
if (map_state == -3)
|
|
map_state = mapObj->getNFrame() - 1;
|
|
ms = ObjectMapStateGetActive(mapObj, map_state);
|
|
if (ms) {
|
|
obj = ObjectSliceFromMap(
|
|
G, (ObjectSlice*) origObj, mapObj, state, map_state);
|
|
|
|
if (!origObj) {
|
|
ObjectSetName(obj, slice_name);
|
|
ExecutiveManageObject(G, obj, -1, false);
|
|
}
|
|
PRINTFB(G, FB_ObjectMesh, FB_Actions)
|
|
" SliceMap: created \"%s\".\n", slice_name ENDFB(G);
|
|
|
|
} else if (!multi) {
|
|
return pymol::make_error(
|
|
"State ", map_state + 1, " not present in map ", map_name);
|
|
}
|
|
if (multi) {
|
|
origObj = obj;
|
|
map_state++;
|
|
state++;
|
|
if (map_state >= mapObj->State.size())
|
|
break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
return pymol::make_error("Map or brick object ", map_name, " not found.");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveLoadCoordset(PyMOLGlobals* G,
|
|
pymol::zstring_view oname, PyObject* model, int frame, bool quiet)
|
|
{
|
|
auto origObj = ExecutiveFindObjectByName(G, oname.c_str());
|
|
if (!origObj || origObj->type != cObjectMolecule) {
|
|
return pymol::make_error("Invalid object molecule.");
|
|
}
|
|
|
|
PBlock(G);
|
|
auto obj =
|
|
ObjectMoleculeLoadCoords(G, (ObjectMolecule*) origObj, model, frame);
|
|
PUnblock(G);
|
|
if (!obj) {
|
|
return pymol::make_error("Load Coordset Error");
|
|
}
|
|
if (frame < 0)
|
|
frame = obj->NCSet - 1;
|
|
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" CmdLoad: Coordinates appended into object \"%s\", state %d.\n",
|
|
oname.c_str(), frame + 1 ENDFB(G);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* Discard all bonds and do distance based bonding.
|
|
* Implementation of `cmd.rebond()`
|
|
*
|
|
* @param oname object name
|
|
* @param state object state, negative values fall back to current state
|
|
*/
|
|
pymol::Result<> ExecutiveRebond(
|
|
PyMOLGlobals* G, const char* oname, int state, bool pbc)
|
|
{
|
|
auto obj = ExecutiveFindObjectMoleculeByName(G, oname);
|
|
if (!obj) {
|
|
return pymol::make_error("cannot find object");
|
|
}
|
|
|
|
auto cs = obj->getCoordSet(state);
|
|
if (!cs) {
|
|
return pymol::make_error("no such state");
|
|
}
|
|
|
|
ObjectMoleculeRemoveBonds(obj, 0, 0);
|
|
|
|
// Cases where we want to discretely rebond that isn't a pbc case?
|
|
if (obj->DiscreteFlag && pbc) {
|
|
ObjectMoleculeConnectDiscrete(obj, true, 3, pbc);
|
|
} else {
|
|
ObjectMoleculeConnect(obj, cs, true, 3, pbc);
|
|
}
|
|
|
|
obj->invalidate(cRepAll, cRepInvAll, -1);
|
|
|
|
return {};
|
|
}
|
|
|
|
bool ExecutiveIsSpecRecType(
|
|
PyMOLGlobals* G, pymol::zstring_view name, ExecRec_t execType)
|
|
{
|
|
for (const auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
|
|
if (rec.name == name) {
|
|
return rec.type == execType;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
pymol::Result<> ExecutiveAddBondByIndices(PyMOLGlobals* G,
|
|
pymol::zstring_view oname, unsigned int atm1, unsigned int atm2, int order)
|
|
{
|
|
auto obj = ExecutiveFindObject<ObjectMolecule>(G, oname.c_str());
|
|
if (!obj) {
|
|
return pymol::make_error("Cannot find object ", oname);
|
|
}
|
|
return ObjectMoleculeAddBondByIndices(obj, atm1, atm2, order);
|
|
}
|
|
|
|
pymol::Result<> ExecutiveLoadTraj(PyMOLGlobals* G, pymol::zstring_view oname,
|
|
pymol::zstring_view fname, int frame, int type, int interval, int average,
|
|
int start, int stop, int max, pymol::zstring_view str1, int image,
|
|
const float* shift, pymol::zstring_view plugin, int quiet)
|
|
{
|
|
auto s1 = SelectorTmp::make(G, str1.c_str());
|
|
p_return_if_error(s1);
|
|
bool ok = true;
|
|
auto origObj = ExecutiveFindObjectByName(G, oname.c_str());
|
|
if (!origObj) {
|
|
return pymol::make_error(
|
|
"Must load object topology before loading trajectory.");
|
|
}
|
|
if (origObj->type != cObjectMolecule) {
|
|
return pymol::make_error("Object '", oname, "' is not a molecular object.");
|
|
}
|
|
if ((type == cLoadTypeTRJ) && (!plugin.empty()))
|
|
type = cLoadTypeTRJ2;
|
|
switch (type) {
|
|
case cLoadTypeTRJ: /* this is the ascii AMBER trajectory format... */
|
|
PRINTFD(G, FB_CCmd) " ExecutiveLoadTraj-DEBUG: loading TRJ\n" ENDFD;
|
|
ObjectMoleculeLoadTRJFile(G, (ObjectMolecule*) origObj, fname.c_str(),
|
|
frame, interval, average, start, stop, max, s1->getName(), image, shift,
|
|
quiet);
|
|
PRINTFB(G, FB_Executive, FB_Actions)
|
|
" ExecutiveLoadTraj: \"%s\" appended into object \"%s\".\n"
|
|
" ExecutiveLoadTraj: %d total states in the object.\n",
|
|
fname.c_str(), oname.c_str(),
|
|
((ObjectMolecule*) origObj)->NCSet ENDFB(G);
|
|
break;
|
|
default:
|
|
ok = PlugIOManagerLoadTraj(G, (ObjectMolecule*) origObj, fname.c_str(),
|
|
frame, interval, average, start, stop, max, s1->getName(), image, shift,
|
|
quiet, plugin.c_str());
|
|
}
|
|
if (ok) {
|
|
return {};
|
|
} else {
|
|
return pymol::make_error("Could not load trajectory");
|
|
}
|
|
}
|
|
|
|
pymol::Result<ExecutiveRMSInfo> ExecutiveFit(PyMOLGlobals* G,
|
|
pymol::zstring_view str1, pymol::zstring_view str2, int mode, int cutoff,
|
|
int cycles, int quiet, pymol::zstring_view object, int state1, int state2,
|
|
int matchmaker)
|
|
{
|
|
SelectorTmp s1(G, str1.c_str());
|
|
SelectorTmp s2(G, str2.c_str());
|
|
ExecutiveRMSInfo rmsinfo;
|
|
ExecutiveRMS(G, s1.getName(), s2.getName(), mode, cutoff, cycles, quiet,
|
|
object.c_str(), state1, state2, false, matchmaker, &rmsinfo);
|
|
return rmsinfo;
|
|
}
|
|
|
|
std::string ExecutiveGetGroupMemberNames(
|
|
PyMOLGlobals* G, pymol::zstring_view groupName)
|
|
{
|
|
std::string str;
|
|
for (auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
|
|
if (groupName == rec.group_name) {
|
|
str += std::string(rec.name) + " ";
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
* Set the `visible` property and emit `ReportEnabledChange` if the property
|
|
* changed. If it doesn't change, do nothing.
|
|
*/
|
|
void SpecRec::setEnabled(PyMOLGlobals* G, bool enabled)
|
|
{
|
|
if (enabled != visible) {
|
|
visible = enabled;
|
|
ReportEnabledChange(G, this);
|
|
}
|
|
}
|
|
|
|
std::vector<OrderRec> ExecutiveGetOrderOf(
|
|
PyMOLGlobals* G, pymol::zstring_view nameListView)
|
|
{
|
|
auto I = G->Executive;
|
|
std::vector<OrderRec> recs;
|
|
for (auto& rec :
|
|
ExecutiveGetSpecRecsFromPattern(G, nameListView, true, false)) {
|
|
auto pos = ListGetPosition(I->Spec, &rec);
|
|
recs.emplace_back(rec.name, *pos);
|
|
}
|
|
auto sort_by_pos = [](const OrderRec& a, const OrderRec& b) {
|
|
return a.pos < b.pos;
|
|
};
|
|
std::sort(recs.begin(), recs.end(), sort_by_pos);
|
|
return recs;
|
|
}
|
|
|
|
void ExecutiveSetOrderOf(PyMOLGlobals* G, const std::vector<OrderRec>& recs)
|
|
{
|
|
auto I = G->Executive;
|
|
for (const auto& oRec : recs) {
|
|
auto rec = ExecutiveFindSpec(G, oRec.name);
|
|
auto detachedRec = ListDetachT(I->Spec, rec);
|
|
ListInsertAt(I->Spec, detachedRec, oRec.pos);
|
|
}
|
|
if (!recs.empty()) {
|
|
ExecutiveInvalidatePanelList(G);
|
|
}
|
|
}
|
|
|
|
pymol::Result<> ExecutiveBackgroundColor(
|
|
PyMOLGlobals* G, pymol::zstring_view color)
|
|
{
|
|
SettingSet_color(G->Setting, cSetting_bg_rgb, color.c_str());
|
|
SettingGenerateSideEffects(G, cSetting_bg_rgb, nullptr, -1, 0);
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveBackgroundColor(PyMOLGlobals* G, int colorIdx)
|
|
{
|
|
SettingSet_i(G->Setting, cSetting_bg_rgb, colorIdx);
|
|
SettingGenerateSideEffects(G, cSetting_bg_rgb, nullptr, -1, 0);
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveCurveNew(PyMOLGlobals* G,
|
|
pymol::zstring_view curveName, pymol::zstring_view curvetype)
|
|
{
|
|
auto oldObj = ExecutiveFindObject<ObjectCurve>(G, curveName);
|
|
if (oldObj) {
|
|
return pymol::make_error("Curve of this name already exists.");
|
|
}
|
|
auto obj = std::make_unique<ObjectCurve>(G);
|
|
obj->setName(curveName);
|
|
ExecutiveManageObject(G, obj.release(), false, true);
|
|
return {};
|
|
}
|
|
|
|
static pymol::Result<> ExecutiveMoveObjectOnCurve(
|
|
PyMOLGlobals* G, pymol::CObject& mobileObj, ObjectCurve& curveObj, float t)
|
|
{
|
|
auto pos = curveObj.getPosition(t);
|
|
if (!mobileObj.TTTFlag) {
|
|
initializeTTT44f(mobileObj.TTT);
|
|
mobileObj.TTTFlag = true;
|
|
}
|
|
auto ttt = pymol::TTT::from_pymol_2_legacy(mobileObj.TTT);
|
|
ttt.setTranslation(pos);
|
|
std::copy_n(
|
|
glm::value_ptr(pymol::TTT::as_pymol_2_legacy(ttt)), 16, mobileObj.TTT);
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* @return the center of an object's extent
|
|
* @param objName the name of object to face
|
|
*/
|
|
static pymol::Result<glm::vec3> ExecutiveGetObjectExtentCenter(
|
|
PyMOLGlobals* G, pymol::zstring_view name)
|
|
{
|
|
glm::vec3 min;
|
|
glm::vec3 max;
|
|
bool asTransformed = true;
|
|
bool quiet = true;
|
|
auto ext = ExecutiveGetExtent(G, name.c_str(), glm::value_ptr(min),
|
|
glm::value_ptr(max), asTransformed, cStateCurrent, quiet);
|
|
if (!ext) {
|
|
return pymol::make_error("Couldn't get extent of: ", name.c_str());
|
|
}
|
|
return (min + max) / 2.0f;
|
|
}
|
|
|
|
/**
|
|
* Moves the camera along a curve
|
|
* @param tarObj the curve to move the camera along
|
|
* @param t the progress along the curve
|
|
*/
|
|
static pymol::Result<> ExecutiveMoveCameraOnCurve(
|
|
PyMOLGlobals* G, ObjectCurve& curveObj, float t)
|
|
{
|
|
auto& cam = G->Scene->m_view;
|
|
auto pos = curveObj.getPosition(t);
|
|
auto dir = glm::normalize(curveObj.getNormalizedDirection(t));
|
|
auto tfx = glm::lookAt(pos, pos + dir, glm::vec3(0.0f, 1.0f, 0.0f));
|
|
cam.setView(SceneView::FromWorldHomogeneous(tfx, cam.getView()));
|
|
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveMoveOnCurve(PyMOLGlobals* G,
|
|
pymol::zstring_view mobileObjectName, pymol::zstring_view curveObjectName,
|
|
float t)
|
|
{
|
|
t = std::clamp(t, 0.0f, 1.0f);
|
|
auto curveObj = ExecutiveFindObject<ObjectCurve>(G, curveObjectName);
|
|
if (!curveObj) {
|
|
return pymol::make_error("Curve object not found.");
|
|
}
|
|
if (mobileObjectName == "_Camera") {
|
|
return ExecutiveMoveCameraOnCurve(G, *curveObj, t);
|
|
}
|
|
auto mobileObj = ExecutiveFindObjectByName(G, mobileObjectName);
|
|
if (!mobileObj) {
|
|
return pymol::make_error("Mobile object not found.");
|
|
}
|
|
if (curveObj == mobileObj) {
|
|
return pymol::make_error("Mobile and curve cannot be the same.");
|
|
}
|
|
return ExecutiveMoveObjectOnCurve(G, *mobileObj, *curveObj, t);
|
|
}
|
|
|
|
/**
|
|
* Directs the view toward an object
|
|
* @param tarObj the object to face
|
|
*/
|
|
static pymol::Result<> ExecutiveCameraLookAt(
|
|
PyMOLGlobals* G, pymol::CObject& tarObj)
|
|
{
|
|
auto& cam = G->Scene->m_view;
|
|
|
|
auto target = ExecutiveGetObjectExtentCenter(G, tarObj.Name);
|
|
if (!target) {
|
|
return target.error();
|
|
}
|
|
|
|
auto tfx = glm::lookAt(cam.worldPos(), *target, glm::vec3(0.0f, 1.0f, 0.0f));
|
|
cam.setView(SceneView::FromWorldHomogeneous(tfx, cam.getView()));
|
|
return {};
|
|
}
|
|
|
|
static pymol::Result<> ExecutiveObjectLookAt(
|
|
PyMOLGlobals* G, pymol::CObject& targetObj, pymol::CObject& mobileObj)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<> ExecutiveLookAt(PyMOLGlobals* G,
|
|
pymol::zstring_view targetObjectName, pymol::zstring_view mobileObjectName)
|
|
{
|
|
auto targetObj = ExecutiveFindObjectByName(G, targetObjectName);
|
|
if (!targetObj) {
|
|
return pymol::make_error("Target object not found.");
|
|
}
|
|
if (mobileObjectName == "_Camera") {
|
|
return ExecutiveCameraLookAt(G, *targetObj);
|
|
}
|
|
auto mobileObj = ExecutiveFindObjectByName(G, mobileObjectName);
|
|
if (!mobileObj) {
|
|
return pymol::make_error("Mobile object not found.");
|
|
}
|
|
return ExecutiveObjectLookAt(G, *targetObj, *mobileObj);
|
|
}
|
|
|
|
/**
|
|
* Updates an object's dependencies.
|
|
* @param obj the object to update
|
|
* @param visited a set of objects that have already been visited
|
|
*/
|
|
static void ExecutiveUpdateObjectsImpl(PyMOLGlobals* G,
|
|
const pymol::CObject& obj,
|
|
std::unordered_set<const pymol::CObject*>& visited)
|
|
{
|
|
if (visited.count(&obj)) {
|
|
return;
|
|
}
|
|
auto& deps = G->Executive->m_objDeps[&obj];
|
|
deps.clear();
|
|
visited.insert(&obj);
|
|
auto is_mol_in_dist = [&](const auto& mol, const auto& dist) {
|
|
for (const auto ds : dist->DSet) {
|
|
const auto ds_deps = ds->getDependentObjects();
|
|
if (ds_deps.count(mol)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
// TODO: This only detects mol <--> dist dependencies.
|
|
for (auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
|
|
if (rec.type != cExecObject || rec.obj == &obj) {
|
|
continue;
|
|
}
|
|
switch (rec.obj->type) {
|
|
case cObjectMolecule: {
|
|
auto mol = static_cast<ObjectMolecule*>(rec.obj);
|
|
if (auto measure = dynamic_cast<const ObjectDist*>(&obj)) {
|
|
if (is_mol_in_dist(mol, measure)) {
|
|
deps.insert(rec.obj);
|
|
ExecutiveUpdateObjectsImpl(G, *rec.obj, visited);
|
|
}
|
|
}
|
|
} break;
|
|
case cObjectMeasurement: {
|
|
auto measure = static_cast<ObjectDist*>(rec.obj);
|
|
if (auto mol = dynamic_cast<const ObjectMolecule*>(&obj)) {
|
|
if (is_mol_in_dist(mol, measure)) {
|
|
deps.insert(rec.obj);
|
|
ExecutiveUpdateObjectsImpl(G, *rec.obj, visited);
|
|
}
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Erase self
|
|
deps.erase(&obj);
|
|
}
|
|
|
|
pymol::Result<> ExecutiveUpdateObjectDeps(
|
|
PyMOLGlobals* G, const pymol::CObject& obj)
|
|
{
|
|
std::unordered_set<const pymol::CObject*> visited;
|
|
ExecutiveUpdateObjectsImpl(G, obj, visited);
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<std::unordered_set<const pymol::CObject*>> ExecutiveGetObjectDeps(
|
|
PyMOLGlobals* G, const pymol::CObject& obj, unsigned int depth)
|
|
{
|
|
auto obj_set = G->Executive->m_objDeps[&obj];
|
|
std::unordered_set<const pymol::CObject*> visited;
|
|
visited.insert(&obj);
|
|
|
|
while (depth--) {
|
|
std::unordered_set<const pymol::CObject*> new_set;
|
|
for (auto& dep : obj_set) {
|
|
if (visited.count(dep)) {
|
|
continue;
|
|
}
|
|
visited.insert(dep);
|
|
const auto& deps = G->Executive->m_objDeps[dep];
|
|
new_set.insert(deps.begin(), deps.end());
|
|
}
|
|
obj_set.insert(new_set.begin(), new_set.end());
|
|
}
|
|
|
|
// Self doesn't count as dependency
|
|
obj_set.erase(&obj);
|
|
return obj_set;
|
|
}
|
|
|
|
/**
|
|
* Run TM-align on two selections and return results.
|
|
*
|
|
* @param mobile_sele mobile selection (will be transformed)
|
|
* @param target_sele target selection (stays fixed)
|
|
* @param mobile_state state of mobile selection (0-based)
|
|
* @param target_state state of target selection (0-based)
|
|
* @param quiet suppress output
|
|
* @param transform apply superposition transform
|
|
* @param oname name for alignment object (empty = don't create)
|
|
* @param fast use fast mode (fewer iterations)
|
|
*/
|
|
pymol::Result<pymol::usalign::TMAlignResult> ExecutiveUSalign(PyMOLGlobals* G,
|
|
const char* mobile_sele, const char* target_sele, int mobile_state,
|
|
int target_state, int quiet, int transform, const char* oname, int fast)
|
|
{
|
|
// Resolve selections
|
|
auto sele_mobile = SelectorIndexByName(G, mobile_sele);
|
|
if (sele_mobile < 0)
|
|
return pymol::make_error("Invalid mobile selection: ", mobile_sele);
|
|
|
|
auto sele_target = SelectorIndexByName(G, target_sele);
|
|
if (sele_target < 0)
|
|
return pymol::make_error("Invalid target selection: ", target_sele);
|
|
|
|
// Extract CA coordinates and sequences
|
|
struct ResidueInfo {
|
|
glm::dvec3 coord;
|
|
char seq_char;
|
|
AtomInfoType* ai;
|
|
};
|
|
|
|
auto extract_ca = [&](SelectorID_t sele,
|
|
int state) -> std::vector<ResidueInfo> {
|
|
std::vector<ResidueInfo> residues;
|
|
SeleCoordIterator iter(G, sele, state);
|
|
while (iter.next()) {
|
|
auto* ai = iter.getAtomInfo();
|
|
if (ai->flags & cAtomFlag_guide) {
|
|
float* c = iter.getCoord();
|
|
ResidueInfo ri;
|
|
ri.coord = glm::dvec3(c[0], c[1], c[2]);
|
|
ri.seq_char = SeekerGetAbbr(G, LexStr(G, ai->resn), 'O', 'X');
|
|
ri.ai = ai;
|
|
residues.push_back(ri);
|
|
}
|
|
}
|
|
return residues;
|
|
};
|
|
|
|
auto mobile_res = extract_ca(sele_mobile, mobile_state);
|
|
auto target_res = extract_ca(sele_target, target_state);
|
|
|
|
if (mobile_res.size() < 3) {
|
|
return pymol::make_error("Mobile selection has fewer than 3 guide atoms (",
|
|
mobile_res.size(), ")");
|
|
}
|
|
if (target_res.size() < 3) {
|
|
return pymol::make_error("Target selection has fewer than 3 guide atoms (",
|
|
target_res.size(), ")");
|
|
}
|
|
|
|
// Build coordinate vectors and sequences
|
|
std::vector<glm::dvec3> mobile_ca, target_ca;
|
|
std::string mobile_seq, target_seq;
|
|
mobile_ca.reserve(mobile_res.size());
|
|
target_ca.reserve(target_res.size());
|
|
|
|
for (const auto& r : mobile_res) {
|
|
mobile_ca.push_back(r.coord);
|
|
mobile_seq.push_back(r.seq_char);
|
|
}
|
|
for (const auto& r : target_res) {
|
|
target_ca.push_back(r.coord);
|
|
target_seq.push_back(r.seq_char);
|
|
}
|
|
|
|
// Run TM-align
|
|
auto result = pymol::usalign::TMalign(
|
|
target_ca, mobile_ca, target_seq, mobile_seq, fast != 0);
|
|
|
|
if (result.aligned_length < 1) {
|
|
return pymol::make_error("TM-align failed to find any alignment");
|
|
}
|
|
|
|
// Print results
|
|
if (!quiet) {
|
|
PRINTFB(G, FB_Executive, FB_Results)
|
|
" USalign: TM-score= %6.4f (normalized by target, N=%d, d0=%.2f)\n",
|
|
result.tm_score_target, static_cast<int>(target_ca.size()),
|
|
result.d0_target ENDFB(G);
|
|
PRINTFB(G, FB_Executive, FB_Results)
|
|
" USalign: TM-score= %6.4f (normalized by mobile, N=%d, d0=%.2f)\n",
|
|
result.tm_score_mobile, static_cast<int>(mobile_ca.size()),
|
|
result.d0_mobile ENDFB(G);
|
|
PRINTFB(G, FB_Executive, FB_Results)
|
|
" USalign: Aligned length= %d, RMSD= %5.2f, Seq_ID=n_identical/n_aligned= "
|
|
"%4.3f\n",
|
|
result.aligned_length, result.rmsd, result.seq_identity ENDFB(G);
|
|
}
|
|
|
|
// Apply transform to mobile object
|
|
if (transform) {
|
|
// Convert double-precision Superposition to float TTT
|
|
// USalign convention: y_aligned = R * x + t
|
|
// where x = mobile coords, y = target coords
|
|
// The rotation R and translation t transform mobile -> target space
|
|
|
|
const auto& sup = result.transform;
|
|
|
|
// Build a legacy-style 16-float TTT matrix
|
|
// TTT format: [R00 R01 R02 pre_x] [R10 R11 R12 pre_y]
|
|
// [R20 R21 R22 pre_z] [tx ty tz 1]
|
|
// where pre is the pre-translation (origin), and t is post-translation
|
|
// For a simple rotation+translation (no origin): pre=0, R=rotation,
|
|
// t=translation
|
|
|
|
glm::mat3 rot_f(sup.rotation);
|
|
glm::quat q = glm::quat_cast(rot_f);
|
|
glm::vec3 t(sup.translation);
|
|
|
|
// Create TTT: pretranslate=0, rotate=q, posttranslate=t
|
|
pymol::TTT ttt(glm::vec3(0.0f), q, t);
|
|
|
|
// Convert to legacy float[16] format for ExecuteCombineObjectTTT
|
|
auto legacy = pymol::TTT::as_pymol_2_legacy(ttt);
|
|
float tttf[16];
|
|
std::memcpy(tttf, glm::value_ptr(legacy), 16 * sizeof(float));
|
|
|
|
// Follow the same pattern as ExecutiveAlign:
|
|
// 1. Copy target's TTT and state matrix to mobile (reset to same frame)
|
|
// 2. Combine the alignment transform (reverse_order=true)
|
|
// Note: Only the first object in the mobile selection is transformed,
|
|
// matching ExecutiveAlign behavior for multi-object selections.
|
|
ObjectMolecule* mobile_obj = SelectorGetFirstObjectMolecule(G, sele_mobile);
|
|
ObjectMolecule* target_obj =
|
|
SelectorGetSingleObjectMolecule(G, sele_target);
|
|
if (mobile_obj && target_obj) {
|
|
ExecutiveMatrixCopy(G, target_obj->Name, mobile_obj->Name, 1, 1,
|
|
target_state, mobile_state, false, 0, quiet);
|
|
ExecutiveMatrixCopy(G, target_obj->Name, mobile_obj->Name, 2, 2,
|
|
target_state, mobile_state, false, 0, quiet);
|
|
ExecutiveCombineObjectTTT(G, mobile_obj->Name, tttf, true, -1);
|
|
}
|
|
}
|
|
|
|
// Create alignment object
|
|
if (oname && oname[0]) {
|
|
int align_state = target_state;
|
|
if (align_state < 0) {
|
|
align_state = SceneGetState(G);
|
|
}
|
|
|
|
ObjectMolecule* trg_obj = SelectorGetSingleObjectMolecule(G, sele_target);
|
|
ObjectMolecule* mob_obj = SelectorGetFirstObjectMolecule(G, sele_mobile);
|
|
|
|
if (trg_obj && mob_obj) {
|
|
int n_pair = result.aligned_length;
|
|
pymol::vla<int> align_vla(n_pair * 3);
|
|
int* id_p = align_vla.data();
|
|
|
|
for (int k = 0; k < n_pair; k++) {
|
|
int mi = result.mobile_indices[k];
|
|
int ti = result.target_indices[k];
|
|
if (mi < static_cast<int>(mobile_res.size()) &&
|
|
ti < static_cast<int>(target_res.size())) {
|
|
id_p[0] = AtomInfoCheckUniqueID(G, target_res[ti].ai);
|
|
id_p[1] = AtomInfoCheckUniqueID(G, mobile_res[mi].ai);
|
|
id_p[2] = 0;
|
|
id_p += 3;
|
|
}
|
|
}
|
|
|
|
ObjectAlignment* obj = nullptr;
|
|
{
|
|
pymol::CObject* execObj = ExecutiveFindObjectByName(G, oname);
|
|
if (execObj && execObj->type != cObjectAlignment) {
|
|
ExecutiveDelete(G, oname);
|
|
} else {
|
|
obj = dynamic_cast<ObjectAlignment*>(execObj);
|
|
}
|
|
}
|
|
obj = ObjectAlignmentDefine(
|
|
G, obj, align_vla, align_state, true, trg_obj, mob_obj);
|
|
obj->Color = ColorGetIndex(G, "yellow");
|
|
ObjectSetName(obj, oname);
|
|
ExecutiveManageObject(G, obj, 0, quiet);
|
|
SceneInvalidate(G);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|