1 /*
2 A* -------------------------------------------------------------------
3 B* This file contains source code for the PyMOL computer program
4 C* copyright 1998-2004 by Warren Lyford Delano of DeLano Scientific.
5 D* -------------------------------------------------------------------
6 E* It is unlawful to modify or remove this copyright notice.
7 F* -------------------------------------------------------------------
8 G* Please see the accompanying LICENSE file for further information.
9 H* -------------------------------------------------------------------
10 I* Additional authors of this source file include:
11 -*
12 -*
13 -*
14 Z* -------------------------------------------------------------------
15 */
16
17 #include <algorithm>
18 #include <cctype>
19 #include <functional>
20 #include <string>
21 #include <vector>
22
23 #include"os_python.h"
24 #include"os_numpy.h"
25 #include"os_std.h"
26
27 #include"Base.h"
28 #include"Map.h"
29 #include"Vector.h"
30 #include"Err.h"
31 #include"Word.h"
32 #include"Util.h"
33 #include"PConv.h"
34 #include"P.h"
35 #include"RingFinder.h"
36 #include"AtomIterators.h"
37
38 #include"MemoryDebug.h"
39 #include"Selector.h"
40 #include"Executive.h"
41 #include"ObjectMolecule.h"
42 #include"CoordSet.h"
43 #include"DistSet.h"
44 #include"Word.h"
45 #include"Scene.h"
46 #include"CGO.h"
47 #include"Seq.h"
48 #include"Editor.h"
49 #include"Seeker.h"
50 #include "Lex.h"
51 #include "Mol2Typing.h"
52
53 #include"OVContext.h"
54 #include"OVLexicon.h"
55 #include"OVOneToAny.h"
56 #include"Parse.h"
57
58 #include"ListMacros.h"
59
60 #ifdef _PYMOL_IP_PROPERTIES
61 #endif
62
63 #include "pymol/zstring_view.h"
64
65 #include "SelectorDef.h"
66
67 using SelectorInfoIter_t = decltype(CSelectorManager::Info)::iterator;
68
69 /**
70 * Prefix for temporary selections.
71 *
72 * Must be a valid name for the selection language, so it can't contain any
73 * characters which are selection operators (e.g. "_!" is not valid).
74 *
75 * Ideally a prefix which can't be used by the user. This is currently
76 * only true if the `validate_object_names` setting is off (default).
77 */
78 #define cSelectorTmpPrefix "_#"
79
80 #define cDummyOrigin 0
81 #define cDummyCenter 1
82
83
84 /* special selections, unknown to executive */
85 #define cSelectorSecretsPrefix "_!"
86 #define cColorectionFormat "_!c_%s_%d"
87
88 static WordKeyValue rep_names[] = {
89 {"spheres", cRepSphereBit},
90 {"sticks", cRepCylBit},
91 {"surface", cRepSurfaceBit},
92 {"labels", cRepLabelBit},
93 {"nb_spheres", cRepNonbondedSphereBit},
94 {"cartoon", cRepCartoonBit},
95 {"ribbon", cRepRibbonBit},
96 {"lines", cRepLineBit},
97 {"dots", cRepDotBit},
98 {"mesh", cRepMeshBit},
99 {"nonbonded", cRepNonbondedBit},
100 {"ellipsoid", cRepEllipsoidBit},
101 {"",},
102 };
103
104 static const char *backbone_names[] = {
105 // protein
106 "CA", "C", "O", "N", "OXT", "H",
107 // nucleic acid
108 "P", "OP1", "OP2", "OP3", "C1'", "C2'", "O2'",
109 "C3'", "O3'", "C4'", "O4'", "C5'", "O5'",
110 "H1'", "H3'", "H4'",
111 "H2'", "H2''", "H12'", "H22'",
112 "H5'", "H5''", "H15'", "H25'",
113 "HO2'", "HO3'", "HO5'",
114 ""
115 };
116
117 /// Helper type which replaces `int*` return types
118 typedef std::unique_ptr<int[]> sele_array_t;
sele_array_calloc(sele_array_t & sele,size_t count)119 inline void sele_array_calloc(sele_array_t& sele, size_t count)
120 {
121 sele.reset(new int[count]());
122 }
123
124 struct EvalElem {
125 int level, imp_op_level;
126 int type; /* 0 = value 1 = operation 2 = pre-operation */
127 unsigned int code;
128 std::string m_text;
129 sele_array_t sele;
130
131 // Helpers for refactoring `sele` type
sele_dataEvalElem132 int* sele_data() { return sele.get(); }
sele_freeEvalElem133 void sele_free() { sele.reset(); }
sele_callocEvalElem134 void sele_calloc(size_t count) { sele_array_calloc(sele, count); }
135
136 // TODO replace with pymol::Error handling
sele_check_okEvalElem137 void sele_check_ok(int& ok) { CHECKOK(ok, sele_data()); }
sele_err_chk_ptrEvalElem138 void sele_err_chk_ptr(PyMOLGlobals* G) { ErrChkPtr(G, sele_data()); }
139
140 /// read-only access to text
textEvalElem141 const char* text() const { return m_text.c_str(); }
142 };
143
144 typedef struct {
145 int depth1;
146 int depth2;
147 int depth3;
148 int depth4;
149 int sum;
150 int frag;
151 } WalkDepthRec;
152
153 static pymol::Result<sele_array_t> SelectorSelect(
154 PyMOLGlobals* G, const char* sele, int state, SelectorID_t domain, int quiet);
155 static std::vector<int> SelectorGetInterstateVLA(PyMOLGlobals* G, int sele1,
156 int state1, int sele2, int state2, float cutoff);
157
158 static int SelectorModulate1(PyMOLGlobals * G, EvalElem * base, int state);
159 static int SelectorSelect0(PyMOLGlobals * G, EvalElem * base);
160 static int SelectorSelect2(PyMOLGlobals * G, EvalElem * base, int state);
161 static int SelectorLogic1(PyMOLGlobals * G, EvalElem * base, int state);
162 static int SelectorLogic2(PyMOLGlobals * G, EvalElem * base);
163 static int SelectorOperator22(PyMOLGlobals * G, EvalElem * base, int state);
164 static pymol::Result<sele_array_t> SelectorEvaluate(
165 PyMOLGlobals* G, std::vector<std::string>& word, int state, int quiet);
166 static std::vector<std::string> SelectorParse(PyMOLGlobals * G, const char *s);
167 static void SelectorPurgeMembers(PyMOLGlobals * G, SelectorID_t sele);
168 static int SelectorEmbedSelection(PyMOLGlobals * G, const int *atom, pymol::zstring_view name,
169 ObjectMolecule * obj, int no_dummies, int exec_manage);
170 static int *SelectorGetIndexVLA(PyMOLGlobals * G, SelectorID_t sele);
171 static int *SelectorGetIndexVLAImpl(PyMOLGlobals * G, CSelector *I, int sele);
172 static void SelectorClean(PyMOLGlobals * G);
173 static void SelectorCleanImpl(PyMOLGlobals * G, CSelector *I);
174 static int SelectorCheckNeighbors(PyMOLGlobals * G, int maxDepth, ObjectMolecule * obj,
175 int at1, int at2, int *zero, int *scratch);
176
177 static sele_array_t SelectorUpdateTableSingleObject(PyMOLGlobals * G, ObjectMolecule * obj,
178 int req_state,
179 int no_dummies, int *idx,
180 int n_idx, int numbered_tags);
181
182
183 /*========================================================================*/
184 /**
185 * Iterator over the selector table. If `SelectorUpdateTable(G,
186 * cSelectorUpdateTableAllStates, -1)` was called, this would be all atoms.
187 *
188 * Does NOT provide coord or coordset access
189 *
190 * @pre Selector table is up-to-date
191 */
192 class SelectorAtomIterator : public AbstractAtomIterator {
193 CSelector * selector;
194
195 public:
196 int a; //!< index in selector table
197
SelectorAtomIterator(CSelector * I)198 SelectorAtomIterator(CSelector* I)
199 : selector(I)
200 {
201 reset();
202 }
203
reset()204 void reset() override {
205 a = cNDummyAtoms - 1;
206 }
207
208 bool next() override;
209 };
210
next()211 bool SelectorAtomIterator::next() {
212 if ((++a) >= selector->Table.size())
213 return false;
214
215 auto& table_a = selector->Table[a];
216
217 atm = table_a.atom;
218 obj = selector->Obj[table_a.model];
219
220 return true;
221 }
222
223 /**
224 * Add atom `ai` to selection `sele`
225 */
SelectorManagerInsertMember(CSelectorManager & self,AtomInfoType & ai,int sele,int tag=1)226 static void SelectorManagerInsertMember(
227 CSelectorManager& self, AtomInfoType& ai, int sele, int tag = 1)
228 {
229 int m;
230 if (self.FreeMember > 0) {
231 m = self.FreeMember;
232 self.FreeMember = self.Member[m].next;
233 } else {
234 m = self.Member.size();
235 self.Member.emplace_back();
236 }
237 self.Member[m].selection = sele;
238 self.Member[m].tag = tag;
239 self.Member[m].next = ai.selEntry;
240 ai.selEntry = m;
241 }
242
243 /*========================================================================*/
SelectorGetUniqueTmpName(PyMOLGlobals * G,char * out)244 static void SelectorGetUniqueTmpName(PyMOLGlobals* G, char* out)
245 {
246 sprintf(out, "%s%d", cSelectorTmpPrefix, G->SelectorMgr->NSelection);
247 }
248
SelectorIsTmp(pymol::zstring_view name)249 static bool SelectorIsTmp(pymol::zstring_view name)
250 {
251 assert(name);
252 return name.starts_with(cSelectorTmpPrefix);
253 }
254
255 /*========================================================================*/
SelectorGetObjAtmOffset(CSelector * I,ObjectMolecule * obj,int offset)256 static int SelectorGetObjAtmOffset(CSelector * I, ObjectMolecule * obj, int offset)
257 {
258 if(I->SeleBaseOffsetsValid) {
259 return obj->SeleBase + offset;
260 } else {
261 ov_diff stop_below = obj->SeleBase;
262 ov_diff stop_above = I->Table.size() - 1;
263 int result = stop_below;
264 int step = offset;
265 int cur;
266 int proposed;
267 int prior1 = -1, prior2 = -1;
268
269 /* non-linear hunt to find atom */
270
271 result = stop_below;
272 cur = I->Table[result].atom;
273 while(step > 1) {
274 if(cur < offset) {
275 stop_below = result + 1;
276 while(step > 1) {
277 proposed = result + step;
278 if(proposed <= stop_above) {
279 if(I->Obj[I->Table[proposed].model] == obj) {
280 if(proposed == prior1) {
281 proposed--;
282 step--; /* guarantee progress (avoid flip flop) */
283 }
284 result = prior1 = proposed;
285 break;
286 } else if(stop_above > proposed) {
287 stop_above = proposed - 1;
288 }
289 }
290 step = (step >> 1);
291 }
292 } else if(cur > offset) {
293 stop_above = result - 1;
294 while(step > 1) {
295 proposed = result - step;
296 if(proposed >= stop_below) {
297 if(I->Obj[I->Table[proposed].model] == obj) {
298 if(proposed == prior2) {
299 proposed++;
300 step--; /* guarantee progress (avoid flip flop) */
301 }
302 result = prior2 = proposed;
303 break;
304 }
305 }
306 step = (step >> 1);
307 }
308 } else
309 return result;
310 cur = I->Table[result].atom;
311 if(cur == offset)
312 return result;
313 }
314
315 {
316 /* failsafe / linear search */
317 int dir = 1;
318 if(cur > offset)
319 dir = -1;
320 while(1) { /* TODO: optimize this search algorithm! */
321 if(cur == offset)
322 return result;
323 if(dir > 0) {
324 if(result >= stop_above)
325 break;
326 result++;
327 } else {
328 if(result <= stop_below)
329 break;
330 result--;
331 }
332 if(I->Obj[I->Table[result].model] != obj)
333 break;
334 cur = I->Table[result].atom;
335 }
336 }
337 }
338 return -1;
339 }
340
341 #define STYP_VALU 0
342 #define STYP_OPR1 1
343 #define STYP_OPR2 2
344 #define STYP_SEL0 3
345 #define STYP_SEL1 4
346 #define STYP_SEL2 5
347 #define STYP_LIST 6
348 #define STYP_PRP1 7
349 #define STYP_SEL3 8
350 #define STYP_PVAL 0
351 #define STYP_OP22 9 /* sele oper arg1 arg2 sele */
352
353
354 /* code | type | priority */
355
356 #define SELE_NOT1 ( 0x0100 | STYP_OPR1 | 0x70 )
357 #define SELE_BYR1 ( 0x0200 | STYP_OPR1 | 0x20 )
358 #define SELE_AND2 ( 0x0300 | STYP_OPR2 | 0x60 )
359 #define SELE_OR_2 ( 0x0400 | STYP_OPR2 | 0x40 )
360 #define SELE_IN_2 ( 0x0500 | STYP_OPR2 | 0x40 )
361 #define SELE_ALLz ( 0x0600 | STYP_SEL0 | 0x90 )
362 #define SELE_NONz ( 0x0700 | STYP_SEL0 | 0x90 )
363 #define SELE_HETz ( 0x0800 | STYP_SEL0 | 0x80 )
364 #define SELE_HYDz ( 0x0900 | STYP_SEL0 | 0x90 )
365 #define SELE_VISz ( 0x0A00 | STYP_SEL0 | 0x90 )
366 #define SELE_ARD_ ( 0x0B00 | STYP_PRP1 | 0x30 )
367 #define SELE_EXP_ ( 0x0C00 | STYP_PRP1 | 0x30 )
368 #define SELE_NAMs ( 0x0D00 | STYP_SEL1 | 0x80 )
369 #define SELE_ELEs ( 0x0E00 | STYP_SEL1 | 0x80 )
370 #define SELE_RSIs ( 0x0F00 | STYP_SEL1 | 0x80 )
371 #define SELE_CHNs ( 0x1000 | STYP_SEL1 | 0x80 )
372 #define SELE_SEGs ( 0x1100 | STYP_SEL1 | 0x80 )
373 #define SELE_MODs ( 0x1200 | STYP_SEL1 | 0x80 )
374 #define SELE_IDXs ( 0x1300 | STYP_SEL1 | 0x80 )
375 #define SELE_RSNs ( 0x1400 | STYP_SEL1 | 0x80 )
376 #define SELE_SELs ( 0x1500 | STYP_SEL1 | 0x80 )
377 #define SELE_BVLx ( 0x1600 | STYP_SEL2 | 0x80 )
378 #define SELE_ALTs ( 0x1700 | STYP_SEL1 | 0x80 )
379 #define SELE_FLGs ( 0x1800 | STYP_SEL1 | 0x80 )
380 #define SELE_GAP_ ( 0x1900 | STYP_PRP1 | 0x80 )
381 #define SELE_TTYs ( 0x1A00 | STYP_SEL1 | 0x80 )
382 #define SELE_NTYs ( 0x1B00 | STYP_SEL1 | 0x80 )
383 #define SELE_PCHx ( 0x1C00 | STYP_SEL2 | 0x80 )
384 #define SELE_FCHx ( 0x1D00 | STYP_SEL2 | 0x80 )
385 #define SELE_ID_s ( 0x1E00 | STYP_SEL1 | 0x80 )
386 #define SELE_BNDz ( 0x1F00 | STYP_SEL0 | 0x80 )
387 #define SELE_LIK2 ( 0x2000 | STYP_OPR2 | 0x40 )
388 #define SELE_NGH1 ( 0x2100 | STYP_OPR1 | 0x20 )
389 #define SELE_QVLx ( 0x2200 | STYP_SEL2 | 0x80 )
390 #define SELE_BYO1 ( 0x2300 | STYP_OPR1 | 0x20 )
391 #define SELE_SSTs ( 0x2400 | STYP_SEL1 | 0x80 )
392 #define SELE_STAs ( 0x2500 | STYP_SEL1 | 0x80 )
393 #define SELE_PREz ( 0x2500 | STYP_SEL0 | 0x80 )
394 #define SELE_WIT_ ( 0x2600 | STYP_OP22 | 0x30 )
395 #define SELE_ORIz ( 0x2700 | STYP_SEL0 | 0x90 )
396 #define SELE_CENz ( 0x2800 | STYP_SEL0 | 0x90 )
397 #define SELE_ENAz ( 0x2900 | STYP_SEL0 | 0x90 )
398 #define SELE_REPs ( 0x2A00 | STYP_SEL1 | 0x80 )
399 #define SELE_COLs ( 0x2B00 | STYP_SEL1 | 0x80 )
400 #define SELE_HBDs ( 0x2C00 | STYP_SEL0 | 0x80 )
401 #define SELE_HBAs ( 0x2D00 | STYP_SEL0 | 0x80 )
402 #define SELE_BYC1 ( 0x2E00 | STYP_OPR1 | 0x20 )
403 #define SELE_BYS1 ( 0x2F00 | STYP_OPR1 | 0x20 )
404 #define SELE_BYM1 ( 0x3000 | STYP_OPR1 | 0x20 )
405 #define SELE_BYF1 ( 0x3100 | STYP_OPR1 | 0x20 )
406 #define SELE_EXT_ ( 0x3200 | STYP_PRP1 | 0x30 )
407 #define SELE_BON1 ( 0x3300 | STYP_OPR1 | 0x50 )
408 #define SELE_FST1 ( 0x3400 | STYP_OPR1 | 0x30 )
409 #define SELE_CAS1 ( 0x3500 | STYP_OPR1 | 0x30 )
410 #define SELE_BEY_ ( 0x3600 | STYP_OP22 | 0x30 )
411 #define SELE_POLz ( 0x3700 | STYP_SEL0 | 0x90 )
412 #define SELE_SOLz ( 0x3800 | STYP_SEL0 | 0x90 )
413 #define SELE_ORGz ( 0x3900 | STYP_SEL0 | 0x90 )
414 #define SELE_INOz ( 0x3A00 | STYP_SEL0 | 0x90 )
415 #define SELE_GIDz ( 0x3B00 | STYP_SEL0 | 0x90 )
416 #define SELE_RNKs ( 0x3C00 | STYP_SEL1 | 0x80 )
417 #define SELE_PEPs ( 0x3D00 | STYP_SEL1 | 0x80 )
418 #define SELE_ACCz ( 0x3E00 | STYP_SEL0 | 0x90 )
419 #define SELE_DONz ( 0x3F00 | STYP_SEL0 | 0x90 )
420 #define SELE_LST1 ( 0x4000 | STYP_OPR1 | 0x30 )
421 #define SELE_NTO_ ( 0x4100 | STYP_OP22 | 0x30 )
422 #define SELE_CCLs ( 0x4200 | STYP_SEL1 | 0x80 )
423 #define SELE_RCLs ( 0x4300 | STYP_SEL1 | 0x80 )
424 #define SELE_PTDz ( 0x4400 | STYP_SEL0 | 0x90 )
425 #define SELE_MSKz ( 0x4500 | STYP_SEL0 | 0x90 )
426 #define SELE_IOR2 ( 0x4600 | STYP_OPR2 | 0x10 )
427 #define SELE_FXDz ( 0x4700 | STYP_SEL0 | 0x90 )
428 #define SELE_RSTz ( 0x4800 | STYP_SEL0 | 0x90 )
429 #define SELE_ANT2 ( 0x4900 | STYP_OPR2 | 0x60 )
430 #define SELE_BYX1 ( 0x4A00 | STYP_OPR1 | 0x20 )
431 #define SELE_STRO ( 0x4B00 | STYP_SEL1 | 0x80 )
432 #define SELE_METz ( 0x4C00 | STYP_SEL0 | 0x90 )
433 #define SELE_BB_z ( 0x4D00 | STYP_SEL0 | 0x90 )
434 #define SELE_SC_z ( 0x4E00 | STYP_SEL0 | 0x90 )
435 #define SELE_PROP ( 0x4F00 | STYP_SEL3 | 0x80 )
436 #define SELE_XVLx ( 0x5000 | STYP_SEL2 | 0x80 )
437 #define SELE_YVLx ( 0x5100 | STYP_SEL2 | 0x80 )
438 #define SELE_ZVLx ( 0x5200 | STYP_SEL2 | 0x80 )
439 #define SELE_CUST ( 0x5300 | STYP_SEL1 | 0x80 )
440 #define SELE_RING ( 0x5400 | STYP_OPR1 | 0x20 )
441 #define SELE_LABs ( 0x5500 | STYP_SEL1 | 0x80 )
442 #define SELE_PROz ( 0x5600 | STYP_SEL0 | 0x90 )
443 #define SELE_NUCz ( 0x5700 | STYP_SEL0 | 0x90 )
444
445 #define SEL_PREMAX 0x8
446
447 static WordKeyValue Keyword[] = {
448 {"not", SELE_NOT1},
449 {"!", SELE_NOT1},
450
451 {"neighbor", SELE_NGH1},
452 {"nbr;", SELE_NGH1}, /* deprecated */
453 {"nbr.", SELE_NGH1},
454
455 {"byfragment", SELE_BYF1},
456 {"byfrag", SELE_BYF1},
457 {"bf.", SELE_BYF1},
458
459 {"byresidue", SELE_BYR1},
460 {"byresi", SELE_BYR1}, /* unofficial */
461 {"byres", SELE_BYR1},
462 {"br;", SELE_BYR1}, /* deprecated */
463 {"br.", SELE_BYR1},
464 {"b;", SELE_BYR1}, /* deprecated */
465
466 {"bychain", SELE_BYC1},
467 {"bc.", SELE_BYC1},
468
469 {"byobject", SELE_BYO1},
470 {"byobj", SELE_BYO1},
471 {"bo;", SELE_BYO1}, /* deprecated */
472 {"bo.", SELE_BYO1},
473
474 {"bound_to", SELE_BON1},
475 {"bto.", SELE_BON1},
476
477 {"bymolecule", SELE_BYM1},
478 {"bymol", SELE_BYM1},
479 {"bm.", SELE_BYM1},
480
481 {"bysegment", SELE_BYS1},
482 {"byseg", SELE_BYS1},
483 {"bysegi", SELE_BYS1}, /* unofficial */
484 {"bs.", SELE_BYS1},
485
486 {"bycalpha", SELE_CAS1},
487 {"bca.", SELE_CAS1},
488
489 {"first", SELE_FST1},
490 {"last", SELE_LST1},
491
492 {"and", SELE_AND2},
493 {"&", SELE_AND2},
494 {"or", SELE_OR_2},
495 {"+", SELE_OR_2}, /* added to mitigate damage caused by the obj1+obj2 parser bug */
496 {"-", SELE_ANT2}, /* added to provide natural complement to the above: an AND NOT or SUBTRACT operation */
497 {"|", SELE_OR_2},
498 {"in", SELE_IN_2},
499
500 {"like", SELE_LIK2},
501 {"l;", SELE_LIK2},
502 {"l.", SELE_LIK2},
503
504 {cKeywordAll, SELE_ALLz}, /* 0 parameter */
505 {"*", SELE_ALLz}, /* 0 parameter */
506
507 {cKeywordNone, SELE_NONz}, /* 0 parameter */
508 {"hetatm", SELE_HETz}, /* 0 parameter */
509 {"het", SELE_HETz}, /* 0 parameter */
510
511 {"hydrogens", SELE_HYDz}, /* 0 parameter */
512 {"hydro", SELE_HYDz}, /* 0 parameter */
513 {"h;", SELE_HYDz}, /* deprecated */
514 {"h.", SELE_HYDz}, /* 0 parameter */
515
516 {"hba.", SELE_HBAs},
517 {"hbd.", SELE_HBDs},
518
519 {"visible", SELE_VISz}, /* 0 parameter */
520 {"v;", SELE_VISz}, /* 0 parameter */
521 {"v.", SELE_VISz}, /* 0 parameter */
522
523 {"around", SELE_ARD_}, /* 1 parameter */
524 {"a;", SELE_ARD_}, /* deprecated */
525 {"a.", SELE_ARD_}, /* 1 parameter */
526
527 {"expand", SELE_EXP_}, /* 1 parameter */
528 {"x;", SELE_EXP_}, /* 1 parameter */
529 {"x.", SELE_EXP_}, /* 1 parameter */
530
531 {"extend", SELE_EXT_}, /* 1 parameter */
532 {"xt.", SELE_EXT_}, /* 1 parameter */
533
534 {"name", SELE_NAMs},
535 {"n;", SELE_NAMs}, /* deprecated */
536 {"n.", SELE_NAMs},
537
538 {"symbol", SELE_ELEs},
539 {"element", SELE_ELEs},
540 {"elem", SELE_ELEs},
541 {"e;", SELE_ELEs}, /* deprecated */
542 {"e.", SELE_ELEs},
543
544 {"enabled", SELE_ENAz},
545
546 {"residue", SELE_RSIs},
547 {"resi", SELE_RSIs},
548 {"resident", SELE_RSIs},
549 {"resid", SELE_RSIs},
550 {"i;", SELE_RSIs}, /* deprecated */
551 {"i.", SELE_RSIs},
552
553 {"rep", SELE_REPs},
554
555 {"color", SELE_COLs},
556 {"cartoon_color", SELE_CCLs},
557 {"ribbon_color", SELE_RCLs},
558
559 {"altloc", SELE_ALTs},
560 {"alt", SELE_ALTs},
561
562 {"flag", SELE_FLGs},
563 {"f;", SELE_FLGs}, /* deprecated */
564 {"f.", SELE_FLGs},
565
566 {"gap", SELE_GAP_},
567
568 {"partial_charge", SELE_PCHx},
569 {"pc;", SELE_PCHx}, /* deprecated */
570 {"pc.", SELE_PCHx},
571
572 {"masked", SELE_MSKz},
573 {"msk.", SELE_MSKz},
574
575 {"protected", SELE_PTDz},
576
577 {"formal_charge", SELE_FCHx},
578 {"fc;", SELE_FCHx}, /* deprecated */
579 {"fc.", SELE_FCHx},
580
581 {"numeric_type", SELE_NTYs},
582 {"nt;", SELE_NTYs}, /* deprecated */
583 {"nt.", SELE_NTYs},
584
585 {"text_type", SELE_TTYs},
586 {"custom", SELE_CUST},
587 {"tt;", SELE_TTYs}, /* deprecated */
588 {"tt.", SELE_TTYs},
589
590 {"chain", SELE_CHNs},
591 {"c;", SELE_CHNs}, /* deprecated */
592 {"c.", SELE_CHNs},
593
594 {cKeywordCenter, SELE_CENz},
595 {"bonded", SELE_BNDz},
596
597 {"segment", SELE_SEGs},
598 {"segid", SELE_SEGs},
599 {"segi", SELE_SEGs},
600 {"s;", SELE_SEGs}, /* deprecated */
601 {"s.", SELE_SEGs},
602
603 {"ss", SELE_SSTs},
604
605 {"state", SELE_STAs},
606
607 {"object", SELE_MODs},
608 {"o.", SELE_MODs},
609
610 {cKeywordOrigin, SELE_ORIz},
611
612 {"model", SELE_MODs},
613 {"m;", SELE_MODs}, /* deprecated */
614 {"m.", SELE_MODs},
615
616 {"index", SELE_IDXs},
617 {"idx.", SELE_IDXs},
618
619 {"id", SELE_ID_s},
620 {"ID", SELE_ID_s},
621 {"rank", SELE_RNKs},
622
623 {"within", SELE_WIT_},
624 {"w.", SELE_WIT_},
625
626 {"near_to", SELE_NTO_},
627 {"nto.", SELE_NTO_},
628
629 {"beyond", SELE_BEY_},
630 {"be.", SELE_BEY_},
631
632 {"donors", SELE_DONz},
633 {"don.", SELE_DONz},
634
635 {"acceptors", SELE_ACCz},
636 {"acc.", SELE_ACCz},
637
638 {"pepseq", SELE_PEPs},
639 {"ps.", SELE_PEPs},
640
641 /*
642 { "nucseq", SELE_NUCs },
643 { "ns.", SELE_NUCs },
644 */
645
646 {"fixed", SELE_FXDz},
647 {"fxd.", SELE_FXDz},
648
649 {"restrained", SELE_RSTz},
650 {"rst.", SELE_RSTz},
651
652 {"polymer", SELE_POLz},
653 {"pol.", SELE_POLz},
654
655 {"polymer.protein", SELE_PROz},
656 {"polymer.nucleic", SELE_NUCz},
657
658 #if 0
659 // User survey winners. Not activated (yet) but ObjectMakeValidName
660 // prints a deprecation warning if these names are used to name
661 // objects or selections.
662 {"protein", SELE_PROz},
663 {"nucleic", SELE_NUCz},
664
665 {"pro.", SELE_PROz},
666 {"nuc.", SELE_NUCz},
667 #endif
668
669 {"organic", SELE_ORGz},
670 {"org.", SELE_ORGz},
671
672 {"inorganic", SELE_INOz},
673 {"ino.", SELE_INOz},
674
675 {"solvent", SELE_SOLz},
676 {"sol.", SELE_SOLz},
677
678 {"guide", SELE_GIDz},
679
680 {"present", SELE_PREz},
681 {"pr.", SELE_PREz},
682
683 {"resname", SELE_RSNs},
684 {"resn", SELE_RSNs},
685 {"r;", SELE_RSNs}, /* deprecated */
686 {"r.", SELE_RSNs},
687
688 {"%", SELE_SELs},
689 {"b", SELE_BVLx}, /* 2 operand selection operator */
690 {"q", SELE_QVLx}, /* 2 operand selection operator */
691
692 {"stereo", SELE_STRO},
693
694 {"bycell", SELE_BYX1},
695
696 {"metals", SELE_METz}, /* 0 parameter */
697
698 {"backbone", SELE_BB_z},
699 {"bb.", SELE_BB_z},
700
701 {"sidechain", SELE_SC_z},
702 {"sc.", SELE_SC_z},
703
704 {"p.", SELE_PROP},
705
706 {"x", SELE_XVLx},
707 {"y", SELE_YVLx},
708 {"z", SELE_ZVLx},
709
710 {"byring", SELE_RING},
711 {"label", SELE_LABs},
712
713 {"", 0}
714 };
715
716 #define SCMP_GTHN 0x01
717 #define SCMP_LTHN 0x02
718 #define SCMP_RANG 0x03
719 #define SCMP_EQAL 0x04
720
721 static WordKeyValue AtOper[] = {
722 {">", SCMP_GTHN},
723 {"<", SCMP_LTHN},
724 {"in", SCMP_RANG},
725 {"=", SCMP_EQAL},
726 {"", 0}
727 };
728
fcmp(float a,float b,int oper)729 static short fcmp(float a, float b, int oper) {
730 switch (oper) {
731 case SCMP_GTHN:
732 return (a > b);
733 case SCMP_LTHN:
734 return (a < b);
735 case SCMP_EQAL:
736 return fabs(a - b) < R_SMALL4;
737 }
738 printf("ERROR: invalid operator %d\n", oper);
739 return false;
740 }
741
742 #define cINTER_ENTRIES 11
743
SelectorRenameObjectAtoms(PyMOLGlobals * G,ObjectMolecule * obj,SelectorID_t sele,bool force,bool update_table)744 int SelectorRenameObjectAtoms(PyMOLGlobals* G, ObjectMolecule* obj,
745 SelectorID_t sele, bool force, bool update_table)
746 {
747 int result = 0;
748 int obj_nAtom = obj->NAtom;
749
750 if(update_table) {
751 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
752 }
753 if(obj_nAtom) {
754 int *flag = pymol::calloc<int>(obj_nAtom);
755 if(!flag) {
756 result = -1;
757 } else {
758 const AtomInfoType *ai = obj->AtomInfo.data();
759 int a;
760 for(a = 0; a < obj_nAtom; a++) {
761 if(SelectorIsMember(G, ai->selEntry, sele)) {
762 flag[a] = true;
763 result = true;
764 }
765 ai++;
766 }
767 if (!result && !force) {
768 // nothing selected, no need to continue
769 return 0;
770 }
771 result = ObjectMoleculeRenameAtoms(obj, flag, force);
772 }
773 FreeP(flag);
774 }
775 return result;
776 }
777
SelectorResidueVLAsTo3DMatchScores(PyMOLGlobals * G,CMatch * match,int * vla1,int n1,int state1,int * vla2,int n2,int state2,float seq_wt,float radius,float scale,float base,float coord_wt,float rms_exp)778 int SelectorResidueVLAsTo3DMatchScores(PyMOLGlobals * G, CMatch * match,
779 int *vla1, int n1, int state1,
780 int *vla2, int n2, int state2,
781 float seq_wt,
782 float radius, float scale, float base,
783 float coord_wt, float rms_exp)
784 {
785 CSelector *I = G->Selector;
786 int a, b, *vla;
787 int n_max = (n1 > n2) ? n1 : n2;
788 float *inter1 = pymol::calloc<float>(cINTER_ENTRIES * n1);
789 float *inter2 = pymol::calloc<float>(cINTER_ENTRIES * n2);
790 float *v_ca = pymol::calloc<float>(3 * n_max);
791 if(inter1 && inter2 && v_ca) {
792 int pass;
793
794 for(pass = 0; pass < 2; pass++) {
795 ObjectMolecule *obj;
796 const CoordSet *cs;
797 const int *neighbor = NULL;
798 const AtomInfoType *atomInfo = NULL;
799 const ObjectMolecule *last_obj = NULL;
800 float **dist_mat;
801 float *inter;
802 int state;
803 int n;
804 if(!pass) {
805 vla = vla1;
806 state = state1;
807 inter = inter1;
808 n = n1;
809 dist_mat = match->da;
810 } else {
811 vla = vla2;
812 state = state2;
813 inter = inter2;
814 n = n2;
815 dist_mat = match->db;
816 }
817
818 if(state < 0)
819 state = 0;
820 for(a = 0; a < n; a++) {
821 int at_ca1;
822 float *vv_ca = v_ca + a * 3;
823
824 obj = I->Obj[vla[0]];
825 at_ca1 = vla[1];
826 if(obj != last_obj) {
827 ObjectMoleculeUpdateNeighbors(obj);
828 last_obj = obj;
829 neighbor = obj->Neighbor;
830 atomInfo = obj->AtomInfo;
831 }
832
833 if(state < obj->NCSet)
834 cs = obj->CSet[state];
835 else
836 cs = NULL;
837 if(cs && neighbor && atomInfo) {
838 int idx_ca1 = cs->atmToIdx(at_ca1);
839
840 if(idx_ca1 >= 0) {
841 int mem0, mem1, mem2, mem3, mem4;
842 int nbr0, nbr1, nbr2, nbr3;
843 const float *v_ca1 = cs->coordPtr(idx_ca1);
844 int idx_cb1 = -1;
845 int cnt = 0;
846
847 copy3f(v_ca1, vv_ca);
848 copy3f(v_ca1, inter + 8);
849
850 /* find attached CB */
851
852 mem0 = at_ca1;
853 nbr0 = neighbor[mem0] + 1;
854 while((mem1 = neighbor[nbr0]) >= 0) {
855 if((atomInfo[mem1].protons == cAN_C) &&
856 (atomInfo[mem1].name == G->lex_const.CB)) {
857 idx_cb1 = cs->atmToIdx(mem1);
858 break;
859 }
860 nbr0 += 2;
861 }
862
863 /* find remote CA, CB */
864
865 if(idx_cb1 >= 0) {
866 const float *v_cb1 = cs->coordPtr(idx_cb1);
867
868 mem0 = at_ca1;
869 nbr0 = neighbor[mem0] + 1;
870 while((mem1 = neighbor[nbr0]) >= 0) {
871
872 nbr1 = neighbor[mem1] + 1;
873 while((mem2 = neighbor[nbr1]) >= 0) {
874 if(mem2 != mem0) {
875 int idx_ca2 = -1;
876
877 nbr2 = neighbor[mem2] + 1;
878 while((mem3 = neighbor[nbr2]) >= 0) {
879 if((mem3 != mem1) && (mem3 != mem0)) {
880 if((atomInfo[mem3].protons == cAN_C) &&
881 (atomInfo[mem3].name == G->lex_const.CA)) {
882 idx_ca2 = cs->atmToIdx(mem3);
883 break;
884 }
885 }
886 nbr2 += 2;
887 }
888 if(idx_ca2 >= 0) {
889 const float *v_ca2 = cs->coordPtr(idx_ca2);
890
891 nbr2 = neighbor[mem2] + 1;
892 while((mem3 = neighbor[nbr2]) >= 0) {
893 if((mem3 != mem1) && (mem3 != mem0)) {
894 int idx_cb2 = -1;
895 nbr3 = neighbor[mem3] + 1;
896 while((mem4 = neighbor[nbr3]) >= 0) {
897 if((mem4 != mem2) && (mem4 != mem1) && (mem4 != mem0)) {
898 if((atomInfo[mem4].protons == cAN_C) &&
899 (atomInfo[mem4].name == G->lex_const.CB)) {
900 idx_cb2 = cs->atmToIdx(mem4);
901 break;
902 }
903 }
904 nbr3 += 2;
905 }
906
907 if(idx_cb2 >= 0) {
908 const float *v_cb2 = NULL;
909 v_cb2 = cs->coordPtr(idx_cb2);
910 {
911 float angle = get_dihedral3f(v_cb1, v_ca1, v_ca2, v_cb2);
912 if(idx_cb1 < idx_cb2) {
913 inter[0] = (float) cos(angle);
914 inter[1] = (float) sin(angle);
915 } else {
916 inter[2] = (float) cos(angle);
917 inter[3] = (float) sin(angle);
918 }
919 }
920 cnt++;
921 }
922 }
923 nbr2 += 2;
924 }
925 }
926 }
927 nbr1 += 2;
928 }
929 nbr0 += 2;
930 }
931 }
932 }
933 }
934 vla += 3;
935 inter += cINTER_ENTRIES;
936 }
937 if(dist_mat) {
938 for(a = 0; a < n; a++) { /* optimize this later */
939 float *vv_ca = v_ca + a * 3;
940 for(b = 0; b < n; b++) {
941 float *vv_cb = v_ca + b * 3;
942 float diff = (float) diff3f(vv_ca, vv_cb);
943 dist_mat[a][b] = diff;
944 dist_mat[b][a] = diff;
945 }
946 }
947 }
948 {
949 std::unique_ptr<MapType> map(MapNew(G, radius, v_ca, n, nullptr));
950 if(!pass) {
951 inter = inter1;
952 } else {
953 inter = inter2;
954 }
955 if(map) {
956 for(a = 0; a < n; a++) {
957 float *v_ca1 = v_ca + 3 * a;
958 float *i_ca1 = inter + cINTER_ENTRIES * a;
959 for (const auto b : MapEIter(*map, v_ca1)) {
960 float *v_ca2 = v_ca + 3 * b;
961 if(a != b) {
962 if(within3f(v_ca1, v_ca2, radius)) {
963 float *i_ca2 = inter + cINTER_ENTRIES * b;
964 i_ca1[4] += i_ca2[0]; /* add dihedral vectors head-to-tail */
965 i_ca1[5] += i_ca2[1];
966 i_ca1[6] += i_ca2[2];
967 i_ca1[7] += i_ca2[3];
968 }
969 }
970 }
971 }
972 for(a = 0; a < n; a++) {
973 float nf = (float) sqrt(inter[4] * inter[4] + inter[5] * inter[5]);
974 if(nf > 0.0001F) {
975 inter[4] = inter[4] / nf;
976 inter[5] = inter[5] / nf;
977 }
978 nf = (float) sqrt(inter[6] * inter[6] + inter[7] * inter[7]);
979 if(nf > 0.0001F) {
980
981 inter[6] = inter[6] / nf;
982 inter[7] = inter[7] / nf;
983 }
984 inter += cINTER_ENTRIES;
985 }
986 }
987 }
988 }
989 {
990 const float _0F = 0.0F;
991
992 if((scale != 0.0F) || (seq_wt != 0.0F)) {
993 for(a = 0; a < n1; a++) {
994 float *i1 = inter1 + cINTER_ENTRIES * a;
995 for(b = 0; b < n2; b++) {
996 float *i2 = inter2 + cINTER_ENTRIES * b;
997 float sm[cINTER_ENTRIES], comp1, comp2, comp3 = 1.0F;
998 float score;
999 int c;
1000 for(c = 0; c < (cINTER_ENTRIES - 1); c += 2) {
1001 if(((i1[c] == _0F) && (i1[c + 1] == _0F))
1002 || ((i2[c] == _0F) && (i2[c + 1] == _0F))) {
1003 /* handle glycine case */
1004 sm[c] = 1.0F;
1005 sm[c + 1] = 1.0F;
1006 } else {
1007 sm[c] = i1[c] + i2[c];
1008 sm[c + 1] = i1[c + 1] + i2[c + 1];
1009 }
1010 }
1011 comp1 = (float)
1012 ((sqrt(sm[0] * sm[0] + sm[1] * sm[1]) +
1013 sqrt(sm[2] * sm[2] + sm[3] * sm[3])) * 0.25);
1014 comp2 = (float)
1015 ((sqrt(sm[4] * sm[4] + sm[5] * sm[5]) +
1016 sqrt(sm[6] * sm[6] + sm[7] * sm[7])) * 0.25);
1017 score = scale * (comp1 * comp2 - base);
1018 if(coord_wt != 0.0) {
1019 float diff = (float) diff3f(i1 + 8, i2 + 8);
1020 comp3 = (float) -log(diff / rms_exp);
1021 score = (1 - coord_wt) * score + coord_wt * comp3 * scale;
1022 }
1023 match->mat[a][b] = seq_wt * match->mat[a][b] + score;
1024 }
1025 }
1026 }
1027 }
1028 }
1029 FreeP(inter1);
1030 FreeP(inter2);
1031 FreeP(v_ca);
1032 return 1;
1033 }
1034
SelectorNameIsKeyword(PyMOLGlobals * G,const char * name)1035 bool SelectorNameIsKeyword(PyMOLGlobals * G, const char *name)
1036 {
1037 auto I = G->SelectorMgr;
1038 std::string lowername = name;
1039 std::transform(lowername.begin(), lowername.end(), lowername.begin(),
1040 [](unsigned char c) { return std::tolower(c); });
1041 return I->Key.count(lowername) != 0;
1042 }
1043
1044
1045 /*========================================================================*/
SelectorIsSelectionDiscrete(PyMOLGlobals * G,SelectorID_t sele,bool update_table)1046 static bool SelectorIsSelectionDiscrete(
1047 PyMOLGlobals* G, SelectorID_t sele, bool update_table)
1048 {
1049 CSelector *I = G->Selector;
1050
1051 if(update_table) {
1052 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
1053 }
1054
1055 for(int a = cNDummyAtoms; a < I->Table.size(); a++) {
1056 auto& table_a = I->Table[a];
1057 auto obj = I->Obj[table_a.model];
1058 auto ai = obj->AtomInfo + table_a.atom;
1059 if(SelectorIsMember(G, ai->selEntry, sele)) {
1060 if(obj->DiscreteFlag) {
1061 return true;
1062 }
1063 }
1064 }
1065 return false;
1066 }
1067
SelectorUpdateTableMultiObjectIdxTag(PyMOLGlobals * G,ObjectMolecule ** obj_list,int no_dummies,int ** idx_list,int * n_idx_list,int n_obj)1068 static sele_array_t SelectorUpdateTableMultiObjectIdxTag(PyMOLGlobals * G,
1069 ObjectMolecule ** obj_list,
1070 int no_dummies,
1071 int **idx_list, int *n_idx_list,
1072 int n_obj)
1073 {
1074 int c = 0;
1075 int modelCnt;
1076 sele_array_t result{};
1077 CSelector *I = G->Selector;
1078
1079 PRINTFD(G, FB_Selector)
1080 "SelectorUpdateTableMultiObject-Debug: entered ...\n" ENDFD;
1081
1082 SelectorClean(G);
1083
1084 I->SeleBaseOffsetsValid = true; /* all states -> all atoms -> offsets valid */
1085 I->NCSet = 0;
1086 if(no_dummies) {
1087 modelCnt = 0;
1088 c = 0;
1089 } else {
1090 modelCnt = cNDummyModels;
1091 c = cNDummyAtoms;
1092 }
1093 for(int b = 0; b < n_obj; b++) {
1094 auto obj = obj_list[b];
1095 c += obj->NAtom;
1096 if(I->NCSet < obj->NCSet)
1097 I->NCSet = obj->NCSet;
1098 modelCnt++;
1099 }
1100 sele_array_calloc(result, c);
1101 I->Table = std::vector<TableRec>(c);
1102 I->Obj = std::vector<ObjectMolecule*>(modelCnt, nullptr);
1103 if(no_dummies) {
1104 modelCnt = 0;
1105 c = 0;
1106 } else {
1107 c = cNDummyAtoms;
1108 modelCnt = cNDummyModels;
1109 }
1110 for(int b = 0; b < n_obj; b++) {
1111 auto obj = obj_list[b];
1112 auto idx = idx_list[b];
1113 auto n_idx = n_idx_list[b];
1114
1115 I->Obj[modelCnt] = obj;
1116 obj->SeleBase = c;
1117 for(int a = 0; a < obj->NAtom; a++) {
1118 I->Table[c].model = modelCnt;
1119 I->Table[c].atom = a;
1120 c++;
1121 }
1122 if(idx && n_idx) {
1123 if(n_idx > 0) {
1124 for(int a = 0; a < n_idx; a++) {
1125 int at = idx[2 * a]; /* index first */
1126 int pri = idx[2 * a + 1]; /* then priority */
1127 if((at >= 0) && (at < obj->NAtom)) {
1128 result[obj->SeleBase + at] = pri;
1129 }
1130 }
1131 }
1132 }
1133 modelCnt++;
1134 }
1135 I->Table.resize(c);
1136
1137 PRINTFD(G, FB_Selector)
1138 "SelectorUpdateTableMultiObject-Debug: leaving...\n" ENDFD;
1139
1140 return (result);
1141 }
1142
SelectorClassifyAtoms(PyMOLGlobals * G,int sele,int preserve,ObjectMolecule * only_object)1143 int SelectorClassifyAtoms(PyMOLGlobals * G, int sele, int preserve,
1144 ObjectMolecule * only_object)
1145 {
1146 CSelector *I = G->Selector;
1147 ObjectMolecule *obj, *last_obj = NULL, *obj0, *obj1 = NULL;
1148 int a, aa, at, a0, a1;
1149 AtomInfoType *ai, *last_ai = NULL, *ai0, *ai1;
1150 unsigned int mask;
1151 int n_dummies = 0;
1152
1153 int auto_show_classified = SettingGetGlobal_i(G, cSetting_auto_show_classified);
1154 int auto_show_mask = (auto_show_classified != 2) ? 0 : cRepBitmask;
1155
1156 int visRep_organic = cRepCylBit | cRepNonbondedSphereBit;
1157 int visRep_inorganic = cRepSphereBit;
1158 int visRep_polymer = cRepCartoonBit;
1159
1160 const lexborrow_t lex_pseudo = LexBorrow(G, "pseudo");
1161
1162 // detect large systems
1163 if (auto_show_classified == -1 &&
1164 only_object &&
1165 only_object->NAtom * (only_object->DiscreteFlag ?
1166 1 : only_object->NCSet) > 5e5) {
1167 auto_show_classified = 3;
1168 }
1169
1170 if (auto_show_classified == 3) {
1171 visRep_organic = visRep_inorganic = cRepLineBit | cRepNonbondedBit;
1172 visRep_polymer = cRepRibbonBit;
1173 }
1174
1175 if(only_object) {
1176 SelectorUpdateTableSingleObject(G, only_object, cSelectorUpdateTableAllStates,
1177 true, NULL, 0, false);
1178 n_dummies = 0;
1179 } else {
1180 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
1181 n_dummies = cNDummyAtoms;
1182 }
1183 a = 0;
1184 while(a < I->Table.size()) {
1185 obj = I->Obj[I->Table[a].model];
1186 at = I->Table[a].atom;
1187 ai = obj->AtomInfo + at;
1188
1189 if(SelectorIsMember(G, ai->selEntry, sele) &&
1190 ((!AtomInfoSameResidueP(G, ai, last_ai)))) {
1191
1192 AtomInfoType *guide_atom = NULL;
1193
1194 /* delimit residue */
1195
1196 a0 = a - 1;
1197 while(a0 >= n_dummies) {
1198 obj0 = I->Obj[I->Table[a0].model];
1199 if(obj0 != obj)
1200 break;
1201 ai0 = obj0->AtomInfo + I->Table[a0].atom;
1202 if(!AtomInfoSameResidue(G, ai0, ai))
1203 break;
1204 a0--;
1205 }
1206
1207 a1 = a + 1;
1208 while(a1 < I->Table.size()) {
1209 obj1 = I->Obj[I->Table[a1].model];
1210 if(obj1 != obj)
1211 break;
1212 ai1 = obj1->AtomInfo + I->Table[a1].atom;
1213 if(!AtomInfoSameResidue(G, ai1, ai))
1214 break;
1215 a1++;
1216 }
1217
1218 a0++;
1219 a1--;
1220
1221 mask = 0;
1222 if(!ai->hetatm && AtomInfoKnownProteinResName(LexStr(G, ai->resn)))
1223 mask = cAtomFlag_polymer | cAtomFlag_protein;
1224 else if(!ai->hetatm && AtomInfoKnownNucleicResName(LexStr(G, ai->resn)))
1225 mask = cAtomFlag_polymer | cAtomFlag_nucleic;
1226 else if(AtomInfoKnownWaterResName(G, LexStr(G, ai->resn)))
1227 mask = cAtomFlag_solvent;
1228 else {
1229
1230 /* does this residue have a canonical atoms? */
1231
1232 bool found_only_h = true;
1233 int found_ca = false;
1234 int found_n = false;
1235 int found_c = false;
1236 int found_o = false;
1237 int found_oh2 = false;
1238 int found_carbon = false;
1239 int found_cn_bond = false;
1240 int found_nc_bond = false;
1241 int found_o3_bond = false;
1242 int found_o3star = false;
1243 int found_c3star = false;
1244 int found_c4star = false;
1245 int found_c5star = false;
1246 int found_o5star = false;
1247 int found_p_bond = false;
1248 if(obj != last_obj) {
1249 ObjectMoleculeUpdateNeighbors(obj);
1250 last_obj = obj;
1251 }
1252
1253 ai0 = obj->AtomInfo + I->Table[a0].atom;
1254 for(aa = a0; aa <= a1; aa++) {
1255 if(ai0->protons == cAN_C) {
1256 const char *name = LexStr(G, ai0->name);
1257 found_carbon = true;
1258 switch (name[0]) {
1259 case 'C':
1260 switch (name[1]) {
1261 case 0:
1262 found_c = true;
1263 found_cn_bond =
1264 ObjectMoleculeIsAtomBondedToName(obj, I->Table[aa].atom, "N", 0);
1265 break;
1266 case 'A':
1267 switch (name[2]) {
1268 case 0:
1269 found_ca = true;
1270 guide_atom = ai0;
1271 break;
1272 }
1273 case '3':
1274 switch (name[2]) {
1275 case '*':
1276 case '\'':
1277 guide_atom = ai0;
1278 found_c3star = true;
1279 break;
1280 }
1281 break;
1282 case '4':
1283 switch (name[2]) {
1284 case '*':
1285 case '\'':
1286 found_c4star = true;
1287 break;
1288 }
1289 break;
1290 case '5':
1291 switch (name[2]) {
1292 case '*':
1293 case '\'':
1294 found_c5star = true;
1295 break;
1296 }
1297 break;
1298 }
1299 }
1300 } else if(ai0->protons == cAN_N) {
1301 const char *name = LexStr(G, ai0->name);
1302 switch (name[0]) {
1303 case 'N':
1304 switch (name[1]) {
1305 case 0:
1306 found_n = true;
1307 found_nc_bond =
1308 ObjectMoleculeIsAtomBondedToName(obj, I->Table[aa].atom, "C", 0);
1309 break;
1310 }
1311 }
1312 } else if(ai0->protons == cAN_O) {
1313 const char *name = LexStr(G, ai0->name);
1314 switch (name[0]) {
1315 case 'O':
1316 switch (name[1]) {
1317 case 0:
1318 found_o = true;
1319 break;
1320 case 'H':
1321 switch (name[2]) {
1322 case '2':
1323 found_oh2 = true;
1324 break;
1325 }
1326 case '3':
1327 switch (name[2]) {
1328 case '*':
1329 case '\'':
1330 found_o3star = true;
1331 found_o3_bond =
1332 ObjectMoleculeIsAtomBondedToName(obj, I->Table[aa].atom, "P", 0);
1333 break;
1334 }
1335 break;
1336 case '5':
1337 switch (name[2]) {
1338 case '*':
1339 case '\'':
1340 found_o5star = true;
1341 break;
1342 }
1343 break;
1344 }
1345 }
1346 } else if(ai0->protons == cAN_P) {
1347
1348 const char *name = LexStr(G, ai0->name);
1349 switch (name[0]) {
1350 case 'P':
1351 switch (name[1]) {
1352 case 0:
1353 found_p_bond =
1354 (ObjectMoleculeIsAtomBondedToName(obj, I->Table[aa].atom, "O3*", 0)
1355 || ObjectMoleculeIsAtomBondedToName(obj, I->Table[aa].atom, "O3'", 0));
1356 break;
1357 }
1358 }
1359 }
1360
1361 if (!ai0->isHydrogen()) {
1362 found_only_h = false;
1363 }
1364
1365 ai0++;
1366 }
1367
1368 if(found_ca && found_n && found_c && found_o && (found_cn_bond || found_nc_bond)) {
1369 mask = cAtomFlag_polymer | cAtomFlag_protein;
1370 } else if (found_o3star && found_c3star && found_c4star && found_c5star
1371 && found_o5star && (found_o3_bond || found_p_bond)) {
1372 mask = cAtomFlag_polymer | cAtomFlag_nucleic;
1373 } else if(found_carbon)
1374 mask = cAtomFlag_organic;
1375 else if((found_o || found_oh2) && (a1 == a0))
1376 mask = cAtomFlag_solvent;
1377 else if (!found_only_h) {
1378 // exclude hydrogens, they get misclassified as
1379 // 'inorganic' if they are not sorted
1380 mask = cAtomFlag_inorganic;
1381 }
1382 }
1383
1384 /* mark which atoms we can write to */
1385
1386 ai0 = obj->AtomInfo + I->Table[a0].atom;
1387 if(preserve) {
1388 printf("NOT IMPLEMENTED\n");
1389 } else {
1390 auto visRep_polymer_obj = visRep_polymer;
1391 if (obj->NAtom < 50) {
1392 // prevent single residue objects from disappearing
1393 visRep_polymer_obj |= visRep_organic;
1394 }
1395
1396 for(aa = a0; aa <= a1; aa++) {
1397 if(SelectorIsMember(G, ai0->selEntry, sele))
1398 {
1399 // apply styles if atom was unclassified
1400 if (auto_show_classified && !(ai0->flags & cAtomFlag_class)) {
1401 if (mask & cAtomFlag_organic) {
1402 ai0->visRep = (ai0->visRep & auto_show_mask) | visRep_organic;
1403 } else if (mask & cAtomFlag_inorganic) {
1404 ai0->visRep = (ai0->visRep & auto_show_mask) | visRep_inorganic;
1405 } else if (mask & cAtomFlag_polymer) {
1406 ai0->visRep = (ai0->visRep & auto_show_mask) | visRep_polymer_obj;
1407 }
1408
1409 // hide Desmond virtual sites and off-centered partial charges
1410 if (ai0->name == lex_pseudo) {
1411 ai0->visRep = 0;
1412 }
1413 }
1414 ai0->flags = (ai0->flags & cAtomFlag_class_mask) | mask;
1415 }
1416 ai0++;
1417 }
1418 }
1419
1420 if((mask & cAtomFlag_polymer)) {
1421 ai0 = obj->AtomInfo + I->Table[a0].atom;
1422 for(aa = a0; !guide_atom && aa <= a1; aa++) {
1423 if(ai0->protons == cAN_C) {
1424 const char *name = LexStr(G, ai0->name);
1425 switch (name[0]) {
1426 case 'C':
1427 switch (name[1]) {
1428 case 'A':
1429 switch (name[2]) {
1430 case 0:
1431 guide_atom = ai0;
1432 break;
1433 }
1434 break;
1435 case '4':
1436 switch (name[2]) { /* use C4* as guide atom for nucleic acids */
1437 case '*':
1438 case '\'':
1439 guide_atom = ai0;
1440 break;
1441 }
1442 break;
1443 }
1444 }
1445 }
1446 ai0++;
1447 }
1448 }
1449
1450 if(guide_atom)
1451 guide_atom->flags |= cAtomFlag_guide;
1452
1453 if(a1 > (a + 1))
1454 a = a1;
1455 }
1456 a++;
1457 }
1458 return true;
1459 }
1460
SelectorGetSpacialMapFromSeleCoord(PyMOLGlobals * G,int sele,int state,float cutoff,float ** coord_vla)1461 MapType *SelectorGetSpacialMapFromSeleCoord(PyMOLGlobals * G, int sele, int state,
1462 float cutoff, float **coord_vla)
1463 {
1464 int *index_vla = NULL;
1465 float *coord = NULL;
1466 int n, nc = 0;
1467 MapType *result = NULL;
1468 if(sele < 0)
1469 return NULL;
1470 else {
1471 auto ptr = pymol::make_unique<CSelector>(G, G->SelectorMgr);
1472 CSelector mapSele(G, G->SelectorMgr);
1473 auto I = &mapSele;
1474 SelectorUpdateTableImpl(G, I, state, -1);
1475 index_vla = SelectorGetIndexVLAImpl(G, I, sele);
1476
1477 if(index_vla) {
1478 n = VLAGetSize(index_vla);
1479 if(n)
1480 coord = VLAlloc(float, n * 3);
1481 if(coord) {
1482 int i, a;
1483 int st, sta;
1484 ObjectMolecule *obj;
1485 CoordSet *cs;
1486 int at;
1487 int idx;
1488 for(i = 0; i < n; i++) {
1489 a = index_vla[i];
1490
1491 obj = I->Obj[I->Table[a].model];
1492 at = +I->Table[a].atom;
1493 for(st = 0; st < I->NCSet; st++) {
1494
1495 if((state < 0) || (st == state)) {
1496
1497 sta = st;
1498 if(sta < obj->NCSet)
1499 cs = obj->CSet[sta];
1500 else
1501 cs = NULL;
1502 if(cs) {
1503 idx = cs->atmToIdx(at);
1504 } else {
1505 idx = -1;
1506 }
1507 if(idx >= 0) {
1508 VLACheck(coord, float, nc * 3 + 2);
1509 const float* src = cs->coordPtr(idx);
1510 float* dst = coord + 3 * nc;
1511 copy3f(src, dst);
1512 nc++;
1513 }
1514 }
1515 }
1516 }
1517 if(nc) {
1518 result = MapNew(G, cutoff, coord, nc, NULL);
1519 }
1520 }
1521 }
1522 }
1523 VLAFreeP(index_vla);
1524 if(coord)
1525 VLASize(coord, float, nc * 3);
1526 *(coord_vla) = coord;
1527 return (result);
1528 }
1529
SelectGetInfoIter(PyMOLGlobals * G,const char * name,ov_size minMatch,int ignCase)1530 static SelectorInfoIter_t SelectGetInfoIter(
1531 PyMOLGlobals* G, const char* name, ov_size minMatch, int ignCase)
1532 {
1533 auto& Info = G->SelectorMgr->Info;
1534 auto end_offset = Info.end();
1535
1536 while(name[0] == '?')
1537 name++;
1538
1539 for (auto offset = Info.begin(); offset != end_offset; ++offset) {
1540 if (offset->name == name) {
1541 return offset;
1542 }
1543 }
1544 /* not found, so try partial/ignored-case match */
1545 int best_match = -1;
1546 auto best_offset = end_offset;
1547
1548 for (auto offset = Info.begin(); offset != end_offset; ++offset) {
1549 int wm = WordMatch(G, name, offset->name.c_str(), ignCase);
1550 if(wm < 0) { /* exact match is always good */
1551 best_offset = offset;
1552 best_match = wm;
1553 break;
1554 }
1555 if(wm > 0) {
1556 if(best_match < wm) {
1557 best_match = wm;
1558 best_offset = offset;
1559 } else if(best_match == wm) { /* uh oh -- ambiguous match */
1560 best_offset = end_offset;
1561 }
1562 }
1563 }
1564 if((best_match < 0) || (best_match > (int) minMatch))
1565 return best_offset;
1566
1567 return end_offset;
1568 }
1569
1570 /**
1571 * Only called once: When doing `cmd.delete("all")`
1572 */
SelectorDefragment(PyMOLGlobals * G)1573 void SelectorDefragment(PyMOLGlobals * G)
1574 {
1575 CSelector* S = G->Selector;
1576 auto I = S->mgr;
1577 /* restore new member ordering so that CPU can continue to get good cache hit */
1578
1579 int n_free = 0;
1580 auto m = I->FreeMember;
1581 while(m) {
1582 n_free++;
1583 m = I->Member[m].next;
1584 }
1585 if(n_free) {
1586 std::vector<int> list(n_free);
1587 auto l = list.data();
1588 auto m = I->FreeMember;
1589 while(m) {
1590 *(l++) = m;
1591 m = I->Member[m].next;
1592 }
1593 std::sort(list.begin(), list.end());
1594 auto NMember = int(I->Member.size()) - 1;
1595 while(n_free > 5000) { /* compact inactive members when possible */
1596 if(list[n_free - 1] == NMember) {
1597 NMember--;
1598 n_free--;
1599 } else
1600 break;
1601 }
1602 for(int a = 0; a < (n_free - 1); a++) {
1603 I->Member[list[a]].next = list[a + 1];
1604 }
1605 I->Member[list[n_free - 1]].next = 0;
1606 I->FreeMember = list[0];
1607 I->Member.resize(NMember + 1);
1608 }
1609 }
1610
1611 typedef struct {
1612 int color;
1613 SelectorID_t sele;
1614 } ColorectionRec;
1615
SelectorDeleteSeleAtIter(PyMOLGlobals * G,SelectorInfoIter_t it)1616 static void SelectorDeleteSeleAtIter(PyMOLGlobals* G, SelectorInfoIter_t it)
1617 {
1618 SelectorPurgeMembers(G, it->ID);
1619 G->SelectorMgr->Info.erase(it);
1620 }
1621
SelectorGetNameFromIndex(PyMOLGlobals * G,SelectorID_t index)1622 const char *SelectorGetNameFromIndex(PyMOLGlobals * G, SelectorID_t index)
1623 {
1624 auto I = G->SelectorMgr;
1625 for(int a = 1; a < I->Info.size(); a++) {
1626 if(I->Info[a].ID == index) {
1627 return I->Info[a].name.c_str();
1628 }
1629 }
1630 return nullptr;
1631 }
1632
1633 #ifndef _PYMOL_NOPY
SelectorDeleteIndex(PyMOLGlobals * G,SelectorID_t index)1634 static void SelectorDeleteIndex(PyMOLGlobals * G, SelectorID_t index)
1635 {
1636 auto I = G->SelectorMgr;
1637 auto it = std::find_if(I->Info.begin() + 1, I->Info.end(),
1638 [index](const SelectionInfoRec& rec) { return rec.ID == index; });
1639 if (it != I->Info.end()) {
1640 SelectorDeleteSeleAtIter(G, it);
1641 }
1642 }
1643 #endif
1644
1645 #define cSSMaxHBond 6
1646
1647 #define cSSHelix3HBond 0x0001
1648 #define cSSHelix4HBond 0x0002
1649 #define cSSHelix5HBond 0x0004
1650 #define cSSGotPhiPsi 0x0008
1651 #define cSSPhiPsiHelix 0x0010
1652 #define cSSPhiPsiNotHelix 0x0020
1653 #define cSSPhiPsiStrand 0x0040
1654 #define cSSPhiPsiNotStrand 0x0080
1655 #define cSSAntiStrandSingleHB 0x0100
1656 #define cSSAntiStrandDoubleHB 0x0200
1657 #define cSSAntiStrandBuldgeHB 0x0400
1658 #define cSSAntiStrandSkip 0x0800
1659 #define cSSParaStrandSingleHB 0x1000
1660 #define cSSParaStrandDoubleHB 0x2000
1661 #define cSSParaStrandSkip 0x4000
1662
1663 #define cSSBreakSize 5
1664
1665 typedef struct {
1666 int real;
1667 int ca, n, c, o; /* indices in selection-table space */
1668 float phi, psi;
1669 char ss, ss_save;
1670 int flags;
1671 int n_acc, n_don;
1672 int acc[cSSMaxHBond]; /* interactions where this residue is an acceptor */
1673 int don[cSSMaxHBond]; /* interactions where this residue is a donor */
1674 ObjectMolecule *obj;
1675 int preserve;
1676 int present;
1677 } SSResi;
1678
SelectorAssignSS(PyMOLGlobals * G,int target,int present,int state_value,int preserve,ObjectMolecule * single_object,int quiet)1679 int SelectorAssignSS(PyMOLGlobals * G, int target, int present,
1680 int state_value, int preserve, ObjectMolecule * single_object,
1681 int quiet)
1682 {
1683
1684 /* PyMOL's secondary structure assignment algorithm:
1685
1686 General principal -- if it looks like a duck, then it's a duck:
1687
1688 I. Helices
1689 - must have reasonably helical geometry within the helical span
1690 - near-ideal geometry guarantees helix assignment
1691 - a continuous ladder stre i+3, i+4, or i+5 hydrogen bonding
1692 with permissible geometry can reinforce marginal cases
1693 - a minimum helix is three residues with i+3 H-bond
1694
1695 II. Sheets
1696 - Hydrogen bonding ladders are the primary guide
1697 - Out-of-the envelope
1698 - 1-residue gaps in sheets are filled unless there
1699 is a turn.
1700 */
1701
1702 CSelector *I = G->Selector;
1703 SSResi *res;
1704 int n_res = 0;
1705 int state_start, state_stop, state;
1706 int consensus = true;
1707 int first_last_only = false;
1708 int first_pass = true;
1709
1710 if(!single_object) {
1711 if(state_value < 0) {
1712 switch (state_value) {
1713 case -2: /* api: state=-1: current global state */
1714 case -3: /* api: state=-2: effective object states TO DO! */
1715 SelectorUpdateTable(G, state_value, -1);
1716 break;
1717 default:
1718 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
1719 break;
1720 }
1721 } else {
1722 SelectorUpdateTable(G, state_value, -1);
1723 }
1724 } else {
1725 SelectorUpdateTableSingleObject(G, single_object, state_value, false, NULL, 0, 0);
1726 }
1727
1728 res = VLACalloc(SSResi, 1000);
1729
1730 if(state_value < 0) {
1731 if(state_value == -4)
1732 consensus = false;
1733 if(state_value == -5)
1734 first_last_only = true;
1735 state_start = 0;
1736 state_stop = SelectorGetSeleNCSet(G, target);
1737
1738 if (state_value == -2) {
1739 /* api: state=-1: current global state */
1740 StateIterator iter(G, NULL, state_value, state_stop);
1741 if (iter.next()) {
1742 state_start = iter.state;
1743 state_stop = iter.state + 1;
1744 }
1745 }
1746 } else {
1747 state_start = state_value;
1748 state_stop = state_value + 1;
1749 }
1750 for(state = state_start; state < state_stop; state++) {
1751 int a;
1752 ObjectMolecule *obj;
1753 int aa, a0, a1, at, idx;
1754 AtomInfoType *ai, *ai0, *ai1;
1755 CoordSet *cs;
1756 ObjectMolecule *last_obj = NULL;
1757 /* first, we need to count the number of residues under consideration */
1758
1759 if(first_pass) {
1760 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
1761
1762 obj = I->Obj[I->Table[a].model];
1763 at = +I->Table[a].atom;
1764 ai = obj->AtomInfo + at;
1765
1766 /* see if CA coordinates exist... */
1767
1768 if(SelectorIsMember(G, ai->selEntry, present)) {
1769
1770 if((ai->protons == cAN_C) && (WordMatchExact(G, G->lex_const.CA, ai->name, true))) {
1771
1772 if(last_obj != obj) {
1773 ObjectMoleculeUpdateNeighbors(obj);
1774 ObjectMoleculeVerifyChemistry(obj, state_value);
1775 last_obj = obj;
1776 }
1777 /* delimit residue */
1778
1779 a0 = a - 1;
1780 while(a0 >= cNDummyAtoms) {
1781 ai0 = I->Obj[I->Table[a0].model]->AtomInfo + I->Table[a0].atom;
1782 if(!AtomInfoSameResidue(G, ai0, ai))
1783 break;
1784 a0--;
1785 }
1786
1787 a1 = a + 1;
1788 while(a1 < I->Table.size()) {
1789 ai1 = I->Obj[I->Table[a1].model]->AtomInfo + I->Table[a1].atom;
1790 if(!AtomInfoSameResidue(G, ai1, ai))
1791 break;
1792 a1++;
1793 }
1794
1795 {
1796 int found_N = 0;
1797 int found_O = 0;
1798 int found_C = 0;
1799
1800 /* locate key atoms */
1801
1802 for(aa = a0 + 1; aa < a1; aa++) {
1803 ai = I->Obj[I->Table[aa].model]->AtomInfo + I->Table[aa].atom;
1804 if((ai->protons == cAN_C) && (WordMatchExact(G, G->lex_const.C, ai->name, true))) {
1805 found_C = aa;
1806 }
1807 if((ai->protons == cAN_N) && (WordMatchExact(G, G->lex_const.N, ai->name, true))) {
1808 found_N = aa;
1809 }
1810 if((ai->protons == cAN_O) && (WordMatchExact(G, G->lex_const.O, ai->name, true))) {
1811 found_O = aa;
1812 }
1813 }
1814
1815 if((found_C) && (found_N) && (found_O)) {
1816
1817 VLACheck(res, SSResi, n_res);
1818 res[n_res].n = found_N;
1819 res[n_res].o = found_O;
1820 res[n_res].c = found_C;
1821 res[n_res].ca = a;
1822 res[n_res].obj = I->Obj[I->Table[a].model];
1823 res[n_res].real = true;
1824
1825 n_res++;
1826
1827 } else {
1828 if(!quiet) {
1829 PRINTFB(G, FB_Selector, FB_Warnings)
1830 " AssignSS-Warning: Ignoring incomplete residue /%s/%s/%s/%d%c ...\n",
1831 obj->Name, LexStr(G, ai->segi), LexStr(G, ai->chain), ai->resv, ai->getInscode(true) ENDFB(G);
1832 }
1833 }
1834 }
1835 }
1836 }
1837 } /* count pass */
1838
1839 if(preserve) { /* if we're in preserve mode, then mark which objects don't get changed */
1840 int a, b;
1841 char ss;
1842 ObjectMolecule *p_obj = NULL;
1843 SSResi *r, *r2;
1844 for(a = 0; a < n_res; a++) {
1845 r = res + a;
1846 if(r->real) {
1847 if(p_obj != r->obj) {
1848 ss = r->obj->AtomInfo[I->Table[r->ca].atom].ssType[0];
1849 if((ss == 'S') || (ss == 'H') || (ss == 's') || (ss == 'h')) {
1850 p_obj = r->obj;
1851
1852 b = a;
1853 while(b >= 0) {
1854 r2 = res + b;
1855 if(p_obj == r2->obj)
1856 r2->preserve = true;
1857 b--;
1858 }
1859 b = a + 1;
1860 while(b < n_res) {
1861 r2 = res + b;
1862 if(p_obj == r2->obj)
1863 r2->preserve = true;
1864 b++;
1865 }
1866 }
1867 }
1868 }
1869 }
1870 }
1871 /* printf("n_res %d\n",n_res); */
1872
1873 /* now, let's repack res. into discrete chunks so that we can do easy gap & ladder analysis */
1874
1875 {
1876 SSResi *res2;
1877 int a;
1878 int n_res2 = 0;
1879 int add_break;
1880 int at_ca0, at_ca1;
1881
1882 res2 = VLACalloc(SSResi, n_res * 2);
1883
1884 for(a = 0; a < n_res; a++) {
1885 add_break = false;
1886
1887 if(!a) {
1888 add_break = true;
1889 } else if(res[a].obj != res[a - 1].obj) {
1890 add_break = true;
1891 } else if(res[a].obj) {
1892 at_ca0 = I->Table[res[a].ca].atom;
1893 at_ca1 = I->Table[res[a - 1].ca].atom;
1894 if(!ObjectMoleculeCheckBondSep(res[a].obj, at_ca0, at_ca1, 3)) { /* CA->N->C->CA = 3 bonds */
1895 add_break = true;
1896 }
1897 }
1898
1899 if(add_break) {
1900 n_res2 += cSSBreakSize;
1901 }
1902
1903 VLACheck(res2, SSResi, n_res2);
1904 res2[n_res2] = res[a];
1905 n_res2++;
1906 }
1907
1908 n_res2 += cSSBreakSize;
1909 VLACheck(res2, SSResi, n_res2);
1910
1911 VLAFreeP(res);
1912 res = res2;
1913 n_res = n_res2;
1914 }
1915 first_pass = false;
1916 }
1917
1918 /* okay, the rest of this loop runs for each coordinate set */
1919
1920 {
1921 int b;
1922 for(a = 0; a < n_res; a++) {
1923 res[a].present = res[a].real;
1924
1925 if(res[a].present) {
1926 obj = res[a].obj;
1927 if(state < obj->NCSet)
1928 cs = obj->CSet[state];
1929 else
1930 cs = NULL;
1931 for(b = 0; b < 4; b++) {
1932 if(cs) {
1933 switch (b) {
1934 case 0:
1935 at = I->Table[res[a].n].atom;
1936 break;
1937 case 1:
1938 at = I->Table[res[a].o].atom;
1939 break;
1940 case 2:
1941 at = I->Table[res[a].c].atom;
1942 break;
1943 default:
1944 case 3:
1945 at = I->Table[res[a].ca].atom;
1946 break;
1947 }
1948 idx = cs->atmToIdx(at);
1949 } else
1950 idx = -1;
1951 if(idx < 0) {
1952 res[a].present = false;
1953 }
1954 }
1955 }
1956 }
1957 }
1958
1959 /* next, we need to record hydrogen bonding relationships */
1960
1961 {
1962
1963 float *v0, *v1;
1964 int n1;
1965 int at;
1966
1967 int a, aa;
1968 int a0, a1; /* SS res space */
1969 int at0, at1; /* object-atom space */
1970 int exclude;
1971
1972 ObjectMolecule *obj0, *obj1;
1973
1974 CoordSet *cs;
1975 float cutoff;
1976 HBondCriteria hbcRec, *hbc;
1977 int *zero = NULL, *scratch = NULL;
1978
1979 {
1980 int max_n_atom = I->Table.size();
1981 ObjectMolecule *lastObj = NULL;
1982 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
1983 ObjectMolecule *obj = I->Obj[I->Table[a].model];
1984 if(obj != lastObj) {
1985 if(max_n_atom < obj->NAtom)
1986 max_n_atom = obj->NAtom;
1987 lastObj = obj;
1988 }
1989 }
1990 zero = pymol::calloc<int>(max_n_atom);
1991 scratch = pymol::malloc<int>(max_n_atom);
1992 }
1993
1994 for(a = 0; a < n_res; a++) {
1995 res[a].n_acc = 0;
1996 res[a].n_don = 0;
1997 }
1998 hbc = &hbcRec;
1999 ObjectMoleculeInitHBondCriteria(G, hbc);
2000
2001 /* use parameters which reflect the spirit of Kabsch and Sander
2002 ( i.e. long hydrogen-bonds/polar electrostatic interactions ) */
2003
2004 hbc->maxAngle = 63.0F;
2005 hbc->maxDistAtMaxAngle = 3.2F;
2006 hbc->maxDistAtZero = 4.0F;
2007 hbc->power_a = 1.6F;
2008 hbc->power_b = 5.0F;
2009 hbc->cone_dangle = 0.0F; /* 180 deg. */
2010 if(hbc->maxDistAtMaxAngle != 0.0F) {
2011 hbc->factor_a = 0.5F / (float) pow(hbc->maxAngle, hbc->power_a);
2012 hbc->factor_b = 0.5F / (float) pow(hbc->maxAngle, hbc->power_b);
2013 }
2014
2015 cutoff = hbc->maxDistAtMaxAngle;
2016 if(cutoff < hbc->maxDistAtZero) {
2017 cutoff = hbc->maxDistAtZero;
2018 }
2019
2020 n1 = 0;
2021
2022 const size_t table_size = I->Table.size();
2023 auto coords_flat = std::vector<float>(3 * table_size);
2024 auto* coords = pymol::reshape<3>(coords_flat.data());
2025 auto Flag1 = std::vector<MapFlag_t>(table_size, 0);
2026 auto Flag2 = std::vector<int>(table_size, 0);
2027
2028 for(a = 0; a < n_res; a++) {
2029 if(res[a].present) {
2030 obj0 = res[a].obj;
2031
2032 if(obj0) {
2033 /* map will contain the h-bond backbone nitrogens */
2034
2035 aa = res[a].n;
2036 at = I->Table[aa].atom;
2037 Flag2[aa] = a; /* so we can find the atom again... */
2038
2039 cs = obj0->getCoordSet(state);
2040 if (!cs)
2041 continue;
2042
2043 if (CoordSetGetAtomVertex(cs, at, coords[aa])) {
2044 Flag1[aa] = true;
2045 n1++;
2046 }
2047
2048 /* also copy O coordinates for usage below */
2049
2050 aa = res[a].o;
2051 at = I->Table[aa].atom;
2052 CoordSetGetAtomVertex(cs, at, coords[aa]);
2053 }
2054 }
2055 }
2056
2057 if(n1) {
2058 short too_many_atoms = false;
2059 std::unique_ptr<MapType> map(MapNewFlagged(G, -cutoff,
2060 pymol::flatten(coords), table_size, nullptr, Flag1.data()));
2061 if(map) {
2062
2063 for(a0 = 0; a0 < n_res; a0++) {
2064
2065 if(res[a0].obj) {
2066
2067 /* now iterate through carbonyls */
2068 obj0 = res[a0].obj;
2069 const auto as0 = res[a0].o;
2070 at0 = I->Table[as0].atom;
2071
2072 v0 = coords[as0];
2073
2074 int nat = 0;
2075 for (const auto as1 : MapEIter(*map, v0)) {
2076 v1 = coords[as1];
2077
2078 if(within3f(v0, v1, cutoff)) {
2079
2080 obj1 = I->Obj[I->Table[as1].model];
2081 at1 = I->Table[as1].atom;
2082
2083 if(obj0 == obj1) { /* don't count hbonds between adjacent residues */
2084 exclude = SelectorCheckNeighbors(G, 5, obj0, at0, at1,
2085 zero, scratch);
2086 } else {
2087 exclude = false;
2088 }
2089
2090 /* if(!exclude) {
2091 printf("at1 %s %s vs at0 %s %s\n",
2092 obj1->AtomInfo[at1].resi,
2093 obj1->AtomInfo[at1].name,
2094 obj0->AtomInfo[at0].resi,
2095 obj0->AtomInfo[at0].name
2096 );
2097 }
2098 */
2099 if((!exclude) && ObjectMoleculeGetCheckHBond(NULL, NULL, obj1, /* donor first */
2100 at1, state, obj0, /* then acceptor */
2101 at0, state, hbc)) {
2102
2103 /* printf(" found hbond between acceptor resi %s and donor resi %s\n",
2104 res[a0].obj->AtomInfo[at0].resi,
2105 res[I->Flag2[as1]].obj->AtomInfo[I->Table[as1].atom].resi); */
2106
2107 a1 = Flag2[as1]; /* index in SS n_res space */
2108
2109 /* store acceptor link */
2110
2111 n1 = res[a0].n_acc;
2112 if(n1 < (cSSMaxHBond - 1)) {
2113 res[a0].acc[n1] = a1;
2114 res[a0].n_acc = n1 + 1;
2115 }
2116
2117 /* store donor link */
2118
2119 n1 = res[a1].n_don;
2120 if(n1 < (cSSMaxHBond - 1)) {
2121 res[a1].don[n1] = a0;
2122 res[a1].n_don = n1 + 1;
2123 }
2124 }
2125 }
2126 nat++;
2127 }
2128 if (nat > 1000){ // if map returns more than 1000 atoms within 4, should be a dss error
2129 too_many_atoms = true;
2130 break;
2131 }
2132 }
2133 }
2134 }
2135 if (too_many_atoms){
2136 PRINTFB(G, FB_Selector, FB_Errors)
2137 " %s: ERROR: Unreasonable number of neighbors for dss, cannot assign secondary structure.\n", __func__ ENDFB(G);
2138 }
2139 }
2140 FreeP(zero);
2141 FreeP(scratch);
2142 }
2143
2144 { /* compute phi, psi's */
2145
2146 SSResi *r;
2147 int a;
2148
2149 float helix_psi_delta, helix_phi_delta;
2150 float strand_psi_delta, strand_phi_delta;
2151
2152 float helix_psi_target = SettingGet_f(G, NULL, NULL, cSetting_ss_helix_psi_target);
2153 float helix_psi_include =
2154 SettingGet_f(G, NULL, NULL, cSetting_ss_helix_psi_include);
2155 float helix_psi_exclude =
2156 SettingGet_f(G, NULL, NULL, cSetting_ss_helix_psi_exclude);
2157
2158 float helix_phi_target = SettingGet_f(G, NULL, NULL, cSetting_ss_helix_phi_target);
2159 float helix_phi_include =
2160 SettingGet_f(G, NULL, NULL, cSetting_ss_helix_phi_include);
2161 float helix_phi_exclude =
2162 SettingGet_f(G, NULL, NULL, cSetting_ss_helix_phi_exclude);
2163
2164 float strand_psi_target =
2165 SettingGet_f(G, NULL, NULL, cSetting_ss_strand_psi_target);
2166 float strand_psi_include =
2167 SettingGet_f(G, NULL, NULL, cSetting_ss_strand_psi_include);
2168 float strand_psi_exclude =
2169 SettingGet_f(G, NULL, NULL, cSetting_ss_strand_psi_exclude);
2170
2171 float strand_phi_target =
2172 SettingGet_f(G, NULL, NULL, cSetting_ss_strand_phi_target);
2173 float strand_phi_include =
2174 SettingGet_f(G, NULL, NULL, cSetting_ss_strand_phi_include);
2175 float strand_phi_exclude =
2176 SettingGet_f(G, NULL, NULL, cSetting_ss_strand_phi_exclude);
2177
2178 for(a = 0; a < n_res; a++) {
2179 r = res + a;
2180 if(r->real && ((r - 1)->real)) {
2181 r->flags = 0;
2182
2183 if(ObjectMoleculeGetPhiPsi
2184 (r->obj, I->Table[r->ca].atom, &r->phi, &r->psi, state)) {
2185 r->flags |= cSSGotPhiPsi;
2186
2187 helix_psi_delta = (float) fabs(r->psi - helix_psi_target);
2188 strand_psi_delta = (float) fabs(r->psi - strand_psi_target);
2189 helix_phi_delta = (float) fabs(r->phi - helix_phi_target);
2190 strand_phi_delta = (float) fabs(r->phi - strand_phi_target);
2191
2192 if(helix_psi_delta > 180.0F)
2193 helix_psi_delta = 360.0F - helix_psi_delta;
2194 if(strand_psi_delta > 180.0F)
2195 strand_psi_delta = 360.0F - strand_psi_delta;
2196 if(helix_phi_delta > 180.0F)
2197 helix_phi_delta = 360.0F - helix_phi_delta;
2198 if(strand_phi_delta > 180.0F)
2199 strand_phi_delta = 360.0F - strand_phi_delta;
2200
2201 /* printf("helix %d strand %d\n",helix_delta,strand_delta); */
2202
2203 if((helix_psi_delta > helix_psi_exclude) ||
2204 (helix_phi_delta > helix_phi_exclude)) {
2205 r->flags |= cSSPhiPsiNotHelix;
2206 } else if((helix_psi_delta < helix_psi_include) &&
2207 (helix_phi_delta < helix_phi_include)) {
2208 r->flags |= cSSPhiPsiHelix;
2209 }
2210
2211 if((strand_psi_delta > strand_psi_exclude) ||
2212 (strand_phi_delta > strand_phi_exclude)) {
2213 r->flags |= cSSPhiPsiNotStrand;
2214 } else if((strand_psi_delta < strand_psi_include) &&
2215 (strand_phi_delta < strand_phi_include)) {
2216 r->flags |= cSSPhiPsiStrand;
2217 }
2218 }
2219 }
2220 }
2221 }
2222
2223 /* by default, tentatively assign everything as loop */
2224
2225 {
2226 int a;
2227 for(a = cSSBreakSize; a < (n_res - cSSBreakSize); a++) {
2228 if(res[a].present)
2229 res[a].ss = 'L';
2230 }
2231 }
2232
2233 {
2234 SSResi *r, *r2;
2235 int a, b, c;
2236
2237 for(a = cSSBreakSize; a < (n_res - cSSBreakSize); a++) {
2238 r = res + a;
2239 if(r->real) {
2240
2241 /* look for tell-tale i+3,4,5 hydrogen bonds for helix */
2242
2243 /* is residue an acceptor for i+3,4,5 residue? */
2244 for(b = 0; b < r->n_acc; b++) {
2245 r->flags |=
2246 ((r->acc[b] == (a + 3)) ? cSSHelix3HBond : 0) |
2247 ((r->acc[b] == (a + 4)) ? cSSHelix4HBond : 0) |
2248 ((r->acc[b] == (a + 5)) ? cSSHelix5HBond : 0);
2249
2250 }
2251
2252 /* is residue a donor for i-3,4,5 residue */
2253 for(b = 0; b < r->n_don; b++) {
2254 r->flags |=
2255 ((r->don[b] == (a - 3)) ? cSSHelix3HBond : 0) |
2256 ((r->don[b] == (a - 4)) ? cSSHelix4HBond : 0) |
2257 ((r->don[b] == (a - 5)) ? cSSHelix5HBond : 0);
2258
2259 }
2260
2261 /* if(r->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) {
2262 printf("HelixHB %s \n",
2263 r->obj->AtomInfo[I->Table[r->ca].atom].resi);
2264 }
2265 */
2266
2267 /* look for double h-bonded antiparallel beta sheet pairs:
2268 *
2269 * \ /\ /
2270 * N C
2271 * # O
2272 * O #
2273 * C N
2274 * / \/ \
2275 *
2276 */
2277
2278 for(b = 0; b < r->n_acc; b++) { /* iterate through acceptors */
2279 r2 = (res + r->acc[b]);
2280 if(r2->real) {
2281 for(c = 0; c < r2->n_acc; c++) {
2282 if(r2->acc[c] == a) { /* found a pair */
2283 r->flags |= cSSAntiStrandDoubleHB;
2284 r2->flags |= cSSAntiStrandDoubleHB;
2285
2286 /* printf("anti double %s to %s\n",
2287 r->obj->AtomInfo[I->Table[r->ca].atom].resi,
2288 r2->obj->AtomInfo[I->Table[r2->ca].atom].resi); */
2289
2290 }
2291 }
2292 }
2293 }
2294
2295 /* look for antiparallel beta buldges
2296 *
2297 * CCNC
2298 * \ / O \ /
2299 * N C
2300 * # O
2301 * O #
2302 * C N
2303 * / \/ \
2304 *
2305 */
2306
2307 for(b = 0; b < r->n_acc; b++) { /* iterate through acceptors */
2308 r2 = (res + r->acc[b]) + 1; /* go forward 1 */
2309 if(r2->real) {
2310 for(c = 0; c < r2->n_acc; c++) {
2311 if(r2->acc[c] == a) { /* found a buldge */
2312 r->flags |= cSSAntiStrandDoubleHB;
2313 r2->flags |= cSSAntiStrandBuldgeHB;
2314 (r2 - 1)->flags |= cSSAntiStrandBuldgeHB;
2315
2316 /* printf("anti BULDGE %s to %s %s\n",
2317 r->obj->AtomInfo[I->Table[r->ca].atom].resi,
2318 r2->obj->AtomInfo[I->Table[r2->ca].atom].resi,
2319 r2->obj->AtomInfo[I->Table[(r2-1)->ca].atom].resi); */
2320
2321 }
2322 }
2323 }
2324 }
2325
2326 /* look for antiparallel beta sheet ladders (single or double)
2327 *
2328 * O
2329 * N C
2330 * \ / \/ \ /
2331 * C N
2332 * O #
2333 * # O
2334 * N C
2335 * / \ /\ / \
2336 * C N
2337 * O
2338 */
2339
2340 if((r + 1)->real && (r + 2)->real) {
2341
2342 for(b = 0; b < r->n_acc; b++) { /* iterate through acceptors */
2343 r2 = (res + r->acc[b]) - 2; /* go back 2 */
2344 if(r2->real) {
2345
2346 for(c = 0; c < r2->n_acc; c++) {
2347
2348 if(r2->acc[c] == a + 2) { /* found a ladder */
2349
2350 (r)->flags |= cSSAntiStrandSingleHB;
2351 (r + 1)->flags |= cSSAntiStrandSkip;
2352 (r + 2)->flags |= cSSAntiStrandSingleHB;
2353
2354 (r2)->flags |= cSSAntiStrandSingleHB;
2355 (r2 + 1)->flags |= cSSAntiStrandSkip;
2356 (r2 + 2)->flags |= cSSAntiStrandSingleHB;
2357
2358 /* printf("anti ladder %s %s to %s %s\n",
2359 r->obj->AtomInfo[I->Table[r->ca].atom].resi,
2360 r->obj->AtomInfo[I->Table[(r+2)->ca].atom].resi,
2361 r2->obj->AtomInfo[I->Table[r2->ca].atom].resi,
2362 r2->obj->AtomInfo[I->Table[(r2+2)->ca].atom].resi); */
2363 }
2364 }
2365 }
2366 }
2367 }
2368
2369 /* look for parallel beta sheet ladders
2370 *
2371
2372 * \ /\ /
2373 * C N
2374 * O #
2375 * # O
2376 * N C
2377 * / \ /\ / \
2378 * C N
2379 * O
2380 */
2381
2382 if((r + 1)->real && (r + 2)->real) {
2383
2384 for(b = 0; b < r->n_acc; b++) { /* iterate through acceptors */
2385 r2 = (res + r->acc[b]);
2386 if(r2->real) {
2387
2388 for(c = 0; c < r2->n_acc; c++) {
2389
2390 if(r2->acc[c] == a + 2) { /* found a ladder */
2391
2392 (r)->flags |= cSSParaStrandSingleHB;
2393 (r + 1)->flags |= cSSParaStrandSkip;
2394 (r + 2)->flags |= cSSParaStrandSingleHB;
2395
2396 (r2)->flags |= cSSParaStrandDoubleHB;
2397
2398 /* printf("parallel ladder %s %s to %s \n",
2399 r->obj->AtomInfo[I->Table[r->ca].atom].resi,
2400 r->obj->AtomInfo[I->Table[(r+2)->ca].atom].resi,
2401 r2->obj->AtomInfo[I->Table[r2->ca].atom].resi); */
2402 }
2403 }
2404 }
2405 }
2406 }
2407 }
2408 }
2409 }
2410
2411 {
2412 int a;
2413 SSResi *r;
2414 /* convert flags to assignments */
2415
2416 /* HELICES FIRST */
2417
2418 for(a = cSSBreakSize; a < (n_res - cSSBreakSize); a++) {
2419 r = res + a;
2420
2421 if(r->real) {
2422 /* clean internal helical residues are easy to find using H-bonds */
2423
2424 if(((r - 1)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2425 ((r)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2426 ((r + 1)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond))) {
2427 if(!(r->flags & (cSSPhiPsiNotHelix))) {
2428 r->ss = 'H';
2429 }
2430 }
2431
2432 /*
2433 if(((r-1)->flags & (cSSHelix3HBond )) &&
2434 ((r )->flags & (cSSHelix3HBond )) &&
2435 ((r+1)->flags & (cSSHelix3HBond ))) {
2436 if(!(r->flags & (cSSPhiPsiNotHelix))) {
2437 r->ss = 'H';
2438 }
2439 }
2440
2441 if(((r-1)->flags & (cSSHelix4HBond)) &&
2442 ((r )->flags & (cSSHelix4HBond)) &&
2443 ((r+1)->flags & (cSSHelix4HBond))) {
2444 if(!(r->flags & (cSSPhiPsiNotHelix))) {
2445 r->ss = 'H';
2446 }
2447 }
2448
2449 if(((r-1)->flags & (cSSHelix5HBond)) &&
2450 ((r )->flags & (cSSHelix5HBond)) &&
2451 ((r+1)->flags & (cSSHelix5HBond))) {
2452 if(!(r->flags & (cSSPhiPsiNotHelix))) {
2453 r->ss = 'H';
2454 }
2455 }
2456 */
2457
2458 }
2459 }
2460
2461 for(a = cSSBreakSize; a < (n_res - cSSBreakSize); a++) {
2462 r = res + a;
2463
2464 if(r->real) {
2465
2466 /* occasionally they'll be one whacked out residue missing h-bonds...
2467 in an otherwise good segment */
2468
2469 if(((r - 2)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2470 ((r - 1)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2471 ((r - 1)->flags & (cSSPhiPsiHelix)) &&
2472 ((r)->flags & (cSSPhiPsiHelix)) &&
2473 ((r + 1)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2474 ((r + 1)->flags & (cSSPhiPsiHelix)) &&
2475 ((r + 2)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond))
2476 ) {
2477 r->ss = 'h';
2478 }
2479 }
2480 }
2481
2482 for(a = cSSBreakSize; a < (n_res - cSSBreakSize); a++) {
2483 r = res + a;
2484 if(r->real) {
2485 if(r->ss == 'h') {
2486 r->flags |= (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond);
2487 r->ss = 'H';
2488 }
2489 }
2490 }
2491
2492 for(a = cSSBreakSize; a < (n_res - cSSBreakSize); a++) {
2493 r = res + a;
2494
2495 if(r->real) {
2496
2497 /* deciding where the helix ends is trickier -- here we use helix geometry */
2498
2499 if(((r)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2500 ((r)->flags & (cSSPhiPsiHelix)) &&
2501 ((r + 1)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2502 ((r + 1)->flags & (cSSPhiPsiHelix)) &&
2503 ((r + 2)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2504 ((r + 2)->flags & (cSSPhiPsiHelix)) && ((r + 1)->ss == 'H')
2505 ) {
2506 r->ss = 'H';
2507 }
2508
2509 if(((r)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2510 ((r)->flags & (cSSPhiPsiHelix)) &&
2511 ((r - 1)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2512 ((r - 1)->flags & (cSSPhiPsiHelix)) &&
2513 ((r - 2)->flags & (cSSHelix3HBond | cSSHelix4HBond | cSSHelix5HBond)) &&
2514 ((r - 2)->flags & (cSSPhiPsiHelix)) && ((r - 1)->ss == 'H')
2515 ) {
2516 r->ss = 'H';
2517 }
2518
2519 }
2520 }
2521
2522 /* THEN SHEETS/STRANDS */
2523
2524 for(a = cSSBreakSize; a < (n_res - cSSBreakSize); a++) {
2525 r = res + a;
2526 if(r->real) {
2527
2528 /* Antiparallel Sheets */
2529
2530 if(((r)->flags & (cSSAntiStrandDoubleHB)) &&
2531 (!((r->flags & (cSSPhiPsiNotStrand))))) {
2532 (r)->ss = 'S';
2533 }
2534
2535 if(((r)->flags & (cSSAntiStrandBuldgeHB)) && /* no strand geometry filtering for buldges.. */
2536 ((r + 1)->flags & (cSSAntiStrandBuldgeHB))) {
2537 (r)->ss = 'S';
2538 (r + 1)->ss = 'S';
2539 }
2540
2541 if(((r - 1)->flags & (cSSAntiStrandDoubleHB)) &&
2542 ((r)->flags & (cSSAntiStrandSkip)) &&
2543 (!(((r)->flags & (cSSPhiPsiNotStrand)))) &&
2544 ((r + 1)->flags & (cSSAntiStrandSingleHB | cSSAntiStrandDoubleHB))) {
2545
2546 (r)->ss = 'S';
2547 }
2548
2549 if(((r - 1)->flags & (cSSAntiStrandSingleHB | cSSAntiStrandDoubleHB)) &&
2550 ((r)->flags & (cSSAntiStrandSkip)) &&
2551 (!(((r)->flags & (cSSPhiPsiNotStrand)))) &&
2552 ((r + 1)->flags & (cSSAntiStrandDoubleHB))) {
2553 (r)->ss = 'S';
2554 }
2555
2556 /* include open "ladders" if PHIPSI geometry supports assignment */
2557
2558 if(((r - 1)->flags & (cSSAntiStrandSingleHB | cSSAntiStrandDoubleHB)) &&
2559 ((r - 1)->flags & (cSSPhiPsiStrand)) &&
2560 (!(((r - 1)->flags & (cSSPhiPsiNotStrand)))) &&
2561 ((r)->flags & (cSSPhiPsiStrand)) &&
2562 (!(((r - 1)->flags & (cSSPhiPsiNotStrand)))) &&
2563 ((r + 1)->flags & (cSSAntiStrandSingleHB | cSSAntiStrandDoubleHB)) &&
2564 ((r + 1)->flags & (cSSPhiPsiStrand))) {
2565
2566 (r - 1)->ss = 'S';
2567 (r)->ss = 'S';
2568 (r + 1)->ss = 'S';
2569 }
2570
2571 /* Parallel Sheets */
2572
2573 if(((r)->flags & (cSSParaStrandDoubleHB)) &&
2574 (!(((r)->flags & (cSSPhiPsiNotStrand))))) {
2575 (r)->ss = 'S';
2576 }
2577
2578 if(((r - 1)->flags & (cSSParaStrandDoubleHB)) &&
2579 ((r)->flags & (cSSParaStrandSkip)) &&
2580 (!(((r)->flags & (cSSPhiPsiNotStrand)))) &&
2581 ((r + 1)->flags & (cSSParaStrandSingleHB | cSSParaStrandDoubleHB))) {
2582
2583 (r)->ss = 'S';
2584 }
2585
2586 if(((r - 1)->flags & (cSSParaStrandSingleHB | cSSParaStrandDoubleHB)) &&
2587 ((r)->flags & (cSSParaStrandSkip)) &&
2588 (!(((r)->flags & (cSSPhiPsiNotStrand)))) &&
2589 ((r + 1)->flags & (cSSParaStrandDoubleHB))) {
2590 (r)->ss = 'S';
2591 }
2592
2593 /* include open "ladders" if PHIPSI geometry supports assignment */
2594
2595 if(((r - 1)->flags & (cSSParaStrandSingleHB | cSSParaStrandDoubleHB)) &&
2596 ((r - 1)->flags & (cSSPhiPsiStrand)) &&
2597 ((r)->flags & (cSSParaStrandSkip)) &&
2598 ((r)->flags & (cSSPhiPsiStrand)) &&
2599 ((r + 1)->flags & (cSSParaStrandSingleHB | cSSParaStrandDoubleHB)) &&
2600 ((r + 1)->flags & (cSSPhiPsiStrand))) {
2601
2602 (r - 1)->ss = 'S';
2603 (r)->ss = 'S';
2604 (r + 1)->ss = 'S';
2605
2606 }
2607 }
2608 }
2609 }
2610
2611 {
2612 int a, b;
2613 SSResi *r, *r2;
2614 int repeat = true;
2615 int found;
2616
2617 while(repeat) {
2618 repeat = false;
2619
2620 for(a = cSSBreakSize; a < (n_res - cSSBreakSize); a++) {
2621 r = res + a;
2622 if(r->real) {
2623
2624 /* make sure we don't have any 2-residue segments */
2625
2626 if((r->ss == 'S') && ((r + 1)->ss == 'S') &&
2627 (((r - 1)->ss != 'S') && ((r + 2)->ss != 'S'))) {
2628 r->ss = 'L';
2629 (r + 1)->ss = 'L';
2630 repeat = true;
2631 }
2632 if((r->ss == 'H') && ((r + 1)->ss == 'H') &&
2633 (((r - 1)->ss != 'H') && ((r + 2)->ss != 'H'))) {
2634 r->ss = 'L';
2635 (r + 1)->ss = 'L';
2636 repeat = true;
2637 }
2638
2639 /* make sure we don't have any 1-residue segments */
2640
2641 if((r->ss == 'S') && (((r - 1)->ss != 'S') && ((r + 1)->ss != 'S'))) {
2642 r->ss = 'L';
2643 repeat = true;
2644 }
2645 if((r->ss == 'H') && (((r - 1)->ss != 'H') && ((r + 1)->ss != 'H'))) {
2646 r->ss = 'L';
2647 repeat = true;
2648 }
2649
2650 /* double-check to make sure every terminal strand residue
2651 that should have a partner has one */
2652
2653 if((r->ss == 'S') && (((r - 1)->ss != 'S') || ((r + 1)->ss != 'S'))) {
2654
2655 found = false;
2656
2657 for(b = 0; b < r->n_acc; b++) {
2658 r2 = res + r->acc[b];
2659 if(r2->ss == r->ss) {
2660 found = true;
2661 break;
2662 }
2663 }
2664
2665 if(!found) {
2666 for(b = 0; b < r->n_don; b++) {
2667 r2 = res + r->don[b];
2668 if(r2->ss == r->ss) {
2669 found = true;
2670 break;
2671 }
2672 }
2673 }
2674
2675 if(!found) {
2676 /* allow these strand "skip" residues to persist if a neighbor has hydrogen bonds */
2677 if(r->flags & (cSSAntiStrandSkip | cSSParaStrandSkip)) {
2678
2679 if((r + 1)->ss == r->ss)
2680 for(b = 0; b < (r + 1)->n_acc; b++) {
2681 r2 = res + (r + 1)->acc[b];
2682 if(r2->ss == r->ss) {
2683 found = true;
2684 break;
2685 }
2686 }
2687
2688 if(!found) {
2689 if((r - 1)->ss == r->ss) {
2690 for(b = 0; b < (r - 1)->n_don; b++) {
2691 r2 = res + (r - 1)->don[b];
2692 if(r2->ss == r->ss) {
2693 found = true;
2694 break;
2695 }
2696 }
2697 }
2698 }
2699 }
2700 }
2701
2702 if(!found) {
2703 r->ss = 'L';
2704 repeat = true;
2705 }
2706 }
2707 }
2708 }
2709 }
2710 }
2711
2712 {
2713 int a;
2714 for(a = 0; a < n_res; a++) { /* now apply consensus or union behavior, if appropriate */
2715 if(res[a].present) {
2716 if(res[a].ss_save) {
2717 if(res[a].ss != res[a].ss_save) {
2718 if(consensus) {
2719 res[a].ss = res[a].ss_save = 'L';
2720 } else if(res[a].ss == 'L')
2721 res[a].ss = res[a].ss_save;
2722 }
2723 }
2724 res[a].ss_save = res[a].ss;
2725 }
2726 }
2727 }
2728
2729 {
2730 int a, aa;
2731 ObjectMolecule *obj = NULL, *last_obj = NULL;
2732 AtomInfoType *ai;
2733 int changed_flag = false;
2734
2735 for(a = 0; a < n_res; a++) {
2736 if(res[a].present && (!res[a].preserve)) {
2737
2738 aa = res[a].ca;
2739 obj = I->Obj[I->Table[aa].model];
2740
2741 if(obj != last_obj) {
2742 if(changed_flag && last_obj) {
2743 last_obj->invalidate(cRepCartoon, cRepInvRep, -1);
2744 SceneChanged(G);
2745 changed_flag = false;
2746 }
2747 last_obj = obj;
2748 }
2749 ai = obj->AtomInfo + I->Table[aa].atom;
2750
2751 if(SelectorIsMember(G, ai->selEntry, target)) {
2752 ai->ssType[0] = res[a].ss;
2753 ai->cartoon = 0; /* switch back to auto */
2754 ai->ssType[1] = 0;
2755 changed_flag = true;
2756 }
2757 }
2758 }
2759
2760 if(changed_flag && last_obj) {
2761 last_obj->invalidate(cRepCartoon, cRepInvRep, -1);
2762 SceneChanged(G);
2763 changed_flag = false;
2764 }
2765 }
2766 if(first_last_only && (state == state_start))
2767 state = state_stop - 2;
2768 }
2769
2770 VLAFreeP(res);
2771 return 1;
2772 }
2773
SelectorColorectionGet(PyMOLGlobals * G,const char * prefix)2774 PyObject *SelectorColorectionGet(PyMOLGlobals * G, const char *prefix)
2775 {
2776 #ifdef _PYMOL_NOPY
2777 return NULL;
2778 #else
2779 auto I = G->Selector;
2780 auto IM = G->SelectorMgr;
2781 PyObject *result = NULL;
2782 int n_used = 0;
2783 ColorectionRec *used = NULL, tmp;
2784 ov_size a, b;
2785 int found;
2786 int color;
2787 AtomInfoType *ai;
2788 used = VLAlloc(ColorectionRec, 1000);
2789
2790 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
2791 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
2792 ai = I->Obj[I->Table[a].model]->AtomInfo + I->Table[a].atom;
2793 color = ai->color;
2794 found = false;
2795 for(b = 0; b < n_used; b++) {
2796 if(used[b].color == color) {
2797 tmp = used[0]; /* optimize to minimize N^2 effects */
2798 used[0] = used[b];
2799 used[b] = tmp;
2800 found = true;
2801 break;
2802 }
2803 }
2804 if(!found) {
2805 VLACheck(used, ColorectionRec, n_used);
2806 used[n_used] = used[0];
2807 used[0].color = color;
2808 n_used++;
2809 }
2810 }
2811 for(a = 0; a < n_used; a++) {
2812 /* create selections */
2813
2814 SelectorID_t sele = IM->NSelection++;
2815 used[a].sele = sele;
2816 IM->Info.emplace_back(SelectionInfoRec(
2817 sele, pymol::string_format(cColorectionFormat, prefix, used[a].color)));
2818 }
2819
2820 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
2821 ai = I->Obj[I->Table[a].model]->AtomInfo + I->Table[a].atom;
2822 color = ai->color;
2823 for(b = 0; b < n_used; b++) {
2824 if(used[b].color == color) {
2825 tmp = used[0]; /* optimize to minimize N^2 effects */
2826 used[0] = used[b];
2827 used[b] = tmp;
2828
2829 /* add selection onto atom */
2830 SelectorManagerInsertMember(*IM, *ai, used[0].sele);
2831 break;
2832 }
2833 }
2834 }
2835
2836 VLASize(used, ColorectionRec, n_used * 2);
2837 result = PConvIntVLAToPyList((int *) used);
2838 VLAFreeP(used);
2839 return (result);
2840 #endif
2841 }
2842
SelectorColorectionApply(PyMOLGlobals * G,PyObject * list,const char * prefix)2843 int SelectorColorectionApply(PyMOLGlobals * G, PyObject * list, const char *prefix)
2844 {
2845 #ifdef _PYMOL_NOPY
2846 return 0;
2847 #else
2848
2849 CSelector *I = G->Selector;
2850 int ok = true;
2851 ColorectionRec *used = NULL;
2852 ov_size n_used = 0;
2853 int a, b;
2854 AtomInfoType *ai;
2855 ObjectMolecule *obj, *last = NULL;
2856
2857 if(ok)
2858 ok = (list != NULL);
2859 if(ok)
2860 ok = PyList_Check(list);
2861 if(ok)
2862 n_used = PyList_Size(list) / 2;
2863 if(ok)
2864 ok = ((used = VLAlloc(ColorectionRec, n_used)) != NULL);
2865 if(ok)
2866 ok = PConvPyListToIntArrayInPlace(list, (int *) used, n_used * 2);
2867 if(ok) {
2868
2869 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
2870
2871 for(b = 0; b < n_used; b++) { /* update selection indices */
2872 auto name = pymol::string_format(cColorectionFormat, prefix, used[b].color);
2873 used[b].sele = SelectorIndexByName(G, name.c_str());
2874 }
2875
2876 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
2877 obj = I->Obj[I->Table[a].model];
2878 ai = obj->AtomInfo + I->Table[a].atom;
2879
2880 for(b = 0; b < n_used; b++) {
2881 if(SelectorIsMember(G, ai->selEntry, used[b].sele)) {
2882 ai->color = used[b].color;
2883 if(obj != last) {
2884 obj->invalidate(cRepAll, cRepInvColor, -1);
2885 last = obj;
2886 }
2887 break;
2888 }
2889 }
2890 }
2891 }
2892 VLAFreeP(used);
2893 return (ok);
2894 #endif
2895 }
2896
SelectorColorectionSetName(PyMOLGlobals * G,PyObject * list,const char * prefix,char * new_prefix)2897 int SelectorColorectionSetName(PyMOLGlobals * G, PyObject * list, const char *prefix,
2898 char *new_prefix)
2899 {
2900 #ifdef _PYMOL_NOPY
2901 return 0;
2902 #else
2903
2904 int ok = true;
2905 ColorectionRec *used = NULL;
2906 ov_size n_used = 0;
2907 ov_size b;
2908
2909 if(ok)
2910 ok = (list != NULL);
2911 if(ok)
2912 ok = PyList_Check(list);
2913 if(ok)
2914 n_used = PyList_Size(list) / 2;
2915 if(ok)
2916 ok = ((used = VLAlloc(ColorectionRec, n_used)) != NULL);
2917 if(ok)
2918 ok = PConvPyListToIntArrayInPlace(list, (int *) used, n_used * 2);
2919 if(ok) {
2920 for(b = 0; b < n_used; b++) { /* update selection indices */
2921 auto name = pymol::string_format(cColorectionFormat, prefix, used[b].color);
2922 auto new_name = pymol::string_format(cColorectionFormat, new_prefix, used[b].color);
2923 SelectorSetName(G, new_name.c_str(), name.c_str());
2924 }
2925 }
2926 VLAFreeP(used);
2927 return (ok);
2928 #endif
2929
2930 }
2931
SelectorColorectionFree(PyMOLGlobals * G,PyObject * list,const char * prefix)2932 int SelectorColorectionFree(PyMOLGlobals * G, PyObject * list, const char *prefix)
2933 {
2934 #ifdef _PYMOL_NOPY
2935 return 0;
2936 #else
2937
2938 int ok = true;
2939 ColorectionRec *used = NULL;
2940 ov_size n_used = 0;
2941 ov_size b;
2942
2943 if(ok)
2944 ok = (list != NULL);
2945 if(ok)
2946 ok = PyList_Check(list);
2947 if(ok)
2948 n_used = PyList_Size(list) / 2;
2949 if(ok)
2950 ok = ((used = VLAlloc(ColorectionRec, n_used)) != NULL);
2951 if(ok)
2952 ok = PConvPyListToIntArrayInPlace(list, (int *) used, n_used * 2);
2953 if(ok) {
2954
2955 for(b = 0; b < n_used; b++) { /* update selection indices */
2956 auto name = pymol::string_format(cColorectionFormat, prefix, used[b].color);
2957 used[b].sele = SelectorIndexByName(G, name.c_str());
2958 }
2959
2960 for(b = 0; b < n_used; b++) {
2961 SelectorDeleteIndex(G, used[b].sele);
2962 }
2963 }
2964 VLAFreeP(used);
2965 return (ok);
2966 #endif
2967
2968 }
2969
SelectorSecretsAsPyList(PyMOLGlobals * G)2970 PyObject *SelectorSecretsAsPyList(PyMOLGlobals * G)
2971 {
2972 auto I = G->SelectorMgr;
2973
2974 auto n_secret = std::count_if(I->Info.begin(), I->Info.end(), //
2975 [](const SelectionInfoRec& rec) {
2976 return pymol::starts_with(rec.name, cSelectorSecretsPrefix);
2977 });
2978 auto result = PyList_New(n_secret);
2979 n_secret = 0;
2980 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
2981 for(int a = 0; a < I->Info.size(); a++) {
2982 if(pymol::starts_with(I->Info[a].name, cSelectorSecretsPrefix)) {
2983 auto list = PyList_New(2);
2984 PyList_SetItem(list, 0, PyString_FromString(I->Info[a].name.c_str()));
2985 PyList_SetItem(list, 1, SelectorAsPyList(G, I->Info[a].ID));
2986 PyList_SetItem(result, n_secret, list);
2987 n_secret++;
2988 }
2989 }
2990 return (result);
2991 }
2992
SelectorSecretsFromPyList(PyMOLGlobals * G,PyObject * list)2993 int SelectorSecretsFromPyList(PyMOLGlobals * G, PyObject * list)
2994 {
2995 int ok = true;
2996 ov_size n_secret = 0;
2997 ov_size a;
2998 PyObject *entry = NULL;
2999 std::string name;
3000 ov_size ll = 0;
3001 if(ok)
3002 ok = (list != NULL);
3003 if(ok)
3004 ok = PyList_Check(list);
3005 if(ok)
3006 n_secret = PyList_Size(list);
3007 if(ok) {
3008 for(a = 0; a < n_secret; a++) {
3009 if(ok)
3010 entry = PyList_GetItem(list, a);
3011 if(ok)
3012 ok = (entry != NULL);
3013 if(ok)
3014 ok = PyList_Check(entry);
3015 if(ok)
3016 ll = PyList_Size(entry);
3017 if(ok & (ll > 1)) {
3018 if(ok){
3019 ok = PConvFromPyListItem(G, entry, 0, name);
3020 }
3021 if(ok){
3022 CPythonVal *val = CPythonVal_PyList_GetItem(G, entry, 1);
3023 ok = SelectorFromPyList(G, name.c_str(), val);
3024 CPythonVal_Free(val);
3025 }
3026 }
3027 if(!ok)
3028 break;
3029 }
3030 }
3031 return (ok);
3032 }
3033
3034 typedef struct {
3035 int atom;
3036 int tag;
3037 } SelAtomTag;
3038
SelectorAsPyList(PyMOLGlobals * G,SelectorID_t sele1)3039 PyObject *SelectorAsPyList(PyMOLGlobals * G, SelectorID_t sele1)
3040 { /* assumes SelectorUpdateTable has been called */
3041 CSelector *I = G->Selector;
3042 int a, b;
3043 int at;
3044 int s;
3045 SelAtomTag **vla_list = NULL;
3046 int n_obj = 0;
3047 int n_idx = 0;
3048 int cur = -1;
3049 ObjectMolecule **obj_list = NULL;
3050 ObjectMolecule *obj, *cur_obj = NULL;
3051 PyObject *result = NULL;
3052 PyObject *obj_pyobj;
3053 PyObject *idx_pyobj;
3054 PyObject *tag_pyobj;
3055
3056 vla_list = VLACalloc(SelAtomTag *, 10);
3057 obj_list = VLAlloc(ObjectMolecule *, 10);
3058
3059 n_idx = 0;
3060 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
3061 int tag;
3062 at = I->Table[a].atom;
3063 obj = I->Obj[I->Table[a].model];
3064 s = obj->AtomInfo[at].selEntry;
3065 if((tag = SelectorIsMember(G, s, sele1))) {
3066 if(cur_obj != obj) {
3067 if(n_idx) {
3068 VLASize(vla_list[cur], SelAtomTag, n_idx);
3069 }
3070 cur++;
3071 VLACheck(vla_list, SelAtomTag *, n_obj);
3072 vla_list[cur] = VLAlloc(SelAtomTag, 1000);
3073 VLACheck(obj_list, ObjectMolecule *, n_obj);
3074 obj_list[cur] = obj;
3075 cur_obj = obj;
3076 n_obj++;
3077 n_idx = 0;
3078 }
3079 VLACheck(vla_list[cur], SelAtomTag, n_idx);
3080 vla_list[cur][n_idx].atom = at;
3081 vla_list[cur][n_idx].tag = tag;
3082 n_idx++;
3083 }
3084 }
3085 if(cur_obj) {
3086 if(n_idx) {
3087 VLASize(vla_list[cur], SelAtomTag, n_idx);
3088 }
3089 }
3090 if(n_obj) {
3091 result = PyList_New(n_obj);
3092 for(a = 0; a < n_obj; a++) {
3093 obj_pyobj = PyList_New(3);
3094 n_idx = VLAGetSize(vla_list[a]);
3095 idx_pyobj = PyList_New(n_idx);
3096 tag_pyobj = PyList_New(n_idx);
3097 for(b = 0; b < n_idx; b++) {
3098 PyList_SetItem(idx_pyobj, b, PyInt_FromLong(vla_list[a][b].atom));
3099 PyList_SetItem(tag_pyobj, b, PyInt_FromLong(vla_list[a][b].tag));
3100 }
3101 VLAFreeP(vla_list[a]);
3102 PyList_SetItem(obj_pyobj, 0, PyString_FromString(obj_list[a]->Name));
3103 PyList_SetItem(obj_pyobj, 1, idx_pyobj);
3104 PyList_SetItem(obj_pyobj, 2, tag_pyobj);
3105 PyList_SetItem(result, a, obj_pyobj);
3106 }
3107 } else {
3108 result = PyList_New(0);
3109 }
3110 VLAFreeP(vla_list);
3111 VLAFreeP(obj_list);
3112 return (result);
3113 }
3114
SelectorFromPyList(PyMOLGlobals * G,const char * name,PyObject * list)3115 int SelectorFromPyList(PyMOLGlobals * G, const char *name, PyObject * list)
3116 {
3117 int ok = true;
3118 auto I = G->SelectorMgr;
3119 ov_size a, b;
3120 ov_size ll;
3121 PyObject *obj_list = NULL;
3122 PyObject *idx_list = NULL, *tag_list;
3123 ov_size n_obj = 0, n_idx = 0;
3124 int idx, tag;
3125 const char *oname;
3126 ObjectMolecule *obj;
3127 int singleAtomFlag = true;
3128 int singleObjectFlag = true;
3129 ObjectMolecule *singleObject = NULL;
3130 int singleAtom = -1;
3131
3132 if(ok)
3133 ok = PyList_Check(list);
3134 if(ok)
3135 n_obj = PyList_Size(list);
3136
3137 /* get rid of existing selection */
3138 SelectorDelete(G, name);
3139
3140 int sele = I->NSelection++;
3141 I->Info.emplace_back(SelectionInfoRec(sele, name));
3142 if(ok) {
3143 for(a = 0; a < n_obj; a++) {
3144 ll = 0;
3145 if(ok)
3146 obj_list = PyList_GetItem(list, a);
3147 if(ok)
3148 ok = PyList_Check(obj_list);
3149 if(ok)
3150 ll = PyList_Size(obj_list);
3151 if(ok)
3152 ok = PConvPyStrToStrPtr(PyList_GetItem(obj_list, 0), &oname);
3153 obj = NULL;
3154 if(ok)
3155 obj = ExecutiveFindObjectMoleculeByName(G, oname);
3156 if(ok && obj) {
3157 if(ok)
3158 idx_list = PyList_GetItem(obj_list, 1);
3159 if(ll > 2)
3160 tag_list = PyList_GetItem(obj_list, 2);
3161 else
3162 tag_list = NULL;
3163 if(ok)
3164 ok = PyList_Check(idx_list);
3165 if(ok)
3166 n_idx = PyList_Size(idx_list);
3167 for(b = 0; b < n_idx; b++) {
3168 if(ok)
3169 ok = PConvPyIntToInt(PyList_GetItem(idx_list, b), &idx);
3170 if(tag_list)
3171 PConvPyIntToInt(PyList_GetItem(tag_list, b), &tag);
3172 else
3173 tag = 1;
3174 if(ok && (idx < obj->NAtom)) {
3175 SelectorManagerInsertMember(*I, obj->AtomInfo[idx], sele, tag);
3176
3177 /* take note of selections which are one atom/one object */
3178 if(singleObjectFlag) {
3179 if(singleObject) {
3180 if(obj != singleObject) {
3181 singleObjectFlag = false;
3182 }
3183 } else {
3184 singleObject = obj;
3185 }
3186 }
3187
3188 if(singleAtomFlag) {
3189 if(singleAtom >= 0) {
3190 if(idx != singleAtom) {
3191 singleAtomFlag = false;
3192 }
3193 } else {
3194 singleAtom = idx;
3195 }
3196 }
3197 }
3198 }
3199 }
3200 }
3201 { /* make note of single atom/object selections */
3202 auto& info = I->Info.back();
3203 if(singleObjectFlag && singleObject) {
3204 info.theOneObject = singleObject;
3205 if(singleAtomFlag && (singleAtom >= 0)) {
3206 info.theOneAtom = singleAtom;
3207 }
3208 }
3209 }
3210 }
3211 return (ok);
3212 }
3213
SelectorVdwFit(PyMOLGlobals * G,int sele1,int state1,int sele2,int state2,float buffer,int quiet)3214 int SelectorVdwFit(PyMOLGlobals * G, int sele1, int state1, int sele2, int state2,
3215 float buffer, int quiet)
3216 {
3217 CSelector *I = G->Selector;
3218 float sumVDW = 0.0, dist;
3219 int a1, a2;
3220 AtomInfoType *ai1, *ai2;
3221 int at1, at2;
3222 CoordSet *cs1, *cs2;
3223 ObjectMolecule *obj1, *obj2;
3224 int idx1, idx2;
3225 int a;
3226
3227 if(state1 < 0)
3228 state1 = 0;
3229 if(state2 < 0)
3230 state2 = 0;
3231
3232 if(state1 != state2) {
3233 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3234 } else {
3235 SelectorUpdateTable(G, state1, -1);
3236 }
3237
3238 auto vla = SelectorGetInterstateVLA(
3239 G, sele1, state1, sele2, state2, 2 * MAX_VDW + buffer);
3240 const int c = vla.size() / 2;
3241
3242 if(c) {
3243 auto adj = std::vector<float>(vla.size());
3244
3245 for(a = 0; a < c; a++) {
3246 a1 = vla[a * 2];
3247 a2 = vla[a * 2 + 1];
3248
3249 at1 = I->Table[a1].atom;
3250 at2 = I->Table[a2].atom;
3251
3252 obj1 = I->Obj[I->Table[a1].model];
3253 obj2 = I->Obj[I->Table[a2].model];
3254
3255 if((state1 < obj1->NCSet) && (state2 < obj2->NCSet)) {
3256 cs1 = obj1->CSet[state1];
3257 cs2 = obj2->CSet[state2];
3258 if(cs1 && cs2) { /* should always be true */
3259
3260 ai1 = obj1->AtomInfo + at1;
3261 ai2 = obj2->AtomInfo + at2;
3262
3263 idx1 = cs1->AtmToIdx[at1]; /* these are also pre-validated */
3264 idx2 = cs2->AtmToIdx[at2];
3265
3266 sumVDW = ai1->vdw + ai2->vdw;
3267 dist = (float) diff3f(cs1->coordPtr(idx1), cs2->coordPtr(idx2));
3268
3269 if(dist < (sumVDW + buffer)) {
3270 float shift = (dist - (sumVDW + buffer)) / 2.0F;
3271 adj[2 * a] = ai1->vdw + shift;
3272 adj[2 * a + 1] = ai2->vdw + shift;
3273 } else {
3274 adj[2 * a] = ai1->vdw;
3275 adj[2 * a + 1] = ai2->vdw;
3276 }
3277
3278 }
3279 }
3280 }
3281
3282 for(a = 0; a < c; a++) {
3283 a1 = vla[a * 2];
3284 a2 = vla[a * 2 + 1];
3285
3286 at1 = I->Table[a1].atom;
3287 at2 = I->Table[a2].atom;
3288
3289 obj1 = I->Obj[I->Table[a1].model];
3290 obj2 = I->Obj[I->Table[a2].model];
3291
3292 if((state1 < obj1->NCSet) && (state2 < obj2->NCSet)) {
3293 cs1 = obj1->CSet[state1];
3294 cs2 = obj2->CSet[state2];
3295 if(cs1 && cs2) { /* should always be true */
3296
3297 ai1 = obj1->AtomInfo + at1;
3298 ai2 = obj2->AtomInfo + at2;
3299
3300 if(adj[2 * a] < ai1->vdw) {
3301 ai1->vdw = adj[2 * a];
3302 }
3303
3304 if(adj[2 * a + 1] < ai2->vdw) {
3305 ai2->vdw = adj[2 * a + 1];
3306 }
3307
3308 }
3309 }
3310 }
3311 }
3312
3313 return true;
3314 }
3315
3316
3317 /*========================================================================*/
3318
SelectorGetPairIndices(PyMOLGlobals * G,int sele1,int state1,int sele2,int state2,int mode,float cutoff,float h_angle,int ** indexVLA,ObjectMolecule *** objVLA)3319 int SelectorGetPairIndices(PyMOLGlobals * G, int sele1, int state1, int sele2, int state2,
3320 int mode, float cutoff, float h_angle,
3321 int **indexVLA, ObjectMolecule *** objVLA)
3322 {
3323 CSelector *I = G->Selector;
3324 float dist;
3325 int a1, a2;
3326 int at1, at2;
3327 CoordSet *cs1, *cs2;
3328 ObjectMolecule *obj1, *obj2;
3329 int idx1, idx2;
3330 int a;
3331 int dist_cnt = 0;
3332 float dir[3];
3333 float v1[3], v2[3];
3334 int flag;
3335 float angle_cutoff = 0.0;
3336
3337 if(mode == 1) {
3338 angle_cutoff = (float) cos(PI * h_angle / 180.0);
3339 }
3340
3341 if(state1 < 0)
3342 state1 = 0;
3343 if(state2 < 0)
3344 state2 = 0;
3345
3346 if(state1 != state2) {
3347 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3348 } else {
3349 SelectorUpdateTable(G, state1, -1);
3350 }
3351 if(cutoff < 0)
3352 cutoff = 1000.0;
3353
3354 auto vla = SelectorGetInterstateVLA(G, sele1, state1, sele2, state2, cutoff);
3355 const int c = vla.size() / 2;
3356
3357 (*indexVLA) = VLAlloc(int, 1000);
3358 (*objVLA) = VLAlloc(ObjectMolecule *, 1000);
3359
3360 for(a = 0; a < c; a++) {
3361 a1 = vla[a * 2];
3362 a2 = vla[a * 2 + 1];
3363
3364 if(a1 != a2) {
3365 at1 = I->Table[a1].atom;
3366 at2 = I->Table[a2].atom;
3367
3368 obj1 = I->Obj[I->Table[a1].model];
3369 obj2 = I->Obj[I->Table[a2].model];
3370
3371 if(state1 < obj1->NCSet && state2 < obj2->NCSet) {
3372 cs1 = obj1->CSet[state1];
3373 cs2 = obj2->CSet[state2];
3374 if(cs1 && cs2) {
3375 idx1 = cs1->atmToIdx(at1);
3376 idx2 = cs2->atmToIdx(at2);
3377
3378 if((idx1 >= 0) && (idx2 >= 0)) {
3379 subtract3f(cs1->coordPtr(idx1), cs2->coordPtr(idx2), dir);
3380 dist = (float) length3f(dir);
3381 if(dist > R_SMALL4) {
3382 float dist_1 = 1.0F / dist;
3383 scale3f(dir, dist_1, dir);
3384 }
3385 if(dist < cutoff) {
3386 if(mode == 1) { /* coarse hydrogen bonding assessment */
3387 flag = false;
3388 if(ObjectMoleculeGetAvgHBondVector(obj1, at1, state1, v1, NULL) > 0.3)
3389 if(dot_product3f(v1, dir) < -angle_cutoff)
3390 flag = true;
3391 if(ObjectMoleculeGetAvgHBondVector(obj2, at2, state2, v2, NULL) > 0.3)
3392 if(dot_product3f(v2, dir) > angle_cutoff)
3393 flag = true;
3394 } else
3395 flag = true;
3396
3397 if(flag) {
3398 VLACheck((*objVLA), ObjectMolecule *, dist_cnt + 1);
3399 VLACheck((*indexVLA), int, dist_cnt + 1);
3400 (*objVLA)[dist_cnt] = obj1;
3401 (*indexVLA)[dist_cnt] = at1;
3402 dist_cnt++;
3403 (*objVLA)[dist_cnt] = obj2;
3404 (*indexVLA)[dist_cnt] = at2;
3405 dist_cnt++;
3406 }
3407 }
3408 }
3409 }
3410 }
3411 }
3412 }
3413
3414 VLASize((*objVLA), ObjectMolecule *, dist_cnt);
3415 VLASize((*indexVLA), int, dist_cnt);
3416 dist_cnt = dist_cnt / 2;
3417 return (dist_cnt);
3418 }
3419
3420
3421 /*========================================================================*/
SelectorCreateAlignments(PyMOLGlobals * G,int * pair,int sele1,int * vla1,int sele2,int * vla2,const char * name1,const char * name2,int identical,int atomic_input)3422 int SelectorCreateAlignments(PyMOLGlobals * G,
3423 int *pair, int sele1, int *vla1, int sele2,
3424 int *vla2, const char *name1, const char *name2,
3425 int identical, int atomic_input)
3426 {
3427 CSelector *I = G->Selector;
3428 int *flag1 = NULL, *flag2 = NULL;
3429 int *p;
3430 int i, np;
3431 int cnt;
3432 int mod1, mod2; /* model indexes */
3433 int at1, at2, at1a, at2a; /* atoms indexes */
3434 int vi1, vi2; /* vla indexes */
3435 int index1, index2; /* indices in the selection array */
3436 AtomInfoType *ai1, *ai2, *ai1a, *ai2a; /* atom information pointers */
3437 ObjectMolecule *obj1, *obj2;
3438 int cmp;
3439 PRINTFD(G, FB_Selector)
3440 " %s-DEBUG: entry.\n", __func__ ENDFD cnt = 0;
3441 /* number of pairs of atoms */
3442 np = VLAGetSize(pair) / 2;
3443 if(np) {
3444
3445 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1); /* unnecessary? */
3446 /* flags initialized to false */
3447 flag1 = pymol::calloc<int>(I->Table.size());
3448 flag2 = pymol::calloc<int>(I->Table.size());
3449
3450 /* we need to create two selection arrays: for the matched
3451 * atoms in the original selections */
3452 p = pair;
3453 for(i = 0; i < np; i++) { /* iterate through all pairs of matched residues */
3454 vi1 = *(p++);
3455 vi2 = *(p++);
3456
3457 /* find positions in the selection arrays */
3458 /* SelectorGetResidueVLA returns a VLA with 3 entries per atom, index0=model,
3459 * index1=atom#, hence: *3, and *3+1 */
3460 mod1 = vla1[vi1 * 3];
3461 at1 = vla1[vi1 * 3 + 1];
3462
3463 mod2 = vla2[vi2 * 3];
3464 at2 = vla2[vi2 * 3 + 1];
3465
3466 PRINTFD(G, FB_Selector)
3467 " S.C.A.-DEBUG: mod1 %d at1 %d mod2 %d at2 %d\n", mod1, at1, mod2, at2
3468 ENDFD obj1 = I->Obj[mod1];
3469 obj2 = I->Obj[mod2];
3470
3471 ai1 = obj1->AtomInfo + at1;
3472 ai2 = obj2->AtomInfo + at2;
3473 at1a = at1;
3474 at2a = at2;
3475 ai1a = ai1;
3476 ai2a = ai2;
3477
3478 if(atomic_input) {
3479 index1 = SelectorGetObjAtmOffset(I, obj1, at1a);
3480 index2 = SelectorGetObjAtmOffset(I, obj2, at2a);
3481 flag1[index1] = true;
3482 flag2[index2] = true;
3483 cnt++;
3484 } else {
3485
3486 /* search back to first atom in residue */
3487 while(at1a > 0 && AtomInfoSameResidue(G, ai1a, ai1a - 1)) { ai1a--; at1a--; }
3488 while(at2a > 0 && AtomInfoSameResidue(G, ai2a, ai2a - 1)) { ai2a--; at2a--; }
3489
3490 while(1) { /* match up all matching atom names in each residue */
3491 cmp = AtomInfoNameOrder(G, ai1a, ai2a);
3492 if(cmp == 0) { /* atoms match */
3493 index1 = SelectorGetObjAtmOffset(I, obj1, at1a);
3494 index2 = SelectorGetObjAtmOffset(I, obj2, at2a);
3495
3496 PRINTFD(G, FB_Selector)
3497 " S.C.A.-DEBUG: compare %s %s %d\n", LexStr(G, ai1a->name), LexStr(G, ai2a->name), cmp
3498 ENDFD PRINTFD(G, FB_Selector)
3499 " S.C.A.-DEBUG: entry %d %d\n",
3500 ai1a->selEntry, ai2a->selEntry ENDFD if((index1 >= 0) && (index2 >= 0)) {
3501 if(SelectorIsMember(G, ai1a->selEntry, sele1) &&
3502 SelectorIsMember(G, ai2a->selEntry, sele2)) {
3503 if((!identical) || (ai1a->resn == ai2a->resn)) {
3504 flag1[index1] = true;
3505 flag2[index2] = true;
3506 cnt++;
3507 }
3508 }
3509 }
3510 at1a++;
3511 at2a++;
3512 } else if(cmp < 0) { /* 1 is before 2 */
3513 at1a++;
3514 } else if(cmp > 0) { /* 1 is after 2 */
3515 at2a++;
3516 }
3517 if(at1a >= obj1->NAtom)
3518 break;
3519 if(at2a >= obj2->NAtom)
3520 break;
3521 ai1a = obj1->AtomInfo + at1a;
3522 ai2a = obj2->AtomInfo + at2a;
3523 /* make sure we're still in the same residue */
3524 if(!AtomInfoSameResidue(G, ai1a, ai1))
3525 break;
3526 if(!AtomInfoSameResidue(G, ai2a, ai2))
3527 break;
3528 }
3529 }
3530 }
3531 if(cnt) {
3532 SelectorEmbedSelection(G, flag1, name1, NULL, false, -1);
3533 SelectorEmbedSelection(G, flag2, name2, NULL, false, -1);
3534 }
3535 FreeP(flag1);
3536 FreeP(flag2);
3537 }
3538 PRINTFD(G, FB_Selector)
3539 " %s-DEBUG: exit, cnt = %d.\n", __func__, cnt ENDFD return cnt;
3540 }
3541
3542
3543 /*========================================================================*/
SelectorCountStates(PyMOLGlobals * G,int sele)3544 int SelectorCountStates(PyMOLGlobals * G, int sele)
3545 {
3546 CSelector *I = G->Selector;
3547 int a;
3548 int result = 0;
3549 int n_frame;
3550 int at1;
3551 ObjectMolecule *last = NULL;
3552 ObjectMolecule *obj;
3553 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3554 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
3555 obj = I->Obj[I->Table[a].model];
3556 if(obj != last) {
3557 at1 = I->Table[a].atom;
3558 if(SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele)) {
3559 {
3560 n_frame = obj->getNFrame();
3561 if(result < n_frame)
3562 result = n_frame;
3563 }
3564 last = obj;
3565 }
3566 }
3567 }
3568 return (result);
3569 }
3570
3571
3572 /*========================================================================*/
SelectorCheckIntersection(PyMOLGlobals * G,int sele1,int sele2)3573 int SelectorCheckIntersection(PyMOLGlobals * G, int sele1, int sele2)
3574 {
3575 CSelector *I = G->Selector;
3576
3577 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3578 for(int a = cNDummyAtoms; a < I->Table.size(); a++) {
3579 auto obj = I->Obj[I->Table[a].model];
3580 auto at1 = I->Table[a].atom;
3581 if(SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele1) &&
3582 SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele2))
3583 return 1;
3584 }
3585 return 0;
3586 }
3587
3588
3589 /*========================================================================*/
SelectorCountAtoms(PyMOLGlobals * G,int sele,int state)3590 int SelectorCountAtoms(PyMOLGlobals * G, int sele, int state)
3591 {
3592 CSelector *I = G->Selector;
3593 int result = 0;
3594
3595 SelectorUpdateTable(G, state, -1);
3596 for(int a = cNDummyAtoms; a < I->Table.size(); a++) {
3597 auto obj = I->Obj[I->Table[a].model];
3598 auto at1 = I->Table[a].atom;
3599 if(SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele)) {
3600 result++;
3601 }
3602 }
3603 return (result);
3604 }
3605
3606 /*========================================================================*/
SelectorSetDeleteFlagOnSelectionInObject(PyMOLGlobals * G,int sele,ObjectMolecule * obj,signed char val)3607 void SelectorSetDeleteFlagOnSelectionInObject(PyMOLGlobals * G, int sele, ObjectMolecule *obj, signed char val){
3608 CSelector *I = G->Selector;
3609
3610 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3611 for(int a = cNDummyAtoms; a < I->Table.size(); a++) {
3612 auto obj0 = I->Obj[I->Table[a].model];
3613 if (obj==obj0){
3614 auto at1 = I->Table[a].atom;
3615 if(SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele)) {
3616 obj->AtomInfo[at1].deleteFlag = val;
3617 }
3618 }
3619 }
3620 }
3621
3622 /*========================================================================*/
SelectorGetResidueVLA(PyMOLGlobals * G,SelectorID_t sele,int ca_only,ObjectMolecule * exclude)3623 int *SelectorGetResidueVLA(PyMOLGlobals * G, SelectorID_t sele, int ca_only,
3624 ObjectMolecule * exclude)
3625 {
3626 /* returns a VLA containing atom indices followed by residue integers
3627 (residue names packed as characters into integers)
3628 The indices are the first and last residue in the selection...
3629 */
3630 CSelector *I = G->Selector;
3631 int *result = NULL, *r;
3632 AtomInfoType *ai1 = NULL, *ai2;
3633
3634 /* update the selector's table */
3635 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3636
3637 /* make room for model#, at#, rcode, per atom */
3638 result = VLAlloc(int, I->Table.size() * 3);
3639
3640 r = result;
3641 PRINTFD(G, FB_Selector)
3642 " %s-DEBUG: entry, sele = %d\n", __func__, sele ENDFD;
3643
3644 for(SeleAtomIterator iter(G, sele); iter.next();) {
3645 if(iter.obj == exclude)
3646 continue;
3647
3648 ai2 = iter.getAtomInfo();
3649
3650 if(ca_only) {
3651 if(!(ai2->flags & cAtomFlag_guide))
3652 continue;
3653 } else if(ai1 && AtomInfoSameResidue(G, ai1, ai2)) {
3654 continue;
3655 }
3656
3657 *(r++) = I->Table[iter.a].model;
3658 *(r++) = I->Table[iter.a].atom;
3659
3660 const char * resn = LexStr(G, ai2->resn);
3661 *(r) = (resn[0] << (8 * 2));
3662 if (resn[0] && resn[1]) {
3663 *(r) |= (resn[1] << (8 * 1));
3664 *(r) |= (resn[2] << (8 * 0));
3665 }
3666 r++;
3667
3668 ai1 = ai2;
3669 }
3670 if(result) {
3671 VLASize(result, int, (ov_size) (r - result));
3672 }
3673 PRINTFD(G, FB_Selector)
3674 " %s-DEBUG: exit, result = %p, size = %d\n", __func__,
3675 (void *) result, (unsigned int) VLAGetSize(result)
3676 ENDFD;
3677
3678 return (result);
3679 }
3680
3681
3682 /*========================================================================*/
3683 /* bad to make this non-static? */
3684 /* static int *SelectorGetIndexVLA(PyMOLGlobals * G, int sele) */
SelectorGetIndexVLA(PyMOLGlobals * G,SelectorID_t sele)3685 static int *SelectorGetIndexVLA(PyMOLGlobals * G, SelectorID_t sele)
3686 {
3687 return (SelectorGetIndexVLAImpl(G, G->Selector, sele));
3688 }
SelectorGetIndexVLAImpl(PyMOLGlobals * G,CSelector * I,SelectorID_t sele)3689 static int *SelectorGetIndexVLAImpl(PyMOLGlobals * G, CSelector *I, SelectorID_t sele)
3690 { /* assumes updated tables */
3691 int a, c = 0;
3692 int *result = NULL;
3693 ObjectMolecule *obj;
3694 int at1;
3695
3696 result = VLAlloc(int, (I->Table.size() / 10) + 1);
3697 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
3698 obj = I->Obj[I->Table[a].model];
3699 at1 = I->Table[a].atom;
3700 if(SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele)) {
3701 VLACheck(result, int, c);
3702 result[c++] = a;
3703 }
3704 }
3705 VLASize(result, int, c);
3706 return (result);
3707 }
3708
3709
3710 /*========================================================================*/
SelectorUpdateObjectSele(PyMOLGlobals * G,ObjectMolecule * obj)3711 void SelectorUpdateObjectSele(PyMOLGlobals * G, ObjectMolecule * obj)
3712 {
3713 if(obj->Name[0]) {
3714 SelectorDelete(G, obj->Name);
3715 SelectorCreate(G, obj->Name, NULL, obj, true, NULL);
3716 /* create a selection with same name */
3717 if(SettingGetGlobal_b(G, cSetting_auto_classify_atoms))
3718 {
3719 SelectorClassifyAtoms(G, 0, false, obj);
3720
3721 // for file formats other than PDB
3722 if (obj->need_hetatm_classification) {
3723 for (auto ai = obj->AtomInfo.data(), ai_end = ai + obj->NAtom;
3724 ai != ai_end; ++ai) {
3725 if (!(ai->flags & cAtomFlag_polymer)) {
3726 ai->hetatm = true;
3727 ai->flags |= cAtomFlag_ignore;
3728 }
3729 }
3730 obj->need_hetatm_classification = false;
3731 }
3732 }
3733 }
3734 }
3735
3736
3737 /*========================================================================*/
SelectorLogSele(PyMOLGlobals * G,const char * name)3738 void SelectorLogSele(PyMOLGlobals * G, const char *name)
3739 {
3740 CSelector *I = G->Selector;
3741 int a;
3742 std::string line, buf1;
3743 int cnt = -1;
3744 int first = 1;
3745 int append = 0;
3746 ObjectMolecule *obj;
3747 int at1;
3748 int sele;
3749 int logging;
3750 int robust;
3751 logging = SettingGetGlobal_i(G, cSetting_logging);
3752 robust = SettingGetGlobal_b(G, cSetting_robust_logs);
3753 if(logging) {
3754 sele = SelectorIndexByName(G, name);
3755 if(sele >= 0) {
3756 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3757 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
3758 obj = I->Obj[I->Table[a].model];
3759 at1 = I->Table[a].atom;
3760 if(SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele)) {
3761 if(cnt < 0) {
3762 if(first) {
3763 switch (logging) {
3764 case cPLog_pml:
3765 line = pymol::string_format("_ cmd.select(\"%s\",\"(", name);
3766 break;
3767 case cPLog_pym:
3768 line = pymol::string_format("cmd.select(\"%s\",\"(", name);
3769 break;
3770 }
3771 append = 0;
3772 cnt = 0;
3773 first = 0;
3774 } else {
3775 switch (logging) {
3776 case cPLog_pml:
3777 line = pymol::string_format("_ cmd.select(\"%s\",\"(%s", name, name);
3778 break;
3779 case cPLog_pym:
3780 line = pymol::string_format("cmd.select(\"%s\",\"(%s", name, name);
3781 break;
3782 }
3783 append = 1;
3784 cnt = 0;
3785 }
3786 }
3787 if(append)
3788 line += "|";
3789 if(robust)
3790 buf1 = ObjectMoleculeGetAtomSeleFast(obj, at1);
3791 else
3792 buf1 = pymol::string_format("%s`%d", obj->Name, at1 + 1);
3793 line += buf1;
3794 append = 1;
3795 cnt++;
3796 if(line.size() > (sizeof(OrthoLineType) / 2)) {
3797 line += ")\")\n";
3798 PLog(G, line.c_str(), cPLog_no_flush);
3799 cnt = -1;
3800 }
3801 }
3802 }
3803 if(cnt > 0) {
3804 line += ")\")\n";
3805 PLog(G, line.c_str(), cPLog_no_flush);
3806 PLogFlush(G);
3807 }
3808 }
3809 }
3810 }
3811
3812
3813 /*========================================================================*/
3814 /*
3815 * This is the most heavily called routine in interactive PyMOL
3816 *
3817 * s: AtomInfoType.selEntry
3818 * sele: selection index or 0 for "all"
3819 */
SelectorIsMember(PyMOLGlobals * G,SelectorMemberOffset_t s,SelectorID_t sele)3820 int SelectorIsMember(PyMOLGlobals * G, SelectorMemberOffset_t s, SelectorID_t sele)
3821 {
3822 if(sele > 1) {
3823 const MemberType *mem, *member = G->SelectorMgr->Member.data();
3824 for (; s; s = mem->next) {
3825 mem = member + s;
3826 if (mem->selection == sele)
3827 return mem->tag;
3828 }
3829 } else if(!sele)
3830 return true; /* "all" is selection number 0, unordered */
3831 return false;
3832 }
3833
3834
3835 /*========================================================================*/
SelectorMoveMember(PyMOLGlobals * G,SelectorMemberOffset_t s,SelectorID_t sele_old,SelectorID_t sele_new)3836 bool SelectorMoveMember(PyMOLGlobals * G, SelectorMemberOffset_t s, SelectorID_t sele_old, SelectorID_t sele_new)
3837 {
3838 auto I = G->SelectorMgr;
3839 int result = false;
3840 while(s) {
3841 if(I->Member[s].selection == sele_old) {
3842 I->Member[s].selection = sele_new;
3843 result = true;
3844 }
3845 s = I->Member[s].next;
3846 }
3847 return result;
3848 }
3849
3850
3851 /*========================================================================*/
SelectorGetFastSingleObjectMolecule(PyMOLGlobals * G,SelectorID_t sele)3852 ObjectMolecule *SelectorGetFastSingleObjectMolecule(PyMOLGlobals * G, SelectorID_t sele)
3853 {
3854 auto I = G->SelectorMgr;
3855 ObjectMolecule *result = NULL;
3856 auto it = std::find_if(I->Info.begin(), I->Info.end(),
3857 [sele](const SelectionInfoRec& rec) { return rec.ID == sele; });
3858 if (it != I->Info.end()) {
3859 auto& info = *it;
3860 if (info.justOneObject()) {
3861 if(ExecutiveValidateObjectPtr(G, (CObject *) info.theOneObject, cObjectMolecule))
3862 result = info.theOneObject;
3863 } else {
3864 result = SelectorGetSingleObjectMolecule(G, sele); /* fallback onto slow approach */
3865 }
3866 }
3867 return (result);
3868 }
3869
3870
3871 /*========================================================================*/
SelectorGetFastSingleAtomObjectIndex(PyMOLGlobals * G,SelectorID_t sele,int * index)3872 ObjectMolecule *SelectorGetFastSingleAtomObjectIndex(PyMOLGlobals * G, SelectorID_t sele,
3873 int *index)
3874 {
3875 auto I = G->SelectorMgr;
3876 ObjectMolecule *result = NULL;
3877 auto it = std::find_if(I->Info.begin(), I->Info.end(),
3878 [sele](const SelectionInfoRec& rec) { return rec.ID == sele; });
3879 if (it != I->Info.end()) {
3880 auto& info = *it;
3881 if (info.justOneAtom()) {
3882 ObjectMolecule *obj = info.theOneObject;
3883 int at = info.theOneAtom;
3884 if(ExecutiveValidateObjectPtr(G, (CObject *) obj, cObjectMolecule)) {
3885 if((at < obj->NAtom) && SelectorIsMember(G, obj->AtomInfo[at].selEntry, sele)) {
3886 *index = at;
3887 return obj;
3888 }
3889 }
3890 }
3891 /* fallback onto slow approach */
3892 {
3893 auto res = SelectorGetSingleAtomObjectIndex(G, sele);
3894 if(res) {
3895 std::tie(result, *index) = res.result();
3896 }
3897 }
3898 }
3899 return (result);
3900 }
3901
3902
3903 /*========================================================================
3904 * SelectorGetSingleObjectMolecule -- get a ptr to the molecule indiecated
3905 * by the selection parameter
3906 * PARAMS
3907 * (int) selection #
3908 * RETURNS
3909 * (ptr) pts to the ObjectMolecule or NULL if not found
3910 */
SelectorGetSingleObjectMolecule(PyMOLGlobals * G,SelectorID_t sele)3911 ObjectMolecule *SelectorGetSingleObjectMolecule(PyMOLGlobals * G, SelectorID_t sele)
3912 {
3913 /* slow way */
3914
3915 int a;
3916 ObjectMolecule *result = NULL;
3917 ObjectMolecule *obj;
3918 CSelector *I = G->Selector;
3919 int at1;
3920 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3921
3922 for(a = 0; a < I->Table.size(); a++) {
3923 obj = I->Obj[I->Table[a].model];
3924 at1 = I->Table[a].atom;
3925 if(SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele)) {
3926 if(result) {
3927 if(result != obj) {
3928 result = NULL;
3929 break;
3930 }
3931 } else {
3932 result = obj;
3933 }
3934 }
3935 }
3936 return (result);
3937 }
3938
3939
3940 /*========================================================================*/
SelectorGetFirstObjectMolecule(PyMOLGlobals * G,SelectorID_t sele)3941 ObjectMolecule *SelectorGetFirstObjectMolecule(PyMOLGlobals * G, SelectorID_t sele)
3942 {
3943 /* slow way */
3944
3945 int a;
3946 ObjectMolecule *result = NULL;
3947 ObjectMolecule *obj;
3948 CSelector *I = G->Selector;
3949 int at1;
3950 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3951
3952 for(a = 0; a < I->Table.size(); a++) {
3953 obj = I->Obj[I->Table[a].model];
3954 at1 = I->Table[a].atom;
3955 if(SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele)) {
3956 result = obj;
3957 break;
3958 }
3959 }
3960 return (result);
3961 }
3962
3963
3964 /*========================================================================*/
SelectorGetObjectMoleculeVLA(PyMOLGlobals * G,SelectorID_t sele)3965 ObjectMolecule **SelectorGetObjectMoleculeVLA(PyMOLGlobals * G, SelectorID_t sele)
3966 {
3967 int a;
3968 ObjectMolecule *last = NULL;
3969 ObjectMolecule *obj, **result = NULL;
3970 CSelector *I = G->Selector;
3971 int at1;
3972 int n = 0;
3973 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
3974
3975 result = VLAlloc(ObjectMolecule *, 10);
3976 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
3977 obj = I->Obj[I->Table[a].model];
3978 at1 = I->Table[a].atom;
3979 if(SelectorIsMember(G, obj->AtomInfo[at1].selEntry, sele)) {
3980 if(obj != last) {
3981 VLACheck(result, ObjectMolecule *, n);
3982 result[n] = obj;
3983 last = obj;
3984 n++;
3985 }
3986 }
3987 }
3988 VLASize(result, ObjectMolecule *, n);
3989 return (result);
3990 }
3991
3992
3993 /*========================================================================*/
SelectorGetSingleAtomObjectIndex(PyMOLGlobals * G,SelectorID_t sele)3994 pymol::Result<std::pair<ObjectMolecule*, int>> SelectorGetSingleAtomObjectIndex(
3995 PyMOLGlobals* G, SelectorID_t sele)
3996 {
3997 /* slow way */
3998
3999 bool found_it = false;
4000 void *iterator = NULL;
4001 ObjectMolecule *obj = NULL;
4002 std::pair<ObjectMolecule*, int> result;
4003
4004 while(ExecutiveIterateObjectMolecule(G, &obj, &iterator)) {
4005 const AtomInfoType *ai = obj->AtomInfo.data();
4006 for(int a = 0; a < obj->NAtom; a++) {
4007 int s = (ai++)->selEntry;
4008 if(SelectorIsMember(G, s, sele)) {
4009 if(found_it) {
4010 return pymol::Error("More than one atom found"); /* ADD'L EXIT POINT */
4011 } else {
4012 result = std::make_pair(obj, a);
4013 found_it = true;
4014 }
4015 }
4016 }
4017 }
4018 if(found_it) {
4019 return result;
4020 } else {
4021 return pymol::Error("Not found");
4022 }
4023 }
4024
4025
4026 /*========================================================================*/
SelectorGetSingleAtomVertex(PyMOLGlobals * G,int sele,int state)4027 pymol::Result<std::array<float, 3>> SelectorGetSingleAtomVertex(PyMOLGlobals * G, int sele, int state)
4028 {
4029 auto atom_index_result = SelectorGetSingleAtomObjectIndex(G, sele);
4030 p_return_if_error(atom_index_result);
4031 {
4032 auto obj_idx = atom_index_result.result();
4033 std::array<float, 3> v;
4034 auto found_it = ObjectMoleculeGetAtomTxfVertex(obj_idx.first, state, obj_idx.second, v.data());
4035 if(found_it) {
4036 return v;
4037 } else {
4038 return pymol::Error("Invalid Atom");
4039 }
4040 }
4041 }
4042
4043
4044 /*========================================================================*/
SelectorDeletePrefixSet(PyMOLGlobals * G,const char * pref)4045 void SelectorDeletePrefixSet(PyMOLGlobals * G, const char *pref)
4046 {
4047 auto I = G->SelectorMgr;
4048 int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
4049
4050 while(1) {
4051 auto it = SelectGetInfoIter(G, pref, strlen(pref), ignore_case);
4052 if (it == I->Info.end()) {
4053 break;
4054 }
4055
4056 // important to use a copy, otherwise you'll delete all objects
4057 auto name_copy = it->name;
4058 ExecutiveDelete(G, name_copy.c_str());
4059 }
4060 }
4061
4062
4063 /*========================================================================*/
4064 #define MAX_DEPTH 1000
4065
SelectorCheckNeighbors(PyMOLGlobals * G,int maxDist,ObjectMolecule * obj,int at1,int at2,int * zero,int * scratch)4066 static int SelectorCheckNeighbors(PyMOLGlobals * G, int maxDist, ObjectMolecule * obj,
4067 int at1, int at2, int *zero, int *scratch)
4068 {
4069 int s;
4070 int a, a1;
4071 int stkDepth = 0;
4072 int si = 0;
4073 int stk[MAX_DEPTH];
4074 int dist = 0;
4075
4076 zero[at1] = dist;
4077 scratch[si++] = at1;
4078 stk[stkDepth] = at1;
4079 stkDepth++;
4080
4081 while(stkDepth) { /* this will explore a tree */
4082 stkDepth--;
4083 a = stk[stkDepth];
4084 dist = zero[a] + 1;
4085
4086 s = obj->Neighbor[a]; /* add neighbors onto the stack */
4087 s++; /* skip count */
4088 while(1) {
4089 a1 = obj->Neighbor[s];
4090 if(a1 == at2) {
4091 while(si--) {
4092 zero[scratch[si]] = 0;
4093 }
4094 /* EXIT POINT 1 */
4095 return 1;
4096 }
4097 if(a1 >= 0) {
4098 if((!zero[a1]) && (stkDepth < MAX_DEPTH) && (dist < maxDist)) {
4099 zero[a1] = dist;
4100 scratch[si++] = a1;
4101 stk[stkDepth] = a1;
4102 stkDepth++;
4103 }
4104 } else
4105 break;
4106 s += 2;
4107 }
4108 }
4109 while(si--) {
4110 zero[scratch[si]] = 0;
4111 }
4112 /* EXIT POINT 2 */
4113 return 0;
4114 }
4115
4116
4117 /*========================================================================*/
4118 static
SelectorWalkTree(PyMOLGlobals * G,int * atom,int * comp,int * toDo,int ** stk,int stkDepth,ObjectMolecule * obj,int sele1,int sele2,int sele3,int sele4)4119 int SelectorWalkTree(PyMOLGlobals * G, int *atom, int *comp, int *toDo, int **stk,
4120 int stkDepth, ObjectMolecule * obj,
4121 int sele1, int sele2, int sele3, int sele4)
4122 {
4123 int s;
4124 int c = 0;
4125 int a, a1;
4126 int seleFlag;
4127 AtomInfoType *ai;
4128
4129 while(stkDepth) { /* this will explore a tree, stopping at protected atoms */
4130 stkDepth--;
4131 a = (*stk)[stkDepth];
4132 toDo[a] = 0;
4133 seleFlag = false;
4134 ai = obj->AtomInfo + a;
4135 s = ai->selEntry;
4136 seleFlag = SelectorIsMember(G, s, sele1);
4137 if(!seleFlag)
4138 seleFlag = SelectorIsMember(G, s, sele2);
4139 if(!seleFlag)
4140 seleFlag = SelectorIsMember(G, s, sele3);
4141 if(!seleFlag)
4142 seleFlag = SelectorIsMember(G, s, sele4);
4143 if(!seleFlag) {
4144 if(!(ai->protekted == 1)) { /* if not explicitly protected... */
4145 atom[a] = 1; /* mark this atom into the selection */
4146 comp[a] = 1;
4147 }
4148 s = obj->Neighbor[a]; /* add neighbors onto the stack */
4149 s++; /* skip count */
4150 while(1) {
4151 a1 = obj->Neighbor[s];
4152 if(a1 >= 0) {
4153 if(toDo[a1]) {
4154 VLACheck((*stk), int, stkDepth);
4155 (*stk)[stkDepth] = a1;
4156 stkDepth++;
4157 }
4158 } else
4159 break;
4160 s += 2;
4161 }
4162 c++;
4163 }
4164 }
4165 return (c);
4166 }
4167
4168
4169 /*========================================================================*/
SelectorWalkTreeDepth(PyMOLGlobals * G,int * atom,int * comp,int * toDo,int ** stk,int stkDepth,ObjectMolecule * obj,int sele1,int sele2,int sele3,int sele4,int ** extraStk,WalkDepthRec * wd)4170 static int SelectorWalkTreeDepth(PyMOLGlobals * G, int *atom, int *comp, int *toDo,
4171 int **stk, int stkDepth, ObjectMolecule * obj, int sele1,
4172 int sele2, int sele3, int sele4, int **extraStk,
4173 WalkDepthRec * wd)
4174 {
4175 int s;
4176 int c = 0;
4177 int a, a1;
4178 int seleFlag;
4179 int depth;
4180 AtomInfoType *ai;
4181
4182 wd->depth1 = -1;
4183 wd->depth2 = -1;
4184 wd->depth3 = -1;
4185 wd->depth4 = -1;
4186 VLACheck(*extraStk, int, stkDepth);
4187 UtilZeroMem(*extraStk, sizeof(int) * stkDepth);
4188
4189 while(stkDepth) { /* this will explore a tree, stopping at protected atoms */
4190 stkDepth--;
4191 a = (*stk)[stkDepth];
4192 depth = ((*extraStk)[stkDepth] + 1);
4193 seleFlag = false;
4194 ai = obj->AtomInfo + a;
4195 s = ai->selEntry;
4196
4197 /* record how many cycles it take to reach each & any picked atoms */
4198
4199 seleFlag = false;
4200 if(SelectorIsMember(G, s, sele1)) {
4201 if(((wd->depth1 < 0) || (wd->depth1 > depth))) {
4202 wd->depth1 = depth;
4203 }
4204 seleFlag = true;
4205 }
4206 if(SelectorIsMember(G, s, sele2)) {
4207 if(((wd->depth2 < 0) || (wd->depth2 > depth))) {
4208 wd->depth2 = depth;
4209 }
4210 seleFlag = true;
4211 }
4212 if(SelectorIsMember(G, s, sele3)) {
4213 if(((wd->depth3 < 0) || (wd->depth3 > depth))) {
4214 wd->depth3 = depth;
4215 }
4216 seleFlag = true;
4217 }
4218 if(SelectorIsMember(G, s, sele4)) {
4219 if(((wd->depth4 < 0) || (wd->depth4 > depth))) {
4220 wd->depth4 = depth;
4221 }
4222 seleFlag = true;
4223 }
4224
4225 if(!seleFlag) {
4226 toDo[a] = 0;
4227 if(!(ai->protekted == 1)) { /* if not explicitly protected... */
4228 atom[a] = 1; /* mark this atom into the selection */
4229 comp[a] = 1;
4230 }
4231 s = obj->Neighbor[a]; /* add neighbors onto the stack */
4232 s++; /* skip count */
4233 while(1) {
4234 a1 = obj->Neighbor[s];
4235 if(a1 >= 0) {
4236 if(toDo[a1]) {
4237 VLACheck((*stk), int, stkDepth);
4238 (*stk)[stkDepth] = a1;
4239 VLACheck((*extraStk), int, stkDepth);
4240 (*extraStk)[stkDepth] = depth;
4241 stkDepth++;
4242 }
4243 } else
4244 break;
4245 s += 2;
4246 }
4247 c++;
4248 }
4249 }
4250 return (c);
4251 }
4252
4253
4254 /*========================================================================*/
4255
SelectorIsAtomBondedToSele(PyMOLGlobals * G,ObjectMolecule * obj,int sele1atom,int sele2)4256 int SelectorIsAtomBondedToSele(PyMOLGlobals * G, ObjectMolecule * obj, int sele1atom,
4257 int sele2)
4258 {
4259 int a0, a2, s, ss;
4260 int bonded = false;
4261 ObjectMoleculeUpdateNeighbors(obj);
4262
4263 a0 = ObjectMoleculeGetAtomIndex(obj, sele1atom);
4264
4265 if(a0 >= 0) {
4266 s = obj->Neighbor[a0];
4267 s++; /* skip count */
4268 while(1) {
4269 a2 = obj->Neighbor[s];
4270 if(a2 < 0)
4271 break;
4272 ss = obj->AtomInfo[a2].selEntry;
4273 if(SelectorIsMember(G, ss, sele2)) {
4274 bonded = true;
4275 break;
4276 }
4277 s += 2;
4278 }
4279 }
4280 return bonded;
4281 }
4282
update_min_walk_depth(WalkDepthRec * minWD,int frag,WalkDepthRec * wd,int sele1,int sele2,int sele3,int sele4)4283 static void update_min_walk_depth(WalkDepthRec * minWD,
4284 int frag, WalkDepthRec * wd,
4285 int sele1, int sele2, int sele3, int sele4)
4286 {
4287 /* first, does this fragment even qualify ? */
4288 int qualifies = true;
4289 int cnt = 0;
4290 wd->sum = 0;
4291 if(sele1 >= 0) {
4292 if(wd->depth1 < 0) {
4293 qualifies = false;
4294 } else {
4295 wd->sum += wd->depth1;
4296 cnt++;
4297 }
4298 }
4299 if(sele2 >= 0) {
4300 if(wd->depth2 < 0) {
4301 qualifies = false;
4302 } else {
4303 wd->sum += wd->depth2;
4304 cnt++;
4305 }
4306 }
4307 if(sele3 >= 0) {
4308 if(wd->depth3 < 0) {
4309 qualifies = false;
4310 } else {
4311 wd->sum += wd->depth3;
4312 cnt++;
4313 }
4314 }
4315 if(sele4 >= 0) {
4316 if(wd->depth4 < 0) {
4317 qualifies = false;
4318 } else {
4319 wd->sum += wd->depth4;
4320 cnt++;
4321 }
4322 }
4323 if(qualifies && (cnt > 1)) {
4324
4325 /* is it better than the current min? */
4326
4327 if((!minWD->frag) || (wd->sum < minWD->sum)) {
4328 (*minWD) = (*wd);
4329 minWD->frag = frag;
4330 }
4331 }
4332 }
4333
4334
4335 /*========================================================================*/
SelectorSubdivide(PyMOLGlobals * G,const char * pref,SelectorID_t sele1,SelectorID_t sele2,SelectorID_t sele3,SelectorID_t sele4,const char * fragPref,const char * compName,int * bondMode)4336 int SelectorSubdivide(PyMOLGlobals* G, //
4337 const char* pref, //
4338 SelectorID_t sele1, //
4339 SelectorID_t sele2, //
4340 SelectorID_t sele3, //
4341 SelectorID_t sele4, //
4342 const char* fragPref, const char* compName, int* bondMode)
4343 {
4344 CSelector *I = G->Selector;
4345 int a0 = 0, a1 = 0, a2;
4346 int *atom = NULL;
4347 int *toDo = NULL;
4348 int *comp = NULL;
4349 int *pkset = NULL;
4350 int set_cnt = 0;
4351 int nFrag = 0;
4352 int *stk = NULL;
4353 int stkDepth;
4354 int c, s;
4355 int cycFlag = false;
4356 std::string name, link_sele;
4357 ObjectMolecule *obj1 = NULL, *obj2 = NULL, *obj3 = NULL, *obj4 = NULL;
4358 int index1 = 0, index2 = 0, index3 = 0, index4 = 0;
4359
4360 /* this is seriously getting out of hand -- need to switch over to arrays soon */
4361
4362 int *atom1_base = NULL, *atom2_base = NULL, *atom3_base = NULL, *atom4_base = NULL;
4363 int *toDo1_base = NULL, *toDo2_base = NULL, *toDo3_base = NULL, *toDo4_base = NULL;
4364 int *comp1_base = NULL, *comp2_base = NULL, *comp3_base = NULL, *comp4_base = NULL;
4365 int *pkset1_base = NULL, *pkset2_base = NULL, *pkset3_base = NULL, *pkset4_base = NULL;
4366
4367 PRINTFD(G, FB_Selector)
4368 " SelectorSubdivideObject: entered...\n" ENDFD;
4369 SelectorDeletePrefixSet(G, pref);
4370 SelectorDeletePrefixSet(G, fragPref);
4371 ExecutiveDelete(G, cEditorLink);
4372 ExecutiveDelete(G, cEditorSet);
4373 /* delete any existing matches */
4374
4375 obj1 = SelectorGetFastSingleAtomObjectIndex(G, sele1, &index1);
4376 obj2 = SelectorGetFastSingleAtomObjectIndex(G, sele2, &index2);
4377 obj3 = SelectorGetFastSingleAtomObjectIndex(G, sele3, &index3);
4378 obj4 = SelectorGetFastSingleAtomObjectIndex(G, sele4, &index4);
4379
4380 if(obj1 || obj2 || obj3 || obj4) {
4381
4382 if(obj1)
4383 ObjectMoleculeUpdateNeighbors(obj1);
4384 if(obj2)
4385 ObjectMoleculeUpdateNeighbors(obj2);
4386 if(obj3)
4387 ObjectMoleculeUpdateNeighbors(obj3);
4388 if(obj4)
4389 ObjectMoleculeUpdateNeighbors(obj4);
4390
4391 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
4392
4393 comp = pymol::calloc<int>(I->Table.size());
4394 atom = pymol::malloc<int>(I->Table.size());
4395 toDo = pymol::malloc<int>(I->Table.size());
4396 pkset = pymol::calloc<int>(I->Table.size());
4397
4398 /* NOTE: SeleBase only safe with cSelectorUpdateTableAllStates! */
4399
4400 if(obj1) {
4401 atom1_base = atom + obj1->SeleBase;
4402 toDo1_base = toDo + obj1->SeleBase;
4403 comp1_base = comp + obj1->SeleBase;
4404 pkset1_base = pkset + obj1->SeleBase;
4405 }
4406
4407 if(obj2) {
4408 atom2_base = atom + obj2->SeleBase;
4409 toDo2_base = toDo + obj2->SeleBase;
4410 comp2_base = comp + obj2->SeleBase;
4411 pkset2_base = pkset + obj2->SeleBase;
4412 }
4413
4414 if(obj3) {
4415 atom3_base = atom + obj3->SeleBase;
4416 toDo3_base = toDo + obj3->SeleBase;
4417 comp3_base = comp + obj3->SeleBase;
4418 pkset3_base = pkset + obj3->SeleBase;
4419 }
4420
4421 if(obj4) {
4422 atom4_base = atom + obj4->SeleBase;
4423 toDo4_base = toDo + obj4->SeleBase;
4424 comp4_base = comp + obj4->SeleBase;
4425 pkset4_base = pkset + obj4->SeleBase;
4426 }
4427
4428 stk = VLAlloc(int, 100);
4429
4430 {
4431 int a;
4432 int *p1;
4433 p1 = toDo;
4434 for(a = 0; a < I->Table.size(); a++)
4435 *(p1++) = true;
4436 }
4437
4438 if(*bondMode) {
4439 /* verify bond mode, or clear the flag */
4440
4441 *bondMode = false;
4442
4443 if((sele1 >= 0) && (sele2 >= 0) && (sele3 < 0) && (sele4 < 0) && (obj1 == obj2)) {
4444 /* two selections only, in same object... */
4445
4446 a0 = index1;
4447 a1 = index2;
4448
4449 if((a0 >= 0) && (a1 >= 0)) {
4450 s = obj1->Neighbor[a0]; /* add neighbors onto the stack */
4451 s++; /* skip count */
4452 while(1) {
4453 a2 = obj1->Neighbor[s];
4454 if(a2 < 0)
4455 break;
4456 if(a2 == a1) {
4457 *bondMode = true;
4458 break;
4459 }
4460 s += 2;
4461 }
4462 }
4463 }
4464 }
4465
4466 /* ===== BOND MODE ===== (sele0 and sele1 only) */
4467
4468 if(*bondMode) {
4469 if(obj1 == obj2) { /* just to be safe */
4470
4471 pkset1_base[a0] = 1;
4472 pkset1_base[a1] = 1;
4473 SelectorEmbedSelection(G, pkset, cEditorBond, NULL, false, -1);
4474
4475 a0 = index1;
4476 if(a0 >= 0) {
4477 stkDepth = 0;
4478 s = obj1->Neighbor[a0]; /* add neighbors onto the stack */
4479 s++; /* skip count */
4480 while(1) {
4481 a1 = obj1->Neighbor[s];
4482 if(a1 >= 0) {
4483 if(toDo1_base[a1]) {
4484 VLACheck(stk, int, stkDepth);
4485 stk[stkDepth] = a1;
4486 stkDepth++;
4487 }
4488 } else
4489 break;
4490 s += 2;
4491 }
4492 UtilZeroMem(atom, sizeof(int) * I->Table.size());
4493 atom1_base[a0] = 1; /* create selection for this atom alone as fragment base atom */
4494 comp1_base[a0] = 1;
4495 name = pymol::string_format("%s%1d", fragPref, nFrag + 1);
4496 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4497 c =
4498 SelectorWalkTree(G, atom1_base, comp1_base, toDo1_base, &stk, stkDepth, obj1,
4499 sele1, sele2, -1, -1) + 1;
4500 name = pymol::string_format("%s%1d", pref, nFrag + 1);
4501
4502 /* check for cyclic situation */
4503 cycFlag = false;
4504 a2 = index2;
4505 if(a2 >= 0) {
4506 stkDepth = 0;
4507 s = obj1->Neighbor[a2]; /* add neighbors onto the stack */
4508 s++; /* skip count */
4509 while(1) {
4510 a1 = obj1->Neighbor[s];
4511 if(a1 < 0)
4512 break;
4513 if((a1 >= 0) && (a1 != a0)) {
4514 if(!toDo1_base[a1]) {
4515 cycFlag = true; /* we have a cycle... */
4516 break;
4517 }
4518 }
4519 s += 2;
4520 }
4521 }
4522 if(cycFlag) { /* cyclic situation is a bit complex... */
4523
4524 a0 = index2;
4525 if(a0 >= 0) {
4526 stkDepth = 0;
4527 s = obj1->Neighbor[a0]; /* add neighbors onto the stack */
4528 s++; /* skip count */
4529 while(1) {
4530 a1 = obj1->Neighbor[s];
4531 if(a1 >= 0) {
4532 if(toDo1_base[a1]) {
4533 VLACheck(stk, int, stkDepth);
4534 stk[stkDepth] = a1;
4535 stkDepth++;
4536 }
4537 } else
4538 break;
4539 s += 2;
4540 }
4541 atom1_base[a0] = 1;
4542 comp1_base[a0] = 1;
4543 c =
4544 SelectorWalkTree(G, atom1_base, comp1_base, toDo1_base, &stk, stkDepth,
4545 obj1, sele1, sele2, -1, -1) + 1;
4546 }
4547 }
4548 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4549 nFrag++;
4550 }
4551
4552 if(!cycFlag) {
4553 a0 = index2;
4554 if(a0 >= 0) {
4555 stkDepth = 0;
4556 s = obj1->Neighbor[a0]; /* add neighbors onto the stack */
4557 s++; /* skip count */
4558 while(1) {
4559 a1 = obj1->Neighbor[s];
4560 if(a1 >= 0) {
4561 if(toDo1_base[a1]) {
4562 VLACheck(stk, int, stkDepth);
4563 stk[stkDepth] = a1;
4564 stkDepth++;
4565 }
4566 } else
4567 break;
4568 s += 2;
4569 }
4570
4571 UtilZeroMem(atom, sizeof(int) * I->Table.size());
4572 atom1_base[a0] = 1; /* create selection for this atom alone as fragment base atom */
4573 comp1_base[a0] = 1;
4574 name = pymol::string_format("%s%1d", fragPref, nFrag + 1);
4575 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4576 c =
4577 SelectorWalkTree(G, atom1_base, comp1_base, toDo1_base, &stk, stkDepth,
4578 obj1, sele1, sele2, -1, -1) + 1;
4579 name = pymol::string_format("%s%1d", pref, nFrag + 1);
4580 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4581 nFrag++;
4582 }
4583 }
4584 }
4585 } else {
4586 /* ===== WALK MODE ===== (any combination of sele0, sele1, sele2, sele3 */
4587
4588 int *extraStk = VLAlloc(int, 50);
4589 WalkDepthRec curWalk, minWalk;
4590 minWalk.sum = 0;
4591 minWalk.frag = 0;
4592
4593 if(obj1) {
4594 a0 = index1;
4595 if(a0 >= 0) {
4596 pkset1_base[a0] = 1;
4597 set_cnt++;
4598 comp1_base[a0] = 1;
4599 stkDepth = 0;
4600 s = obj1->Neighbor[a0]; /* add neighbors onto the stack */
4601 s++; /* skip count */
4602 while(1) {
4603 a1 = obj1->Neighbor[s];
4604 if(a1 < 0)
4605 break;
4606 if(toDo1_base[a1]) {
4607 stkDepth = 1;
4608 stk[0] = a1;
4609 UtilZeroMem(atom, sizeof(int) * I->Table.size());
4610 atom1_base[a1] = 1; /* create selection for this atom alone as fragment base atom */
4611 comp1_base[a1] = 1;
4612 name = pymol::string_format("%s%1d", fragPref, nFrag + 1);
4613 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4614 atom1_base[a1] = 0;
4615 c = SelectorWalkTreeDepth(G, atom1_base, comp1_base, toDo1_base, &stk,
4616 stkDepth, obj1, sele1, sele2, sele3, sele4,
4617 &extraStk, &curWalk);
4618 if(c) {
4619 nFrag++;
4620 name = pymol::string_format("%s%1d", pref, nFrag);
4621 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4622 update_min_walk_depth(&minWalk,
4623 nFrag, &curWalk, sele1, sele2, sele3, sele4);
4624 }
4625 }
4626 s += 2;
4627 }
4628 }
4629 }
4630
4631 if(obj2) {
4632 a0 = index2;
4633 if(a0 >= 0) {
4634 pkset2_base[a0] = 1;
4635 set_cnt++;
4636 comp2_base[a0] = 1;
4637 stkDepth = 0;
4638 s = obj2->Neighbor[a0]; /* add neighbors onto the stack */
4639 s++; /* skip count */
4640 while(1) {
4641 a1 = obj2->Neighbor[s];
4642 if(a1 < 0)
4643 break;
4644 if(toDo2_base[a1]) {
4645 stkDepth = 1;
4646 stk[0] = a1;
4647 UtilZeroMem(atom, sizeof(int) * I->Table.size());
4648 atom2_base[a1] = 1; /* create selection for this atom alone as fragment base atom */
4649 comp2_base[a1] = 1;
4650 name = pymol::string_format("%s%1d", fragPref, nFrag + 1);
4651 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4652 atom2_base[a1] = 0;
4653 c = SelectorWalkTreeDepth(G, atom2_base, comp2_base, toDo2_base, &stk,
4654 stkDepth, obj2, sele1, sele2, sele3, sele4,
4655 &extraStk, &curWalk);
4656 if(c) {
4657 nFrag++;
4658 name = pymol::string_format("%s%1d", pref, nFrag);
4659 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4660 update_min_walk_depth(&minWalk,
4661 nFrag, &curWalk, sele1, sele2, sele3, sele4);
4662 }
4663 }
4664 s += 2;
4665 }
4666 }
4667 }
4668
4669 if(obj3) {
4670 a0 = index3;
4671 if(a0 >= 0) {
4672 pkset3_base[a0] = 1;
4673 set_cnt++;
4674 comp3_base[a0] = 1;
4675 stkDepth = 0;
4676 s = obj3->Neighbor[a0]; /* add neighbors onto the stack */
4677 s++; /* skip count */
4678 while(1) {
4679 a1 = obj3->Neighbor[s];
4680 if(a1 < 0)
4681 break;
4682 if(toDo3_base[a1]) {
4683 stkDepth = 1;
4684 stk[0] = a1;
4685 UtilZeroMem(atom, sizeof(int) * I->Table.size());
4686 atom3_base[a1] = 1; /* create selection for this atom alone as fragment base atom */
4687 comp3_base[a1] = 1;
4688 name = pymol::string_format("%s%1d", fragPref, nFrag + 1);
4689 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4690 atom3_base[a1] = 0;
4691 c = SelectorWalkTreeDepth(G, atom3_base, comp3_base, toDo3_base, &stk,
4692 stkDepth, obj3, sele1, sele2, sele3, sele4,
4693 &extraStk, &curWalk);
4694 if(c) {
4695 nFrag++;
4696 name = pymol::string_format("%s%1d", pref, nFrag);
4697 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4698 update_min_walk_depth(&minWalk,
4699 nFrag, &curWalk, sele1, sele2, sele3, sele4);
4700
4701 }
4702 }
4703 s += 2;
4704 }
4705 }
4706 }
4707
4708 if(obj4) {
4709 a0 = index4;
4710 if(a0 >= 0) {
4711 pkset4_base[a0] = 1;
4712 set_cnt++;
4713 comp4_base[a0] = 1;
4714 stkDepth = 0;
4715 s = obj4->Neighbor[a0]; /* add neighbors onto the stack */
4716 s++; /* skip count */
4717 while(1) {
4718 a1 = obj4->Neighbor[s];
4719 if(a1 < 0)
4720 break;
4721 if(toDo4_base[a1]) {
4722 stkDepth = 1;
4723 stk[0] = a1;
4724 UtilZeroMem(atom, sizeof(int) * I->Table.size());
4725 atom4_base[a1] = 1; /* create selection for this atom alone as fragment base atom */
4726 comp4_base[a1] = 1;
4727 name = pymol::string_format("%s%1d", fragPref, nFrag + 1);
4728 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4729 atom4_base[a1] = 0;
4730 c = SelectorWalkTreeDepth(G, atom4_base, comp4_base, toDo4_base, &stk,
4731 stkDepth, obj4, sele1, sele2, sele3, sele4,
4732 &extraStk, &curWalk);
4733 if(c) {
4734 nFrag++;
4735 name = pymol::string_format("%s%1d", pref, nFrag);
4736 SelectorEmbedSelection(G, atom, name, NULL, false, -1);
4737 update_min_walk_depth(&minWalk,
4738 nFrag, &curWalk, sele1, sele2, sele3, sele4);
4739 }
4740 }
4741 s += 2;
4742 }
4743 }
4744 }
4745
4746 if(minWalk.frag) { /* create the linking selection if one exists */
4747 link_sele = pymol::string_format("%s%d|?pk1|?pk2|?pk3|?pk4", pref, minWalk.frag);
4748 }
4749 VLAFreeP(extraStk);
4750 }
4751
4752 if(set_cnt > 1) {
4753 SelectorEmbedSelection(G, pkset, cEditorSet, NULL, false, -1);
4754 }
4755
4756 if(nFrag) {
4757 SelectorEmbedSelection(G, comp, compName, NULL, false, -1);
4758 }
4759
4760 if(!link_sele.empty())
4761 SelectorCreate(G, cEditorLink, link_sele.c_str(), NULL, true, NULL);
4762
4763 FreeP(toDo);
4764 FreeP(atom);
4765 FreeP(comp);
4766 FreeP(pkset);
4767 VLAFreeP(stk);
4768 SelectorClean(G);
4769 }
4770 PRINTFD(G, FB_Selector)
4771 " SelectorSubdivideObject: leaving...nFrag %d\n", nFrag ENDFD;
4772
4773 return (nFrag);
4774 }
4775
4776
4777 /*========================================================================*/
SelectorGetSeleNCSet(PyMOLGlobals * G,SelectorID_t sele)4778 int SelectorGetSeleNCSet(PyMOLGlobals * G, SelectorID_t sele)
4779 {
4780 CSelector *I = G->Selector;
4781
4782 int a, s, at = 0;
4783 ObjectMolecule *obj, *last_obj = NULL;
4784 int result = 0;
4785
4786 if((obj = SelectorGetFastSingleAtomObjectIndex(G, sele, &at))) {
4787 int a = obj->NCSet;
4788 CoordSet *cs;
4789 int idx;
4790
4791 while(a--) {
4792 cs = obj->CSet[a];
4793 idx = cs->atmToIdx(at);
4794 if(idx >= 0) {
4795 result = a + 1;
4796 break;
4797 }
4798 }
4799 } else {
4800 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
4801 obj = I->Obj[I->Table[a].model];
4802 if(obj != last_obj) {
4803 at = I->Table[a].atom;
4804 s = obj->AtomInfo[at].selEntry;
4805 if(SelectorIsMember(G, s, sele)) {
4806 if(result < obj->NCSet) {
4807 result = obj->NCSet;
4808 last_obj = obj;
4809 }
4810 }
4811 }
4812 }
4813 }
4814 return (result);
4815 }
4816
4817
4818 /*========================================================================*/
SelectorGetArrayNCSet(PyMOLGlobals * G,const sele_array_t & uptr,int no_dummies)4819 static int SelectorGetArrayNCSet(
4820 PyMOLGlobals* G, const sele_array_t& uptr, int no_dummies)
4821 {
4822 const int* array = uptr.get();
4823 CSelector *I = G->Selector;
4824 int a;
4825 ObjectMolecule *obj;
4826 int result = 0;
4827 int start = 0;
4828 if(no_dummies)
4829 start = cNDummyAtoms;
4830 for(a = start; a < I->Table.size(); a++) {
4831 if(*(array++)) {
4832 if(a >= cNDummyAtoms) {
4833 obj = I->Obj[I->Table[a].model];
4834 if(result < obj->NCSet)
4835 result = obj->NCSet;
4836 } else {
4837 if(result < 1)
4838 result = 1; /* selected dummy has at least one CSet */
4839 }
4840
4841 }
4842 }
4843 return (result);
4844 }
4845
4846
4847 /*========================================================================*/
SelectorSumVDWOverlap(PyMOLGlobals * G,int sele1,int state1,int sele2,int state2,float adjust)4848 float SelectorSumVDWOverlap(PyMOLGlobals * G, int sele1, int state1, int sele2,
4849 int state2, float adjust)
4850 {
4851 CSelector *I = G->Selector;
4852 float result = 0.0;
4853 float sumVDW = 0.0, dist;
4854 int a1, a2;
4855 AtomInfoType *ai1, *ai2;
4856 int at1, at2;
4857 CoordSet *cs1, *cs2;
4858 ObjectMolecule *obj1, *obj2;
4859 int idx1, idx2;
4860 int a;
4861
4862 if(state1 < 0)
4863 state1 = 0;
4864 if(state2 < 0)
4865 state2 = 0;
4866
4867 if(state1 != state2) {
4868 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
4869 } else {
4870 SelectorUpdateTable(G, state1, -1);
4871 }
4872
4873 auto vla = SelectorGetInterstateVLA(
4874 G, sele1, state1, sele2, state2, 2 * MAX_VDW + adjust);
4875 const int c = vla.size() / 2;
4876
4877 for(a = 0; a < c; a++) {
4878 a1 = vla[a * 2];
4879 a2 = vla[a * 2 + 1];
4880
4881 at1 = I->Table[a1].atom;
4882 at2 = I->Table[a2].atom;
4883
4884 obj1 = I->Obj[I->Table[a1].model];
4885 obj2 = I->Obj[I->Table[a2].model];
4886
4887 if((state1 < obj1->NCSet) && (state2 < obj2->NCSet)) {
4888 cs1 = obj1->CSet[state1];
4889 cs2 = obj2->CSet[state2];
4890 if(cs1 && cs2) { /* should always be true */
4891
4892 ai1 = obj1->AtomInfo + at1;
4893 ai2 = obj2->AtomInfo + at2;
4894
4895 idx1 = cs1->AtmToIdx[at1]; /* these are also pre-validated */
4896 idx2 = cs2->AtmToIdx[at2];
4897
4898 sumVDW = ai1->vdw + ai2->vdw + adjust;
4899 dist = (float) diff3f(cs1->coordPtr(idx1), cs2->coordPtr(idx2));
4900
4901 if(dist < sumVDW) {
4902 result += ((sumVDW - dist) / 2.0F);
4903 }
4904 }
4905 }
4906 }
4907 return (result);
4908 }
4909
4910 /*========================================================================*/
4911 /**
4912 * Find all pairs between `sele1` and `sele2` which are within a distance cutoff.
4913 *
4914 * @param cutoff Distance cutoff
4915 * @return List of selector table index pairs
4916 */
SelectorGetInterstateVLA(PyMOLGlobals * G,int sele1,int state1,int sele2,int state2,float cutoff)4917 std::vector<int> SelectorGetInterstateVLA(
4918 PyMOLGlobals* G, int sele1, int state1, int sele2, int state2, float cutoff)
4919 { /* Assumes valid tables */
4920 const size_t table_size = G->Selector->Table.size();
4921 auto coords_flat = std::vector<float>(3 * table_size);
4922 auto* coords = pymol::reshape<3>(coords_flat.data());
4923
4924 // number of atoms in `sele1`
4925 int n1 = 0;
4926
4927 // mask on selected atoms in `sele1`
4928 auto flags = std::vector<MapFlag_t>(table_size);
4929
4930 // copy coordinates of selection 1
4931 for (SeleCoordIterator iter(G, sele1, state1, false); iter.next();) {
4932 copy3(iter.getCoord(), coords[iter.a]);
4933 flags[iter.a] = true;
4934 n1++;
4935 }
4936
4937 if (n1 == 0) {
4938 // no atoms in `sele1`
4939 return {};
4940 }
4941
4942 std::unique_ptr<MapType> map(MapNewFlagged(
4943 G, -cutoff, pymol::flatten(coords), table_size, nullptr, flags.data()));
4944
4945 if (!map) {
4946 PRINTFB(G, FB_Selector, FB_Errors)
4947 " Selector-Error: unexpected map allocation failure\n" ENDFB(G);
4948 return {};
4949 }
4950
4951 std::vector<int> out;
4952
4953 for (SeleCoordIterator iter(G, sele2, state2, false); iter.next();) {
4954 const float* v2 = iter.getCoord();
4955 for (const auto a1 : MapEIter(*map, v2)) {
4956 if (within3f(coords[a1], v2, cutoff)) {
4957 out.push_back(a1);
4958 out.push_back(iter.a);
4959 }
4960 }
4961 }
4962
4963 return out;
4964 }
4965
4966
4967 /*========================================================================*/
SelectorMapMaskVDW(PyMOLGlobals * G,int sele1,ObjectMapState * oMap,float buffer,int state)4968 int SelectorMapMaskVDW(PyMOLGlobals * G, int sele1, ObjectMapState * oMap, float buffer,
4969 int state)
4970 {
4971 CSelector *I = G->Selector;
4972 float *v2;
4973 int n1;
4974 int a, b, c;
4975 int at;
4976 int s;
4977 ObjectMolecule *obj;
4978 CoordSet *cs;
4979 int state1, state2;
4980 int once_flag;
4981
4982 c = 0;
4983 n1 = 0;
4984 SelectorUpdateTable(G, state, -1);
4985
4986 const size_t table_size = I->Table.size();
4987 auto coords_flat = std::vector<float>(table_size * 3);
4988 auto* coords = pymol::reshape<3>(coords_flat.data());
4989 auto Flag1 = std::vector<MapFlag_t>(table_size, 0);
4990
4991 for(a = 0; a < I->Table.size(); a++) {
4992 at = I->Table[a].atom;
4993 obj = I->Obj[I->Table[a].model];
4994 s = obj->AtomInfo[at].selEntry;
4995 if(SelectorIsMember(G, s, sele1)) {
4996 once_flag = true;
4997 for(state2 = 0; state2 < obj->NCSet; state2++) {
4998 if(state < 0)
4999 once_flag = false;
5000 if(!once_flag)
5001 state1 = state2;
5002 else
5003 state1 = state;
5004 if(state1 < obj->NCSet)
5005 cs = obj->CSet[state1];
5006 else
5007 cs = NULL;
5008 if(cs) {
5009 if(CoordSetGetAtomVertex(cs, at, coords[a])) {
5010 Flag1[a] = true;
5011 n1++;
5012 }
5013 }
5014 if(once_flag)
5015 break;
5016 }
5017 }
5018 }
5019 /* now create and apply voxel map */
5020 c = 0;
5021 if(n1) {
5022 std::unique_ptr<MapType> map(MapNewFlagged(G, -(buffer + MAX_VDW),
5023 pymol::flatten(coords), table_size, nullptr, Flag1.data()));
5024 if(map) {
5025 for(a = oMap->Min[0]; a <= oMap->Max[0]; a++) {
5026 for(b = oMap->Min[1]; b <= oMap->Max[1]; b++) {
5027 for(c = oMap->Min[2]; c <= oMap->Max[2]; c++) {
5028 F3(oMap->Field->data, a, b, c) = 0.0;
5029
5030 v2 = F4Ptr(oMap->Field->points, a, b, c, 0);
5031
5032 for (const auto j : MapEIter(*map, v2)) {
5033 const auto* ai =
5034 I->Obj[I->Table[j].model]->AtomInfo + I->Table[j].atom;
5035 if (within3f(coords[j], v2, ai->vdw + buffer)) {
5036 F3(oMap->Field->data, a, b, c) = 1.0;
5037 }
5038 }
5039 }
5040 }
5041 }
5042 oMap->Active = true;
5043 }
5044 }
5045 return (c);
5046 }
5047
max2d(double a,double b)5048 static double max2d(double a, double b)
5049 {
5050 if(a > b)
5051 return a;
5052 else
5053 return b;
5054 }
5055
max6d(double a,double b,double c,double d,double e,double f)5056 static double max6d(double a, double b, double c, double d, double e, double f)
5057 {
5058
5059 if(d > a)
5060 a = d;
5061 if(e > b)
5062 b = e;
5063 if(f > c)
5064 c = f;
5065 if(b > a)
5066 a = b;
5067 if(c > a)
5068 a = c;
5069 return (a);
5070 }
5071
5072 #define D_SMALL10 1e-10
5073
5074 typedef double AtomSF[11];
5075
5076
5077 /*========================================================================*/
SelectorMapGaussian(PyMOLGlobals * G,int sele1,ObjectMapState * oMap,float buffer,int state,int normalize,int use_max,int quiet,float resolution)5078 int SelectorMapGaussian(PyMOLGlobals * G, int sele1, ObjectMapState * oMap,
5079 float buffer, int state, int normalize, int use_max, int quiet,
5080 float resolution)
5081 {
5082 CSelector *I = G->Selector;
5083 float *v2;
5084 int n1, n2;
5085 int a, b, c;
5086 int at;
5087 int s, idx;
5088 AtomInfoType *ai;
5089 ObjectMolecule *obj;
5090 CoordSet *cs;
5091 int state1, state2;
5092 float *point = NULL, *fp;
5093 int *sfidx = NULL, *ip;
5094 float *b_factor = NULL, *bf, bfact;
5095 float *occup = NULL, *oc;
5096 int prot;
5097 int once_flag;
5098 float d, e_val;
5099 double sum, sumsq;
5100 float mean, stdev;
5101 double sf[256][11], *sfp;
5102 AtomSF *atom_sf = NULL;
5103 double b_adjust = (double) SettingGetGlobal_f(G, cSetting_gaussian_b_adjust);
5104 double elim = 7.0;
5105 double rcut2;
5106 float rcut;
5107 float max_rcut = 0.0F;
5108 float b_floor = SettingGetGlobal_f(G, cSetting_gaussian_b_floor);
5109 float blur_factor = 1.0F;
5110
5111 {
5112 if(resolution < R_SMALL4)
5113 resolution = SettingGetGlobal_f(G, cSetting_gaussian_resolution);
5114 if(resolution < 1.0 )
5115 resolution = 1.0F;
5116 blur_factor = 2.0F / resolution;
5117 /* a gaussian_resolution of 2.0 is considered perfect ? Hmm...where is this from??? */
5118
5119 }
5120
5121 if(b_adjust > 500.0)
5122 b_adjust = 500.0; /* constrain to be somewhat reasonable */
5123
5124 for(a = 0; a < 256; a++) {
5125 sf[a][0] = -1.0;
5126 }
5127
5128 sf[cAN_H][0] = 0.493002;
5129 sf[cAN_H][1] = 10.510900;
5130 sf[cAN_H][2] = 0.322912;
5131 sf[cAN_H][3] = 26.125700;
5132 sf[cAN_H][4] = 0.140191;
5133 sf[cAN_H][5] = 3.142360;
5134 sf[cAN_H][6] = 0.040810;
5135 sf[cAN_H][7] = 57.799698;
5136 sf[cAN_H][8] = 0.003038;
5137 sf[cAN_H][9] = 0.0;
5138
5139 /* LP currently using scattering factors of carbon
5140 (Roche Pocket viewer relies upon this behavior) */
5141
5142 sf[cAN_LP][0] = 2.310000;
5143 sf[cAN_LP][1] = 20.843899;
5144 sf[cAN_LP][2] = 1.020000;
5145 sf[cAN_LP][3] = 10.207500;
5146 sf[cAN_LP][4] = 1.588600;
5147 sf[cAN_LP][5] = 0.568700;
5148 sf[cAN_LP][6] = 0.865000;
5149 sf[cAN_LP][7] = 51.651199;
5150 sf[cAN_LP][8] = 0.215600;
5151 sf[cAN_LP][9] = 0.0;
5152
5153 sf[cAN_C][0] = 2.310000;
5154 sf[cAN_C][1] = 20.843899;
5155 sf[cAN_C][2] = 1.020000;
5156 sf[cAN_C][3] = 10.207500;
5157 sf[cAN_C][4] = 1.588600;
5158 sf[cAN_C][5] = 0.568700;
5159 sf[cAN_C][6] = 0.865000;
5160 sf[cAN_C][7] = 51.651199;
5161 sf[cAN_C][8] = 0.215600;
5162 sf[cAN_C][9] = 0.0;
5163
5164 sf[cAN_O][0] = 3.048500;
5165 sf[cAN_O][1] = 13.277100;
5166 sf[cAN_O][2] = 2.286800;
5167 sf[cAN_O][3] = 5.701100;
5168 sf[cAN_O][4] = 1.546300;
5169 sf[cAN_O][5] = 0.323900;
5170 sf[cAN_O][6] = 0.867000;
5171 sf[cAN_O][7] = 32.908897;
5172 sf[cAN_O][8] = 0.250800;
5173 sf[cAN_O][9] = 0.0;
5174
5175 sf[cAN_N][0] = 12.212600;
5176 sf[cAN_N][1] = 0.005700;
5177 sf[cAN_N][2] = 3.132200;
5178 sf[cAN_N][3] = 9.893300;
5179 sf[cAN_N][4] = 2.012500;
5180 sf[cAN_N][5] = 28.997499;
5181 sf[cAN_N][6] = 1.166300;
5182 sf[cAN_N][7] = 0.582600;
5183 sf[cAN_N][8] = -11.528999;
5184 sf[cAN_N][9] = 0.0;
5185
5186 sf[cAN_S][0] = 6.905300;
5187 sf[cAN_S][1] = 1.467900;
5188 sf[cAN_S][2] = 5.203400;
5189 sf[cAN_S][3] = 22.215099;
5190 sf[cAN_S][4] = 1.437900;
5191 sf[cAN_S][5] = 0.253600;
5192 sf[cAN_S][6] = 1.586300;
5193 sf[cAN_S][7] = 56.172001;
5194 sf[cAN_S][8] = 0.866900;
5195 sf[cAN_S][9] = 0.0;
5196
5197 sf[cAN_Cl][0] = 11.460400;
5198 sf[cAN_Cl][1] = 0.010400;
5199 sf[cAN_Cl][2] = 7.196400;
5200 sf[cAN_Cl][3] = 1.166200;
5201 sf[cAN_Cl][4] = 6.255600;
5202 sf[cAN_Cl][5] = 18.519400;
5203 sf[cAN_Cl][6] = 1.645500;
5204 sf[cAN_Cl][7] = 47.778400;
5205 sf[cAN_Cl][8] = 0.866900;
5206 sf[cAN_Cl][9] = 0.0;
5207
5208 sf[cAN_Br][0] = 17.178900;
5209 sf[cAN_Br][1] = 2.172300;
5210 sf[cAN_Br][2] = 5.235800;
5211 sf[cAN_Br][3] = 16.579599;
5212 sf[cAN_Br][4] = 5.637700;
5213 sf[cAN_Br][5] = 0.260900;
5214 sf[cAN_Br][6] = 3.985100;
5215 sf[cAN_Br][7] = 41.432800;
5216 sf[cAN_Br][8] = 2.955700;
5217 sf[cAN_Br][9] = 0.0;
5218
5219 sf[cAN_I][0] = 20.147200;
5220 sf[cAN_I][1] = 4.347000;
5221 sf[cAN_I][2] = 18.994900;
5222 sf[cAN_I][3] = 0.381400;
5223 sf[cAN_I][4] = 7.513800;
5224 sf[cAN_I][5] = 27.765999;
5225 sf[cAN_I][6] = 2.273500;
5226 sf[cAN_I][7] = 66.877602;
5227 sf[cAN_I][8] = 4.071200;
5228 sf[cAN_I][9] = 0.0;
5229
5230 sf[cAN_F][0] = 3.539200;
5231 sf[cAN_F][1] = 10.282499;
5232 sf[cAN_F][2] = 2.641200;
5233 sf[cAN_F][3] = 4.294400;
5234 sf[cAN_F][4] = 1.517000;
5235 sf[cAN_F][5] = 0.261500;
5236 sf[cAN_F][6] = 1.024300;
5237 sf[cAN_F][7] = 26.147600;
5238 sf[cAN_F][8] = 0.277600;
5239 sf[cAN_F][9] = 0.0;
5240
5241 sf[cAN_K][0] = 8.218599;
5242 sf[cAN_K][1] = 12.794900;
5243 sf[cAN_K][2] = 7.439800;
5244 sf[cAN_K][3] = 0.774800;
5245 sf[cAN_K][4] = 1.051900;
5246 sf[cAN_K][5] = 213.186996;
5247 sf[cAN_K][6] = 0.865900;
5248 sf[cAN_K][7] = 41.684097;
5249 sf[cAN_K][8] = 1.422800;
5250 sf[cAN_K][9] = 0.0;
5251
5252 sf[cAN_Mg][0] = 5.420400;
5253 sf[cAN_Mg][1] = 2.827500;
5254 sf[cAN_Mg][2] = 2.173500;
5255 sf[cAN_Mg][3] = 79.261101;
5256 sf[cAN_Mg][4] = 1.226900;
5257 sf[cAN_Mg][5] = 0.380800;
5258 sf[cAN_Mg][6] = 2.307300;
5259 sf[cAN_Mg][7] = 7.193700;
5260 sf[cAN_Mg][8] = 0.858400;
5261 sf[cAN_Mg][9] = 0.0;
5262
5263 sf[cAN_Na][0] = 4.762600;
5264 sf[cAN_Na][1] = 3.285000;
5265 sf[cAN_Na][2] = 3.173600;
5266 sf[cAN_Na][3] = 8.842199;
5267 sf[cAN_Na][4] = 1.267400;
5268 sf[cAN_Na][5] = 0.313600;
5269 sf[cAN_Na][6] = 1.112800;
5270 sf[cAN_Na][7] = 129.423996;
5271 sf[cAN_Na][8] = 0.676000;
5272 sf[cAN_Na][9] = 0.0;
5273
5274 sf[cAN_P][0] = 6.434500;
5275 sf[cAN_P][1] = 1.906700;
5276 sf[cAN_P][2] = 4.179100;
5277 sf[cAN_P][3] = 27.157000;
5278 sf[cAN_P][4] = 1.780000;
5279 sf[cAN_P][5] = 0.526000;
5280 sf[cAN_P][6] = 1.490800;
5281 sf[cAN_P][7] = 68.164497;
5282 sf[cAN_P][8] = 1.114900;
5283 sf[cAN_P][9] = 0.0;
5284
5285 sf[cAN_Zn][0] = 14.074300;
5286 sf[cAN_Zn][1] = 3.265500;
5287 sf[cAN_Zn][2] = 7.031800;
5288 sf[cAN_Zn][3] = 0.233300;
5289 sf[cAN_Zn][4] = 5.162500;
5290 sf[cAN_Zn][5] = 10.316299;
5291 sf[cAN_Zn][6] = 2.410000;
5292 sf[cAN_Zn][7] = 58.709702;
5293 sf[cAN_Zn][8] = 1.304100;
5294 sf[cAN_Zn][9] = 0.0;
5295
5296 sf[cAN_Ca][0] = 8.626600;
5297 sf[cAN_Ca][1] = 10.442100;
5298 sf[cAN_Ca][2] = 7.387300;
5299 sf[cAN_Ca][3] = 0.659900;
5300 sf[cAN_Ca][4] = 1.589900;
5301 sf[cAN_Ca][5] = 85.748398;
5302 sf[cAN_Ca][6] = 1.021100;
5303 sf[cAN_Ca][7] = 178.436996;
5304 sf[cAN_Ca][8] = 1.375100;
5305 sf[cAN_Ca][9] = 0.0;
5306
5307 sf[cAN_Cu][0] = 13.337999;
5308 sf[cAN_Cu][1] = 3.582800;
5309 sf[cAN_Cu][2] = 7.167600;
5310 sf[cAN_Cu][3] = 0.247000;
5311 sf[cAN_Cu][4] = 5.615800;
5312 sf[cAN_Cu][5] = 11.396600;
5313 sf[cAN_Cu][6] = 1.673500;
5314 sf[cAN_Cu][7] = 64.812599;
5315 sf[cAN_Cu][8] = 1.191000;
5316 sf[cAN_Cu][9] = 0.0;
5317
5318 sf[cAN_Fe][0] = 11.769500;
5319 sf[cAN_Fe][1] = 4.761100;
5320 sf[cAN_Fe][2] = 7.357300;
5321 sf[cAN_Fe][3] = 0.307200;
5322 sf[cAN_Fe][4] = 3.522200;
5323 sf[cAN_Fe][5] = 15.353500;
5324 sf[cAN_Fe][6] = 2.304500;
5325 sf[cAN_Fe][7] = 76.880501;
5326 sf[cAN_Fe][8] = 1.036900;
5327 sf[cAN_Fe][9] = 0.0;
5328
5329 sf[cAN_Se][0] = 17.000599;
5330 sf[cAN_Se][1] = 2.409800;
5331 sf[cAN_Se][2] = 5.819600;
5332 sf[cAN_Se][3] = 0.272600;
5333 sf[cAN_Se][4] = 3.973100;
5334 sf[cAN_Se][5] = 15.237200;
5335 sf[cAN_Se][6] = 4.354300;
5336 sf[cAN_Se][7] = 43.816299;
5337 sf[cAN_Se][8] = 2.840900;
5338 sf[cAN_Se][9] = 0.0;
5339
5340 buffer += MAX_VDW;
5341 c = 0;
5342 n1 = 0;
5343 if(state >= cSelectorUpdateTableEffectiveStates) {
5344 SelectorUpdateTable(G, state, -1);
5345 } else {
5346 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
5347 }
5348 for(a = 0; a < I->Table.size(); a++) {
5349 at = I->Table[a].atom;
5350 obj = I->Obj[I->Table[a].model];
5351 s = obj->AtomInfo[at].selEntry;
5352 if(SelectorIsMember(G, s, sele1)) {
5353 once_flag = true;
5354 for(state1 = 0; state1 < obj->NCSet; state1++) {
5355 if(state < 0)
5356 once_flag = false;
5357 if(!once_flag)
5358 state2 = state1;
5359 else
5360 state2 = state;
5361 if(state2 < obj->NCSet)
5362 cs = obj->CSet[state2];
5363 else
5364 cs = NULL;
5365 if(cs) {
5366 idx = cs->atmToIdx(at);
5367 if(idx >= 0) {
5368 n1++;
5369 }
5370 }
5371 if(once_flag)
5372 break;
5373 }
5374 }
5375 }
5376 point = pymol::malloc<float>(3 * n1);
5377 sfidx = pymol::malloc<int>(n1);
5378 b_factor = pymol::malloc<float>(n1);
5379 occup = pymol::malloc<float>(n1);
5380 atom_sf = pymol::malloc<AtomSF>(n1);
5381
5382 if(!quiet) {
5383 PRINTFB(G, FB_ObjectMap, FB_Details)
5384 " ObjectMap: Computing Gaussian map for %d atom positions.\n", n1 ENDFB(G);
5385 }
5386
5387 n1 = 0;
5388 fp = point;
5389 ip = sfidx;
5390 bf = b_factor;
5391 oc = occup;
5392 for(a = 0; a < I->Table.size(); a++) {
5393 at = I->Table[a].atom;
5394 obj = I->Obj[I->Table[a].model];
5395 ai = obj->AtomInfo + at;
5396 s = ai->selEntry;
5397 if(SelectorIsMember(G, s, sele1)) {
5398 once_flag = true;
5399 for(state1 = 0; state1 < obj->NCSet; state1++) {
5400 if(state < 0)
5401 once_flag = false;
5402 if(!once_flag)
5403 state2 = state1;
5404 else
5405 state2 = state;
5406 if(state2 < obj->NCSet)
5407 cs = obj->CSet[state2];
5408 else
5409 cs = NULL;
5410 if(cs) {
5411 if(CoordSetGetAtomVertex(cs, at, fp)) {
5412 prot = ai->protons;
5413 if(sf[prot][0] == -1.0F)
5414 prot = cAN_C;
5415 bfact = ai->b + (float) b_adjust;
5416 if(bfact < b_floor)
5417 bfact = b_floor;
5418 if((bfact > R_SMALL4) && (ai->q > R_SMALL4)) {
5419 fp += 3;
5420 *(ip++) = prot;
5421 *(bf++) = bfact;
5422 *(oc++) = ai->q;
5423 n1++;
5424 }
5425 }
5426 }
5427 if(once_flag)
5428 break;
5429 }
5430 }
5431 }
5432
5433 for(a = 0; a < n1; a++) {
5434 double *src_sf;
5435
5436 src_sf = &sf[sfidx[a]][0];
5437 bfact = b_factor[a];
5438
5439 for(b = 0; b < 10; b += 2) {
5440 double sfa, sfb;
5441 sfa = src_sf[b];
5442 sfb = src_sf[b + 1];
5443
5444 atom_sf[a][b] = occup[a] * sfa * pow(sqrt1d(4 * PI / (sfb + bfact)), 3.0);
5445 atom_sf[a][b + 1] = 4 * PI * PI / (sfb + bfact);
5446
5447 }
5448
5449 rcut2 = max6d(0.0,
5450 (elim + log(max2d(fabs(atom_sf[a][0]), D_SMALL10))) / atom_sf[a][1],
5451 (elim + log(max2d(fabs(atom_sf[a][2]), D_SMALL10))) / atom_sf[a][3],
5452 (elim + log(max2d(fabs(atom_sf[a][4]), D_SMALL10))) / atom_sf[a][5],
5453 (elim + log(max2d(fabs(atom_sf[a][6]), D_SMALL10))) / atom_sf[a][7],
5454 (elim + log(max2d(fabs(atom_sf[a][8]), D_SMALL10))) / atom_sf[a][9]);
5455 rcut = ((float) sqrt1d(rcut2)) / blur_factor;
5456 atom_sf[a][10] = rcut;
5457 if(max_rcut < rcut)
5458 max_rcut = rcut;
5459 }
5460
5461 /* now create and apply voxel map */
5462 c = 0;
5463 if(n1) {
5464 n2 = 0;
5465 std::unique_ptr<MapType> map(MapNew(G, -max_rcut, point, n1, nullptr));
5466 if(map) {
5467 sum = 0.0;
5468 sumsq = 0.0;
5469 for(a = oMap->Min[0]; a <= oMap->Max[0]; a++) {
5470 OrthoBusyFast(G, a - oMap->Min[0], oMap->Max[0] - oMap->Min[0] + 1);
5471 for(b = oMap->Min[1]; b <= oMap->Max[1]; b++) {
5472 for(c = oMap->Min[2]; c <= oMap->Max[2]; c++) {
5473 e_val = 0.0;
5474 v2 = F4Ptr(oMap->Field->points, a, b, c, 0);
5475 if(use_max) {
5476 float e_partial;
5477 for (const auto j : MapEIter(*map, v2)) {
5478 d = (float) diff3f(point + 3 * j, v2) * blur_factor;
5479 /* scale up width */
5480 sfp = atom_sf[j];
5481 if(d < sfp[10]) {
5482 d = d * d;
5483 if(d < R_SMALL8)
5484 d = R_SMALL8;
5485 e_partial = (float) ((sfp[0] * exp(-sfp[1] * d))
5486 + (sfp[2] * exp(-sfp[3] * d))
5487 + (sfp[4] * exp(-sfp[5] * d))
5488 + (sfp[6] * exp(-sfp[7] * d))
5489 + (sfp[8] * exp(-sfp[9] * d))) * blur_factor;
5490 /* scale down intensity */
5491 if(e_partial > e_val)
5492 e_val = e_partial;
5493 }
5494 }
5495 } else {
5496 for (const auto j : MapEIter(*map, v2)) {
5497 d = (float) diff3f(point + 3 * j, v2) * blur_factor;
5498 /* scale up width */
5499 sfp = atom_sf[j];
5500 if(d < sfp[10]) {
5501 d = d * d;
5502 if(d < R_SMALL8)
5503 d = R_SMALL8;
5504 e_val += (float) ((sfp[0] * exp(-sfp[1] * d))
5505 + (sfp[2] * exp(-sfp[3] * d))
5506 + (sfp[4] * exp(-sfp[5] * d))
5507 + (sfp[6] * exp(-sfp[7] * d))
5508 + (sfp[8] * exp(-sfp[9] * d))) * blur_factor;
5509 /* scale down intensity */
5510 }
5511 }
5512 }
5513 F3(oMap->Field->data, a, b, c) = e_val;
5514 sum += e_val;
5515 sumsq += (e_val * e_val);
5516 n2++;
5517 }
5518 }
5519 }
5520 mean = (float) (sum / n2);
5521 stdev = (float) sqrt1d((sumsq - (sum * sum / n2)) / (n2 - 1));
5522 if(normalize) {
5523
5524 if(!quiet) {
5525 PRINTFB(G, FB_ObjectMap, FB_Details)
5526 " ObjectMap: Normalizing: mean = %8.6f & stdev = %8.6f.\n", mean, stdev
5527 ENDFB(G);
5528 }
5529
5530 if(stdev < R_SMALL8)
5531 stdev = R_SMALL8;
5532
5533 for(a = oMap->Min[0]; a <= oMap->Max[0]; a++) {
5534 for(b = oMap->Min[1]; b <= oMap->Max[1]; b++) {
5535 for(c = oMap->Min[2]; c <= oMap->Max[2]; c++) {
5536 fp = F3Ptr(oMap->Field->data, a, b, c);
5537
5538 *fp = (*fp - mean) / stdev;
5539 }
5540 }
5541 }
5542 } else {
5543 if(!quiet) {
5544 PRINTFB(G, FB_ObjectMap, FB_Details)
5545 " ObjectMap: Not normalizing: mean = %8.6f and stdev = %8.6f.\n",
5546 mean, stdev ENDFB(G);
5547 }
5548 }
5549 oMap->Active = true;
5550 }
5551 }
5552 FreeP(point);
5553 FreeP(sfidx);
5554 FreeP(atom_sf);
5555 FreeP(b_factor);
5556 FreeP(occup);
5557 return (c);
5558 }
5559
5560
5561 /*========================================================================*/
SelectorMapCoulomb(PyMOLGlobals * G,int sele1,ObjectMapState * oMap,float cutoff,int state,int neutral,int shift,float shift_power)5562 int SelectorMapCoulomb(PyMOLGlobals * G, int sele1, ObjectMapState * oMap,
5563 float cutoff, int state, int neutral, int shift, float shift_power)
5564 {
5565 CSelector *I = G->Selector;
5566 float *v2;
5567 int a, b, c, j;
5568 int at;
5569 int s, idx;
5570 AtomInfoType *ai;
5571 ObjectMolecule *obj;
5572 CoordSet *cs;
5573 int state1, state2;
5574 int once_flag;
5575 int n_at = 0;
5576 double tot_charge = 0.0;
5577 float *point = NULL;
5578 float *charge = NULL;
5579 int n_point = 0;
5580 int n_occur;
5581 float *v0, *v1;
5582 float c_factor = 1.0F;
5583 float cutoff_to_power = 1.0F;
5584 const float _1 = 1.0F;
5585
5586 if(shift)
5587 cutoff_to_power = (float) pow(cutoff, shift_power);
5588
5589 c_factor = SettingGetGlobal_f(G, cSetting_coulomb_units_factor) /
5590 SettingGetGlobal_f(G, cSetting_coulomb_dielectric);
5591
5592 c = 0;
5593 SelectorUpdateTable(G, state, -1);
5594
5595 point = VLAlloc(float, I->Table.size() * 3);
5596 charge = VLAlloc(float, I->Table.size());
5597
5598 /* first count # of times each atom appears */
5599
5600 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
5601 at = I->Table[a].atom;
5602 obj = I->Obj[I->Table[a].model];
5603 s = obj->AtomInfo[at].selEntry;
5604 ai = obj->AtomInfo + at;
5605 if(SelectorIsMember(G, s, sele1)) {
5606 n_occur = 0;
5607 /* count */
5608 once_flag = true;
5609 for(state2 = 0; state2 < obj->NCSet; state2++) {
5610 if(state < 0)
5611 once_flag = false;
5612 if(!once_flag)
5613 state1 = state2;
5614 else
5615 state1 = state;
5616 if(state1 < obj->NCSet)
5617 cs = obj->CSet[state1];
5618 else
5619 cs = NULL;
5620 if(cs) {
5621 idx = cs->atmToIdx(at);
5622 if(idx >= 0) {
5623 n_occur++;
5624 n_at++;
5625 }
5626 }
5627 if(once_flag)
5628 break;
5629 }
5630 /* copy */
5631 if(n_occur) {
5632 once_flag = true;
5633 for(state2 = 0; state2 < obj->NCSet; state2++) {
5634 if(state < 0)
5635 once_flag = false;
5636 if(!once_flag)
5637 state1 = state2;
5638 else
5639 state1 = state;
5640 if(state1 < obj->NCSet)
5641 cs = obj->CSet[state1];
5642 else
5643 cs = NULL;
5644 if(cs) {
5645 idx = cs->atmToIdx(at);
5646 if(idx >= 0) {
5647 VLACheck(point, float, 3 * n_point + 2);
5648 VLACheck(charge, float, n_point);
5649 v0 = cs->coordPtr(idx);
5650 v1 = point + 3 * n_point;
5651 copy3f(v0, v1);
5652 charge[n_point] = ai->partialCharge * ai->q / n_occur;
5653
5654 tot_charge += charge[n_point];
5655 n_point++;
5656 }
5657 }
5658 if(once_flag)
5659 break;
5660 }
5661 }
5662 }
5663 }
5664
5665 PRINTFB(G, FB_Selector, FB_Details)
5666 " %s: Total charge is %0.3f for %d points (%d atoms).\n", __func__, tot_charge,
5667 n_point, n_at ENDFB(G);
5668
5669 if(neutral && (fabs(tot_charge) > R_SMALL4)) {
5670 float adjust;
5671
5672 adjust = (float) (-tot_charge / n_point);
5673
5674 for(a = 0; a < n_point; a++) {
5675 charge[a] += adjust;
5676 }
5677
5678 PRINTFB(G, FB_Selector, FB_Details)
5679 " %s: Setting net charge to zero...\n", __func__ ENDFB(G);
5680
5681 }
5682
5683 for(a = 0; a < n_point; a++) { /* premultiply c_factor by charges */
5684 charge[a] *= c_factor;
5685 }
5686
5687 /* now create and apply voxel map */
5688 c = 0;
5689 if(n_point) {
5690 int *min = oMap->Min;
5691 int *max = oMap->Max;
5692 CField *data = oMap->Field->data.get();
5693 CField *points = oMap->Field->points.get();
5694 float dist;
5695
5696 if(cutoff > 0.0F) { /* we are using a cutoff */
5697 if(shift) {
5698 PRINTFB(G, FB_Selector, FB_Details)
5699 " %s: Evaluating local Coulomb potential for grid (shift=%0.2f)...\n", __func__,
5700 cutoff ENDFB(G);
5701 } else {
5702 PRINTFB(G, FB_Selector, FB_Details)
5703 " %s: Evaluating Coulomb potential for grid (cutoff=%0.2f)...\n", __func__,
5704 cutoff ENDFB(G);
5705 }
5706
5707 std::unique_ptr<MapType> map(
5708 MapNew(G, -(cutoff), point, n_point, nullptr));
5709 if(map) {
5710 float dx, dy, dz;
5711 float cut = cutoff;
5712 float cut2 = cutoff * cutoff;
5713
5714 for(a = min[0]; a <= max[0]; a++) {
5715 OrthoBusyFast(G, a - min[0], max[0] - min[0] + 1);
5716 for(b = min[1]; b <= max[1]; b++) {
5717 for(c = min[2]; c <= max[2]; c++) {
5718 F3(data, a, b, c) = 0.0F;
5719 v2 = F4Ptr(points, a, b, c, 0);
5720 {
5721 {
5722 for (const auto j : MapEIter(*map, v2)) {
5723 v1 = point + 3 * j;
5724 while(1) {
5725
5726 dx = v1[0] - v2[0];
5727 dy = v1[1] - v2[1];
5728 dx = (float) fabs(dx);
5729 dy = (float) fabs(dy);
5730 if(dx > cut)
5731 break;
5732 dz = v1[2] - v2[2];
5733 dx = dx * dx;
5734 if(dy > cut)
5735 break;
5736 dz = (float) fabs(dz);
5737 dy = dy * dy;
5738 if(dz > cut)
5739 break;
5740 dx = dx + dy;
5741 dz = dz * dz;
5742 if(dx > cut2)
5743 break;
5744 dy = dx + dz;
5745 if(dy > cut2)
5746 break;
5747 dist = (float) sqrt1f(dy);
5748
5749 if(dist > R_SMALL4) {
5750 if(shift) {
5751 if(dist < cutoff) {
5752 F3(data, a, b, c) += (charge[j] / dist) *
5753 (_1 - (float) pow(dist, shift_power) / cutoff_to_power);
5754 }
5755 } else {
5756 F3(data, a, b, c) += charge[j] / dist;
5757 }
5758 }
5759
5760 break;
5761 }
5762 }
5763 }
5764 }
5765 }
5766 }
5767 }
5768 }
5769 } else {
5770 float *v1;
5771 PRINTFB(G, FB_Selector, FB_Details)
5772 " %s: Evaluating Coulomb potential for grid (no cutoff)...\n", __func__
5773 ENDFB(G);
5774
5775 for(a = min[0]; a <= max[0]; a++) {
5776 OrthoBusyFast(G, a - min[0], max[0] - min[0] + 1);
5777 for(b = min[1]; b <= max[1]; b++) {
5778 for(c = min[2]; c <= max[2]; c++) {
5779 F3(data, a, b, c) = 0.0F;
5780 v1 = point;
5781 v2 = F4Ptr(points, a, b, c, 0);
5782 for(j = 0; j < n_point; j++) {
5783 dist = (float) diff3f(v1, v2);
5784 v1 += 3;
5785 if(dist > R_SMALL4) {
5786 F3(data, a, b, c) += charge[j] / dist;
5787 }
5788 }
5789 }
5790 }
5791 }
5792 }
5793 oMap->Active = true;
5794 }
5795 VLAFreeP(point);
5796 VLAFreeP(charge);
5797 return (1);
5798 }
5799
5800
5801 /*========================================================================*/
SelectorAssignAtomTypes(PyMOLGlobals * G,int sele,int state,int quiet,int format)5802 int SelectorAssignAtomTypes(PyMOLGlobals * G, int sele, int state, int quiet, int format)
5803 {
5804 #ifndef NO_MMLIBS
5805 CSelector *I = G->Selector;
5806 int ok = true;
5807
5808 SelectorUpdateTable(G, state, -1);
5809
5810 if(ok) {
5811 ObjectMolecule *prevobj = NULL;
5812 int a;
5813 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
5814 int at = I->Table[a].atom;
5815 ObjectMolecule *obj = I->Obj[I->Table[a].model];
5816 int s = obj->AtomInfo[at].selEntry;
5817 I->Table[a].index = 0;
5818 if(SelectorIsMember(G, s, sele)) {
5819 ObjectMoleculeInvalidateAtomType(obj, state);
5820 }
5821 }
5822 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
5823 int at = I->Table[a].atom;
5824 ObjectMolecule *obj = I->Obj[I->Table[a].model];
5825 int s = obj->AtomInfo[at].selEntry;
5826 I->Table[a].index = 0;
5827 if(obj != prevobj && SelectorIsMember(G, s, sele)) {
5828 ObjectMoleculeUpdateAtomTypeInfoForState(G, obj, state, 1, format);
5829 prevobj = obj;
5830 }
5831 }
5832 }
5833 return 1;
5834 #else
5835 if (format != 1) {
5836 PRINTFB(G, FB_Selector, FB_Errors)
5837 " Error: assign_atom_types only supports format='mol2'\n" ENDFB(G);
5838 return 0;
5839 }
5840
5841 SelectorUpdateTable(G, state, -1);
5842
5843 ObjectMolecule *obj = NULL;
5844
5845 for (SeleAtomIterator iter(G, sele); iter.next();) {
5846 if (obj != iter.obj) {
5847 obj = iter.obj;
5848 ObjectMoleculeVerifyChemistry(obj, state);
5849 }
5850
5851 LexAssign(G, iter.getAtomInfo()->textType,
5852 getMOL2Type(obj, iter.getAtm()));
5853 }
5854 return 1;
5855 #endif
5856 }
5857
5858 /*========================================================================*/
5859 /*
5860 * Get selection coordinates as Nx3 numpy array. Equivalent to
5861 *
5862 * PyMOL> coords = []
5863 * PyMOL> cmd.iterate_state(state, sele, 'coords.append([x,y,z])')
5864 * PyMOL> coords = numpy.array(coords)
5865 */
SelectorGetCoordsAsNumPy(PyMOLGlobals * G,int sele,int state)5866 PyObject *SelectorGetCoordsAsNumPy(PyMOLGlobals * G, int sele, int state)
5867 {
5868 #ifndef _PYMOL_NUMPY
5869 printf("No numpy support\n");
5870 return NULL;
5871 #else
5872
5873 double matrix[16];
5874 double *matrix_ptr = NULL;
5875 float *v_ptr, v_tmp[3], *dataptr;
5876 int i, nAtom = 0;
5877 int typenum = -1;
5878 const int base_size = sizeof(float);
5879 SeleCoordIterator iter(G, sele, state);
5880 CoordSet *mat_cs = NULL;
5881 PyObject *result = NULL;
5882 npy_intp dims[2] = {0, 3};
5883
5884 for(iter.reset(); iter.next();)
5885 nAtom++;
5886
5887 if(!nAtom)
5888 return NULL;
5889
5890 dims[0] = nAtom;
5891
5892 import_array1(NULL);
5893
5894 switch(base_size) {
5895 case 4: typenum = NPY_FLOAT32; break;
5896 case 8: typenum = NPY_FLOAT64; break;
5897 }
5898
5899 if(typenum == -1) {
5900 printf("error: no typenum for float size %d\n", base_size);
5901 return NULL;
5902 }
5903
5904 result = PyArray_SimpleNew(2, dims, typenum);
5905 dataptr = (float*) PyArray_DATA((PyArrayObject *)result);
5906
5907 for(i = 0, iter.reset(); iter.next(); i++) {
5908 v_ptr = iter.getCoord();
5909
5910 if(mat_cs != iter.cs) {
5911 /* compute the effective matrix for output coordinates */
5912 matrix_ptr = ObjectGetTotalMatrix(iter.obj, state, false, matrix) ? matrix : NULL;
5913 mat_cs = iter.cs;
5914 }
5915
5916 if(matrix_ptr) {
5917 transform44d3f(matrix_ptr, v_ptr, v_tmp);
5918 v_ptr = v_tmp;
5919 }
5920
5921 copy3f(v_ptr, dataptr + i * 3);
5922 }
5923
5924 return result;
5925 #endif
5926 }
5927
5928 /*========================================================================*/
5929 /*
5930 * Load coordinates from a Nx3 sequence into the given selection.
5931 * Most efficiant with numpy arrays. Equivalent to
5932 *
5933 * PyMOL> coords = iter(coords)
5934 * PyMOL> cmd.alter_state(state, sele, '(x,y,z) = coords.next()')
5935 */
SelectorLoadCoords(PyMOLGlobals * G,PyObject * coords,int sele,int state)5936 pymol::Result<> SelectorLoadCoords(PyMOLGlobals * G, PyObject * coords, int sele, int state)
5937 {
5938 #ifdef _PYMOL_NOPY
5939 return pymol::Error("Python unavailable.");
5940 #else
5941
5942 double matrix[16];
5943 double *matrix_ptr = NULL;
5944 float v_xyz[3];
5945 int a, b, nAtom = 0, itemsize;
5946 SeleCoordIterator iter(G, sele, state);
5947 CoordSet *mat_cs = NULL;
5948 PyObject *v, *w;
5949 bool is_np_array = false;
5950 void * ptr;
5951
5952 if(!PySequence_Check(coords)) {
5953 return pymol::Error("Passed argument is not a sequence");
5954 }
5955
5956 // atom count in selection
5957 while(iter.next())
5958 nAtom++;
5959
5960 // sequence length must match atom count
5961 if(nAtom != PySequence_Size(coords)) {
5962 return pymol::Error("Atom count mismatch");
5963 }
5964
5965 // detect numpy arrays, allows faster data access (see below)
5966 #ifdef _PYMOL_NUMPY
5967 import_array1(pymol::Error());
5968
5969 if(PyArray_Check(coords)) {
5970 if(PyArray_NDIM((PyArrayObject *)coords) != 2 ||
5971 PyArray_DIM((PyArrayObject *)coords, 1) != 3) {
5972 return pymol::Error("Numpy array shape mismatch");
5973 }
5974 itemsize = PyArray_ITEMSIZE((PyArrayObject *)coords);
5975 switch(itemsize) {
5976 case sizeof(double):
5977 case sizeof(float):
5978 is_np_array = true;
5979 break;
5980 default:
5981 PRINTFB(G, FB_Selector, FB_Warnings)
5982 " LoadCoords-Warning: numpy array with unsupported dtype\n" ENDFB(G);
5983 }
5984 }
5985 #endif
5986
5987 for(a = 0, iter.reset(); iter.next(); a++) {
5988 // get xyz from python
5989 if (is_np_array) {
5990 // fast implementation for numpy arrays only
5991 #ifdef _PYMOL_NUMPY
5992 for(b = 0; b < 3; b++) {
5993 ptr = PyArray_GETPTR2((PyArrayObject *)coords, a, b);
5994
5995 switch(itemsize) {
5996 case sizeof(double):
5997 v_xyz[b] = (float) *((double*)ptr);
5998 break;
5999 default:
6000 v_xyz[b] = *((float*)ptr);
6001 }
6002 }
6003 #endif
6004 } else {
6005 // general implementation for any 2d sequence
6006 v = PySequence_ITEM(coords, a);
6007
6008 // get xyz from python sequence item
6009 for(b = 0; b < 3; b++) {
6010 if(!(w = PySequence_GetItem(v, b)))
6011 break;
6012
6013 v_xyz[b] = (float) PyFloat_AsDouble(w);
6014 Py_DECREF(w);
6015 }
6016
6017 Py_DECREF(v);
6018 }
6019
6020 if(PyErr_Occurred()) {
6021 return pymol::Error("Load Coords error occurred.");
6022 }
6023
6024
6025 // coord set specific stuff
6026 if(mat_cs != iter.cs) {
6027 // update matrix
6028 matrix_ptr = ObjectGetTotalMatrix(iter.obj, state, false, matrix) ? matrix : NULL;
6029 mat_cs = iter.cs;
6030
6031 // invalidate reps
6032 iter.cs->invalidateRep(cRepAll, cRepInvRep);
6033 }
6034
6035 // handle matrix
6036 if(matrix_ptr) {
6037 inverse_transform44d3f(matrix_ptr, v_xyz, v_xyz);
6038 }
6039
6040 // copy coordinates
6041 copy3f(v_xyz, iter.getCoord());
6042 }
6043
6044 #endif
6045 return {};
6046 }
6047
6048 /*========================================================================*/
SelectorUpdateCmd(PyMOLGlobals * G,SelectorID_t sele0,SelectorID_t sele1,int sta0,int sta1,int matchmaker,int quiet)6049 pymol::Result<> SelectorUpdateCmd(PyMOLGlobals* G, //
6050 SelectorID_t sele0, //
6051 SelectorID_t sele1, //
6052 int sta0, int sta1, int matchmaker, int quiet)
6053 {
6054 CSelector *I = G->Selector;
6055 int a, b;
6056 int at0 = 0, at1;
6057 int c0 = 0, c1 = 0;
6058 int i0 = 0, i1;
6059 ObjectMolecule *obj0 = NULL, *obj1;
6060 CoordSet *cs0;
6061 const CoordSet *cs1;
6062 int matched_flag;
6063 int b_start;
6064 int ccc = 0;
6065
6066 bool ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
6067 bool ignore_case_chain = SettingGetGlobal_b(G, cSetting_ignore_case_chain);
6068
6069 PRINTFD(G, FB_Selector)
6070 " %s-Debug: entered sta0 %d sta1 %d", __func__, sta0, sta1 ENDFD;
6071
6072 // either both or none must be "all states"
6073 if (sta0 != sta1) {
6074 if (sta0 == cSelectorUpdateTableAllStates) {
6075 sta0 = sta1;
6076 } else if (sta1 == cSelectorUpdateTableAllStates) {
6077 sta1 = sta0;
6078 }
6079 }
6080
6081 if((sta0 < 0) || (sta1 < 0) || (sta0 != sta1)) {
6082 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
6083 } else {
6084 SelectorUpdateTable(G, sta0, -1);
6085 }
6086
6087 auto vla0 = pymol::vla_take_ownership(SelectorGetIndexVLA(G, sele0));
6088 auto vla1 = pymol::vla_take_ownership(SelectorGetIndexVLA(G, sele1));
6089
6090 if (vla0 && vla1) {
6091 c0 = VLAGetSize(vla0);
6092 c1 = VLAGetSize(vla1);
6093 }
6094
6095 if (c0 < 1 || c1 < 1)
6096 return pymol::make_error("No coordinates updated.");
6097 else {
6098
6099 b = 0;
6100 for(a = 0; a < c1; a++) { /* iterate over source atoms */
6101 /* NOTE, this algorithm is N^2 and slow in the worst case...
6102 however the best case (N) is quite common, especially when merging
6103 files written out of PyMOL */
6104
6105 i1 = vla1[a];
6106 at1 = I->Table[i1].atom;
6107 obj1 = I->Obj[I->Table[i1].model];
6108 matched_flag = false;
6109
6110 switch (matchmaker) {
6111 case 0:
6112 /* simply assume that atoms are stored in PyMOL in the identical order, one for one */
6113 if(b < c0) {
6114 i0 = vla0[b];
6115 at0 = I->Table[i0].atom;
6116 obj0 = I->Obj[I->Table[i0].model];
6117 b++;
6118 matched_flag = true;
6119 }
6120 break;
6121 case 1:
6122 /* match each pair based on atom info */
6123 b_start = b;
6124 matched_flag = false;
6125 while(1) {
6126 i0 = vla0[b];
6127 at0 = I->Table[i0].atom;
6128 obj0 = I->Obj[I->Table[i0].model];
6129 if(obj0 != obj1) {
6130 if(AtomInfoMatch(G, obj1->AtomInfo + at1, obj0->AtomInfo + at0, ignore_case, ignore_case_chain)) {
6131 matched_flag = true;
6132 break;
6133 }
6134 } else if(at0 == at1) {
6135 matched_flag = true;
6136 break;
6137 }
6138 b++;
6139 if(b >= c0)
6140 b = 0;
6141 if(b == b_start)
6142 break;
6143 }
6144 break;
6145 case 2: /* match based on ID */
6146 {
6147 int target = obj1->AtomInfo[at1].id;
6148 b_start = b;
6149 matched_flag = false;
6150 while(1) {
6151 i0 = vla0[b];
6152 at0 = I->Table[i0].atom;
6153 obj0 = I->Obj[I->Table[i0].model];
6154 if(obj0 != obj1) {
6155 if(obj0->AtomInfo[at0].id == target) {
6156 matched_flag = true;
6157 break;
6158 }
6159 } else if(at0 == at1) {
6160 matched_flag = true;
6161 break;
6162 }
6163 b++;
6164 if(b >= c0)
6165 b = 0;
6166 if(b == b_start)
6167 break;
6168 }
6169 }
6170 break;
6171 case 3: /* match based on rank */
6172 {
6173 int target = obj1->AtomInfo[at1].rank;
6174 b_start = b;
6175 matched_flag = false;
6176 while(1) {
6177 i0 = vla0[b];
6178 at0 = I->Table[i0].atom;
6179 obj0 = I->Obj[I->Table[i0].model];
6180 if(obj0 != obj1) {
6181 if(obj0->AtomInfo[at0].rank == target) {
6182 matched_flag = true;
6183 break;
6184 }
6185 } else if(at0 == at1) {
6186 matched_flag = true;
6187 }
6188 b++;
6189 if(b >= c0)
6190 b = 0;
6191 if(b == b_start)
6192 break;
6193 }
6194 }
6195 break;
6196 case 4: /* match based on index */
6197 {
6198 b_start = b;
6199 matched_flag = false;
6200 while(1) {
6201 i0 = vla0[b];
6202 at0 = I->Table[i0].atom;
6203 obj0 = I->Obj[I->Table[i0].model];
6204 if(obj0 != obj1) {
6205 if(at0 == at1) {
6206 matched_flag = true;
6207 break;
6208 }
6209 } else if(at0 == at1) {
6210 matched_flag = true;
6211 break;
6212 }
6213 b++;
6214 if(b >= c0)
6215 b = 0;
6216 if(b == b_start)
6217 break;
6218 }
6219 }
6220 break;
6221 }
6222
6223 if(matched_flag) { /* atom matched, so copy coordinates */
6224 ccc++;
6225
6226 StateIterator iter0(G, obj0->Setting, sta0, obj0->NCSet);
6227 StateIterator iter1(G, obj1->Setting, sta1, obj1->NCSet);
6228
6229 while (iter0.next() && iter1.next()) {
6230 cs0 = obj0->CSet[iter0.state];
6231 cs1 = obj1->CSet[iter1.state];
6232 if (cs1 && cs0) {
6233 int idx0 = cs0->atmToIdx(at0);
6234 int idx1 = cs1->atmToIdx(at1);
6235 if (idx0 >= 0 && idx1 >= 0) {
6236 copy3f(cs1->coordPtr(idx1), cs0->coordPtr(idx0));
6237 }
6238 }
6239 }
6240 }
6241 }
6242 obj0 = NULL;
6243
6244 {
6245 ObjectMolecule **objs = SelectorGetObjectMoleculeVLA(G, sele0);
6246 int sz = VLAGetSize(objs);
6247 for(b = 0; b < sz; b++) {
6248 objs[b]->invalidate(cRepAll, cRepInvCoord, -1);
6249 }
6250 VLAFree(objs);
6251 }
6252 SceneChanged(G);
6253 if(!quiet) {
6254 PRINTFB(G, FB_Selector, FB_Actions)
6255 " Update: coordinates updated for %d atoms.\n", ccc ENDFB(G);
6256
6257 }
6258 }
6259 return {};
6260 }
6261
6262
6263 /*========================================================================*/
6264
SelectorCreateObjectMolecule(PyMOLGlobals * G,SelectorID_t sele,const char * name,int target,int source,int discrete,int zoom,int quiet,int singletons,int copy_properties)6265 int SelectorCreateObjectMolecule(PyMOLGlobals * G, SelectorID_t sele, const char *name,
6266 int target, int source, int discrete,
6267 int zoom, int quiet, int singletons, int copy_properties)
6268 {
6269 CSelector *I = G->Selector;
6270 int ok = true;
6271 int a, b, a2, b1, b2, c, d, s, at;
6272 const BondType *ii1;
6273 int nBond = 0;
6274 int nCSet, nAtom, ts;
6275 int isNew;
6276 CoordSet *cs = NULL;
6277 CoordSet *cs1, *cs2;
6278 ObjectMolecule *obj;
6279 CObject *ob;
6280 ObjectMolecule *targ = NULL;
6281 ObjectMolecule *info_src = NULL;
6282 int static_singletons = SettingGetGlobal_b(G, cSetting_static_singletons);
6283
6284 if(singletons < 0)
6285 singletons = static_singletons;
6286
6287 ob = ExecutiveFindObjectByName(G, name);
6288 if(ob)
6289 if(ob->type == cObjectMolecule)
6290 targ = (ObjectMolecule *) ob;
6291
6292 SelectorUpdateTable(G, source, -1);
6293
6294 if(!targ) {
6295 isNew = true;
6296 if(discrete < 0)
6297 discrete = SelectorIsSelectionDiscrete(G, sele, false);
6298 targ = new ObjectMolecule(G, discrete);
6299 targ->Bond = pymol::vla<BondType>(1);
6300 {
6301 /* copy object color of previous object (if any) */
6302 ObjectMolecule *singleObj = NULL;
6303 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
6304 at = I->Table[a].atom;
6305 I->Table[a].index = -1;
6306 obj = I->Obj[I->Table[a].model];
6307 s = obj->AtomInfo[at].selEntry;
6308 if(SelectorIsMember(G, s, sele)) {
6309 if(!singleObj)
6310 singleObj = obj;
6311 else if(singleObj && (obj != singleObj)) {
6312 singleObj = NULL;
6313 break;
6314 }
6315 }
6316 }
6317 if(singleObj)
6318 targ->Color = singleObj->Color;
6319 /* should also consider copying lots of other stuff from the source object ... */
6320 }
6321 } else {
6322 isNew = false;
6323 }
6324
6325 std::function<void(int)> const body = [&](int const source) {
6326
6327 c = 0;
6328
6329 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
6330 at = I->Table[a].atom;
6331 I->Table[a].index = -1;
6332 obj = I->Obj[I->Table[a].model];
6333 s = obj->AtomInfo[at].selEntry;
6334 if(SelectorIsMember(G, s, sele)) {
6335 I->Table[a].index = c; /* Mark records as to which atom they'll be */
6336 c++;
6337 if(!info_src)
6338 info_src = obj;
6339 }
6340 }
6341 if(isNew && info_src) { /* copy symmetry information, etc. */
6342 if (targ->Symmetry == nullptr && info_src->Symmetry != nullptr) {
6343 targ->Symmetry = new CSymmetry(*info_src->Symmetry);
6344 }
6345 }
6346
6347 if (info_src && source == cSelectorUpdateTableAllStates &&
6348 targ->DiscreteFlag && !info_src->DiscreteFlag) {
6349 for (int state = 0; state < info_src->getNFrame(); ++state) {
6350 body(state);
6351 }
6352 return;
6353 }
6354
6355 nAtom = c;
6356
6357 nBond = 0;
6358 auto bond = pymol::vla<BondType>(nAtom * 4);
6359 for(a = cNDummyModels; a < I->Obj.size(); a++) { /* find bonds wholly contained in the selection */
6360 obj = I->Obj[a];
6361 ii1 = obj->Bond;
6362 for(b = 0; b < obj->NBond; b++) {
6363 b1 = SelectorGetObjAtmOffset(I, obj, ii1->index[0]);
6364 b2 = SelectorGetObjAtmOffset(I, obj, ii1->index[1]);
6365 if((b1 >= 0) && (b2 >= 0)) {
6366 if((I->Table[b1].index >= 0) && (I->Table[b2].index >= 0)) {
6367 BondType* dst_bond = bond.check(nBond);
6368 {
6369 AtomInfoBondCopy(G, ii1, dst_bond);
6370 dst_bond->index[0] = I->Table[b1].index; /* store what will be the new index */
6371 dst_bond->index[1] = I->Table[b2].index;
6372 /* printf("Selector-DEBUG %d %d\n",dst_bond->index[0],dst_bond->index[1]); */
6373 nBond++;
6374 }
6375 }
6376 }
6377 ii1++;
6378 }
6379 }
6380
6381 pymol::vla<AtomInfoType> atInfo(nAtom);
6382 /* copy the atom info records and create new zero-based IDs */
6383 c = 0;
6384 {
6385 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
6386 if(I->Table[a].index >= 0) {
6387 obj = I->Obj[I->Table[a].model];
6388 at = I->Table[a].atom;
6389 VLACheck(atInfo, AtomInfoType, c);
6390 AtomInfoCopy(G, obj->AtomInfo + at, atInfo + c);
6391 c++;
6392 }
6393 }
6394 }
6395
6396 cs = CoordSetNew(G); /* set up a dummy coordinate set for the merge xref */
6397 cs->NIndex = nAtom;
6398 cs->enumIndices();
6399 cs->TmpBond = std::move(bond); /* load up the bonds */
6400 cs->NTmpBond = nBond;
6401
6402 /* printf("Selector-DEBUG nAtom %d\n",nAtom); */
6403 ObjectMoleculeMerge(targ, std::move(atInfo), cs, false, cAIC_AllMask, true); /* will free atInfo */
6404 /* cs->IdxToAtm will now have the reverse mapping from the new subset
6405 to the new merged molecule */
6406
6407 ObjectMoleculeExtendIndices(targ, -1);
6408 ObjectMoleculeUpdateIDNumbers(targ);
6409 ObjectMoleculeUpdateNonbonded(targ);
6410
6411 if(!isNew) { /* recreate selection table */
6412
6413 SelectorUpdateTable(G, source, -1);
6414
6415 }
6416
6417 /* get maximum state index for the selection...note that
6418 we'll be creating states from 1 up to the maximum required to
6419 capture atoms in the selection
6420 */
6421
6422 nCSet = 0;
6423
6424 {
6425 c = 0;
6426 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
6427 at = I->Table[a].atom;
6428 I->Table[a].index = -1;
6429 obj = I->Obj[I->Table[a].model];
6430 s = obj->AtomInfo[at].selEntry;
6431 if(SelectorIsMember(G, s, sele)) {
6432 I->Table[a].index = c; /* Mark records */
6433 if(nCSet < obj->NCSet)
6434 nCSet = obj->NCSet;
6435 c++;
6436 }
6437 }
6438 }
6439
6440 if(c != nAtom)
6441 ErrFatal(G, "SelectorCreate", "inconsistent selection.");
6442 /* cs->IdxToAtm now has the relevant indexes for the coordinate transfer */
6443
6444 for(StateIterator iter(G, NULL, source, nCSet); iter.next();) {
6445 d = iter.state;
6446 {
6447 cs2 = CoordSetNew(G);
6448 c = 0;
6449 cs2->Coord = pymol::vla<float>(3 * nAtom);
6450 cs2->NAtIndex = targ->NAtom;
6451 cs2->IdxToAtm = pymol::vla<int>(nAtom);
6452 for(a = cNDummyAtoms; a < I->Table.size(); a++) /* any selected atoms in this state? */
6453 if(I->Table[a].index >= 0) {
6454 at = I->Table[a].atom;
6455 obj = I->Obj[I->Table[a].model];
6456 cs1 = NULL;
6457 if(d < obj->NCSet) {
6458 cs1 = obj->CSet[d];
6459 } else if(singletons && (obj->NCSet == 1)) {
6460 cs1 = obj->CSet[0];
6461 }
6462 if(cs1) {
6463 if((!cs2->Name[0]) && (cs1->Name[0])) /* copy the molecule name (if any) */
6464 strcpy(cs2->Name, cs1->Name);
6465
6466 if(CoordSetGetAtomVertex(cs1, at, cs2->coordPtr(c))) {
6467 a2 = cs->IdxToAtm[I->Table[a].index]; /* actual merged atom index */
6468 cs2->IdxToAtm[c] = a2;
6469 c++;
6470 }
6471 }
6472 }
6473 VLASize(cs2->IdxToAtm, int, c);
6474 VLASize(cs2->Coord, float, c * 3);
6475 cs2->NIndex = c;
6476 if(target >= 0) {
6477 ts = target++;
6478 } else {
6479 ts = d;
6480 }
6481 VLACheck(targ->CSet, CoordSet *, ts);
6482 if(targ->NCSet <= ts)
6483 targ->NCSet = ts + 1;
6484 if(targ->CSet[ts])
6485 targ->CSet[ts]->fFree();
6486 targ->CSet[ts] = cs2;
6487 cs2->Obj = targ;
6488 }
6489 }
6490 if(cs)
6491 cs->fFree();
6492
6493 }; // end body
6494
6495 body(source);
6496
6497 targ->updateAtmToIdx();
6498
6499 SceneCountFrames(G);
6500 if(!quiet) {
6501 PRINTFB(G, FB_Selector, FB_Details)
6502 " Selector: found %d atoms.\n", nAtom ENDFB(G);
6503 }
6504 if (ok)
6505 ok &= ObjectMoleculeSort(targ);
6506 if(isNew) {
6507 ObjectSetName((CObject *) targ, name);
6508 ExecutiveManageObject(G, (CObject *) targ, zoom, quiet);
6509 } else {
6510 ExecutiveUpdateObjectSelection(G, (CObject *) targ);
6511 }
6512 SceneChanged(G);
6513 return ok;
6514 }
6515
6516
6517 /*========================================================================*/
SelectorSetName(PyMOLGlobals * G,const char * new_name,const char * old_name)6518 int SelectorSetName(PyMOLGlobals * G, const char *new_name, const char *old_name)
6519 {
6520 auto I = G->SelectorMgr;
6521 int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
6522
6523 auto it = SelectGetInfoIter(G, old_name, 1, ignore_case);
6524 if (it != I->Info.end()) {
6525 it->name = new_name;
6526 return true;
6527 } else {
6528 return false;
6529 }
6530 }
6531
6532
6533 /*========================================================================
6534 * SelectorIndexByName -- fetch the global selector's ID for sname
6535 * PARAMS
6536 * (string) sname, object name
6537 * RETURNS
6538 * (int) index #, or -1 if not found
6539 */
SelectorIndexByName(PyMOLGlobals * G,const char * sname,int ignore_case)6540 SelectorID_t SelectorIndexByName(PyMOLGlobals * G, const char *sname, int ignore_case)
6541 {
6542 auto I = G->SelectorMgr;
6543
6544 if(sname) {
6545 if (ignore_case < 0)
6546 ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
6547
6548 while((sname[0] == '%') || (sname[0] == '?'))
6549 sname++;
6550
6551 auto it = SelectGetInfoIter(G, sname, 1, ignore_case);
6552 if (it == I->Info.end())
6553 return cSelectionInvalid;
6554
6555 if (sname[0] != '_') { /* don't do checking on internal selections */
6556 const char *best;
6557 best = ExecutiveFindBestNameMatch(G, sname); /* suppress spurious matches
6558 of selections with non-selections */
6559 if (best != sname && best != it->name)
6560 return cSelectionInvalid;
6561 }
6562
6563 return it->ID;
6564 }
6565 return cSelectionInvalid;
6566 }
6567
6568
6569 /*========================================================================*/
SelectorPurgeMembers(PyMOLGlobals * G,SelectorID_t sele)6570 static void SelectorPurgeMembers(PyMOLGlobals * G, SelectorID_t sele)
6571 {
6572 auto I = G->SelectorMgr;
6573 void *iterator = NULL;
6574 ObjectMolecule *obj = NULL;
6575 short changed = 0;
6576
6577 if(!I->Member.empty()) {
6578
6579 while(ExecutiveIterateObjectMolecule(G, &obj, &iterator)) {
6580 if(obj->type == cObjectMolecule) {
6581 AtomInfoType *ai = obj->AtomInfo.data();
6582 int a, n_atom = obj->NAtom;
6583 for(a = 0; a < n_atom; a++) {
6584 int s = (ai++)->selEntry;
6585 int l = -1;
6586 while(s) {
6587 auto& i_member_s = I->Member[s];
6588 int nxt = i_member_s.next;
6589 if(i_member_s.selection == sele) {
6590 if(l > 0)
6591 I->Member[l].next = i_member_s.next;
6592 else
6593 ai[-1].selEntry = i_member_s.next;
6594 changed = 1;
6595 i_member_s.next = I->FreeMember;
6596 I->FreeMember = s;
6597 }
6598 l = s;
6599 s = nxt;
6600 }
6601 }
6602 }
6603 }
6604 }
6605 if (changed){
6606 // not sure if this is needed since its in SelectorClean()
6607 ExecutiveInvalidateSelectionIndicatorsCGO(G);
6608 }
6609 }
6610
6611
6612 /*========================================================================*/
SelectorPurgeObjectMembers(PyMOLGlobals * G,ObjectMolecule * obj)6613 int SelectorPurgeObjectMembers(PyMOLGlobals * G, ObjectMolecule * obj)
6614 {
6615 bool changed = false;
6616
6617 auto I = G->SelectorMgr;
6618 if(!I->Member.empty()) {
6619 for(int a = 0; a < obj->NAtom; a++) {
6620 auto s = obj->AtomInfo[a].selEntry;
6621 while(s) {
6622 auto nxt = I->Member[s].next;
6623 I->Member[s].next = I->FreeMember;
6624 I->FreeMember = s;
6625 s = nxt;
6626 }
6627 obj->AtomInfo[a].selEntry = 0;
6628 changed = true;
6629 }
6630 }
6631 if (changed){
6632 // not sure if this is needed since its in SelectorClean()
6633 ExecutiveInvalidateSelectionIndicatorsCGO(G);
6634 }
6635
6636 return 1;
6637 }
6638
6639
6640 /*========================================================================*/
SelectorDelete(PyMOLGlobals * G,const char * sele)6641 void SelectorDelete(PyMOLGlobals * G, const char *sele)
6642
6643
6644 /* should (only) be called by Executive or by Selector, unless the
6645
6646 named selection has never been registered with the executive
6647
6648 (i.e., temporary on-the-fly selection) */
6649 {
6650 auto& Info = G->SelectorMgr->Info;
6651 auto it = SelectGetInfoIter(G, sele, 999,
6652 SettingGetGlobal_b(G, cSetting_ignore_case));
6653
6654 // Does it exist?
6655 if (it == Info.end())
6656 return;
6657
6658 // Never delete the "all" selection
6659 if (it->ID == cSelectionAll)
6660 return;
6661
6662 assert(!SelectorIsTmp(sele) ||
6663 sele == pymol::string_format("%s%d", cSelectorTmpPrefix, it->ID));
6664
6665 // get rid of existing selection
6666 SelectorDeleteSeleAtIter(G, it);
6667 }
6668
6669
6670 /*========================================================================*/
6671 /*
6672 * If `input` is already a name of an object or a valid position keyword
6673 * (center, origin, all, ...), then simply copy it to `store`. Otherwise
6674 * process the selection expression and create a temporary named selection.
6675 *
6676 * Unlike `SelectorGetTmp`, this will accept names of maps, groups, etc.
6677 */
SelectorGetTmp2(PyMOLGlobals * G,const char * input,char * store,bool quiet)6678 int SelectorGetTmp2(PyMOLGlobals * G, const char *input, char *store, bool quiet)
6679 {
6680 auto res = SelectorGetTmp2Result(G, input, store, quiet);
6681 if (res) {
6682 return res.result();
6683 }
6684 PRINTFB(G, FB_Selector, FB_Errors)
6685 " Selector-Error: %s\n", res.error().what().c_str() ENDFB(G);
6686 return -1;
6687 }
6688
6689 pymol::Result<int>
SelectorGetTmp2Result(PyMOLGlobals * G,const char * input,char * store,bool quiet)6690 SelectorGetTmp2Result(PyMOLGlobals * G, const char *input, char *store, bool quiet)
6691 {
6692 /* ASSUMES that store is at least as big as an OrthoLineType */
6693 auto I = G->SelectorMgr;
6694 PRINTFD(G, FB_Selector)
6695 " %s-Debug: entered with \"%s\".\n", __func__, input ENDFD;
6696
6697 store[0] = 0;
6698
6699 /* skip trivial cases */
6700
6701 if(input[0] && !((input[0] == '\'') && (input[1] == '\'') && (!input[2]))) {
6702
6703 /* OKAY, this routine is in flux. Eventually this routine will...
6704
6705 (1) fully parse the input recognizing selection keywords, nested
6706 parens, quotes, escaped strings, etc.
6707
6708 (2) replace selection blocks with temporary selection names
6709
6710 (3) return a space-separated list of names for processing
6711
6712 However, right now, this routine simply handles two cases.
6713
6714 A. where the input is a selection, in which case store is set to a
6715 temporary selection name
6716
6717 B. where the input is simply a list of space-separated name patterns,
6718 in which case store is simply passed along as a copy of the input
6719
6720 */
6721
6722 // make selection if "input" doesn't fit into "store"
6723 int is_selection = strlen(input) >= OrthoLineLength;
6724
6725 // can't pass through temp selections (current SelectorFreeTmp limitation)
6726 if (!is_selection) {
6727 is_selection = SelectorIsTmp(input);
6728 }
6729
6730 const char *p = input;
6731 OrthoLineType word;
6732
6733 if (!is_selection) while(*p) {
6734 /* copy first word/token of p into "word", remainder of string in p */
6735 p = ParseWord(word, p, sizeof(OrthoLineType));
6736 /* see a paren? then this must be a selection */
6737
6738 if(word[0] == '(') {
6739 is_selection = true;
6740 break;
6741 }
6742
6743 if(strchr(word, '/')) {
6744 is_selection = true;
6745 break;
6746 }
6747
6748 /* encounterd a selection keyword? then this must be a selection */
6749
6750 {
6751 auto it = I->Key.find(word);
6752 if (it != I->Key.end()) {
6753 if (it->second != SELE_ALLz && it->second != SELE_ORIz &&
6754 it->second != SELE_CENz) {
6755 is_selection = true;
6756 break;
6757 }
6758 }
6759 }
6760
6761 if(!ExecutiveValidName(G, word)) { /* don't recognize the name? */
6762 if(!ExecutiveValidNamePattern(G, word)) { /* don't recognize this as a pattern? */
6763 is_selection = true; /* must be a selection */
6764 break;
6765 }
6766 }
6767 }
6768 if(is_selection) { /* incur the computational expense of
6769 parsing the input as an atom selection */
6770 SelectorGetUniqueTmpName(G, store);
6771 auto res = SelectorCreate(G, store, input, NULL, quiet, NULL);
6772 if (!res) {
6773 store[0] = 0;
6774 }
6775 return res;
6776 } else { /* otherwise, just parse the input as a space-separated list of names */
6777 /* not a selection */
6778 strcpy(store, input);
6779 }
6780 }
6781 return 0;
6782
6783 }
6784
6785 /*========================================================================*/
6786 /*
6787 * Like SelectorGetTmp2, but doesn't accept names from any non-molecular
6788 * entities like groups or map objects (those will be processed as selection
6789 * expressions).
6790 */
SelectorGetTmp(PyMOLGlobals * G,const char * input,char * store,bool quiet)6791 int SelectorGetTmp(PyMOLGlobals * G, const char *input, char *store, bool quiet)
6792 {
6793 auto res = SelectorGetTmpResult(G, input, store, quiet);
6794 if (res) {
6795 return res.result();
6796 }
6797 PRINTFB(G, FB_Selector, FB_Errors)
6798 " Selector-Error: %s\n", res.error().what().c_str() ENDFB(G);
6799 return -1;
6800 }
6801
6802 pymol::Result<int>
SelectorGetTmpResult(PyMOLGlobals * G,const char * input,char * store,bool quiet)6803 SelectorGetTmpResult(PyMOLGlobals * G, const char *input, char *store, bool quiet)
6804 {
6805 store[0] = 0;
6806
6807 // trivial (but valid) case: empty selection string
6808 if (!input[0])
6809 return 0;
6810
6811 // if object molecule or named selection, then don't create a temp selection
6812 if (ExecutiveIsMoleculeOrSelection(G, input) && !SelectorIsTmp(input)) {
6813 strcpy(store, input);
6814 return 0;
6815 }
6816
6817 // evaluate expression and create a temp selection
6818 SelectorGetUniqueTmpName(G, store);
6819 auto res = SelectorCreate(G, store, input, NULL, quiet, NULL);
6820
6821 if(!res) {
6822 store[0] = 0;
6823 }
6824
6825 return res;
6826 }
6827
6828
6829 /*========================================================================*/
SelectorFreeTmp(PyMOLGlobals * G,const char * name)6830 void SelectorFreeTmp(PyMOLGlobals * G, const char *name)
6831 { /* remove temporary selections */
6832 if (name && SelectorIsTmp(name)) {
6833 ExecutiveDelete(G, name);
6834 }
6835 }
6836
6837
6838 /*========================================================================*/
SelectorEmbedSelection(PyMOLGlobals * G,const int * atom,pymol::zstring_view name,ObjectMolecule * obj,int no_dummies,int exec_managed)6839 static int SelectorEmbedSelection(PyMOLGlobals * G, const int *atom, pymol::zstring_view name,
6840 ObjectMolecule * obj, int no_dummies, int exec_managed)
6841 {
6842 /* either atom or obj should be NULL, not both and not neither */
6843
6844 CSelector *I = G->Selector;
6845 auto IM = I->mgr;
6846 int tag;
6847 int newFlag = true;
6848 int a, sele;
6849 int c = 0;
6850 int start = 0;
6851 int singleAtomFlag = true;
6852 int singleObjectFlag = true;
6853 ObjectMolecule *singleObject = NULL, *selObj;
6854 int singleAtom = -1;
6855 int index;
6856 AtomInfoType *ai;
6857
6858 if(exec_managed < 0) {
6859 if(atom) /* automatic behavior: manage selections defined via atom masks */
6860 exec_managed = true;
6861 else
6862 exec_managed = false;
6863 }
6864
6865 // already exist?
6866 auto it = SelectGetInfoIter(G, name.c_str(), 999,
6867 SettingGetGlobal_b(G, cSetting_ignore_case));
6868 if (it != IM->Info.end()) {
6869 assert(!SelectorIsTmp(name));
6870
6871 // don't allow redefinition of "all"
6872 if (it->ID == cSelectionAll)
6873 return 0;
6874
6875 // get rid of existing selection
6876 SelectorDeleteSeleAtIter(G, it);
6877 newFlag = false;
6878 }
6879
6880 sele = IM->NSelection++;
6881 IM->Info.emplace_back(SelectionInfoRec(sele, name.c_str()));
6882
6883 assert(!SelectorIsTmp(name) ||
6884 name == pymol::string_format(
6885 "%s%d", cSelectorTmpPrefix, IM->Info.back().ID));
6886
6887 if(no_dummies) {
6888 start = 0;
6889 } else {
6890 start = cNDummyAtoms;
6891 }
6892 for(a = start; a < I->Table.size(); a++) {
6893 tag = false;
6894 /* set tag based on passed in atom list or on global atom table */
6895 if(atom) {
6896 if(atom[a])
6897 tag = atom[a];
6898 } else {
6899 if(I->Obj[I->Table[a].model] == obj)
6900 tag = 1;
6901 }
6902 if(tag) {
6903 /* if this this atom is tagged, grab its object
6904 * index, and info record */
6905 selObj = I->Obj[I->Table[a].model];
6906 index = I->Table[a].atom;
6907 ai = selObj->AtomInfo + index;
6908
6909 /* update whether or not this is a selection w/only one object */
6910 if(singleObjectFlag) {
6911 if(singleObject) {
6912 if(selObj != singleObject) {
6913 singleObjectFlag = false;
6914 }
6915 } else {
6916 singleObject = selObj;
6917 }
6918 }
6919 /* update whether or not this is a selection w/only one atom */
6920 if(singleAtomFlag) {
6921 if(singleAtom >= 0) {
6922 if(index != singleAtom) {
6923 singleAtomFlag = false;
6924 }
6925 } else {
6926 singleAtom = index;
6927 }
6928 }
6929
6930 /* store this is the Selectors->Member table, so make sure there's room */
6931 c++;
6932 /* at runtime, selections can now have transient ordering --
6933 but these are not yet persistent through session saves & restores */
6934 SelectorManagerInsertMember(*IM, *ai, sele, tag);
6935 }
6936 }
6937
6938 /* after scanning, update whether or not we touched multiple objects/atoms */
6939 if(c) {
6940 auto& info = IM->Info.back();
6941 if(singleObjectFlag) {
6942 info.theOneObject = singleObject;
6943 if(singleAtomFlag) {
6944 assert(singleAtom >= 0);
6945 info.theOneAtom = singleAtom;
6946 }
6947 }
6948 }
6949
6950 if(exec_managed) {
6951 if(newFlag)
6952 ExecutiveManageSelection(G, name.c_str());
6953 }
6954 PRINTFD(G, FB_Selector)
6955 " Selector: Embedded %s, %d atoms.\n", name.c_str(), c ENDFD;
6956 return (c);
6957 }
6958
6959
6960 /*========================================================================*/
SelectorApplyMultipick(PyMOLGlobals * G,Multipick * mp)6961 static sele_array_t SelectorApplyMultipick(PyMOLGlobals * G, Multipick * mp)
6962 {
6963 CSelector *I = G->Selector;
6964 sele_array_t result;
6965 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
6966 sele_array_calloc(result, I->Table.size());
6967 for (const auto& p : mp->picked) {
6968 assert(p.context.object->type == cObjectMolecule);
6969 auto obj = static_cast<const ObjectMolecule*>(p.context.object);
6970 /* NOTE: SeleBase only safe with cSelectorUpdateTableAllStates! */
6971 result[obj->SeleBase + p.src.index] = true;
6972 }
6973 return (result);
6974 }
6975
6976
6977 /*========================================================================*/
SelectorSelectFromTagDict(PyMOLGlobals * G,const std::unordered_map<int,int> & id2tag)6978 static sele_array_t SelectorSelectFromTagDict(PyMOLGlobals * G, const std::unordered_map<int, int>& id2tag)
6979 {
6980 CSelector *I = G->Selector;
6981 sele_array_t result{};
6982
6983 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1); /* for now, update the entire table */
6984 {
6985 sele_array_calloc(result, I->Table.size());
6986 if(result) {
6987 for(int a = cNDummyAtoms; a < I->Table.size(); a++) {
6988 auto& table_a = I->Table[a];
6989 auto ai = I->Obj[table_a.model]->AtomInfo + table_a.atom;
6990 if(ai->unique_id) {
6991 auto it = id2tag.find(ai->unique_id);
6992 if(it != id2tag.end()) {
6993 result[a] = it->second;
6994 }
6995 }
6996 }
6997 }
6998 }
6999 return (result);
7000 }
7001
7002
7003 /*========================================================================*/
7004
7005 static SelectorCreateResult_t
_SelectorCreate(PyMOLGlobals * G,pymol::zstring_view sname,const char * sele,ObjectMolecule ** obj,int quiet,Multipick * mp,CSeqRow * rowVLA,int nRow,int ** obj_idx,int * n_idx,int n_obj,const std::unordered_map<int,int> * id2tag,int executive_manage,int state,SelectorID_t domain)7006 _SelectorCreate(PyMOLGlobals * G, pymol::zstring_view sname, const char *sele,
7007 ObjectMolecule ** obj, int quiet, Multipick * mp,
7008 CSeqRow * rowVLA, int nRow, int **obj_idx, int *n_idx,
7009 int n_obj, const std::unordered_map<int, int>* id2tag, int executive_manage,
7010 int state, SelectorID_t domain)
7011 {
7012 sele_array_t atom{};
7013 std::string name;
7014 int c = 0;
7015 int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
7016 ObjectMolecule *embed_obj = NULL;
7017
7018 if (!SelectorIsTmp(sname)) {
7019 if (sname.starts_with('%')) {
7020 sname.remove_prefix(1);
7021 }
7022
7023 if (!WordMatchExact(G, cKeywordAll, sname.c_str(), ignore_case)) {
7024 name = UtilCleanStdStr(sname.c_str());
7025 }
7026
7027 if (name.empty()) {
7028 assert(executive_manage);
7029 return pymol::make_error("Invalid selection name '", sname.c_str(), "'");
7030 }
7031
7032 sname = name;
7033 }
7034
7035 {
7036 if(sele) {
7037 auto res = SelectorSelect(G, sele, state, domain, quiet);
7038 p_return_if_error(res);
7039 atom = std::move(res.result());
7040 } else if(id2tag) {
7041 atom = SelectorSelectFromTagDict(G, *id2tag);
7042 } else if(obj && obj[0]) { /* optimized full-object selection */
7043 if(n_obj <= 0) {
7044 embed_obj = *obj;
7045 if(obj_idx && n_idx) {
7046 atom =
7047 SelectorUpdateTableSingleObject(G, embed_obj, cSelectorUpdateTableAllStates,
7048 false, *obj_idx, *n_idx, (n_obj == 0));
7049 } else {
7050 atom =
7051 SelectorUpdateTableSingleObject(G, embed_obj, cSelectorUpdateTableAllStates,
7052 false, NULL, 0, (n_obj == 0));
7053 }
7054 } else {
7055 atom = SelectorUpdateTableMultiObjectIdxTag(G, obj, false, obj_idx, n_idx, n_obj);
7056 }
7057 } else if(mp) {
7058 atom = SelectorApplyMultipick(G, mp);
7059 } else {
7060 return pymol::make_error(__func__, " insufficient arguments");
7061 }
7062 }
7063
7064 c = SelectorEmbedSelection(G, atom.get(), sname, embed_obj, false, executive_manage);
7065 atom.reset();
7066 SelectorClean(G);
7067 /* ignore reporting on quiet */
7068 if(!quiet) {
7069 /* ignore reporting on internal/private names */
7070 if(!sname.starts_with('_')) {
7071 PRINTFB(G, FB_Selector, FB_Actions)
7072 " Selector: selection \"%s\" defined with %d atoms.\n", sname.c_str(), c ENDFB(G);
7073 }
7074 }
7075
7076 PyMOL_NeedRedisplay(G->PyMOL);
7077 return (c);
7078 }
7079
SelectorCreateFromTagDict(PyMOLGlobals * G,const char * sname,const std::unordered_map<int,int> & id2tag,int exec_managed)7080 SelectorCreateResult_t SelectorCreateFromTagDict(PyMOLGlobals * G, const char *sname, const std::unordered_map<int, int>& id2tag,
7081 int exec_managed)
7082 {
7083 return _SelectorCreate(G, sname, NULL, NULL, true, NULL, NULL, 0, NULL, NULL, 0, &id2tag,
7084 exec_managed, -1, -1);
7085 }
7086
SelectorCreateEmpty(PyMOLGlobals * G,const char * name,int exec_managed)7087 SelectorCreateResult_t SelectorCreateEmpty(PyMOLGlobals * G, const char *name, int exec_managed)
7088 {
7089 return _SelectorCreate(G, name, "none", NULL, 1, NULL, NULL, 0, NULL, 0, 0, NULL,
7090 exec_managed, -1, -1);
7091 }
7092
SelectorCreateSimple(PyMOLGlobals * G,const char * name,const char * sele)7093 SelectorCreateResult_t SelectorCreateSimple(PyMOLGlobals * G, const char *name, const char *sele)
7094 {
7095 return _SelectorCreate(G, name, sele, NULL, 1, NULL, NULL, 0, NULL, 0, 0, NULL, -1, -1,
7096 -1);
7097 }
7098
SelectorCreateFromObjectIndices(PyMOLGlobals * G,const char * sname,ObjectMolecule * obj,int * idx,int n_idx)7099 SelectorCreateResult_t SelectorCreateFromObjectIndices(PyMOLGlobals * G, const char *sname, ObjectMolecule * obj,
7100 int *idx, int n_idx)
7101 {
7102 return _SelectorCreate(G, sname, NULL, &obj, true, NULL, NULL, 0, &idx, &n_idx, -1, NULL, -1, -1, -1);
7103 /* n_obj = -1 disables numbered tags */
7104 }
7105
SelectorCreateOrderedFromObjectIndices(PyMOLGlobals * G,const char * sname,ObjectMolecule * obj,int * idx,int n_idx)7106 SelectorCreateResult_t SelectorCreateOrderedFromObjectIndices(PyMOLGlobals * G, const char *sname,
7107 ObjectMolecule * obj, int *idx, int n_idx)
7108 {
7109 return _SelectorCreate(G, sname, NULL, &obj, true, NULL, NULL, 0, &idx, &n_idx, 0, NULL, -1, -1, -1);
7110 /* assigned numbered tags */
7111 }
7112
SelectorCreateOrderedFromMultiObjectIdxTag(PyMOLGlobals * G,const char * sname,ObjectMolecule ** obj,int ** idx_tag,int * n_idx,int n_obj)7113 SelectorCreateResult_t SelectorCreateOrderedFromMultiObjectIdxTag(PyMOLGlobals * G, const char *sname,
7114 ObjectMolecule ** obj,
7115 int **idx_tag, int *n_idx, int n_obj)
7116 {
7117 return _SelectorCreate(G, sname, NULL, obj, true, NULL, NULL, 0, idx_tag, n_idx, n_obj,
7118 NULL, -1, -1, -1);
7119 }
7120
SelectorCreate(PyMOLGlobals * G,const char * sname,const char * sele,ObjectMolecule * obj,int quiet,Multipick * mp)7121 SelectorCreateResult_t SelectorCreate(PyMOLGlobals * G, const char *sname, const char *sele, ObjectMolecule * obj,
7122 int quiet, Multipick * mp)
7123 {
7124 return _SelectorCreate(G, sname, sele, &obj, quiet, mp, NULL, 0, NULL, 0, 0, NULL, -1,
7125 -1, -1);
7126 }
7127
SelectorCreateWithStateDomain(PyMOLGlobals * G,const char * sname,const char * sele,ObjectMolecule * obj,int quiet,Multipick * mp,int state,const char * domain)7128 SelectorCreateResult_t SelectorCreateWithStateDomain(PyMOLGlobals * G, const char *sname, const char *sele,
7129 ObjectMolecule * obj, int quiet, Multipick * mp,
7130 int state, const char *domain)
7131 {
7132 SelectorID_t domain_sele = cSelectionInvalid;
7133 ObjectNameType valid_name;
7134
7135 UtilNCopy(valid_name, sname, sizeof(valid_name));
7136 if(SettingGetGlobal_b(G, cSetting_validate_object_names)) {
7137 ObjectMakeValidName(G, valid_name);
7138 sname = valid_name;
7139 }
7140
7141 if(domain && domain[0]) {
7142 if(!WordMatchExact(G, cKeywordAll, domain, true)) { /* allow domain=all */
7143 domain_sele = SelectorIndexByName(G, domain);
7144 if(domain_sele < 0) {
7145
7146 PRINTFB(G, FB_Selector, FB_Errors)
7147 "Selector-Error: Invalid domain selection name \"%s\".\n", domain ENDFB(G);
7148 return -1;
7149 }
7150 }
7151 }
7152 return _SelectorCreate(G, sname, sele, &obj, quiet, mp, NULL, 0, NULL, 0, 0, NULL, -1,
7153 state, domain_sele);
7154 }
7155
7156
7157 /*========================================================================*/
~CSelector()7158 CSelector::~CSelector()
7159 {
7160 ExecutiveInvalidateSelectionIndicatorsCGO(G);
7161 }
7162
SelectorClean(PyMOLGlobals * G)7163 static void SelectorClean(PyMOLGlobals* G)
7164 {
7165 auto I = G->Selector;
7166 I->Table.clear();
7167 I->Obj.clear();
7168 }
7169
7170 /*========================================================================*/
SelectorUpdateTableSingleObject(PyMOLGlobals * G,ObjectMolecule * obj,int req_state,int no_dummies,int * idx,int n_idx,int numbered_tags)7171 static sele_array_t SelectorUpdateTableSingleObject(PyMOLGlobals * G, ObjectMolecule * obj,
7172 int req_state,
7173 int no_dummies, int *idx,
7174 int n_idx, int numbered_tags)
7175 {
7176 int a = 0;
7177 int c = 0;
7178 int modelCnt;
7179 sele_array_t result{};
7180 int tag = true;
7181 int state = req_state;
7182 CSelector *I = G->Selector;
7183
7184 PRINTFD(G, FB_Selector)
7185 "SelectorUpdateTableSingleObject-Debug: entered for %s...\n", obj->Name ENDFD;
7186
7187 SelectorClean(G);
7188
7189 switch (req_state) {
7190 case cSelectorUpdateTableAllStates:
7191 state = req_state;
7192 break;
7193 case cSelectorUpdateTableEffectiveStates:
7194 state = obj->getCurrentState();
7195 break;
7196 case cSelectorUpdateTableCurrentState:
7197 state = SceneGetState(G);
7198 break;
7199 default:
7200 if(req_state < 0)
7201 state = cSelectorUpdateTableAllStates; /* fail safe */
7202 break;
7203 }
7204
7205 switch (req_state) {
7206 case cSelectorUpdateTableAllStates:
7207 I->SeleBaseOffsetsValid = true; /* all states -> all atoms -> offsets valid */
7208 break;
7209 default:
7210 I->SeleBaseOffsetsValid = false; /* not including all atoms, so atom-based offsets are invalid */
7211 break;
7212 }
7213
7214 I->NCSet = 0;
7215 if(no_dummies) {
7216 modelCnt = 0;
7217 c = 0;
7218 } else {
7219 modelCnt = cNDummyModels;
7220 c = cNDummyAtoms;
7221 }
7222 c += obj->NAtom;
7223 if(I->NCSet < obj->NCSet)
7224 I->NCSet = obj->NCSet;
7225 modelCnt++;
7226 I->Table = std::vector<TableRec>(c);
7227 I->Obj = std::vector<ObjectMolecule*>(modelCnt, nullptr);
7228 if(no_dummies) {
7229 modelCnt = 0;
7230 c = 0;
7231 } else {
7232 c = cNDummyAtoms;
7233 modelCnt = cNDummyModels;
7234 }
7235 I->Obj[modelCnt] = obj;
7236
7237 obj->SeleBase = c;
7238
7239 if(state < 0) {
7240 for(a = 0; a < obj->NAtom; a++) {
7241 I->Table[c].model = modelCnt;
7242 I->Table[c].atom = a;
7243 c++;
7244 }
7245 } else if(state < obj->NCSet) {
7246 auto rec = I->Table.data() + c;
7247 CoordSet *cs = obj->CSet[state];
7248 if(cs) {
7249 for(a = 0; a < obj->NAtom; a++) {
7250 int ix;
7251 ix = cs->atmToIdx(a);
7252 if(ix >= 0) {
7253 rec->model = modelCnt;
7254 rec->atom = a;
7255 rec++;
7256 }
7257 }
7258 }
7259 c = rec - I->Table.data();
7260 }
7261
7262 if(idx && n_idx) {
7263 sele_array_calloc(result, c);
7264 if(n_idx > 0) {
7265 for(a = 0; a < n_idx; a++) {
7266 int at = idx[a];
7267 if(numbered_tags)
7268 tag = a + SELECTOR_BASE_TAG;
7269 if((at >= 0) && (at < obj->NAtom)) {
7270 /* create an ordered selection based on the input order of the object indices */
7271 result[obj->SeleBase + at] = tag;
7272 }
7273 }
7274 } else { /* -1 terminated list */
7275 int *at_idx = idx;
7276 int at;
7277 a = SELECTOR_BASE_TAG + 1;
7278 while((at = *(at_idx++)) >= 0) {
7279 if(numbered_tags) {
7280 tag = a++;
7281 }
7282 if((at >= 0) && (at < obj->NAtom)) {
7283 /* create an ordered selection based on the input order of the object indices */
7284 result[obj->SeleBase + at] = tag;
7285 }
7286 }
7287 }
7288 }
7289 I->Obj.resize(modelCnt);
7290 I->Table.resize(c);
7291
7292 PRINTFD(G, FB_Selector)
7293 "SelectorUpdateTableSingleObject-Debug: leaving...\n" ENDFD;
7294
7295 return (result);
7296 }
7297
7298
7299 /*========================================================================*/
SelectorUpdateTable(PyMOLGlobals * G,int req_state,SelectorID_t domain)7300 int SelectorUpdateTable(PyMOLGlobals * G, int req_state, SelectorID_t domain)
7301 {
7302 return (SelectorUpdateTableImpl(G, G->Selector, req_state, domain));
7303 }
7304
SelectorUpdateTableImpl(PyMOLGlobals * G,CSelector * I,int req_state,SelectorID_t domain)7305 int SelectorUpdateTableImpl(PyMOLGlobals * G, CSelector *I, int req_state, SelectorID_t domain)
7306 {
7307 int a = 0;
7308 ov_size c = 0;
7309 int modelCnt;
7310 int state = req_state;
7311 void *iterator = NULL;
7312 ObjectMolecule *obj = NULL;
7313
7314 /* Origin and Center are dummy objects */
7315 if(!I->Origin)
7316 I->Origin.reset(ObjectMoleculeDummyNew(G, cObjectMoleculeDummyOrigin));
7317
7318 if(!I->Center)
7319 I->Center.reset(ObjectMoleculeDummyNew(G, cObjectMoleculeDummyCenter));
7320
7321 SelectorClean(G);
7322 I->NCSet = 0;
7323
7324 /* take a summary of PyMOL's current state; foreach molecular object
7325 * sum up the number of atoms, count how many models, states, etc... */
7326 modelCnt = cNDummyModels;
7327 c = cNDummyAtoms;
7328 while(ExecutiveIterateObjectMolecule(G, &obj, &iterator)) {
7329 c += obj->NAtom;
7330 if(I->NCSet < obj->NCSet)
7331 I->NCSet = obj->NCSet;
7332 modelCnt++;
7333 }
7334 /* allocate space for each atom, in the record table */
7335 I->Table = std::vector<TableRec>(c);
7336 I->Obj = std::vector<ObjectMolecule*>(modelCnt, nullptr);
7337
7338 switch (req_state) {
7339 case cSelectorUpdateTableAllStates:
7340 I->SeleBaseOffsetsValid = true; /* all states -> all atoms -> offsets valid */
7341 break;
7342 default:
7343 I->SeleBaseOffsetsValid = false; /* not including all atoms, so atom-based offsets are invalid */
7344 break;
7345 }
7346
7347 c = 0;
7348 modelCnt = 0;
7349
7350 /* update the origin and center dummies */
7351 obj = I->Origin.get();
7352 if(obj) {
7353 I->Obj[modelCnt] = I->Origin.get();
7354 obj->SeleBase = c; /* make note of where this object starts */
7355 for(a = 0; a < obj->NAtom; a++) {
7356 I->Table[c].model = modelCnt;
7357 I->Table[c].atom = a;
7358 c++;
7359 }
7360 modelCnt++;
7361 }
7362
7363 obj = I->Center.get();
7364 if(obj) {
7365 I->Obj[modelCnt] = I->Center.get();
7366 obj->SeleBase = c; /* make note of where this object starts */
7367 for(a = 0; a < obj->NAtom; a++) {
7368 I->Table[c].model = modelCnt;
7369 I->Table[c].atom = a;
7370 c++;
7371 }
7372 modelCnt++;
7373 }
7374
7375 while(ExecutiveIterateObjectMolecule(G, &obj, &iterator)) {
7376 int skip_flag = false;
7377 if(req_state < 0) {
7378 switch (req_state) {
7379 case cSelectorUpdateTableAllStates:
7380 state = -1; /* all states */
7381 /* proceed... */
7382 break;
7383 case cSelectorUpdateTableCurrentState:
7384 state = SettingGetGlobal_i(G, cSetting_state) - 1;
7385 break;
7386 case cSelectorUpdateTableEffectiveStates:
7387 state = obj->getCurrentState();
7388 break;
7389 default: /* unknown input -- fail safe (all states) */
7390 state = -1;
7391 break;
7392 }
7393 } else {
7394 if(state >= obj->NCSet)
7395 skip_flag = true;
7396 else if(!obj->CSet[state])
7397 skip_flag = true;
7398 }
7399
7400 if(!skip_flag) {
7401 /* fill in the table */
7402 I->Obj[modelCnt] = obj;
7403 {
7404 int n_atom = obj->NAtom;
7405 auto rec = I->Table.data() + c;
7406 TableRec *start_rec = rec;
7407 if(state < 0) { /* all states */
7408 if(domain < 0) { /* domain=all */
7409 for(a = 0; a < n_atom; a++) {
7410 rec->model = modelCnt;
7411 rec->atom = a;
7412 rec++;
7413 }
7414 } else {
7415 const AtomInfoType *ai = obj->AtomInfo.data();
7416 int included_one = false;
7417 int excluded_one = false;
7418 for(a = 0; a < n_atom; a++) {
7419 if(SelectorIsMember(G, ai->selEntry, domain)) {
7420 rec->model = modelCnt;
7421 rec->atom = a;
7422 rec++;
7423 included_one = true;
7424 } else {
7425 excluded_one = true;
7426 }
7427 ai++;
7428 }
7429 if(included_one && excluded_one)
7430 I->SeleBaseOffsetsValid = false; /* partial objects in domain, so
7431 base offsets are invalid */
7432 }
7433 } else { /* specific states */
7434 CoordSet *cs;
7435 int idx;
7436 if(domain < 0) {
7437 for(a = 0; a < n_atom; a++) {
7438 /* does coordinate exist for this atom in the requested state? */
7439 if(state < obj->NCSet)
7440 cs = obj->CSet[state];
7441 else
7442 cs = NULL;
7443 if(cs) {
7444 idx = cs->atmToIdx(a);
7445 if(idx >= 0) {
7446 rec->model = modelCnt;
7447 rec->atom = a;
7448 rec++;
7449 }
7450 }
7451 }
7452 } else {
7453 const AtomInfoType *ai = obj->AtomInfo.data();
7454 for(a = 0; a < n_atom; a++) {
7455 /* does coordinate exist for this atom in the requested state? */
7456 if(state < obj->NCSet)
7457 cs = obj->CSet[state];
7458 else
7459 cs = NULL;
7460 if(cs) {
7461 idx = cs->atmToIdx(a);
7462 if(idx >= 0) {
7463 if(SelectorIsMember(G, ai->selEntry, domain)) {
7464 rec->model = modelCnt;
7465 rec->atom = a;
7466 rec++;
7467 }
7468 }
7469 }
7470 ai++;
7471 }
7472 }
7473 }
7474 if(rec != start_rec) { /* skip excluded models */
7475 modelCnt++;
7476 obj->SeleBase = c; /* make note of where this object starts */
7477 c += (rec - start_rec);
7478 } else {
7479 obj->SeleBase = 0;
7480 }
7481 }
7482 }
7483 }
7484 I->Obj.resize(modelCnt);
7485 I->Table.resize(c);
7486 /* printf("selector update table state=%d, natom=%d\n",req_state,c); */
7487 return (true);
7488 }
7489
7490
7491 /*========================================================================*/
SelectorSelect(PyMOLGlobals * G,const char * sele,int state,SelectorID_t domain,int quiet)7492 static pymol::Result<sele_array_t> SelectorSelect(
7493 PyMOLGlobals* G, const char* sele, int state, SelectorID_t domain, int quiet)
7494 {
7495 SelectorUpdateTable(G, state, domain);
7496 auto parsed = SelectorParse(G, sele);
7497 if (!parsed.empty()) {
7498 return SelectorEvaluate(G, parsed, state, quiet);
7499 }
7500 return {};
7501 }
7502
7503
7504 /*========================================================================*/
SelectorModulate1(PyMOLGlobals * G,EvalElem * base,int state)7505 static int SelectorModulate1(PyMOLGlobals * G, EvalElem * base, int state)
7506 {
7507 CSelector *I = G->Selector;
7508 int a, d, e;
7509 int c = 0;
7510 float dist;
7511 int nbond;
7512 float *v2;
7513 CoordSet *cs;
7514 int ok = true;
7515 int nCSet;
7516 int n1, at, idx;
7517 ObjectMolecule *obj;
7518
7519 if(state < 0) {
7520 switch (state) {
7521 case -2:
7522 case -3:
7523 state = SceneGetState(G);
7524 break;
7525 }
7526 }
7527
7528 base[1].sele = std::move(base[0].sele); /* base1 has the mask */
7529 base->sele_calloc(I->Table.size());
7530 base->sele_check_ok(ok);
7531 if (!ok)
7532 return false;
7533 switch (base[1].code) {
7534 case SELE_ARD_:
7535 case SELE_EXP_:
7536 if(!sscanf(base[2].text(), "%f", &dist))
7537 ok = ErrMessage(G, "Selector", "Invalid distance.");
7538 if(ok) {
7539 for(d = 0; d < I->NCSet; d++) {
7540 if((state < 0) || (d == state)) {
7541 n1 = 0;
7542
7543 const size_t table_size = I->Table.size();
7544 auto coords_flat = std::vector<float>(table_size * 3);
7545 auto* coords = pymol::reshape<3>(coords_flat.data());
7546 auto Flag1 = std::vector<MapFlag_t>(table_size, 0);
7547
7548 for(a = 0; a < I->Table.size(); a++) {
7549 at = I->Table[a].atom;
7550 obj = I->Obj[I->Table[a].model];
7551 if(d < obj->NCSet)
7552 cs = obj->CSet[d];
7553 else
7554 cs = NULL;
7555 if(cs) {
7556 if (CoordSetGetAtomVertex(cs, at, coords[a])) {
7557 Flag1[a] = true;
7558 n1++;
7559 }
7560 }
7561 }
7562 if(n1) {
7563 std::unique_ptr<MapType> map(MapNewFlagged(G, -dist,
7564 pymol::flatten(coords), table_size, nullptr, Flag1.data()));
7565 CHECKOK(ok, map);
7566 if(ok) {
7567 nCSet = SelectorGetArrayNCSet(G, base[1].sele, false);
7568 for(e = 0; ok && e < nCSet; e++) {
7569 if((state < 0) || (e == state)) {
7570 for(a = 0; ok && a < I->Table.size(); a++) {
7571 if(base[1].sele[a]) {
7572 at = I->Table[a].atom;
7573 obj = I->Obj[I->Table[a].model];
7574 if(e < obj->NCSet)
7575 cs = obj->CSet[e];
7576 else
7577 cs = NULL;
7578 if(cs) {
7579 idx = cs->atmToIdx(at);
7580 if(idx >= 0) {
7581 v2 = cs->coordPtr(idx);
7582 for (const auto j : MapEIter(*map, v2)) {
7583 if (!base[0].sele[j] &&
7584 (!base[1].sele[j] ||
7585 base[1].code == SELE_EXP_)) {
7586 /*exclude current selection */
7587 if (within3f(coords[j], v2, dist))
7588 base[0].sele[j] = true;
7589 }
7590 }
7591 }
7592 }
7593 }
7594 }
7595 }
7596 }
7597 }
7598 }
7599 }
7600 }
7601 }
7602 break;
7603
7604 case SELE_EXT_:
7605 if(sscanf(base[2].text(), "%d", &nbond) != 1)
7606 ok = ErrMessage(G, "Selector", "Invalid bond count.");
7607 if(ok) {
7608 ObjectMolecule *lastObj = NULL;
7609 int a, n, a0, a1, a2;
7610 std::copy_n(base[1].sele_data(), I->Table.size(), base[0].sele_data());
7611 while((nbond--) > 0) {
7612 std::swap(base[1].sele, base[0].sele);
7613 for(a = cNDummyAtoms; ok && a < I->Table.size(); a++) {
7614 if(base[1].sele[a]) {
7615 if(I->Obj[I->Table[a].model] != lastObj) {
7616 lastObj = I->Obj[I->Table[a].model];
7617 ObjectMoleculeUpdateNeighbors(lastObj);
7618 }
7619 a0 = I->Table[a].atom;
7620 n = lastObj->Neighbor[a0];
7621 n++;
7622 while(1) {
7623 a1 = lastObj->Neighbor[n];
7624 if(a1 < 0)
7625 break;
7626 if((a2 = SelectorGetObjAtmOffset(I, lastObj, a1)) >= 0) {
7627 base[0].sele[a2] = 1;
7628 n += 2;
7629 }
7630 }
7631 }
7632 }
7633 }
7634 base[1].sele_free();
7635 }
7636 break;
7637
7638 case SELE_GAP_:
7639 if(!sscanf(base[2].text(), "%f", &dist))
7640 ok = ErrMessage(G, "Selector", "Invalid distance.");
7641 if(ok) {
7642 for(a = 0; a < I->Table.size(); a++) {
7643 obj = I->Obj[I->Table[a].model];
7644 at = I->Table[a].atom;
7645 I->Table[a].f1 = obj->AtomInfo[at].vdw;
7646 base[0].sele[a] = true; /* start selected, subtract off */
7647 c = I->Table.size();
7648 }
7649 for(d = 0; d < I->NCSet; d++) {
7650 if((state < 0) || (d == state)) {
7651 n1 = 0;
7652
7653 auto Flag1 = std::vector<MapFlag_t>(I->Table.size(), 0);
7654 auto Vertex = std::vector<float>(I->Table.size() * 3, 0.0f);
7655
7656 for(a = 0; a < I->Table.size(); a++) {
7657 obj = I->Obj[I->Table[a].model];
7658 at = I->Table[a].atom;
7659 if(d < obj->NCSet)
7660 cs = obj->CSet[d];
7661 else
7662 cs = NULL;
7663 if(cs) {
7664 if(CoordSetGetAtomVertex(cs, at, Vertex.data() + 3 * a)) {
7665 Flag1[a] = true;
7666 n1++;
7667 }
7668 }
7669 }
7670 if(n1) {
7671 std::unique_ptr<MapType> map(MapNewFlagged(G, -(dist + 2 * MAX_VDW),
7672 Vertex.data(), I->Table.size(), nullptr, Flag1.data()));
7673 CHECKOK(ok, map);
7674 if(ok) {
7675
7676 nCSet = SelectorGetArrayNCSet(G, base[1].sele, false);
7677 for(e = 0; ok && e < nCSet; e++) {
7678 if((state < 0) || (e == state)) {
7679 for(a = 0; ok && a < I->Table.size(); a++) {
7680 if(base[1].sele[a]) {
7681 at = I->Table[a].atom;
7682 obj = I->Obj[I->Table[a].model];
7683 if(e < obj->NCSet)
7684 cs = obj->CSet[e];
7685 else
7686 cs = NULL;
7687 if(cs) {
7688 idx = cs->atmToIdx(at);
7689
7690 if(idx >= 0) {
7691 v2 = cs->coordPtr(idx);
7692 for (const auto j : MapEIter(*map, v2)) {
7693 if((base[0].sele[j]) && (!base[1].sele[j])) { /*exclude current selection */
7694 if(within3f(Vertex.data() + 3 * j, v2, dist + /* eliminate atoms w/o gap */
7695 I->Table[a].f1 + I->Table[j].f1)) {
7696 base[0].sele[j] = false;
7697 c--;
7698 }
7699 } else if(base[1].sele[j]) {
7700 base[0].sele[j] = false;
7701 c--;
7702 }
7703 }
7704 }
7705 }
7706 }
7707 }
7708 }
7709 }
7710 }
7711 }
7712 }
7713 }
7714 }
7715 break;
7716 }
7717 base[1].sele_free();
7718 if(Feedback(G, FB_Selector, FB_Debugging)) {
7719 c = 0;
7720 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7721 if(base[0].sele[a])
7722 c++;
7723 fprintf(stderr, "SelectorModulate0: %d atoms selected.\n", c);
7724 }
7725 return (ok);
7726
7727 }
7728
7729
7730 /*========================================================================*/
SelectorSelect0(PyMOLGlobals * G,EvalElem * passed_base)7731 static int SelectorSelect0(PyMOLGlobals * G, EvalElem * passed_base)
7732 {
7733 CSelector *I = G->Selector;
7734 int a, b, flag;
7735 EvalElem *base = passed_base;
7736 int c = 0;
7737 ObjectMolecule *obj, *cur_obj = NULL;
7738 CoordSet *cs;
7739
7740 base->type = STYP_LIST;
7741 base->sele_calloc(I->Table.size());
7742 base->sele_err_chk_ptr(G);
7743
7744 switch (base->code) {
7745 case SELE_HBAs:
7746 case SELE_HBDs:
7747 case SELE_DONz:
7748 case SELE_ACCz:
7749
7750 {
7751 /* first, verify chemistry for all atoms... */
7752 ObjectMolecule *lastObj = NULL, *obj;
7753 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
7754 obj = I->Obj[I->Table[a].model];
7755 if(obj != lastObj) {
7756 ObjectMoleculeUpdateNeighbors(obj);
7757 ObjectMoleculeVerifyChemistry(obj, -1);
7758 lastObj = obj;
7759 }
7760 }
7761 }
7762 switch (base->code) {
7763 case SELE_HBAs:
7764 case SELE_ACCz:
7765 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7766 base[0].sele[a] = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].hb_acceptor;
7767 break;
7768 case SELE_HBDs:
7769 case SELE_DONz:
7770 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7771 base[0].sele[a] = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].hb_donor;
7772 break;
7773
7774 }
7775 break;
7776 case SELE_NONz:
7777 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7778 base[0].sele[a] = false;
7779 break;
7780 case SELE_BNDz:
7781 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7782 base[0].sele[a] = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].bonded;
7783 break;
7784 case SELE_HETz:
7785 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7786 base[0].sele[a] = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].hetatm;
7787 break;
7788 case SELE_HYDz:
7789 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7790 base[0].sele[a] = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].isHydrogen();
7791 break;
7792 case SELE_METz:
7793 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
7794 base[0].sele[a] = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].isMetal();
7795 }
7796 break;
7797 case SELE_BB_z:
7798 case SELE_SC_z:
7799 {
7800 AtomInfoType *ai;
7801 flag = (base->code == SELE_BB_z);
7802 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
7803 ai = I->Obj[I->Table[a].model]->AtomInfo + I->Table[a].atom;
7804 if(!(ai->flags & cAtomFlag_polymer)) {
7805 base[0].sele[a] = 0;
7806 continue;
7807 }
7808 base[0].sele[a] = !flag;
7809 for(b = 0; backbone_names[b][0]; b++) {
7810 if(!(strcmp(LexStr(G, ai->name), backbone_names[b]))) {
7811 base[0].sele[a] = flag;
7812 break;
7813 }
7814 }
7815 }
7816 }
7817 break;
7818 case SELE_FXDz:
7819 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7820 base[0].sele[a] =
7821 I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & cAtomFlag_fix;
7822 break;
7823 case SELE_RSTz:
7824 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7825 base[0].sele[a] =
7826 I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & cAtomFlag_restrain;
7827 break;
7828 case SELE_POLz:
7829 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7830 base[0].sele[a] =
7831 I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & cAtomFlag_polymer;
7832 break;
7833 case SELE_PROz:
7834 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7835 base[0].sele[a] =
7836 I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & cAtomFlag_protein;
7837 break;
7838 case SELE_NUCz:
7839 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7840 base[0].sele[a] =
7841 I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & cAtomFlag_nucleic;
7842 break;
7843 case SELE_SOLz:
7844 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7845 base[0].sele[a] =
7846 I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & cAtomFlag_solvent;
7847 break;
7848 case SELE_PTDz:
7849 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7850 base[0].sele[a] = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].protekted;
7851 break;
7852 case SELE_MSKz:
7853 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7854 base[0].sele[a] = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].masked;
7855 break;
7856 case SELE_ORGz:
7857 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7858 base[0].sele[a] =
7859 I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & cAtomFlag_organic;
7860 break;
7861 case SELE_INOz:
7862 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7863 base[0].sele[a] =
7864 I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & cAtomFlag_inorganic;
7865 break;
7866 case SELE_GIDz:
7867 for(a = cNDummyAtoms; a < I->Table.size(); a++)
7868 base[0].sele[a] =
7869 I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & cAtomFlag_guide;
7870 break;
7871
7872 case SELE_PREz:
7873 cs = NULL;
7874 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
7875 base[0].sele[a] = false;
7876 obj = I->Obj[I->Table[a].model];
7877 if(obj != cur_obj) { /* different object */
7878 cs = obj->getCoordSet(cSelectorUpdateTableCurrentState);
7879 cur_obj = obj;
7880 }
7881 if(cs) {
7882 if(cs->atmToIdx(I->Table[a].atom) >= 0) {
7883 base[0].sele[a] = true;
7884 c++;
7885 }
7886 }
7887 }
7888 break;
7889 case SELE_ALLz:
7890 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
7891 base[0].sele[a] = true;
7892 c++;
7893 }
7894 break;
7895 case SELE_ORIz:
7896 for(a = 0; a < I->Table.size(); a++) {
7897 base[0].sele[a] = false;
7898 c++;
7899 }
7900 if(I->Origin)
7901 ObjectMoleculeDummyUpdate(I->Origin.get(), cObjectMoleculeDummyOrigin);
7902 base[0].sele[cDummyOrigin] = true;
7903 break;
7904 case SELE_CENz:
7905 for(a = 0; a < I->Table.size(); a++) {
7906 base[0].sele[a] = false;
7907 c++;
7908 }
7909 if(I->Center)
7910 ObjectMoleculeDummyUpdate(I->Center.get(), cObjectMoleculeDummyCenter);
7911 base[0].sele[cDummyCenter] = true;
7912 break;
7913 case SELE_VISz:
7914 {
7915 ObjectMolecule *last_obj = NULL;
7916 AtomInfoType *ai;
7917 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
7918 flag = false;
7919 obj = I->Obj[I->Table[a].model];
7920 if(obj->Enabled) {
7921 ai = obj->AtomInfo + I->Table[a].atom;
7922
7923 if(last_obj != obj) {
7924 ObjectMoleculeUpdateNeighbors(obj);
7925 ObjectMoleculeVerifyChemistry(obj, -1);
7926 last_obj = obj;
7927 }
7928
7929 flag = ai->isVisible();
7930 }
7931 base[0].sele[a] = flag;
7932 if(flag)
7933 c++;
7934 }
7935 }
7936 break;
7937 case SELE_ENAz:
7938 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
7939 flag = (I->Obj[I->Table[a].model]->Enabled);
7940 base[0].sele[a] = flag;
7941 if(flag)
7942 c++;
7943 }
7944 break;
7945 }
7946 PRINTFD(G, FB_Selector)
7947 " %s: %d atoms selected.\n", __func__, c ENDFD;
7948
7949 return (1);
7950 }
7951
7952
7953 /*========================================================================*/
SelectorSelect1(PyMOLGlobals * G,EvalElem * base,int quiet)7954 static pymol::Result<> SelectorSelect1(PyMOLGlobals * G, EvalElem * base, int quiet)
7955 {
7956 CSelector *I = G->Selector;
7957 auto IM = I->mgr;
7958 CWordMatcher *matcher = NULL;
7959 int a, b, c = 0, hit_flag;
7960 ObjectMolecule *obj, *last_obj;
7961 int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
7962 int ignore_case_chain = SettingGetGlobal_b(G, cSetting_ignore_case_chain);
7963 int I_NAtom = I->Table.size();
7964 int *base_0_sele_a;
7965
7966 int model, s, col_idx;
7967 int flag;
7968 int index, state;
7969 int rep_mask;
7970 const char *wildcard = SettingGetGlobal_s(G, cSetting_wildcard);
7971
7972 ObjectMolecule *cur_obj = NULL;
7973 CoordSet *cs = NULL;
7974
7975 base->type = STYP_LIST;
7976 base->sele_calloc(I_NAtom); /* starting with zeros */
7977 base->sele_err_chk_ptr(G);
7978 switch (base->code) {
7979 case SELE_PEPs:
7980 if(base[1].text()[0]) {
7981 AtomInfoType *last_ai0 = NULL, *ai0;
7982 for(a = cNDummyAtoms; a < I_NAtom; a++) {
7983 ai0 = I->Obj[I->Table[a].model]->AtomInfo + I->Table[a].atom;
7984 if(!AtomInfoSameResidueP(G, ai0, last_ai0)) { /* new starting residue */
7985 int match_found = false;
7986 const char *ch = base[1].text(); /* sequence argument */
7987 AtomInfoType *ai1, *last_ai1 = NULL;
7988 for(b = a; b < I_NAtom; b++) {
7989 ai1 = I->Obj[I->Table[b].model]->AtomInfo + I->Table[b].atom;
7990 if(!AtomInfoSameResidueP(G, ai1, last_ai1)) {
7991 if(*ch != '-') { /* if not skipping this residue */
7992 if(!((*ch == '+') || (SeekerGetAbbr(G, LexStr(G, ai1->resn), 'O', 0) == *ch))) { /* if a mismatch */
7993 break;
7994 }
7995 }
7996 ch++;
7997 if(!*ch) { /* end of sequence pattern */
7998 match_found = true;
7999 break;
8000 }
8001 last_ai1 = ai1;
8002 }
8003 }
8004 if(match_found) {
8005 const char *ch = base[1].text(); /* sequence argument */
8006 AtomInfoType *ai1, *last_ai1 = NULL, *ai2;
8007 for(b = a; b < I_NAtom; b++) {
8008 ai1 = I->Obj[I->Table[b].model]->AtomInfo + I->Table[b].atom;
8009 if(!AtomInfoSameResidueP(G, ai1, last_ai1)) {
8010 if(*ch != '-') { /* if not skipping this residue */
8011 if((*ch == '+') || (SeekerGetAbbr(G, LexStr(G, ai1->resn), 'O', 0) == *ch)) { /* if matched */
8012 int d;
8013 for(d = b; d < I_NAtom; d++) {
8014 ai2 = I->Obj[I->Table[d].model]->AtomInfo + I->Table[d].atom; /* complete residue */
8015 if(AtomInfoSameResidue(G, ai1, ai2)) {
8016 c++;
8017 base[0].sele[d] = true;
8018 }
8019 }
8020 }
8021 }
8022 ch++;
8023 if(!*ch) { /* end of sequence pattern */
8024 break;
8025 }
8026 last_ai1 = ai1;
8027 }
8028 }
8029 }
8030 }
8031 }
8032 }
8033 break;
8034 case SELE_IDXs:
8035 {
8036 CWordMatchOptions options;
8037
8038 WordMatchOptionsConfigInteger(&options);
8039
8040 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8041 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8042
8043 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8044 auto& table_a = I->Table[a];
8045 if((*base_0_sele_a = WordMatcherMatchInteger(matcher, table_a.atom + 1)))
8046 c++;
8047 base_0_sele_a++;
8048 }
8049 WordMatcherFree(matcher);
8050 }
8051
8052 }
8053 break;
8054 case SELE_ID_s:
8055 {
8056 CWordMatchOptions options;
8057
8058 WordMatchOptionsConfigInteger(&options);
8059
8060 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8061 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8062
8063 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8064 auto& table_a = I->Table[a];
8065 if((*base_0_sele_a =
8066 WordMatcherMatchInteger(matcher,
8067 I->Obj[table_a.model]->AtomInfo[table_a.atom].id)))
8068 c++;
8069 base_0_sele_a++;
8070 }
8071 WordMatcherFree(matcher);
8072 }
8073 }
8074 break;
8075 case SELE_RNKs:
8076 {
8077 CWordMatchOptions options;
8078
8079 WordMatchOptionsConfigInteger(&options);
8080
8081 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8082
8083 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8084 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8085
8086 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8087 auto& table_a = I->Table[a];
8088 if((*base_0_sele_a =
8089 WordMatcherMatchInteger(matcher,
8090 I->Obj[table_a.model]->AtomInfo[table_a.atom].
8091 rank)))
8092 c++;
8093 base_0_sele_a++;
8094 }
8095 WordMatcherFree(matcher);
8096 }
8097 }
8098 break;
8099 case SELE_NAMs:
8100 {
8101 CWordMatchOptions options;
8102 const char *atom_name_wildcard = SettingGetGlobal_s(G, cSetting_atom_name_wildcard);
8103
8104 if(!atom_name_wildcard[0])
8105 atom_name_wildcard = wildcard;
8106
8107 WordMatchOptionsConfigAlphaList(&options, atom_name_wildcard[0], ignore_case);
8108
8109 matcher = WordMatcherNew(G, base[1].text(), &options, false);
8110
8111 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8112 last_obj = NULL;
8113 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8114 auto& table_a = I->Table[a];
8115 obj = I->Obj[table_a.model];
8116 if(obj != last_obj) {
8117
8118 /* allow objects to have their own atom_name_wildcards...this is a tricky workaround
8119 for handling nucleic acid structures that use "*" in atom names */
8120
8121 const char *atom_name_wildcard =
8122 SettingGet_s(G, obj->Setting, NULL, cSetting_atom_name_wildcard);
8123
8124 if(!atom_name_wildcard[0])
8125 atom_name_wildcard = wildcard;
8126
8127 if(options.wildcard != atom_name_wildcard[0]) {
8128 options.wildcard = atom_name_wildcard[0];
8129 if(matcher)
8130 WordMatcherFree(matcher);
8131 matcher = WordMatcherNew(G, base[1].text(), &options, false);
8132 if(!matcher)
8133 WordPrimeCommaMatch(G, &base[1].m_text[0] /* replace '+' with ',' */);
8134 }
8135 last_obj = obj;
8136 }
8137
8138 const char * name = LexStr(G, obj->AtomInfo[table_a.atom].name);
8139 if(matcher)
8140 hit_flag =
8141 WordMatcherMatchAlpha(matcher,
8142 name);
8143 else
8144 hit_flag = (WordMatchCommaExact(G, base[1].text(),
8145 name,
8146 ignore_case) < 0);
8147
8148 if((*base_0_sele_a = hit_flag))
8149 c++;
8150 base_0_sele_a++;
8151 }
8152 if(matcher)
8153 WordMatcherFree(matcher);
8154 }
8155 break;
8156 case SELE_TTYs:
8157 {
8158 CWordMatchOptions options;
8159
8160 WordMatchOptionsConfigAlphaList(&options, wildcard[0], ignore_case);
8161
8162 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8163
8164 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8165 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8166
8167 #ifndef NO_MMLIBS
8168 #endif
8169 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8170 auto& table_a = I->Table[a];
8171 auto ai = I->Obj[table_a.model]->AtomInfo + table_a.atom;
8172 #ifndef NO_MMLIBS
8173 #endif
8174 if((*base_0_sele_a = WordMatcherMatchAlpha(matcher, LexStr(G, ai->textType))))
8175 c++;
8176 base_0_sele_a++;
8177 }
8178 WordMatcherFree(matcher);
8179 }
8180 }
8181 break;
8182 case SELE_ELEs:
8183 {
8184 CWordMatchOptions options;
8185
8186 WordMatchOptionsConfigAlphaList(&options, wildcard[0], ignore_case);
8187
8188 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8189
8190 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8191 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8192
8193 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8194 auto& table_a = I->Table[a];
8195 if((*base_0_sele_a =
8196 WordMatcherMatchAlpha(matcher,
8197 I->Obj[table_a.model]->AtomInfo[table_a.atom].elem)))
8198 c++;
8199 base_0_sele_a++;
8200 }
8201 WordMatcherFree(matcher);
8202 }
8203 }
8204 break;
8205 case SELE_STRO:
8206 {
8207 CWordMatchOptions options;
8208 WordMatchOptionsConfigAlphaList(&options, wildcard[0], ignore_case);
8209
8210 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8211
8212
8213 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8214 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8215 #ifndef NO_MMLIBS
8216 #endif
8217 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8218 auto& table_a = I->Table[a];
8219 #ifndef NO_MMLIBS
8220 #endif
8221 const char * mmstereotype =
8222 AtomInfoGetStereoAsStr(I->Obj[table_a.model]->AtomInfo + table_a.atom);
8223 if((*base_0_sele_a =
8224 WordMatcherMatchAlpha(matcher,mmstereotype)))
8225 c++;
8226 base_0_sele_a++;
8227 }
8228 WordMatcherFree(matcher);
8229 }
8230 }
8231 break;
8232 case SELE_REPs:
8233 rep_mask = 0;
8234 WordPrimeCommaMatch(G, &base[1].m_text[0] /* replace '+' with ',' */);
8235 for(a = 0; rep_names[a].word[0]; a++) {
8236 if(WordMatchComma(G, base[1].text(), rep_names[a].word, ignore_case) < 0)
8237 rep_mask |= rep_names[a].value;
8238 }
8239 for(SelectorAtomIterator iter(I); iter.next();) {
8240 if(iter.getAtomInfo()->visRep & rep_mask) {
8241 base[0].sele[iter.a] = true;
8242 c++;
8243 } else {
8244 base[0].sele[iter.a] = false;
8245 }
8246 }
8247 break;
8248 case SELE_COLs:
8249 col_idx = ColorGetIndex(G, base[1].text());
8250 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8251 base[0].sele[a] = false;
8252 if(I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].color == col_idx) {
8253 base[0].sele[a] = true;
8254 c++;
8255 }
8256 }
8257 break;
8258 case SELE_CCLs:
8259 case SELE_RCLs:
8260 // setting index
8261 index = (base->code == SELE_CCLs) ? cSetting_cartoon_color : cSetting_ribbon_color;
8262 col_idx = ColorGetIndex(G, base[1].text());
8263 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8264 base[0].sele[a] = false;
8265 {
8266 AtomInfoType *ai = I->Obj[I->Table[a].model]->AtomInfo + I->Table[a].atom;
8267 int value;
8268 if (AtomSettingGetIfDefined(G, ai, index, &value)) {
8269 if(value == col_idx) {
8270 base[0].sele[a] = true;
8271 c++;
8272 }
8273 }
8274 }
8275 }
8276 break;
8277 case SELE_CHNs:
8278 case SELE_SEGs:
8279 case SELE_CUST:
8280 case SELE_LABs:
8281 {
8282 CWordMatchOptions options;
8283
8284 WordMatchOptionsConfigAlphaList(&options, wildcard[0], ignore_case_chain);
8285
8286 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8287
8288 int offset = 0;
8289 switch (base->code) {
8290 case SELE_CHNs:
8291 offset = offsetof(AtomInfoType, chain);
8292 break;
8293 case SELE_SEGs:
8294 offset = offsetof(AtomInfoType, segi);
8295 break;
8296 case SELE_CUST:
8297 offset = offsetof(AtomInfoType, custom);
8298 break;
8299 case SELE_LABs:
8300 offset = offsetof(AtomInfoType, label);
8301 break;
8302 default:
8303 printf("coding error: missing case\n");
8304 }
8305
8306 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8307 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8308
8309 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8310 auto& table_a = I->Table[a];
8311 if((*base_0_sele_a =
8312 WordMatcherMatchAlpha(matcher, LexStr(G,
8313 *reinterpret_cast<decltype(AtomInfoType::chain)*>
8314 (((char*)(I->Obj[table_a.model]->AtomInfo + table_a.atom)) + offset)
8315 ))))
8316 c++;
8317 base_0_sele_a++;
8318 }
8319 WordMatcherFree(matcher);
8320 }
8321 }
8322 break;
8323 case SELE_SSTs:
8324 {
8325 CWordMatchOptions options;
8326
8327 WordMatchOptionsConfigAlphaList(&options, wildcard[0], ignore_case);
8328
8329 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8330
8331 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8332 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8333
8334 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8335 auto& table_a = I->Table[a];
8336 if((*base_0_sele_a =
8337 WordMatcherMatchAlpha(matcher,
8338 I->Obj[table_a.model]->AtomInfo[table_a.atom].
8339 ssType)))
8340 c++;
8341 base_0_sele_a++;
8342 }
8343 WordMatcherFree(matcher);
8344 }
8345 }
8346 break;
8347 case SELE_STAs:
8348 sscanf(base[1].text(), "%d", &state);
8349 state = state - 1;
8350 obj = NULL;
8351
8352 if (state < 0 && state != cSelectorUpdateTableCurrentState) {
8353 return pymol::make_error(
8354 "state ", state + 1, " unsupported (must be -1 (current) or >=1)");
8355 } else {
8356 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8357 base[0].sele[a] = false;
8358 obj = I->Obj[I->Table[a].model];
8359 if(obj != cur_obj) { /* different object */
8360 cs = obj->getCoordSet(state);
8361 cur_obj = obj;
8362 }
8363 if(cs) {
8364 if(cs->atmToIdx(I->Table[a].atom) >= 0) {
8365 base[0].sele[a] = true;
8366 c++;
8367 }
8368 }
8369 }
8370 }
8371 break;
8372 case SELE_ALTs:
8373 {
8374 CWordMatchOptions options;
8375
8376 WordMatchOptionsConfigAlphaList(&options, wildcard[0], ignore_case);
8377
8378 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8379
8380 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8381 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8382
8383 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8384 auto& table_a = I->Table[a];
8385 if((*base_0_sele_a =
8386 WordMatcherMatchAlpha(matcher,
8387 I->Obj[table_a.model]->AtomInfo[table_a.atom].alt)))
8388 c++;
8389 base_0_sele_a++;
8390 }
8391 WordMatcherFree(matcher);
8392 }
8393 }
8394 break;
8395 case SELE_FLGs:
8396 sscanf(base[1].text(), "%d", &flag);
8397 flag = (1 << flag);
8398 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8399 if(I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].flags & flag) {
8400 base[0].sele[a] = true;
8401 c++;
8402 } else
8403 base[0].sele[a] = false;
8404 }
8405 break;
8406 case SELE_NTYs:
8407 {
8408 CWordMatchOptions options;
8409
8410 WordMatchOptionsConfigInteger(&options);
8411
8412 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8413 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8414
8415 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8416 auto& table_a = I->Table[a];
8417 if((*base_0_sele_a =
8418 WordMatcherMatchInteger(matcher,
8419 I->Obj[table_a.model]->AtomInfo[table_a.atom].
8420 customType)))
8421 c++;
8422 base_0_sele_a++;
8423 }
8424 WordMatcherFree(matcher);
8425 }
8426 }
8427 break;
8428 case SELE_RSIs:
8429 {
8430 CWordMatchOptions options;
8431 AtomInfoType *ai;
8432
8433 WordMatchOptionsConfigMixed(&options, wildcard[0], ignore_case);
8434
8435 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8436
8437 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8438 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8439
8440 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8441 auto& table_a = I->Table[a];
8442 ai = I->Obj[table_a.model]->AtomInfo + table_a.atom;
8443 char resi[8];
8444 AtomResiFromResv(resi, sizeof(resi), ai);
8445 if((*base_0_sele_a = WordMatcherMatchMixed(matcher, resi, ai->resv)))
8446 c++;
8447 base_0_sele_a++;
8448 }
8449 WordMatcherFree(matcher);
8450 }
8451 }
8452 break;
8453 case SELE_RSNs:
8454 {
8455 CWordMatchOptions options;
8456
8457 WordMatchOptionsConfigAlphaList(&options, wildcard[0], ignore_case);
8458
8459 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8460
8461 if((matcher = WordMatcherNew(G, base[1].text(), &options, true))) {
8462 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8463
8464 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8465 auto& table_a = I->Table[a];
8466 auto& resn = I->Obj[table_a.model]->AtomInfo[table_a.atom].resn;
8467 if((*base_0_sele_a =
8468 WordMatcherMatchAlpha(matcher, LexStr(G, resn))))
8469 c++;
8470 base_0_sele_a++;
8471 }
8472 WordMatcherFree(matcher);
8473 }
8474 }
8475 break;
8476 case SELE_SELs:
8477 {
8478 const char *word = base[1].text();
8479 WordType activeselename = "";
8480 int enabled_only = false;
8481 CWordMatchOptions options;
8482
8483 if(word[0] == '?') {
8484 word++;
8485 if(word[0] == '?') {
8486 ExecutiveGetActiveSeleName(G, activeselename, false, false);
8487 enabled_only = true;
8488 word++;
8489 }
8490 }
8491 WordMatchOptionsConfigAlpha(&options, wildcard[0], ignore_case);
8492
8493 if((matcher = WordMatcherNew(G, word, &options, false))) {
8494
8495 for(a = 0; a < I_NAtom; a++) /* zero out first before iterating through selections */
8496 base[0].sele[a] = false;
8497
8498 for (const auto& rec : I->mgr->Info) {
8499 if (rec.name.empty()) {
8500 // TODO Can this happen? Why?
8501 PRINTFB(G, FB_Selector, FB_Warnings)
8502 " Selector-Unexpected: Empty selection name (ID:%d)\n",
8503 rec.ID ENDFB(G);
8504 break;
8505 }
8506 if (WordMatcherMatchAlpha(matcher, rec.name.c_str())) {
8507 if (!enabled_only || activeselename == rec.name) {
8508 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8509 s = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].selEntry;
8510 while(s) {
8511 if (I->mgr->Member[s].selection == rec.ID) {
8512 if(!base[0].sele[a]) {
8513 base[0].sele[a] = I->mgr->Member[s].tag;
8514 c++;
8515 }
8516 }
8517 s = IM->Member[s].next;
8518 }
8519 }
8520 }
8521 }
8522 }
8523 WordMatcherFree(matcher);
8524
8525 /* must also allow for group name pattern matches */
8526
8527 {
8528 int group_list_id;
8529 if((group_list_id = ExecutiveGetExpandedGroupListFromPattern(G, word))) {
8530 int last_was_member = false;
8531 last_obj = NULL;
8532 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8533 if(last_obj != I->Obj[I->Table[a].model]) {
8534 last_obj = I->Obj[I->Table[a].model];
8535 last_was_member = ExecutiveCheckGroupMembership(G,
8536 group_list_id,
8537 (CObject *) last_obj);
8538 }
8539 if(last_was_member && !base[0].sele[a]) {
8540 base[0].sele[a] = true;
8541 c++;
8542 }
8543 }
8544 }
8545 ExecutiveFreeGroupList(G, group_list_id);
8546 }
8547
8548 } else if (!enabled_only ||
8549 WordMatchExact(G, activeselename, word, ignore_case)) {
8550 auto it = SelectGetInfoIter(G, word, 1, ignore_case);
8551 if (it != IM->Info.end()) {
8552 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8553 base[0].sele[a] = false;
8554 s = I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom].selEntry;
8555 while(s) {
8556 if (IM->Member[s].selection == it->ID) {
8557 base[0].sele[a] = IM->Member[s].tag;
8558 c++;
8559 }
8560 s = IM->Member[s].next;
8561 }
8562 }
8563 } else {
8564 int group_list_id;
8565 if((group_list_id = ExecutiveGetExpandedGroupList(G, word))) {
8566 int last_was_member = false;
8567 last_obj = NULL;
8568 for(a = 0; a < I_NAtom; a++) /* zero out first before iterating through selections */
8569 base[0].sele[a] = false;
8570 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8571 if(last_obj != I->Obj[I->Table[a].model]) {
8572 last_obj = I->Obj[I->Table[a].model];
8573 last_was_member = ExecutiveCheckGroupMembership(G,
8574 group_list_id,
8575 (CObject *) last_obj);
8576 }
8577 if((base[0].sele[a] = last_was_member))
8578 c++;
8579 }
8580 ExecutiveFreeGroupList(G, group_list_id);
8581 } else if(base[1].m_text[0] == '?') { /* undefined ?sele allowed */
8582 for(a = cNDummyAtoms; a < I_NAtom; a++)
8583 base[0].sele[a] = false;
8584 } else {
8585 return pymol::make_error("Invalid selection name \"", word, "\".");
8586 }
8587 }
8588 }
8589 }
8590 break;
8591 case SELE_MODs:
8592
8593 /* need to change this to handle wildcarded model names */
8594
8595 /* first, trim off and record the atom index if one exists */
8596
8597 index = -1;
8598 auto pos = base[1].m_text.find('`');
8599 if (pos != std::string::npos) {
8600 const char* np = base[1].text() + pos;
8601 if(sscanf(np + 1, "%d", &index) != 1)
8602 index = -1;
8603 else
8604 index--;
8605 base[1].m_text.resize(pos);
8606 }
8607 model = 0;
8608
8609 {
8610 CWordMatchOptions options;
8611 WordMatchOptionsConfigAlpha(&options, wildcard[0], ignore_case);
8612
8613 if((matcher = WordMatcherNew(G, base[1].text(), &options, false))) {
8614
8615 int obj_matches = false;
8616
8617 for(a = 0; a < I_NAtom; a++) /* zero out first before iterating through selections */
8618 base[0].sele[a] = false;
8619
8620 base_0_sele_a = &base[0].sele[cNDummyAtoms];
8621 last_obj = NULL;
8622 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8623 auto& table_a = I->Table[a];
8624 obj = I->Obj[table_a.model];
8625 if(obj != last_obj) {
8626
8627 obj_matches = WordMatcherMatchAlpha(matcher, I->Obj[table_a.model]->Name);
8628 last_obj = obj;
8629 }
8630 if(obj_matches) {
8631 if((index < 0) || (table_a.atom == index)) {
8632 *base_0_sele_a = true;
8633 c++;
8634 }
8635 }
8636 base_0_sele_a++;
8637 }
8638 WordMatcherFree(matcher);
8639 } else {
8640
8641 obj = (ObjectMolecule *) ExecutiveFindObjectByName(G, base[1].text());
8642 if(obj) {
8643 for(a = cNDummyModels; a < I->Obj.size(); a++)
8644 if(I->Obj[a] == obj) {
8645 model = a + 1;
8646 break;
8647 }
8648 }
8649 if(!model)
8650 if(sscanf(base[1].text(), "%i", &model) == 1) {
8651 if(model <= 0)
8652 model = 0;
8653 else if(model > I->Obj.size())
8654 model = 0;
8655 else if(!I->Obj[model])
8656 model = 0;
8657 }
8658 if(model) {
8659 model--;
8660 if(index >= 0) {
8661 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8662 if(I->Table[a].model == model)
8663 if(I->Table[a].atom == index) {
8664 base[0].sele[a] = true;
8665 c++;
8666 } else {
8667 base[0].sele[a] = false;
8668 } else
8669 base[0].sele[a] = false;
8670 }
8671 } else {
8672 for(a = cNDummyAtoms; a < I_NAtom; a++) {
8673 if(I->Table[a].model == model) {
8674 base[0].sele[a] = true;
8675 c++;
8676 } else
8677 base[0].sele[a] = false;
8678 }
8679 }
8680 } else {
8681 return pymol::make_error("invalid model \"", base[1].text(), "\"");
8682 }
8683 }
8684 }
8685 break;
8686 }
8687 PRINTFD(G, FB_Selector)
8688 " %s: %d atoms selected.\n", __func__, c ENDFD;
8689 return {};
8690 }
8691
8692
8693 /*========================================================================*/
SelectorSelect2(PyMOLGlobals * G,EvalElem * base,int state)8694 static int SelectorSelect2(PyMOLGlobals * G, EvalElem * base, int state)
8695 {
8696 int a;
8697 int c = 0;
8698 int ok = true;
8699 int oper;
8700 float comp1;
8701 int exact;
8702 int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
8703
8704 AtomInfoType *at1;
8705 CSelector *I = G->Selector;
8706 base->type = STYP_LIST;
8707 base->sele_calloc(I->Table.size());
8708 base->sele_err_chk_ptr(G);
8709 switch (base->code) {
8710 case SELE_XVLx:
8711 case SELE_YVLx:
8712 case SELE_ZVLx:
8713 oper = WordKey(G, AtOper, base[1].text(), 4, ignore_case, &exact);
8714 switch (oper) {
8715 case SCMP_GTHN:
8716 case SCMP_LTHN:
8717 case SCMP_EQAL:
8718 if(sscanf(base[2].text(), "%f", &comp1) != 1)
8719 ok = ErrMessage(G, "Selector", "Invalid Number");
8720 break;
8721 default:
8722 ok = ErrMessage(G, "Selector", "Invalid Operator.");
8723 break;
8724 }
8725 if(ok) {
8726 ObjectMolecule *obj;
8727 CoordSet *cs;
8728 int at, idx, s, s0 = 0, sN = I->NCSet;
8729
8730 if(state != -1) {
8731 s0 = (state < -1) ? SceneGetState(G) : state;
8732 sN = s0 + 1;
8733 }
8734
8735 for(a = cNDummyAtoms; a < I->Table.size(); a++)
8736 base[0].sele[a] = false;
8737
8738 for(s = s0; s < sN; s++) {
8739 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8740 if(base[0].sele[a])
8741 continue;
8742
8743 obj = I->Obj[I->Table[a].model];
8744 if(s >= obj->NCSet)
8745 continue;
8746
8747 at = I->Table[a].atom;
8748 cs = obj->CSet[s];
8749 idx = cs->atmToIdx(at);
8750 if(idx < 0)
8751 continue;
8752
8753 idx *= 3;
8754 switch (base->code) {
8755 case SELE_ZVLx:
8756 idx++;
8757 case SELE_YVLx:
8758 idx++;
8759 }
8760
8761 base[0].sele[a] = fcmp(cs->Coord[idx], comp1, oper);
8762 }
8763 }
8764 }
8765 break;
8766 case SELE_PCHx:
8767 case SELE_FCHx:
8768 case SELE_BVLx:
8769 case SELE_QVLx:
8770 oper = WordKey(G, AtOper, base[1].text(), 4, ignore_case, &exact);
8771 if(!oper)
8772 ok = ErrMessage(G, "Selector", "Invalid Operator.");
8773 if(ok) {
8774 switch (oper) {
8775 case SCMP_GTHN:
8776 case SCMP_LTHN:
8777 case SCMP_EQAL:
8778 if(sscanf(base[2].text(), "%f", &comp1) != 1)
8779 ok = ErrMessage(G, "Selector", "Invalid Number");
8780 break;
8781 }
8782 if(ok) {
8783 switch (oper) {
8784 case SCMP_GTHN:
8785 switch (base->code) {
8786 case SELE_BVLx:
8787 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8788 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8789 if(at1->b > comp1) {
8790 base[0].sele[a] = true;
8791 c++;
8792 } else {
8793 base[0].sele[a] = false;
8794 }
8795 }
8796 break;
8797 case SELE_QVLx:
8798 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8799 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8800 if(at1->q > comp1) {
8801 base[0].sele[a] = true;
8802 c++;
8803 } else {
8804 base[0].sele[a] = false;
8805 }
8806 }
8807 break;
8808 case SELE_PCHx:
8809 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8810 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8811 if(at1->partialCharge > comp1) {
8812 base[0].sele[a] = true;
8813 c++;
8814 } else {
8815 base[0].sele[a] = false;
8816 }
8817 }
8818 break;
8819 case SELE_FCHx:
8820 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8821 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8822 if(at1->formalCharge > comp1) {
8823 base[0].sele[a] = true;
8824 c++;
8825 } else {
8826 base[0].sele[a] = false;
8827 }
8828 }
8829 break;
8830 }
8831 break;
8832 case SCMP_LTHN:
8833 switch (base->code) {
8834 case SELE_BVLx:
8835 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8836 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8837 if(at1->b < comp1) {
8838 base[0].sele[a] = true;
8839 c++;
8840 } else {
8841 base[0].sele[a] = false;
8842 }
8843 }
8844 break;
8845 case SELE_QVLx:
8846 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8847 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8848 if(at1->q < comp1) {
8849 base[0].sele[a] = true;
8850 c++;
8851 } else {
8852 base[0].sele[a] = false;
8853 }
8854 }
8855 break;
8856 case SELE_PCHx:
8857 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8858 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8859 if(at1->partialCharge < comp1) {
8860 base[0].sele[a] = true;
8861 c++;
8862 } else {
8863 base[0].sele[a] = false;
8864 }
8865 }
8866 break;
8867 case SELE_FCHx:
8868 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8869 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8870 if(at1->formalCharge < comp1) {
8871 base[0].sele[a] = true;
8872 c++;
8873 } else {
8874 base[0].sele[a] = false;
8875 }
8876 }
8877 break;
8878 }
8879 break;
8880 case SCMP_EQAL:
8881 switch (base->code) {
8882 case SELE_BVLx:
8883 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8884 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8885 if(fabs(at1->b - comp1) < R_SMALL4) {
8886 base[0].sele[a] = true;
8887 c++;
8888 } else {
8889 base[0].sele[a] = false;
8890 }
8891 }
8892 break;
8893 case SELE_QVLx:
8894 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8895 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8896 if(fabs(at1->q - comp1) < R_SMALL4) {
8897 base[0].sele[a] = true;
8898 c++;
8899 } else {
8900 base[0].sele[a] = false;
8901 }
8902 }
8903 break;
8904 case SELE_PCHx:
8905 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8906 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8907 if(fabs(at1->partialCharge - comp1) < R_SMALL4) {
8908 base[0].sele[a] = true;
8909 c++;
8910 } else {
8911 base[0].sele[a] = false;
8912 }
8913 }
8914 break;
8915 case SELE_FCHx:
8916 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8917 at1 = &I->Obj[I->Table[a].model]->AtomInfo[I->Table[a].atom];
8918 if(fabs(at1->formalCharge - comp1) < R_SMALL4) {
8919 base[0].sele[a] = true;
8920 c++;
8921 } else {
8922 base[0].sele[a] = false;
8923 }
8924 }
8925 break;
8926 }
8927 break;
8928 }
8929 break;
8930 }
8931 }
8932 }
8933
8934 PRINTFD(G, FB_Selector)
8935 " %s: %d atoms selected.\n", __func__, c ENDFD;
8936 return (ok);
8937 }
8938
8939 /*========================================================================*/
SelectorSelect3(PyMOLGlobals * G,EvalElem * base,int state)8940 static int SelectorSelect3(PyMOLGlobals * G, EvalElem * base, int state)
8941 {
8942 switch (base->code) {
8943 case SELE_PROP:
8944 ErrMessage(G, "Selector", "properties (p.) not supported in Open-Source PyMOL");
8945 return false;
8946 }
8947 return true;
8948 ok_except1:
8949 return false;
8950 }
8951
8952 /*========================================================================*/
8953
8954 /*
8955 * Ring finder subroutine
8956 * Modifies base[0].sele
8957 */
8958 class SelectorRingFinder : public AbstractRingFinder
8959 {
8960 CSelector* m_selector;
8961 EvalElem* m_base;
8962
8963 protected:
onRingFound(ObjectMolecule * obj,const int * indices,size_t size)8964 void onRingFound(
8965 ObjectMolecule* obj, const int* indices, size_t size) override
8966 {
8967 for (size_t i = 0; i < size; ++i) {
8968 int offset = SelectorGetObjAtmOffset(m_selector, obj, indices[i]);
8969 if (offset >= 0)
8970 m_base->sele[offset] = 1;
8971 }
8972 }
8973
8974 public:
SelectorRingFinder(CSelector * selector,EvalElem * base,int maxringsize=7)8975 SelectorRingFinder(CSelector* selector, EvalElem* base, int maxringsize = 7)
8976 : AbstractRingFinder(maxringsize)
8977 , m_selector(selector)
8978 , m_base(base)
8979 {
8980 }
8981 };
8982
8983 /*========================================================================*/
SelectorLogic1(PyMOLGlobals * G,EvalElem * inp_base,int state)8984 static int SelectorLogic1(PyMOLGlobals * G, EvalElem * inp_base, int state)
8985 {
8986 /* some cases in this function still need to be optimized
8987 for performance (see BYR1 for example) */
8988
8989 CSelector *I = G->Selector;
8990 int a, b, tag;
8991 int c = 0;
8992 int flag;
8993 EvalElem *base = inp_base;
8994 AtomInfoType *at1, *at2;
8995 int n_atom = I->Table.size();
8996 int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
8997 int n;
8998 int a0, a1, a2;
8999 ObjectMolecule *lastObj = NULL;
9000
9001 base[0].sele = std::move(base[1].sele);
9002 base[0].type = STYP_LIST;
9003 switch (base->code) {
9004 case SELE_NOT1:
9005 {
9006 int *base_0_sele_a;
9007
9008 base_0_sele_a = base[0].sele_data();
9009 for(a = 0; a < n_atom; a++) {
9010 if((*base_0_sele_a = !*base_0_sele_a))
9011 c++;
9012 base_0_sele_a++;
9013 }
9014 }
9015 break;
9016 case SELE_RING:
9017 {
9018 std::vector<bool> selemask(base[0].sele_data(), base[0].sele_data() + n_atom);
9019 SelectorRingFinder ringfinder(I, base);
9020
9021 std::fill_n(base[0].sele_data(), n_atom, 0);
9022
9023 for (SelectorAtomIterator iter(I); iter.next();) {
9024 if (selemask[iter.a])
9025 ringfinder.apply(iter.obj, iter.getAtm());
9026 }
9027 }
9028 break;
9029 case SELE_NGH1:
9030 base[1].sele = std::move(base[0].sele);
9031 base[0].sele_calloc(n_atom);
9032
9033 for(a = cNDummyAtoms; a < n_atom; a++) {
9034 auto& table_a = I->Table[a];
9035 if((tag = base[1].sele[a])) {
9036 if(I->Obj[table_a.model] != lastObj) {
9037 lastObj = I->Obj[table_a.model];
9038 ObjectMoleculeUpdateNeighbors(lastObj);
9039 }
9040 a0 = table_a.atom;
9041 n = lastObj->Neighbor[a0];
9042 n++;
9043 while(1) {
9044 a1 = lastObj->Neighbor[n];
9045 if(a1 < 0)
9046 break;
9047 if((a2 = SelectorGetObjAtmOffset(I, lastObj, a1)) >= 0) {
9048 if(!base[1].sele[a2])
9049 base[0].sele[a2] = tag;
9050 }
9051 n += 2;
9052 }
9053 }
9054 }
9055 base[1].sele_free();
9056 break;
9057 case SELE_BON1:
9058 base[1].sele = std::move(base[0].sele);
9059 base[0].sele_calloc(n_atom);
9060 for(a = cNDummyAtoms; a < n_atom; a++) {
9061 auto& table_a = I->Table[a];
9062 if((tag = base[1].sele[a])) {
9063 if(I->Obj[table_a.model] != lastObj) {
9064 lastObj = I->Obj[table_a.model];
9065 ObjectMoleculeUpdateNeighbors(lastObj);
9066 }
9067 a0 = table_a.atom;
9068 n = lastObj->Neighbor[a0];
9069 n++;
9070 while(1) {
9071 a1 = lastObj->Neighbor[n];
9072 if(a1 < 0)
9073 break;
9074 if((a2 = SelectorGetObjAtmOffset(I, lastObj, a1)) >= 0) {
9075 if(!base[0].sele[a2])
9076 base[0].sele[a2] = 1;
9077 n += 2;
9078 }
9079 }
9080 }
9081 }
9082 base[1].sele_free();
9083 break;
9084 case SELE_BYO1:
9085 base[1].sele = std::move(base[0].sele);
9086 base[0].sele_calloc(n_atom);
9087 for(a = cNDummyAtoms; a < n_atom; a++) {
9088 if(base[1].sele[a]) {
9089 if(I->Obj[I->Table[a].model] != lastObj) {
9090 lastObj = I->Obj[I->Table[a].model];
9091 b = a;
9092 while(b >= 0) {
9093 if(I->Obj[I->Table[b].model] != lastObj)
9094 break;
9095 base[0].sele[b] = 1;
9096 b--;
9097 }
9098 b = a + 1;
9099 while(b < n_atom) {
9100 if(I->Obj[I->Table[b].model] != lastObj)
9101 break;
9102 base[0].sele[b] = 1;
9103 b++;
9104 }
9105 }
9106 }
9107 }
9108 base[1].sele_free();
9109 break;
9110 case SELE_BYR1: /* ASSUMES atoms are sorted & grouped by residue */
9111 case SELE_CAS1:
9112 {
9113 int *base_0_sele = base[0].sele_data();
9114 int break_atom = -1;
9115 int last_tag = 0;
9116 for(a = cNDummyAtoms; a < n_atom; a++) {
9117 auto& table_a = I->Table[a];
9118 if((tag = base_0_sele[a]) && ((a >= break_atom) || (base_0_sele[a] != last_tag))) {
9119 at1 = &I->Obj[table_a.model]->AtomInfo[table_a.atom];
9120 b = a - 1;
9121 while(b >= 0) {
9122 if(!base_0_sele[b]) {
9123 flag = false;
9124 if(table_a.model == I->Table[b].model) {
9125 at2 = &I->Obj[I->Table[b].model]->AtomInfo[I->Table[b].atom];
9126 if(AtomInfoSameResidue(G, at1, at2)) {
9127 base_0_sele[b] = tag;
9128 c++;
9129 flag = 1;
9130 }
9131 }
9132 if(!flag) {
9133 break;
9134 }
9135 }
9136 b--;
9137 }
9138 b = a + 1;
9139 while(b < n_atom) {
9140 if(!base_0_sele[b]) {
9141 flag = false;
9142 if(table_a.model == I->Table[b].model) {
9143 at2 = &I->Obj[I->Table[b].model]->AtomInfo[I->Table[b].atom];
9144 if(AtomInfoSameResidue(G, at1, at2)) {
9145 base_0_sele[b] = tag;
9146 c++;
9147 flag = 1;
9148 }
9149 }
9150 if(!flag) {
9151 break_atom = b - 1;
9152 last_tag = tag;
9153 break;
9154 }
9155 }
9156 b++;
9157 }
9158 }
9159 }
9160 if(base->code == SELE_CAS1) {
9161 c = 0;
9162 for(a = cNDummyAtoms; a < n_atom; a++) {
9163 auto& table_a = I->Table[a];
9164 if(base_0_sele[a]) {
9165 base_0_sele[a] = false;
9166
9167 if(I->Obj[table_a.model]->AtomInfo[table_a.atom].protons == cAN_C)
9168 if(WordMatchExact(G, G->lex_const.CA,
9169 I->Obj[table_a.model]->AtomInfo[table_a.atom].name,
9170 ignore_case)) {
9171 base_0_sele[a] = true;
9172 c++;
9173 }
9174 }
9175 }
9176 }
9177 }
9178 break;
9179 case SELE_BYC1: /* ASSUMES atoms are sorted & grouped by chain */
9180 {
9181 int *base_0_sele = base[0].sele_data();
9182 int break_atom_high = -1;
9183 int break_atom_low = 0;
9184 int last_tag = 0;
9185 for(a = cNDummyAtoms; a < n_atom; a++) {
9186 auto& table_a = I->Table[a];
9187 if((tag = base_0_sele[a])
9188 && ((a >= break_atom_high) || (base_0_sele[a] != last_tag))) {
9189 if(tag != last_tag)
9190 break_atom_low = 0;
9191 at1 = &I->Obj[table_a.model]->AtomInfo[table_a.atom];
9192 b = a - 1;
9193 while(b >= break_atom_low) {
9194 if(!base_0_sele[b]) {
9195 flag = false;
9196 if(table_a.model == I->Table[b].model) {
9197 at2 = &I->Obj[I->Table[b].model]->AtomInfo[I->Table[b].atom];
9198 if(at1->chain == at2->chain)
9199 if(at1->segi == at2->segi) {
9200 base_0_sele[b] = tag;
9201 c++;
9202 flag = 1;
9203 }
9204 }
9205 if(!flag) {
9206 break_atom_low = b + 1;
9207 break;
9208 }
9209 }
9210 b--;
9211 }
9212 if(b < 0)
9213 break_atom_low = 0;
9214 b = a + 1;
9215 while(b < n_atom) {
9216 if(!base_0_sele[b]) {
9217 flag = false;
9218 if(table_a.model == I->Table[b].model) {
9219 at2 = &I->Obj[I->Table[b].model]->AtomInfo[I->Table[b].atom];
9220 if(at1->chain == at2->chain)
9221 if(at1->segi == at2->segi) {
9222 base_0_sele[b] = tag;
9223 c++;
9224 flag = 1;
9225 }
9226 }
9227 if(!flag) {
9228 break_atom_high = b - 1;
9229 last_tag = tag;
9230 break;
9231 }
9232 }
9233 b++;
9234 }
9235 }
9236 }
9237 }
9238 break;
9239 case SELE_BYS1: /* ASSUMES atoms are sorted & grouped by segi */
9240 {
9241 int *base_0_sele = base[0].sele_data();
9242 int break_atom_high = -1;
9243 int break_atom_low = 0;
9244 int last_tag = 0;
9245 for(a = cNDummyAtoms; a < n_atom; a++) {
9246 auto& table_a = I->Table[cNDummyAtoms];
9247 if((tag = base_0_sele[a])
9248 && ((a >= break_atom_high) || (base_0_sele[a] != last_tag))) {
9249 if(tag != last_tag)
9250 break_atom_low = 0;
9251 at1 = &I->Obj[table_a.model]->AtomInfo[table_a.atom];
9252 b = a - 1;
9253 while(b >= break_atom_low) {
9254 if(!base_0_sele[b]) {
9255 flag = false;
9256 if(table_a.model == I->Table[b].model) {
9257 at2 = &I->Obj[I->Table[b].model]->AtomInfo[I->Table[b].atom];
9258 if(at1->segi == at2->segi) {
9259 base_0_sele[b] = tag;
9260 c++;
9261 flag = 1;
9262 }
9263 }
9264 if(!flag) {
9265 break_atom_low = b + 1;
9266 break;
9267 }
9268 }
9269 b--;
9270 }
9271 b = a + 1;
9272 while(b < n_atom) {
9273 if(!base_0_sele[b]) {
9274 flag = false;
9275 if(table_a.model == I->Table[b].model) {
9276 at2 = &I->Obj[I->Table[b].model]->AtomInfo[I->Table[b].atom];
9277 if(at1->segi == at2->segi) {
9278 base_0_sele[b] = tag;
9279 c++;
9280 flag = 1;
9281 }
9282 }
9283 if(!flag) {
9284 break_atom_high = b - 1;
9285 last_tag = tag;
9286 break;
9287 }
9288 }
9289 b++;
9290 }
9291 }
9292 }
9293 }
9294 break;
9295 case SELE_BYF1: /* first, identify all atom by fragment selection */
9296 {
9297 /* NOTE: this algorithm looks incompatible with selection
9298 tags...need to do some more thinking & work... */
9299
9300 int n_frag = EditorGetNFrag(G);
9301
9302 base[1].sele = std::move(base[0].sele);
9303 base[0].sele_calloc(n_atom);
9304
9305 if(n_frag) {
9306 int a, f, at, s;
9307 int *fsele;
9308 ObjectMolecule *obj;
9309
9310 fsele = pymol::malloc<int>(n_frag + 1);
9311
9312 for(f = 0; f < n_frag; f++) {
9313 auto name = pymol::string_format("%s%1d", cEditorFragPref, f + 1);
9314 fsele[f] = SelectorIndexByName(G, name.c_str());
9315 }
9316
9317 /* mark atoms by fragment */
9318 for(a = 0; a < n_atom; a++) {
9319 at = I->Table[a].atom;
9320 obj = I->Obj[I->Table[a].model];
9321 s = obj->AtomInfo[at].selEntry;
9322 for(f = 0; f < n_frag; f++) {
9323 if(SelectorIsMember(G, s, fsele[f])) {
9324 base[0].sele[a] = f + 1;
9325 }
9326 }
9327 }
9328
9329 /* mark fragments we keep */
9330 for(f = 0; f <= n_frag; f++) {
9331 fsele[f] = 0;
9332 }
9333 for(a = 0; a < n_atom; a++) {
9334 int f = base[0].sele[a];
9335 if(base[1].sele[a] && f)
9336 fsele[f] = 1;
9337 }
9338
9339 /* now set flags */
9340 for(a = 0; a < n_atom; a++) {
9341 c += (base[0].sele[a] = fsele[base[0].sele[a]]);
9342 }
9343
9344 FreeP(fsele);
9345 }
9346 base[1].sele_free();
9347 }
9348 break;
9349 case SELE_BYM1:
9350 {
9351 int s;
9352 int c = 0;
9353 int a, at, a1, aa;
9354 ObjectMolecule *obj, *lastObj = NULL;
9355 int *stk;
9356 int stkDepth = 0;
9357 base[1].sele = std::move(base[0].sele);
9358 base[0].sele_calloc(n_atom);
9359
9360 stk = VLAlloc(int, 50);
9361
9362 for(a = 0; a < n_atom; a++) {
9363 if((tag = base[1].sele[a]) && (!base[0].sele[a])) {
9364 VLACheck(stk, int, stkDepth);
9365 stk[stkDepth] = a;
9366 stkDepth++;
9367
9368 obj = I->Obj[I->Table[a].model];
9369 if(obj != lastObj) {
9370 lastObj = obj;
9371 ObjectMoleculeUpdateNeighbors(obj);
9372 }
9373
9374 while(stkDepth) { /* this will explore a tree */
9375 stkDepth--;
9376 a = stk[stkDepth];
9377 base[0].sele[a] = tag;
9378 c++;
9379 at = I->Table[a].atom; /* start walk from this location */
9380
9381 /* add neighbors onto the stack */
9382 ITERNEIGHBORATOMS(obj->Neighbor, at, a1, s) {
9383 b = obj->Neighbor[s + 1];
9384 if (obj->Bond[b].order > 0) {
9385 if((aa = SelectorGetObjAtmOffset(I, obj, a1)) >= 0) {
9386 if(!base[0].sele[aa]) {
9387 VLACheck(stk, int, stkDepth);
9388 stk[stkDepth] = aa; /* add index in selector space */
9389 stkDepth++;
9390 }
9391 }
9392 }
9393 }
9394 }
9395 }
9396 }
9397 base[1].sele_free();
9398 VLAFreeP(stk);
9399 }
9400 break;
9401 case SELE_BYX1: /* by cell */
9402 base[1].sele = std::move(base[0].sele);
9403 base[0].sele_calloc(n_atom);
9404 {
9405 ObjectMolecule *obj;
9406 CoordSet *cs;
9407 int d, n1, at;
9408 for(d = 0; d < I->NCSet; d++) {
9409 if((state < 0) || (d == state)) {
9410 n1 = 0;
9411
9412 auto Flag1 = std::vector<MapFlag_t>(I->Table.size(), 0);
9413 auto Vertex = std::vector<float>(I->Table.size() * 3, 0.0f);
9414
9415 for(a = 0; a < I->Table.size(); a++) {
9416 at = I->Table[a].atom;
9417 obj = I->Obj[I->Table[a].model];
9418 if(d < obj->NCSet)
9419 cs = obj->CSet[d];
9420 else
9421 cs = NULL;
9422 if(cs) {
9423 CCrystal *cryst = cs->PeriodicBox.get();
9424 if((!cryst) && (obj->Symmetry))
9425 cryst = &obj->Symmetry->Crystal;
9426 if(cryst) {
9427 int idx;
9428 idx = cs->atmToIdx(at);
9429 if(idx >= 0) {
9430 transform33f3f(cryst->RealToFrac, cs->coordPtr(idx),
9431 Vertex.data() + 3 * a);
9432 Flag1[a] = true;
9433 n1++;
9434 }
9435 }
9436 }
9437 }
9438 if(n1) {
9439 std::unique_ptr<MapType> map(MapNewFlagged(G, -1.1, Vertex.data(),
9440 I->Table.size(), nullptr, Flag1.data()));
9441 if(map) {
9442 int e, nCSet;
9443 nCSet = SelectorGetArrayNCSet(G, base[1].sele, false);
9444 for(e = 0; e < nCSet; e++) {
9445 if((state < 0) || (e == state)) {
9446 for(a = 0; a < I->Table.size(); a++) {
9447 if(base[1].sele[a]) {
9448 at = I->Table[a].atom;
9449 obj = I->Obj[I->Table[a].model];
9450 if(e < obj->NCSet)
9451 cs = obj->CSet[e];
9452 else
9453 cs = NULL;
9454 if(cs) {
9455 CCrystal *cryst = cs->PeriodicBox.get();
9456 if((!cryst) && (obj->Symmetry))
9457 cryst = &obj->Symmetry->Crystal;
9458 if(cryst) {
9459 int idx;
9460 idx = cs->atmToIdx(at);
9461 if(idx >= 0) {
9462 float probe[3], probe_i[3];
9463
9464 transform33f3f(cryst->RealToFrac, cs->coordPtr(idx),
9465 probe);
9466 probe_i[0] = (int) floor(probe[0]);
9467 probe_i[1] = (int) floor(probe[1]);
9468 probe_i[2] = (int) floor(probe[2]);
9469
9470 for (const auto j : MapEIter(*map, probe)) {
9471 if(!base[0].sele[j]) {
9472 float *tst = Vertex.data() + 3 * j;
9473 base[0].sele[j] = ((probe_i[0] == (int) floor(tst[0]))
9474 && (probe_i[1] ==
9475 (int) floor(tst[1]))
9476 && (probe_i[2] ==
9477 (int) floor(tst[2])));
9478 }
9479 }
9480 }
9481 }
9482 }
9483 }
9484 }
9485 }
9486 }
9487 }
9488 }
9489 }
9490 }
9491 }
9492 base[1].sele_free();
9493 break;
9494 case SELE_FST1:
9495 base[1].sele = std::move(base[0].sele);
9496 base[0].sele_calloc(n_atom);
9497 for(a = cNDummyAtoms; a < n_atom; a++) {
9498 if(base[1].sele[a]) {
9499 base[0].sele[a] = base[1].sele[a]; /* preserve tag */
9500 break;
9501 }
9502 }
9503 base[1].sele_free();
9504 break;
9505 case SELE_LST1:
9506 {
9507 int last = -1;
9508 base[1].sele = std::move(base[0].sele);
9509 base[0].sele_calloc(n_atom);
9510 for(a = cNDummyAtoms; a < n_atom; a++) {
9511 if(base[1].sele[a]) {
9512 last = a;
9513 }
9514 }
9515 if(last >= 0)
9516 base[0].sele[last] = base[1].sele[last]; /* preserve tag */
9517 }
9518 base[1].sele_free();
9519 break;
9520 }
9521 PRINTFD(G, FB_Selector)
9522 " %s: %d atoms selected.\n", __func__, c ENDFD;
9523 return (1);
9524 }
9525
9526
9527 /*========================================================================*/
SelectorLogic2(PyMOLGlobals * G,EvalElem * base)9528 static int SelectorLogic2(PyMOLGlobals * G, EvalElem * base)
9529 {
9530 CSelector *I = G->Selector;
9531 int a, b, tag;
9532 int c = 0;
9533 int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
9534 int ignore_case_chain = SettingGetGlobal_b(G, cSetting_ignore_case_chain);
9535 int *base_0_sele_a, *base_2_sele_a;
9536 int n_atom = I->Table.size();
9537
9538 AtomInfoType *at1, *at2;
9539
9540 switch (base[1].code) {
9541
9542 case SELE_OR_2:
9543 case SELE_IOR2:
9544 {
9545 base_0_sele_a = base[0].sele_data();
9546 base_2_sele_a = base[2].sele_data();
9547
9548 for(a = 0; a < n_atom; a++) {
9549 if(((*base_0_sele_a) =
9550 (((*base_0_sele_a) >
9551 (*base_2_sele_a)) ? (*base_0_sele_a) : (*base_2_sele_a)))) {
9552 /* use higher tag */
9553 c++;
9554 }
9555 base_0_sele_a++;
9556 base_2_sele_a++;
9557 }
9558 }
9559 break;
9560 case SELE_AND2:
9561
9562 base_0_sele_a = base[0].sele_data();
9563 base_2_sele_a = base[2].sele_data();
9564
9565 for(a = 0; a < n_atom; a++) {
9566 if((*base_0_sele_a) && (*base_2_sele_a)) {
9567 (*base_0_sele_a) =
9568 (((*base_0_sele_a) > (*base_2_sele_a)) ? (*base_0_sele_a) : (*base_2_sele_a));
9569 /* use higher tag */
9570 c++;
9571 } else {
9572 (*base_0_sele_a) = 0;
9573 }
9574 base_0_sele_a++;
9575 base_2_sele_a++;
9576 }
9577 break;
9578 case SELE_ANT2:
9579 base_0_sele_a = base[0].sele_data();
9580 base_2_sele_a = base[2].sele_data();
9581
9582 for(a = 0; a < n_atom; a++) {
9583 if((*base_0_sele_a) && !(*base_2_sele_a)) {
9584 c++;
9585 } else {
9586 (*base_0_sele_a) = 0;
9587 }
9588 base_0_sele_a++;
9589 base_2_sele_a++;
9590 }
9591 break;
9592 case SELE_IN_2:
9593 {
9594 int *base_2_sele_b;
9595 base_0_sele_a = &base[0].sele[cNDummyAtoms];
9596 for(a = cNDummyAtoms; a < n_atom; a++) {
9597 auto& table_a = I->Table[a];
9598 if((tag = *base_0_sele_a)) {
9599 at1 = &I->Obj[table_a.model]->AtomInfo[table_a.atom];
9600 *base_0_sele_a = 0;
9601 base_2_sele_b = &base[2].sele[cNDummyAtoms];
9602 for(b = cNDummyAtoms; b < n_atom; b++) {
9603 auto& table_b = I->Table[b];
9604 if(*base_2_sele_b) {
9605 at2 = &I->Obj[table_b.model]->AtomInfo[table_b.atom];
9606 if(at1->resv == at2->resv)
9607 if(WordMatchExact(G, at1->chain, at2->chain, ignore_case_chain))
9608 if(WordMatchExact(G, at1->name, at2->name, ignore_case))
9609 if(WordMatchExact(G, at1->inscode, at2->inscode, ignore_case))
9610 if(WordMatchExact(G, at1->resn, at2->resn, ignore_case))
9611 if(WordMatchExact(G, at1->segi, at2->segi, ignore_case_chain)) {
9612 *base_0_sele_a = tag;
9613 break;
9614 }
9615 }
9616 base_2_sele_b++;
9617 }
9618 }
9619 if(*(base_0_sele_a++))
9620 c++;
9621 }
9622 }
9623 break;
9624 case SELE_LIK2:
9625 {
9626 int *base_2_sele_b;
9627 base_0_sele_a = &base[0].sele[cNDummyAtoms];
9628 for(a = cNDummyAtoms; a < n_atom; a++) {
9629 auto& table_a = I->Table[a];
9630 if((tag = *base_0_sele_a)) {
9631 at1 = &I->Obj[table_a.model]->AtomInfo[table_a.atom];
9632 *base_0_sele_a = 0;
9633 base_2_sele_b = &base[2].sele[cNDummyAtoms];
9634 for(b = cNDummyAtoms; b < n_atom; b++) {
9635 auto& table_b = I->Table[b];
9636 if(*base_2_sele_b) {
9637 at2 = &I->Obj[table_b.model]->AtomInfo[table_b.atom];
9638 if(at1->resv == at2->resv)
9639 if(WordMatchExact(G, at1->name, at2->name, ignore_case))
9640 if(WordMatchExact(G, at1->inscode, at2->inscode, ignore_case)) {
9641 *base_0_sele_a = tag;
9642 break;
9643 }
9644 }
9645 base_2_sele_b++;
9646 }
9647 }
9648 if(*(base_0_sele_a++))
9649 c++;
9650 }
9651 }
9652 break;
9653 }
9654 base[2].sele_free();
9655 PRINTFD(G, FB_Selector)
9656 " %s: %d atoms selected.\n", __func__, c ENDFD;
9657 return (1);
9658 }
9659
9660
9661 /*========================================================================*/
SelectorOperator22(PyMOLGlobals * G,EvalElem * base,int state)9662 int SelectorOperator22(PyMOLGlobals * G, EvalElem * base, int state)
9663 {
9664 int c = 0;
9665 int a, d, e;
9666 CSelector *I = G->Selector;
9667 ObjectMolecule *obj;
9668
9669 float dist;
9670 CoordSet *cs;
9671 int ok = true;
9672 int nCSet;
9673 int n1, at, idx;
9674 int code = base[1].code;
9675
9676 if(state < 0) {
9677 switch (state) {
9678 case -2:
9679 case -3:
9680 state = SceneGetState(G);
9681 break;
9682 }
9683 }
9684
9685 switch (code) {
9686 case SELE_WIT_:
9687 case SELE_BEY_:
9688 case SELE_NTO_:
9689 if(!sscanf(base[2].text(), "%f", &dist))
9690 ok = ErrMessage(G, "Selector", "Invalid distance.");
9691 if(ok) {
9692 if(dist < 0.0)
9693 dist = 0.0;
9694
9695 const size_t table_size = I->Table.size();
9696 auto coords_flat = std::vector<float>(table_size * 3);
9697 auto* coords = pymol::reshape<3>(coords_flat.data());
9698
9699 /* copy starting mask */
9700 const auto Flag2 = std::move(base[0].sele);
9701 base[0].sele_calloc(table_size);
9702
9703 for(d = 0; d < I->NCSet; d++) {
9704 if((state < 0) || (d == state)) {
9705 n1 = 0;
9706 auto Flag1 = std::vector<MapFlag_t>(table_size);
9707 for(a = 0; a < table_size; a++) {
9708 at = I->Table[a].atom;
9709 obj = I->Obj[I->Table[a].model];
9710 if(d < obj->NCSet)
9711 cs = obj->CSet[d];
9712 else
9713 cs = NULL;
9714 if(cs) {
9715 if(CoordSetGetAtomVertex(cs, at, coords[a])) {
9716 Flag1[a] = true;
9717 n1++;
9718 }
9719 }
9720 }
9721 if(n1) {
9722 std::unique_ptr<MapType> map(MapNewFlagged(G, -dist,
9723 pymol::flatten(coords), table_size, nullptr, Flag1.data()));
9724 CHECKOK(ok, map);
9725 if(ok) {
9726 nCSet = SelectorGetArrayNCSet(G, base[4].sele, false);
9727 for(e = 0; ok && e < nCSet; e++) {
9728 if((state < 0) || (e == state)) {
9729 for(a = 0; a < I->Table.size(); a++) {
9730 if(base[4].sele[a]) {
9731 at = I->Table[a].atom;
9732 obj = I->Obj[I->Table[a].model];
9733 if(e < obj->NCSet)
9734 cs = obj->CSet[e];
9735 else
9736 cs = NULL;
9737 if(cs) {
9738 idx = cs->atmToIdx(at);
9739 if(idx >= 0) {
9740 const float* v2 = cs->coordPtr(idx);
9741 for (const auto j : MapEIter(*map, v2)) {
9742 if (!base[0].sele[j] && Flag2[j] &&
9743 within3f(coords[j], v2, dist) &&
9744 (code != SELE_NTO_ || !base[4].sele[j])) {
9745 base[0].sele[j] = true;
9746 }
9747 }
9748 }
9749 }
9750 }
9751 }
9752 }
9753 }
9754 }
9755 }
9756 }
9757 }
9758 if(code == SELE_BEY_) {
9759 for(a = 0; a < I->Table.size(); a++) {
9760 if(Flag2[a])
9761 base[0].sele[a] = !base[0].sele[a];
9762 }
9763 }
9764 for(a = cNDummyAtoms; a < I->Table.size(); a++)
9765 if(base[0].sele[a])
9766 c++;
9767 }
9768 break;
9769 }
9770 base[4].sele_free();
9771 PRINTFD(G, FB_Selector)
9772 " %s: %d atoms selected.\n", __func__, c ENDFD;
9773 return (1);
9774 }
9775
9776 /**
9777 * Removes matching quotes from a string, at string start as well as after word
9778 * list separators ("+" and ","). Does not consider backslash escaping.
9779 *
9780 * Examples (not sure if all of these are intentional):
9781 * @verbatim
9782 "foo bar" -> foo bar
9783 'foo bar' -> foo bar
9784 "foo"+'bar' -> foo+bar
9785 "foo bar\" -> foo bar\ # backslash has no escape function
9786 "foo" "bar" -> foo "bar" # second pair of quotes not after separator
9787 foo''+''bar -> foo''+bar # first pair of quotes not after separator
9788 "foo"bar" -> foobar" # third quote unmatched
9789 foo'+'bar -> foo'+'bar # no matching quotes after separator
9790 @endverbatim
9791 */
remove_quotes(std::string & str)9792 static void remove_quotes(std::string& str)
9793 {
9794 /* nasty */
9795
9796 char *st = &str[0];
9797 char *p, *q;
9798 char *quote_start = NULL;
9799 char active_quote = 0;
9800 p = st;
9801 q = st;
9802
9803 while(*p) {
9804 if(((*p) == 34) || ((*p) == 39)) {
9805 if(quote_start && (active_quote == *p)) { /* eliminate quotes... */
9806 while(quote_start < (q - 1)) {
9807 *(quote_start) = *(quote_start + 1);
9808 quote_start++;
9809 }
9810 q--;
9811 quote_start = NULL;
9812 p++;
9813 continue;
9814 } else if(quote_start) {
9815 } else {
9816 if(p == st) { /* at start => real quote */
9817 quote_start = q;
9818 active_quote = *p;
9819 } else if((*(p - 1) == '+') || (*(p - 1) == ',')) { /* after separator => real quote */
9820 quote_start = q;
9821 active_quote = *p;
9822 }
9823 }
9824 }
9825 if (q < p) {
9826 *q = *p;
9827 }
9828 ++q;
9829 ++p;
9830 }
9831 if (q < p) {
9832 str.resize(q - st);
9833 }
9834 }
9835
9836 #define STACK_PUSH_VALUE(value) { \
9837 depth++; \
9838 VecCheck(Stack, depth); \
9839 e = Stack.data() + depth; \
9840 e->level = (level << 4) + 1; \
9841 e->imp_op_level = (imp_op_level << 4) + 1; \
9842 imp_op_level = level; \
9843 e->type = STYP_VALU; \
9844 e->m_text = value; \
9845 remove_quotes(e->m_text); \
9846 }
9847
9848 #define STACK_PUSH_OPERATION(ocode) { \
9849 depth++; \
9850 VecCheck(Stack, depth); \
9851 e = Stack.data() + depth; \
9852 e->code = ocode; \
9853 e->level = (level << 4) + ((e->code & 0xF0) >> 4); \
9854 e->imp_op_level = (imp_op_level << 4) + 1; \
9855 imp_op_level = level; \
9856 e->type = (e->code & 0xF); \
9857 }
9858
9859 /**
9860 * Indicates at wich token the parsing stopped
9861 * @param tokens token list
9862 * @param pos token index
9863 * @return " ".join(tokens[:pos + 1]) + "<--"
9864 */
indicate_last_token(const std::vector<std::string> & tokens,int pos)9865 static std::string indicate_last_token(
9866 const std::vector<std::string>& tokens, int pos)
9867 {
9868 std::string msg;
9869 for (int i = 0, i_end = std::min<int>(pos + 1, tokens.size()); //
9870 i < i_end; ++i) {
9871 if (i && tokens[i][0]) {
9872 msg += " ";
9873 }
9874 msg += tokens[i];
9875 }
9876 msg += "<--";
9877 return msg;
9878 }
9879
9880 #define return_error_with_tokens(msg) \
9881 return pymol::make_error(msg, "\n", indicate_last_token(word, c))
9882
9883 #define return_on_error_with_tokens(expr) \
9884 { \
9885 auto _temp_result = (expr); \
9886 if (!_temp_result) { \
9887 return_error_with_tokens(_temp_result.error().what()); \
9888 } \
9889 }
9890
9891 /*========================================================================*/
SelectorEvaluate(PyMOLGlobals * G,std::vector<std::string> & word,int state,int quiet)9892 pymol::Result<sele_array_t> SelectorEvaluate(PyMOLGlobals* G,
9893 std::vector<std::string>& word,
9894 int state, int quiet)
9895 {
9896 int level = 0, imp_op_level = 0;
9897 int depth = 0;
9898 int a, b, c = 0;
9899 int ok = true;
9900 unsigned int code = 0;
9901 int valueFlag = 0; /* are we expecting? */
9902 int opFlag, maxLevel;
9903 int totDepth = 0;
9904 int exact = 0;
9905
9906 int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
9907 /* CFGs can efficiently be parsed by stacks; use a clean stack w/space
9908 * for 10 (was: 100) elements */
9909 EvalElem *e;
9910 auto Stack = std::vector<EvalElem>(10);
9911
9912 /* converts all keywords into code, adds them into a operation list */
9913 while(ok && c < word.size()) {
9914 if(word[c][0] == '#') {
9915 if((!valueFlag) && (!level)) {
9916 word.resize(c); /* terminate selection if we encounter a comment */
9917 break;
9918 }
9919 }
9920 switch (word[c][0]) {
9921 case 0:
9922 break;
9923 case '(':
9924 /* increase stack depth on open parens: (selection ((and token) blah)) */
9925 if(valueFlag)
9926 return_error_with_tokens("Misplaced (.");
9927 if(ok)
9928 level++;
9929 break;
9930 case ')':
9931 /* decrease stack depth */
9932 if(valueFlag)
9933 return_error_with_tokens("Misplaced ).");
9934 if(ok) {
9935 level--;
9936 if(level < 0)
9937 return_error_with_tokens("Syntax error.");
9938 else
9939 imp_op_level = level;
9940 }
9941 if(ok && depth)
9942 Stack[depth].level--;
9943 break;
9944 default:
9945 if(valueFlag > 0) { /* standard operand */
9946 STACK_PUSH_VALUE(word[c]);
9947 valueFlag--;
9948 } else if(valueFlag < 0) { /* operation parameter i.e. around X<-- */
9949 depth++;
9950 VecCheck(Stack, depth);
9951 e = Stack.data() + depth;
9952 e->level = (level << 4) + 1;
9953 e->imp_op_level = (imp_op_level << 4) + 1;
9954 imp_op_level = level;
9955 e->type = STYP_PVAL;
9956 e->m_text = word[c];
9957 valueFlag++;
9958 } else { /* possible keyword... */
9959 code = WordKey(G, Keyword, word[c].c_str(), 4, ignore_case, &exact);
9960 if(!code) {
9961 b = word[c].size() - 1;
9962 if((b > 2) && (word[c][b] == ';')) {
9963 /* kludge to accomodate unnec. ';' usage */
9964 word[c].resize(b);
9965 code = WordKey(G, Keyword, word[c].c_str(), 4, ignore_case, &exact);
9966 } else if(!word[c].compare(0, 2, "p.")) {
9967 // kludge to parse p.propertyname without space after p.
9968 code = SELE_PROP;
9969 exact = 1;
9970 word[c].erase(0, 2);
9971 c--;
9972 }
9973 }
9974 PRINTFD(G, FB_Selector)
9975 " Selector: code %x\n", code ENDFD;
9976 if((code > 0) && (!exact))
9977 if(SelectorIndexByName(G, word[c].c_str()) >= 0)
9978 code = 0; /* favor selections over partial keyword matches */
9979 if(code) {
9980 /* this is a known operation */
9981 STACK_PUSH_OPERATION(code);
9982 switch (e->type) {
9983 case STYP_SEL0:
9984 valueFlag = 0;
9985 break;
9986 case STYP_SEL1:
9987 valueFlag = 1;
9988 break;
9989 case STYP_SEL2:
9990 valueFlag = 2;
9991 break;
9992 case STYP_SEL3:
9993 valueFlag = 3;
9994 break;
9995 case STYP_OPR1:
9996 valueFlag = 0;
9997 break;
9998 case STYP_OPR2:
9999 valueFlag = 0;
10000 break;
10001 case STYP_PRP1:
10002 valueFlag = -1;
10003 break;
10004 case STYP_OP22:
10005 valueFlag = -2;
10006 break;
10007 }
10008 } else {
10009 if((a = std::count(word[c].begin(), word[c].end(),
10010 '/'))) { /* handle slash notation */
10011 if(a > 5) {
10012 return_error_with_tokens("too many slashes in selection macro");
10013 }
10014
10015 // macro codes (some special cases apply! see code below)
10016 const int macrocodes[] = {SELE_SELs, SELE_SEGs, SELE_CHNs, SELE_RSNs, SELE_NAMs};
10017
10018 // two code/value pairs to support resn`resi and name`alt
10019 int codes[] = {0, 0, 0}; // null-terminated
10020 char * values[2];
10021
10022 std::string tmpKW = word[c];
10023 char* q = &tmpKW[0];
10024
10025 // if macro starts with "/" then read from left, otherwise
10026 // read from right
10027 if(*q == '/') {
10028 a = 4;
10029 q++;
10030 }
10031
10032 // loop over macro elements
10033 for(b = 0; q && *q; a--) {
10034 values[0] = q;
10035
10036 // null-terminate current element
10037 if((q = strchr(q, '/')))
10038 *(q++) = '\0';
10039
10040 // skip empty elements
10041 if(!*values[0])
10042 continue;
10043
10044 codes[0] = macrocodes[4 - a];
10045 codes[1] = 0;
10046
10047 // resn`resi or name`alt
10048 if (codes[0] == SELE_RSNs || codes[0] == SELE_NAMs) {
10049 char * backtick = strchr(values[0], '`');
10050 if (backtick) {
10051 if (codes[0] == SELE_RSNs) {
10052 codes[1] = SELE_RSIs;
10053 } else {
10054 codes[1] = SELE_ALTs;
10055 }
10056 values[1] = backtick + 1;
10057 *backtick = '\0';
10058 } else if (codes[0] == SELE_RSNs
10059 && values[0][0] >= '0'
10060 && values[0][0] <= '9') {
10061 // numeric -> resi
10062 codes[0] = SELE_RSIs;
10063 }
10064 }
10065
10066 for (int i = 0; codes[i]; ++i) {
10067 // skip empty and "*" values
10068 if (*values[i] && strcmp(values[i], "*")) {
10069 if(b++)
10070 STACK_PUSH_OPERATION(SELE_AND2);
10071
10072 STACK_PUSH_OPERATION(codes[i]);
10073 STACK_PUSH_VALUE(values[i]);
10074 }
10075 }
10076 }
10077
10078 // all-empty slash macro equals "all"
10079 if(!b)
10080 STACK_PUSH_OPERATION(SELE_ALLz);
10081
10082 } else if(word[c].find('`') != std::string::npos) { /* handle <object`index> syntax */
10083 STACK_PUSH_OPERATION(SELE_MODs);
10084 valueFlag = 1;
10085 c--;
10086 } else { /* handle <selection-name> syntax */
10087 STACK_PUSH_OPERATION(SELE_SELs);
10088 valueFlag = 1;
10089 c--;
10090 }
10091 }
10092 }
10093 break;
10094 }
10095 if(ok)
10096 c++; /* go onto next word */
10097 }
10098 if(level > 0){
10099 return_error_with_tokens("Malformed selection.");
10100 }
10101 if(ok) { /* this is the main operation loop */
10102 totDepth = depth;
10103 opFlag = true;
10104 maxLevel = -1;
10105 for(a = 1; a <= totDepth; a++) {
10106 if(Stack[a].level > maxLevel)
10107 maxLevel = Stack[a].level;
10108 }
10109 level = maxLevel;
10110 PRINTFD(G, FB_Selector)
10111 " Selector: maxLevel %d %d\n", maxLevel, totDepth ENDFD;
10112 if(level >= 0)
10113 while(ok) { /* loop until all ops at all levels have been tried */
10114
10115 /* order & efficiency of this algorithm could be improved... */
10116
10117 PRINTFD(G, FB_Selector)
10118 " Selector: new cycle...\n" ENDFD;
10119 depth = 1;
10120 opFlag = true;
10121 while(ok && opFlag) { /* loop through all entries looking for ops at the current level */
10122 opFlag = false;
10123
10124 if(Stack[depth].level >= level) {
10125 Stack[depth].level = level; /* trim peaks */
10126 }
10127 if(ok)
10128 if(depth > 0)
10129 if((!opFlag) && (Stack[depth].type == STYP_SEL0)) {
10130 opFlag = true;
10131 ok = SelectorSelect0(G, &Stack[depth]);
10132 }
10133 if(ok)
10134 if(depth > 1)
10135 if(Stack[depth - 1].level >= Stack[depth].level) {
10136 if(ok && (!opFlag) && (Stack[depth - 1].type == STYP_SEL1)
10137 && (Stack[depth].type == STYP_VALU)) {
10138 /* 1 argument selection operator */
10139 opFlag = true;
10140 return_on_error_with_tokens(
10141 SelectorSelect1(G, &Stack[depth - 1], quiet));
10142 for(a = depth + 1; a <= totDepth; a++)
10143 Stack[a - 1] = std::move(Stack[a]);
10144 totDepth--;
10145 } else if(ok && (!opFlag) && (Stack[depth - 1].type == STYP_OPR1)
10146 && (Stack[depth].type == STYP_LIST)) {
10147 /* 1 argument logical operator */
10148 opFlag = true;
10149 ok = SelectorLogic1(G, &Stack[depth - 1], state);
10150 for(a = depth + 1; a <= totDepth; a++)
10151 Stack[a - 1] = std::move(Stack[a]);
10152 totDepth--;
10153 } else if((Stack[depth - 1].type == STYP_LIST) &&
10154 (Stack[depth].type == STYP_LIST) &&
10155 (!((Stack[depth - 1].level & 0xF) ||
10156 (Stack[depth].level & 0xF)))) {
10157 /* two adjacent lists at zeroth priority level
10158 for the scope (lowest nibble of level is
10159 zero) is an implicit OR action */
10160 VecCheck(Stack, totDepth + 1);
10161 for(a = totDepth; a >= depth; a--)
10162 Stack[a + 1] = std::move(Stack[a]);
10163 totDepth++;
10164 Stack[depth].type = STYP_OPR2;
10165 Stack[depth].code = SELE_IOR2;
10166 Stack[depth].level = Stack[depth].imp_op_level;
10167 Stack[depth].m_text.clear();
10168 if(level < Stack[depth].level)
10169 level = Stack[depth].level;
10170 opFlag = true;
10171 }
10172 }
10173 if(ok)
10174 if(depth > 2)
10175 if((Stack[depth - 1].level >= Stack[depth].level) &&
10176 (Stack[depth - 1].level >= Stack[depth - 2].level)) {
10177
10178 if(ok && (!opFlag) && (Stack[depth - 1].type == STYP_OPR2)
10179 && (Stack[depth].type == STYP_LIST)
10180 && (Stack[depth - 2].type == STYP_LIST)) {
10181 /* 2 argument logical operator */
10182 ok = SelectorLogic2(G, &Stack[depth - 2]);
10183 opFlag = true;
10184 for(a = depth + 1; a <= totDepth; a++)
10185 Stack[a - 2] = std::move(Stack[a]);
10186 totDepth -= 2;
10187 } else if(ok && (!opFlag) && (Stack[depth - 1].type == STYP_PRP1)
10188 && (Stack[depth].type == STYP_PVAL)
10189 && (Stack[depth - 2].type == STYP_LIST)) {
10190 /* 2 argument logical operator */
10191 ok = SelectorModulate1(G, &Stack[depth - 2], state);
10192 opFlag = true;
10193 for(a = depth + 1; a <= totDepth; a++)
10194 Stack[a - 2] = std::move(Stack[a]);
10195 totDepth -= 2;
10196 }
10197 }
10198 if(ok)
10199 if(depth > 2)
10200 if((Stack[depth - 2].level >= Stack[depth - 1].level) &&
10201 (Stack[depth - 2].level >= Stack[depth].level)) {
10202
10203 if(ok && (!opFlag) && (Stack[depth - 2].type == STYP_SEL2)
10204 && (Stack[depth - 1].type == STYP_VALU)
10205 && (Stack[depth].type == STYP_VALU)) {
10206 /* 2 argument value operator */
10207 ok = SelectorSelect2(G, &Stack[depth - 2], state);
10208 opFlag = true;
10209 for(a = depth + 1; a <= totDepth; a++)
10210 Stack[a - 2] = std::move(Stack[a]);
10211 totDepth -= 2;
10212 }
10213 }
10214 if(ok)
10215 if(depth > 3)
10216 if((Stack[depth - 3].level >= Stack[depth].level) &&
10217 (Stack[depth - 3].level >= Stack[depth - 1].level) &&
10218 (Stack[depth - 3].level >= Stack[depth - 2].level)) {
10219
10220 if(ok && (!opFlag) && (Stack[depth - 3].type == STYP_SEL3)
10221 && (Stack[depth].type == STYP_VALU)
10222 && (Stack[depth - 1].type == STYP_VALU)
10223 && (Stack[depth - 2].type == STYP_VALU)) {
10224 /* 2 argument logical operator */
10225 ok = SelectorSelect3(G, &Stack[depth-3], state);
10226 opFlag = true;
10227 for(a = depth + 1; a <= totDepth; a++)
10228 Stack[a - 3] = std::move(Stack[a]);
10229 totDepth -= 3;
10230 }
10231 }
10232 if(ok)
10233 if(depth > 4)
10234 if((Stack[depth - 3].level >= Stack[depth].level) &&
10235 (Stack[depth - 3].level >= Stack[depth - 1].level) &&
10236 (Stack[depth - 3].level >= Stack[depth - 2].level) &&
10237 (Stack[depth - 3].level >= Stack[depth - 4].level)) {
10238
10239 if(ok && (!opFlag) && (Stack[depth - 3].type == STYP_OP22)
10240 && (Stack[depth - 1].type == STYP_VALU)
10241 && (Stack[depth - 2].type == STYP_VALU)
10242 && (Stack[depth].type == STYP_LIST)
10243 && (Stack[depth - 4].type == STYP_LIST)) {
10244
10245 ok = SelectorOperator22(G, &Stack[depth - 4], state);
10246 opFlag = true;
10247 for(a = depth + 1; a <= totDepth; a++)
10248 Stack[a - 4] = std::move(Stack[a]);
10249 totDepth -= 4;
10250 }
10251
10252 }
10253 if(opFlag) {
10254 depth = 1; /* start back at the left hand side */
10255 } else {
10256 depth = depth + 1;
10257 opFlag = true;
10258 if(depth > totDepth)
10259 break;
10260 }
10261 }
10262 if(level)
10263 level--;
10264 else
10265 break;
10266 }
10267 depth = totDepth;
10268 }
10269
10270 if (!ok) {
10271 return pymol::Error(indicate_last_token(word, c));
10272 }
10273
10274 if (depth != 1) {
10275 return pymol::Error("Malformed selection.");
10276 }
10277
10278 if (Stack[depth].type != STYP_LIST) {
10279 return pymol::Error("Invalid selection.");
10280 }
10281
10282 return std::move(Stack[totDepth].sele); /* return the selection list */
10283 }
10284
10285
10286 /*========================================================================*/
10287 /**
10288 * Break a selection down into tokens and return them in a vector.
10289 * E.g. "(name CA+CB)" -> {"(", "name", "CA+CB", ")"}.
10290 * @param s selection expression to parse
10291 * @return tokens
10292 */
SelectorParse(PyMOLGlobals * G,const char * s)10293 std::vector<std::string> SelectorParse(PyMOLGlobals * G, const char *s)
10294 {
10295 int w_flag = false;
10296 int quote_flag = false;
10297 char quote_char = '"';
10298 const char *p = s;
10299 std::string* q = nullptr;
10300 std::vector<std::string> r;
10301 while(*p) {
10302 if(w_flag) { /* currently in a word, thus q is a valid pointer */
10303 if(quote_flag) {
10304 if(*p != quote_char) {
10305 *q += *p;
10306 } else {
10307 quote_flag = false;
10308 *q += *p;
10309 }
10310 } else
10311 switch (*p) {
10312 case ' ':
10313 w_flag = false;
10314 break;
10315 case ';': /* special word terminator */
10316 *q += *p;
10317 w_flag = false;
10318 break;
10319 case '!': /* single words */
10320 case '&':
10321 case '|':
10322 case '(':
10323 case ')':
10324 case '>':
10325 case '<':
10326 case '=':
10327 case '%':
10328 r.emplace_back(1, *p); /* add new word */
10329 q = &r.back();
10330 w_flag = false;
10331 break;
10332 case '"':
10333 quote_flag = true;
10334 *q += *p;
10335 break;
10336 default:
10337 *q += *p;
10338 break;
10339 }
10340 } else { /*outside a word -- q is undefined */
10341
10342 switch (*p) {
10343 case '!': /* single words */
10344 case '&':
10345 case '|':
10346 case '(':
10347 case ')':
10348 case '>':
10349 case '<':
10350 case '=':
10351 case '%':
10352 r.emplace_back(1, *p); /* add new word */
10353 q = &r.back();
10354 break;
10355 case ' ':
10356 break;
10357 case '"':
10358 quote_flag = true;
10359 quote_char = *p;
10360 w_flag = true;
10361 r.emplace_back(1, *p); /* add new word */
10362 q = &r.back();
10363 break;
10364 default:
10365 w_flag = true;
10366 r.emplace_back(1, *p); /* add new word */
10367 q = &r.back();
10368 break;
10369 }
10370 }
10371 p++;
10372 }
10373
10374 if(Feedback(G, FB_Selector, FB_Debugging)) {
10375 for (auto& word : r) {
10376 fprintf(stderr, "word: %s\n", word.c_str());
10377 }
10378 }
10379 return (r);
10380 }
10381
10382
10383 /*========================================================================*/
10384
SelectorMemoryDump(PyMOLGlobals * G)10385 void SelectorMemoryDump(PyMOLGlobals * G)
10386 {
10387 auto I = G->SelectorMgr;
10388 printf(" SelectorMemory: NSelection %d\n", I->NSelection);
10389 printf(" SelectorMemory: NActive %zu\n", I->Info.size());
10390 printf(" SelectorMemory: NMember %d\n", int(I->Member.size()) - 1);
10391 }
10392
CSelectorManager()10393 CSelectorManager::CSelectorManager()
10394 {
10395 auto I = this;
10396
10397 // indices are >0 by convention
10398 Member.resize(1);
10399
10400 /* create placeholder "all" selection, which is selection 0
10401 and "none" selection, which is selection 1 */
10402 I->Info.emplace_back(I->NSelection++, cKeywordAll);
10403 I->Info.emplace_back(I->NSelection++, cKeywordNone);
10404
10405 assert(I->Info[0].ID == cSelectionAll);
10406 assert(I->Info[1].ID == cSelectionNone);
10407
10408 for (auto kw : Keyword) {
10409 if (!kw.word[0]) {
10410 break;
10411 }
10412 I->Key[kw.word] = kw.value;
10413 }
10414 }
10415
SelectorReinit(PyMOLGlobals * G)10416 void SelectorReinit(PyMOLGlobals * G)
10417 {
10418 SelectorClean(G);
10419 *G->SelectorMgr = CSelectorManager();
10420 }
10421
10422
10423 /*========================================================================*/
CSelector(PyMOLGlobals * G,CSelectorManager * mgr)10424 CSelector::CSelector(PyMOLGlobals* G, CSelectorManager* mgr)
10425 : G(G)
10426 , mgr(mgr)
10427 {
10428 }
10429
10430 /*========================================================================*/
10431
SelectorGetDistSet(PyMOLGlobals * G,DistSet * ds,int sele1,int state1,int sele2,int state2,int mode,float cutoff,float * result)10432 DistSet *SelectorGetDistSet(PyMOLGlobals * G, DistSet * ds,
10433 int sele1, int state1, int sele2, int state2,
10434 int mode, float cutoff, float *result)
10435 {
10436 CSelector *I = G->Selector;
10437 std::vector<int> vla;
10438 int c;
10439 float dist;
10440 int a1, a2;
10441 AtomInfoType *ai1, *ai2;
10442 int at, at1, at2;
10443 CoordSet *cs1, *cs2;
10444 ObjectMolecule *obj, *obj1, *obj2, *lastObj;
10445 int idx1, idx2;
10446 int a;
10447 int nv = 0;
10448 float *vv0, *vv1;
10449 float dist_sum = 0.0;
10450 int dist_cnt = 0;
10451 int s;
10452 int a_keeper = false;
10453 int *zero = NULL, *scratch = NULL;
10454 std::vector<bool> coverage;
10455 HBondCriteria hbcRec, *hbc;
10456 int exclusion = 0;
10457 int bonds_only = 0;
10458 int from_proton = SettingGetGlobal_b(G, cSetting_h_bond_from_proton);
10459 AtomInfoType *h_ai;
10460
10461 bool cutoff_is_ratio_distance_to_vdW = false;
10462
10463 /* if we're creating hydrogen bonds, then set some distance cutoffs */
10464 switch (mode) {
10465 case 1:
10466 bonds_only = 1;
10467 break;
10468 case 2:
10469 exclusion = SettingGetGlobal_i(G, cSetting_h_bond_exclusion);
10470 break;
10471 case 8:
10472 cutoff_is_ratio_distance_to_vdW = true;
10473 mode = 3;
10474 // no break, continue with case 3
10475 case 3:
10476 exclusion = SettingGetGlobal_i(G, cSetting_distance_exclusion);
10477 break;
10478 }
10479
10480 hbc = &hbcRec;
10481 *result = 0.0;
10482 /* if the dist set exists, get info from it, otherwise get a new one */
10483 if(!ds) {
10484 ds = DistSetNew(G);
10485 } else {
10486 nv = ds->NIndex; /* number of vertices */
10487 }
10488
10489 auto& vv = ds->Coord;
10490 vv.reserve(10);
10491
10492 /* update states: if the two are the same, update that one state, else update all states */
10493 if((state1 < 0) || (state2 < 0) || (state1 != state2)) {
10494 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
10495 } else {
10496 SelectorUpdateTable(G, state1, -1);
10497 }
10498
10499 /* find and prepare (neighbortables) in any participating Molecular objects */
10500 if((mode == 1) || (mode == 2) || (mode == 3)) { /* fill in all the neighbor tables */
10501 int max_n_atom = I->Table.size();
10502 lastObj = NULL;
10503 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
10504 /* foreach atom in the session, get its identifier and ObjectMolecule to which it belongs */
10505 at = I->Table[a].atom; /* grab the atom ID from the Selectors->Table */
10506 obj = I->Obj[I->Table[a].model]; /* -- JV -- quick way to get an object from an atom */
10507 s = obj->AtomInfo[at].selEntry; /* grab the selection entry# from this Atoms Info */
10508 if(obj != lastObj) {
10509 if(max_n_atom < obj->NAtom)
10510 max_n_atom = obj->NAtom;
10511 /* if the current atom is in sele1 or sele2 then update it's object's neighbor table */
10512 if(SelectorIsMember(G, s, sele1) || SelectorIsMember(G, s, sele2)) {
10513 ObjectMoleculeUpdateNeighbors(obj);
10514 /* if hbonds (so, more than just distance) */
10515 if(mode == 2)
10516 ObjectMoleculeVerifyChemistry(obj, -1);
10517 lastObj = obj;
10518 }
10519 }
10520 }
10521 /* prepare these for the next round */
10522 zero = pymol::calloc<int>(max_n_atom);
10523 scratch = pymol::malloc<int>(max_n_atom);
10524 }
10525
10526 /* if we're hydrogen bonding, setup the cutoff */
10527 if(mode == 2) {
10528 ObjectMoleculeInitHBondCriteria(G, hbc);
10529 if(cutoff < 0.0F) {
10530 cutoff = hbc->maxDistAtMaxAngle;
10531 if(cutoff < hbc->maxDistAtZero) {
10532 cutoff = hbc->maxDistAtZero;
10533 }
10534 }
10535 }
10536 if(cutoff < 0)
10537 cutoff = 1000.0;
10538
10539 if (mode == 4) {
10540 // centroid distance
10541 float centroid1[3], centroid2[3];
10542 ObjectMoleculeOpRec op;
10543
10544 // get centroid 1
10545 ObjectMoleculeOpRecInit(&op);
10546 op.code = OMOP_CSetSumVertices;
10547 op.cs1 = state1;
10548 ExecutiveObjMolSeleOp(G, sele1, &op);
10549
10550 if (op.i1 > 0) {
10551 scale3f(op.v1, 1.f / op.i1, centroid1);
10552
10553 // get centroid 2
10554 ObjectMoleculeOpRecInit(&op);
10555 op.code = OMOP_CSetSumVertices;
10556 op.cs1 = state2;
10557 ExecutiveObjMolSeleOp(G, sele2, &op);
10558
10559 if (op.i1 > 0) {
10560 scale3f(op.v1, 1.f / op.i1, centroid2);
10561
10562 // store positions in measurement object
10563 VLACheck(vv, float, (nv * 3) + 6);
10564 vv0 = vv + (nv * 3);
10565 nv += 2;
10566 copy3f(centroid1, vv0);
10567 copy3f(centroid2, vv0 + 3);
10568
10569 // for the return value
10570 dist_cnt = 1;
10571 dist_sum = diff3f(centroid1, centroid2);
10572 }
10573 }
10574
10575 // skip searching for pairwise distances
10576 c = 0;
10577 } else {
10578 /* coverage determines if a given atom appears in sel1 and sel2 */
10579 coverage.resize(I->Table.size());
10580
10581 for (SelectorAtomIterator iter(I); iter.next();) {
10582 s = iter.getAtomInfo()->selEntry;
10583 if (SelectorIsMember(G, s, sele1) &&
10584 SelectorIsMember(G, s, sele2))
10585 coverage[iter.a] = true;
10586 }
10587
10588 float cutoff_map = cutoff;
10589 if (cutoff_is_ratio_distance_to_vdW) {
10590 constexpr float vdw_upper_bounds = 3.f;
10591 cutoff_map *= 2 * vdw_upper_bounds;
10592 }
10593
10594 /* this creates an interleaved list of ints for mapping ids to states within a given neighborhood */
10595 vla = SelectorGetInterstateVLA(G, sele1, state1, sele2, state2, cutoff_map);
10596 c = vla.size() / 2;
10597 }
10598
10599 /* for each state */
10600 for(a = 0; a < c; a++) {
10601 /* get the interstate atom identifier for the two atoms to distance */
10602 a1 = vla[a * 2];
10603 a2 = vla[a * 2 + 1];
10604
10605 /* check their coverage to avoid duplicates */
10606 if(a1 < a2 || (a1 != a2 && !(coverage[a1] && coverage[a2]))
10607 || (state1 != state2)) { /* eliminate reverse duplicates */
10608 /* get the object-local atom ID */
10609 at1 = I->Table[a1].atom;
10610 at2 = I->Table[a2].atom;
10611 /* get the object for this global atom ID */
10612 obj1 = I->Obj[I->Table[a1].model];
10613 obj2 = I->Obj[I->Table[a2].model];
10614
10615 /* the states are valid for these two atoms */
10616 if((state1 < obj1->NCSet) && (state2 < obj2->NCSet)) {
10617 /* get the coordinate sets for both atoms */
10618 cs1 = obj1->CSet[state1];
10619 cs2 = obj2->CSet[state2];
10620 if(cs1 && cs2) {
10621 /* for bonding */
10622 float *don_vv = NULL;
10623 float *acc_vv = NULL;
10624
10625 /* grab the appropriate atom information for this object-local atom */
10626 ai1 = obj1->AtomInfo + at1;
10627 ai2 = obj2->AtomInfo + at2;
10628
10629 idx1 = cs1->atmToIdx(at1);
10630 idx2 = cs2->atmToIdx(at2);
10631
10632 if((idx1 >= 0) && (idx2 >= 0)) {
10633 /* actual distance calculation from ptA to ptB */
10634 dist = (float) diff3f(cs1->coordPtr(idx1), cs2->coordPtr(idx2));
10635
10636 if (cutoff_is_ratio_distance_to_vdW) {
10637 dist /= ai1->vdw + ai2->vdw;
10638 }
10639
10640 /* if we pass the boding cutoff */
10641 if(dist < cutoff) {
10642 float h_crd[3];
10643 h_ai = NULL;
10644
10645 a_keeper = true;
10646 if(exclusion && (obj1 == obj2)) {
10647 a_keeper = !SelectorCheckNeighbors(G, exclusion,
10648 obj1, at1, at2, zero, scratch);
10649 } else if(bonds_only) {
10650 a_keeper = SelectorCheckNeighbors(G, 1, obj1, at1, at2, zero, scratch);
10651 }
10652 if(a_keeper && (mode == 2)) {
10653 /* proton comes from ai1 */
10654 if(ai1->hb_donor && ai2->hb_acceptor) {
10655 a_keeper = ObjectMoleculeGetCheckHBond(&h_ai, h_crd,
10656 obj1, at1, state1,
10657 obj2, at2, state2,
10658 hbc);
10659 if(a_keeper) {
10660 if(h_ai && from_proton) {
10661 don_vv = h_crd;
10662 ai1 = h_ai;
10663 }
10664 else {
10665 don_vv = cs1->coordPtr(idx1);
10666 }
10667 acc_vv = cs2->coordPtr(idx2);
10668 }
10669 } else if(ai1->hb_acceptor && ai2->hb_donor) {
10670 /* proton comes from ai2 */
10671 a_keeper = ObjectMoleculeGetCheckHBond(&h_ai, h_crd,
10672 obj2, at2, state2,
10673 obj1, at1, state1,
10674 hbc);
10675
10676 if(a_keeper) {
10677 if(h_ai && from_proton) {
10678 don_vv = h_crd;
10679 ai2 = h_ai;
10680 }
10681 else {
10682 don_vv = cs2->coordPtr(idx2);
10683 }
10684 acc_vv = cs1->coordPtr(idx1);
10685 }
10686 } else {
10687 a_keeper = false;
10688 }
10689 }
10690 if((sele1 == sele2) && (at1 > at2))
10691 a_keeper = false;
10692
10693 if(a_keeper) {
10694
10695 /* Insert DistInfo records for updating distances */
10696 /* Init/Add the elem to the DistInfo list */
10697 ds->MeasureInfo.emplace_front();
10698 auto* atom1Info = &ds->MeasureInfo.front();
10699
10700 // TH
10701 atom1Info->id[0] = AtomInfoCheckUniqueID(G, ai1);
10702 atom1Info->id[1] = AtomInfoCheckUniqueID(G, ai2);
10703
10704 atom1Info->offset = nv; /* offset into this DSet's Coord */
10705 atom1Info->state[0] = state1; /* state1 of sel1 */
10706 atom1Info->state[1] = state2;
10707 atom1Info->measureType = cRepDash; /* DISTANCE-dash */
10708
10709 /* we have a distance we want to keep */
10710 dist_cnt++;
10711 dist_sum += dist;
10712 /* see if vv has room at another 6 floats */
10713 VLACheck(vv, float, (nv * 3) + 6);
10714 vv0 = vv + (nv * 3);
10715
10716 if((mode == 2) && (don_vv) && (acc_vv)) {
10717 *(vv0++) = *(don_vv++);
10718 *(vv0++) = *(don_vv++);
10719 *(vv0++) = *(don_vv++);
10720 *(vv0++) = *(acc_vv++);
10721 *(vv0++) = *(acc_vv++);
10722 *(vv0++) = *(acc_vv++);
10723 } else {
10724 vv1 = cs1->coordPtr(idx1);
10725 *(vv0++) = *(vv1++);
10726 *(vv0++) = *(vv1++);
10727 *(vv0++) = *(vv1++);
10728 vv1 = cs2->coordPtr(idx2);
10729 *(vv0++) = *(vv1++);
10730 *(vv0++) = *(vv1++);
10731 *(vv0++) = *(vv1++);
10732 }
10733
10734 nv += 2;
10735 }
10736 }
10737 }
10738 }
10739 }
10740 }
10741 }
10742 if(dist_cnt)
10743 (*result) = dist_sum / dist_cnt;
10744 FreeP(zero);
10745 FreeP(scratch);
10746 if(vv)
10747 VLASize(vv, float, (nv + 1) * 3);
10748 ds->NIndex = nv;
10749 ds->Coord = vv;
10750 return (ds);
10751 }
10752
SelectorGetAngleSet(PyMOLGlobals * G,DistSet * ds,int sele1,int state1,int sele2,int state2,int sele3,int state3,int mode,float * angle_sum,int * angle_cnt)10753 DistSet *SelectorGetAngleSet(PyMOLGlobals * G, DistSet * ds,
10754 int sele1, int state1,
10755 int sele2, int state2,
10756 int sele3, int state3,
10757 int mode, float *angle_sum, int *angle_cnt)
10758 {
10759 CSelector *I = G->Selector;
10760 int nv = 0;
10761 std::vector<bool> coverage;
10762
10763 if(!ds) {
10764 ds = DistSetNew(G);
10765 } else {
10766 nv = ds->NAngleIndex;
10767 }
10768
10769 auto& vv = ds->AngleCoord;
10770 vv.reserve(10);
10771
10772 if((state1 < 0) || (state2 < 0) || (state3 < 0) || (state1 != state2)
10773 || (state1 != state3)) {
10774 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
10775 } else {
10776 SelectorUpdateTable(G, state1, -1);
10777 }
10778
10779 /* which atoms are involved? */
10780
10781 {
10782 int a, s, at;
10783 ObjectMolecule *obj;
10784
10785 coverage.resize(I->Table.size());
10786 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
10787 at = I->Table[a].atom;
10788 obj = I->Obj[I->Table[a].model];
10789 s = obj->AtomInfo[at].selEntry;
10790 if (SelectorIsMember(G, s, sele1) &&
10791 SelectorIsMember(G, s, sele3))
10792 coverage[a] = true;
10793 }
10794 }
10795
10796 { /* fill in neighbor tables */
10797 int a, s, at;
10798 ObjectMolecule *obj, *lastObj = NULL;
10799 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
10800 at = I->Table[a].atom;
10801 obj = I->Obj[I->Table[a].model];
10802 s = obj->AtomInfo[at].selEntry;
10803 if(obj != lastObj) {
10804 if(SelectorIsMember(G, s, sele1) ||
10805 SelectorIsMember(G, s, sele2) || SelectorIsMember(G, s, sele3)) {
10806 ObjectMoleculeUpdateNeighbors(obj);
10807 lastObj = obj;
10808 }
10809 }
10810 }
10811 }
10812
10813 {
10814 int a, s, at;
10815 ObjectMolecule *obj;
10816 int *list1 = VLAlloc(int, 1000);
10817 int *list2 = VLAlloc(int, 1000);
10818 int *list3 = VLAlloc(int, 1000);
10819 int n1 = 0;
10820 int n2 = 0;
10821 int n3 = 0;
10822 int bonded12, bonded23;
10823
10824 /* now generate three lists of atoms, one for each selection set */
10825
10826 if(list1 && list2 && list3) {
10827 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
10828 at = I->Table[a].atom;
10829 obj = I->Obj[I->Table[a].model];
10830 s = obj->AtomInfo[at].selEntry;
10831 if(SelectorIsMember(G, s, sele1)) {
10832 VLACheck(list1, int, n1);
10833 list1[n1++] = a;
10834 }
10835 if(SelectorIsMember(G, s, sele2)) {
10836 VLACheck(list2, int, n2);
10837 list2[n2++] = a;
10838 }
10839 if(SelectorIsMember(G, s, sele3)) {
10840 VLACheck(list3, int, n3);
10841 list3[n3++] = a;
10842 }
10843 }
10844
10845 /* for each set of 3 atoms in each selection... */
10846
10847 {
10848 int i1, i2, i3;
10849 int a1, a2, a3;
10850 int at1, at2, at3;
10851
10852 /* AtomInfoType *ai1,*ai2,ai3; */
10853 CoordSet *cs1, *cs2, *cs3;
10854 ObjectMolecule *obj1, *obj2, *obj3;
10855
10856 int idx1, idx2, idx3;
10857 float angle;
10858 float d1[3], d2[3];
10859 float *v1, *v2, *v3, *vv0;
10860
10861 for(i1 = 0; i1 < n1; i1++) {
10862 a1 = list1[i1];
10863 at1 = I->Table[a1].atom;
10864 obj1 = I->Obj[I->Table[a1].model];
10865
10866 if(state1 < obj1->NCSet) {
10867 cs1 = obj1->CSet[state1];
10868
10869 if(cs1) {
10870 idx1 = cs1->atmToIdx(at1);
10871
10872 if(idx1 >= 0) {
10873
10874 for(i2 = 0; i2 < n2; i2++) {
10875 a2 = list2[i2];
10876 at2 = I->Table[a2].atom;
10877 obj2 = I->Obj[I->Table[a2].model];
10878
10879 if(state2 < obj2->NCSet) {
10880
10881 cs2 = obj2->CSet[state2];
10882
10883 if(cs2) {
10884 idx2 = cs2->atmToIdx(at2);
10885
10886 if(idx2 >= 0) {
10887 /* neighbor table like BPRec */
10888 bonded12 = ObjectMoleculeAreAtomsBonded2(obj1, at1, obj2, at2);
10889
10890 for(i3 = 0; i3 < n3; i3++) {
10891 a3 = list3[i3];
10892
10893 if( (a1 != a2 || state1 != state2) &&
10894 (a2 != a3 || state2 != state3) &&
10895 (a1 != a3 || state1 != state3)) {
10896 if(!(coverage[a1] && coverage[a3])
10897 || (a1 < a3)
10898 || (state1 != state3)) { /* eliminate alternate-order duplicates */
10899
10900 at3 = I->Table[a3].atom;
10901 obj3 = I->Obj[I->Table[a3].model];
10902
10903 if(state3 < obj3->NCSet) {
10904
10905 cs3 = obj3->CSet[state3];
10906
10907 if(cs3) {
10908 idx3 = cs3->atmToIdx(at3);
10909
10910 if(idx3 >= 0) {
10911
10912 bonded23 =
10913 ObjectMoleculeAreAtomsBonded2(obj2, at2, obj3, at3);
10914
10915 if(!mode || ((mode == 1) && (bonded12 && bonded23))) {
10916 /* store the 3 coordinates */
10917
10918 v1 = cs1->coordPtr(idx1);
10919 v2 = cs2->coordPtr(idx2);
10920 v3 = cs3->coordPtr(idx3);
10921
10922 subtract3f(v1, v2, d1);
10923 subtract3f(v3, v2, d2);
10924
10925 /* Insert DistInfo records for updating distances */
10926 /* Init/Add the elem to the DistInfo list */
10927 ds->MeasureInfo.emplace_front();
10928 auto* atom1Info = &ds->MeasureInfo.front();
10929
10930 // TH
10931 atom1Info->id[0] = AtomInfoCheckUniqueID(G, obj1->AtomInfo + at1);
10932 atom1Info->id[1] = AtomInfoCheckUniqueID(G, obj2->AtomInfo + at2);
10933 atom1Info->id[2] = AtomInfoCheckUniqueID(G, obj3->AtomInfo + at3);
10934
10935 atom1Info->offset = nv; /* offset into this DSet's Coord */
10936 atom1Info->state[0] = state1; /* state1 of sel1 */
10937 atom1Info->state[1] = state2;
10938 atom1Info->state[2] = state3;
10939 atom1Info->measureType = cRepAngle;
10940
10941 angle = get_angle3f(d1, d2);
10942
10943 (*angle_sum) += angle;
10944 (*angle_cnt)++;
10945
10946 VLACheck(vv, float, (nv * 3) + 14);
10947 vv0 = vv + (nv * 3);
10948 *(vv0++) = *(v1++);
10949 *(vv0++) = *(v1++);
10950 *(vv0++) = *(v1++);
10951 *(vv0++) = *(v2++);
10952 *(vv0++) = *(v2++);
10953 *(vv0++) = *(v2++);
10954 *(vv0++) = *(v3++);
10955 *(vv0++) = *(v3++);
10956 *(vv0++) = *(v3++);
10957 *(vv0++) = (float) !bonded12;
10958 /* show line 1 flag */
10959 *(vv0++) = (float) !bonded23;
10960 *(vv0++) = 0.0F;
10961 *(vv0++) = 0.0F; /* label x relative to v2 */
10962 *(vv0++) = 0.0F; /* label y relative to v2 */
10963 *(vv0++) = 0.0F; /* label z relative to v2 */
10964 nv += 5;
10965 }
10966 }
10967 }
10968 }
10969 }
10970 }
10971 }
10972 }
10973 }
10974 }
10975 }
10976 }
10977 }
10978 }
10979 }
10980 }
10981 }
10982 VLAFreeP(list1);
10983 VLAFreeP(list2);
10984 VLAFreeP(list3);
10985 }
10986
10987 if(vv)
10988 VLASize(vv, float, (nv + 1) * 3);
10989 ds->NAngleIndex = nv;
10990 ds->AngleCoord = vv;
10991 return (ds);
10992 }
10993
SelectorGetDihedralSet(PyMOLGlobals * G,DistSet * ds,int sele1,int state1,int sele2,int state2,int sele3,int state3,int sele4,int state4,int mode,float * angle_sum,int * angle_cnt)10994 DistSet *SelectorGetDihedralSet(PyMOLGlobals * G, DistSet * ds,
10995 int sele1, int state1,
10996 int sele2, int state2,
10997 int sele3, int state3,
10998 int sele4, int state4,
10999 int mode, float *angle_sum, int *angle_cnt)
11000 {
11001 CSelector *I = G->Selector;
11002 int nv = 0;
11003 std::vector<bool> coverage14;
11004 std::vector<bool> coverage23;
11005 ObjectMolecule *just_one_object = NULL;
11006 int just_one_atom[4] = { -1, -1, -1, -1 };
11007
11008 if(!ds) {
11009 ds = DistSetNew(G);
11010 } else {
11011 nv = ds->NDihedralIndex;
11012 }
11013
11014 auto& vv = ds->DihedralCoord;
11015 vv.reserve(10);
11016
11017 if((state1 < 0) || (state2 < 0) || (state3 < 0) || (state4 < 0) ||
11018 (state1 != state2) || (state1 != state3) || (state1 != state4)) {
11019 SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
11020 } else {
11021 SelectorUpdateTable(G, state1, -1);
11022 }
11023
11024 /* which atoms are involved? */
11025
11026 {
11027 int a, s, at;
11028 ObjectMolecule *obj;
11029
11030 coverage14.resize(I->Table.size());
11031 coverage23.resize(I->Table.size());
11032 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
11033 bool coverage1 = false;
11034 bool coverage2 = false;
11035 at = I->Table[a].atom;
11036 obj = I->Obj[I->Table[a].model];
11037 if(!a)
11038 just_one_object = obj;
11039 s = obj->AtomInfo[at].selEntry;
11040 if(SelectorIsMember(G, s, sele1)) {
11041 if(obj != just_one_object)
11042 just_one_object = NULL;
11043 else if(just_one_atom[0] == -1)
11044 just_one_atom[0] = a;
11045 else
11046 just_one_atom[0] = -2;
11047 coverage1 = true;
11048 }
11049 if(SelectorIsMember(G, s, sele2)) {
11050 if(obj != just_one_object)
11051 just_one_object = NULL;
11052 else if(just_one_atom[1] == -1)
11053 just_one_atom[1] = a;
11054 else
11055 just_one_atom[1] = -2;
11056 coverage2 = true;
11057 }
11058 if(SelectorIsMember(G, s, sele3)) {
11059 if(obj != just_one_object)
11060 just_one_object = NULL;
11061 else if(just_one_atom[2] == -1)
11062 just_one_atom[2] = a;
11063 else
11064 just_one_atom[2] = -2;
11065 coverage23[a] = coverage2;
11066 }
11067 if(SelectorIsMember(G, s, sele4)) {
11068 if(obj != just_one_object)
11069 just_one_object = NULL;
11070 else if(just_one_atom[3] == -1)
11071 just_one_atom[3] = a;
11072 else
11073 just_one_atom[3] = -2;
11074 coverage14[a] = coverage1;
11075 }
11076 }
11077 }
11078
11079 if(just_one_object) {
11080 ObjectMoleculeUpdateNeighbors(just_one_object);
11081 } else { /* fill in neighbor tables */
11082 int a, s, at;
11083 ObjectMolecule *obj, *lastObj = NULL;
11084 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
11085 at = I->Table[a].atom;
11086 obj = I->Obj[I->Table[a].model];
11087 s = obj->AtomInfo[at].selEntry;
11088 if(obj != lastObj) {
11089 if(SelectorIsMember(G, s, sele1) ||
11090 SelectorIsMember(G, s, sele2) ||
11091 SelectorIsMember(G, s, sele3) || SelectorIsMember(G, s, sele4)
11092 ) {
11093 ObjectMoleculeUpdateNeighbors(obj);
11094 lastObj = obj;
11095 }
11096 }
11097 }
11098 }
11099
11100 {
11101 int a, s, at;
11102 ObjectMolecule *obj;
11103 int *list1 = VLAlloc(int, 1000);
11104 int *list2 = VLAlloc(int, 1000);
11105 int *list3 = VLAlloc(int, 1000);
11106 int *list4 = VLAlloc(int, 1000);
11107 int n1 = 0;
11108 int n2 = 0;
11109 int n3 = 0;
11110 int n4 = 0;
11111 int bonded12, bonded23, bonded34;
11112
11113 /* now generate three lists of atoms, one for each selection set */
11114
11115 if(list1 && list2 && list3 && list4) {
11116
11117 if(just_one_object && (just_one_atom[0] >= 0) && (just_one_atom[1] >= 0)
11118 && (just_one_atom[2] >= 0) && (just_one_atom[3] >= 0)) {
11119 /* optimal case */
11120
11121 list1[0] = just_one_atom[0];
11122 list2[0] = just_one_atom[1];
11123 list3[0] = just_one_atom[2];
11124 list4[0] = just_one_atom[3];
11125
11126 n1 = n2 = n3 = n4 = 1;
11127
11128 } else {
11129
11130 for(a = cNDummyAtoms; a < I->Table.size(); a++) {
11131 at = I->Table[a].atom;
11132 obj = I->Obj[I->Table[a].model];
11133 s = obj->AtomInfo[at].selEntry;
11134 if(SelectorIsMember(G, s, sele1)) {
11135 VLACheck(list1, int, n1);
11136 list1[n1++] = a;
11137 }
11138 if(SelectorIsMember(G, s, sele2)) {
11139 VLACheck(list2, int, n2);
11140 list2[n2++] = a;
11141 }
11142 if(SelectorIsMember(G, s, sele3)) {
11143 VLACheck(list3, int, n3);
11144 list3[n3++] = a;
11145 }
11146 if(SelectorIsMember(G, s, sele4)) {
11147 VLACheck(list4, int, n4);
11148 list4[n4++] = a;
11149 }
11150 }
11151 }
11152
11153 /* for each set of 3 atoms in each selection... */
11154
11155 {
11156 int i1, i2, i3, i4;
11157 int a1, a2, a3, a4;
11158 int at1, at2, at3, at4;
11159
11160 /* AtomInfoType *ai1,*ai2,ai3; */
11161 CoordSet *cs1, *cs2, *cs3, *cs4;
11162 ObjectMolecule *obj1, *obj2, *obj3, *obj4;
11163
11164 int idx1, idx2, idx3, idx4;
11165 float angle;
11166 float *v1, *v2, *v3, *v4, *vv0;
11167
11168 for(i1 = 0; i1 < n1; i1++) {
11169 a1 = list1[i1];
11170 at1 = I->Table[a1].atom;
11171 obj1 = I->Obj[I->Table[a1].model];
11172 if(state1 < obj1->NCSet) {
11173 cs1 = obj1->CSet[state1];
11174
11175 if(cs1) {
11176 idx1 = cs1->atmToIdx(at1);
11177
11178 if(idx1 >= 0) {
11179
11180 for(i2 = 0; i2 < n2; i2++) {
11181 a2 = list2[i2];
11182 at2 = I->Table[a2].atom;
11183 obj2 = I->Obj[I->Table[a2].model];
11184
11185 if(state2 < obj2->NCSet) {
11186
11187 cs2 = obj2->CSet[state2];
11188
11189 if(cs2) {
11190 idx2 = cs2->atmToIdx(at2);
11191
11192 if(idx2 >= 0) {
11193
11194 bonded12 = ObjectMoleculeAreAtomsBonded2(obj1, at1, obj2, at2);
11195
11196 if(!mode || ((mode == 1) && bonded12))
11197 for(i3 = 0; i3 < n3; i3++) {
11198 a3 = list3[i3];
11199 at3 = I->Table[a3].atom;
11200 obj3 = I->Obj[I->Table[a3].model];
11201
11202 if(state3 < obj3->NCSet) {
11203
11204 cs3 = obj3->CSet[state3];
11205
11206 if(cs3) {
11207 idx3 = cs3->atmToIdx(at3);
11208
11209 if(idx3 >= 0) {
11210
11211 bonded23 =
11212 ObjectMoleculeAreAtomsBonded2(obj2, at2, obj3, at3);
11213 if(!mode || ((mode == 1) && bonded23))
11214 for(i4 = 0; i4 < n4; i4++) {
11215 a4 = list4[i4];
11216
11217 if((a1 != a2) && (a1 != a3) && (a1 != a4)
11218 && (a2 != a3) && (a2 != a4) && (a3 != a4)) {
11219 if (!(coverage14[a1] &&
11220 coverage14[a4] &&
11221 coverage23[a2] &&
11222 coverage23[a3])
11223 || (a1 < a4)) {
11224 /* eliminate alternate-order duplicates */
11225
11226 at4 = I->Table[a4].atom;
11227 obj4 = I->Obj[I->Table[a4].model];
11228
11229 if(state4 < obj4->NCSet) {
11230
11231 cs4 = obj4->CSet[state4];
11232
11233 if(cs4) {
11234 idx4 = cs3->atmToIdx(at4);
11235
11236 if(idx4 >= 0) {
11237
11238 bonded34 =
11239 ObjectMoleculeAreAtomsBonded2(obj3, at3,
11240 obj4,
11241 at4);
11242
11243 if(!mode || ((mode == 1) && bonded34)) {
11244 /* store the 3 coordinates */
11245
11246 v1 = cs1->coordPtr(idx1);
11247 v2 = cs2->coordPtr(idx2);
11248 v3 = cs3->coordPtr(idx3);
11249 v4 = cs4->coordPtr(idx4);
11250
11251 /* Insert DistInfo records for updating distances */
11252 /* Init/Add the elem to the DistInfo list */
11253 ds->MeasureInfo.emplace_front();
11254 auto* atom1Info = &ds->MeasureInfo.front();
11255
11256 // TH
11257 atom1Info->id[0] = AtomInfoCheckUniqueID(G, obj1->AtomInfo + at1);
11258 atom1Info->id[1] = AtomInfoCheckUniqueID(G, obj2->AtomInfo + at2);
11259 atom1Info->id[2] = AtomInfoCheckUniqueID(G, obj3->AtomInfo + at3);
11260 atom1Info->id[3] = AtomInfoCheckUniqueID(G, obj4->AtomInfo + at4);
11261
11262 atom1Info->offset = nv; /* offset into this DSet's Coord */
11263
11264 atom1Info->state[0] = state1; /* state1 of sel1 */
11265 atom1Info->state[1] = state2;
11266 atom1Info->state[2] = state3;
11267 atom1Info->state[3] = state4;
11268
11269 atom1Info->measureType = cRepDihedral;
11270
11271 angle = get_dihedral3f(v1, v2, v3, v4);
11272
11273 (*angle_sum) += angle;
11274 (*angle_cnt)++;
11275
11276 VLACheck(vv, float, (nv * 3) + 17);
11277 vv0 = vv + (nv * 3);
11278 ObjectMoleculeGetAtomTxfVertex(obj1,
11279 state1,
11280 at1,
11281 vv0);
11282 ObjectMoleculeGetAtomTxfVertex(obj2,
11283 state2,
11284 at2,
11285 vv0 + 3);
11286 ObjectMoleculeGetAtomTxfVertex(obj3,
11287 state3,
11288 at3,
11289 vv0 + 6);
11290 ObjectMoleculeGetAtomTxfVertex(obj4,
11291 state4,
11292 at4,
11293 vv0 + 9);
11294 vv0 += 12;
11295 *(vv0++) = (float) !bonded12;
11296 *(vv0++) = (float) !bonded23;
11297 *(vv0++) = (float) !bonded34;
11298 *(vv0++) = 0.0F; /* label x relative to v2+v3/2 */
11299 *(vv0++) = 0.0F; /* label y relative to v2+v3/2 */
11300 *(vv0++) = 0.0F; /* label z relative to v2+v3/2 */
11301 nv += 6;
11302 }
11303 }
11304 }
11305 }
11306 }
11307 }
11308 }
11309 }
11310 }
11311 }
11312 }
11313 }
11314 }
11315 }
11316 }
11317 }
11318 }
11319 }
11320 }
11321 }
11322 }
11323 VLAFreeP(list1);
11324 VLAFreeP(list2);
11325 VLAFreeP(list3);
11326 VLAFreeP(list4);
11327 }
11328
11329 if(vv)
11330 VLASize(vv, float, (nv + 1) * 3);
11331 ds->NDihedralIndex = nv;
11332 ds->DihedralCoord = vv;
11333 return (ds);
11334 }
11335
11336
11337 /*========================================================================*/
11338
11339
11340 /*
11341
11342 example selections
11343 cas
11344 backbone
11345 (model 1 and backbone)
11346 (name ca)
11347 (resi 123)
11348 (resi 200:400 and chain A)
11349 (model a)
11350
11351 */
11352
11353
11354 /* In order for selections to be robust during atom insertions
11355 deletions, they are stored not as lists of selected atoms, but
11356 rather in the inverse - as atoms with selection membership
11357 information */
11358
11359
11360 /* Each atom points into the selection heap, a series of linked
11361 entries where each member is selection index */
11362
11363
11364 /* Definition of the selection language:
11365
11366 <sele> = [(] [<not>] <prop> <val-range> [<qual> [<qual-range>] ] [)] { <SEL1> <sele> }
11367
11368 Example selections:
11369
11370 name ca
11371 ( name ca )
11372 name ca around 5 {all atoms within 5 angstroms of any ca atom) }
11373 ( resi 10 )
11374 resi 10:50
11375 resi 10A:50A
11376 resi 10,50
11377 chain A
11378 segi A
11379 model 1 and segi B
11380 model a and segi B
11381 not name ca
11382
11383 */
11384
11385
11386 /* Selection processing, left to right by default, but with parenthesis for grouping
11387 stack based functional language processing
11388 each stack level has a full selection matrix
11389 logical binary operators (or,and) and negation SEL1ation solely on these matrices.
11390 */
11391
11392
11393 /*
11394
11395 (not (name ca or name c) and (name s around 5 and (name c) )) around 6
11396
11397 0:
11398 1: not
11399 2: name
11400 2: ca
11401
11402 0:
11403
11404 1: not
11405 2:<name ca>
11406 not name ca around 5
11407
11408 force compute
11409
11410 0:
11411 1: not
11412
11413 attrib b < 0
11414
11415 */
11416