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