Skip to content

Commit 0a53de0

Browse files
pslacerda1JarrettSJohnson
authored andcommitted
Add support for >=, <=, == operators in selections
1 parent 796d84e commit 0a53de0

File tree

2 files changed

+140
-99
lines changed

2 files changed

+140
-99
lines changed

layer3/Selector.cpp

Lines changed: 129 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -692,13 +692,18 @@ static WordKeyValue Keyword[] = {
692692
#define SCMP_LTHN 0x02
693693
#define SCMP_RANG 0x03
694694
#define SCMP_EQAL 0x04
695+
#define SCMP_GEQL 0x05
696+
#define SCMP_LEQL 0x06
695697

696698
static WordKeyValue AtOper[] = {
697699
{">", SCMP_GTHN},
698700
{"<", SCMP_LTHN},
699701
{"in", SCMP_RANG},
700702
{"=", SCMP_EQAL},
701-
{"", 0}
703+
{"==", SCMP_EQAL},
704+
{">=", SCMP_GEQL},
705+
{"<=", SCMP_LEQL},
706+
{"", 0},
702707
};
703708

704709
static short fcmp(float a, float b, int oper) {
@@ -709,6 +714,10 @@ static short fcmp(float a, float b, int oper) {
709714
return (a < b);
710715
case SCMP_EQAL:
711716
return fabs(a - b) < R_SMALL4;
717+
case SCMP_GEQL:
718+
return (a >= b);
719+
case SCMP_LEQL:
720+
return (a <= b);
712721
}
713722
printf("ERROR: invalid operator %d\n", oper);
714723
return false;
@@ -8443,109 +8452,116 @@ static int SelectorSelect2(PyMOLGlobals * G, EvalElem * base, int state)
84438452
base->sele_calloc(I->Table.size());
84448453
base->sele_err_chk_ptr(G);
84458454
switch (base->code) {
8446-
case SELE_XVLx:
8447-
case SELE_YVLx:
8448-
case SELE_ZVLx:
8449-
oper = WordKey(G, AtOper, base[1].text(), 4, ignore_case, &exact);
8450-
switch (oper) {
8451-
case SCMP_GTHN:
8452-
case SCMP_LTHN:
8453-
case SCMP_EQAL:
8454-
if(sscanf(base[2].text(), "%f", &comp1) != 1)
8455-
ok = ErrMessage(G, "Selector", "Invalid Number");
8456-
break;
8457-
default:
8458-
ok = ErrMessage(G, "Selector", "Invalid Operator.");
8459-
break;
8460-
}
8461-
if(ok) {
8462-
ObjectMolecule *obj;
8463-
CoordSet *cs;
8464-
int at, idx, s, s0 = 0, sN = I->NCSet;
8465-
8466-
if (state != cStateAll) {
8467-
s0 = (state < cStateAll) ? SceneGetState(G) : state;
8468-
sN = s0 + 1;
8455+
case SELE_XVLx:
8456+
case SELE_YVLx:
8457+
case SELE_ZVLx:
8458+
oper = WordKey(G, AtOper, base[1].text(), 4, ignore_case, &exact);
8459+
switch (oper) {
8460+
case SCMP_GTHN:
8461+
case SCMP_LTHN:
8462+
case SCMP_EQAL:
8463+
case SCMP_GEQL:
8464+
case SCMP_LEQL:
8465+
if(sscanf(base[2].text(), "%f", &comp1) != 1)
8466+
ok = ErrMessage(G, "Selector", "Invalid Number");
8467+
break;
8468+
default:
8469+
ok = ErrMessage(G, "Selector", "Invalid Operator.");
8470+
break;
84698471
}
84708472

8471-
for(a = cNDummyAtoms; a < I->Table.size(); a++)
8472-
base[0].sele[a] = false;
8473+
if(ok) {
8474+
ObjectMolecule *obj;
8475+
CoordSet *cs;
8476+
int at, idx, s, s0 = 0, sN = I->NCSet;
84738477

8474-
for(s = s0; s < sN; s++) {
8475-
for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8476-
if(base[0].sele[a])
8477-
continue;
8478+
if (state != cStateAll) {
8479+
s0 = (state < cStateAll) ? SceneGetState(G) : state;
8480+
sN = s0 + 1;
8481+
}
84788482

8479-
obj = I->Obj[I->Table[a].model];
8480-
if(s >= obj->NCSet)
8481-
continue;
8483+
for(s = s0; s < sN; s++) {
8484+
for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8485+
if(base[0].sele[a])
8486+
continue;
84828487

8483-
at = I->Table[a].atom;
8484-
cs = obj->CSet[s];
8485-
idx = cs->atmToIdx(at);
8486-
if(idx < 0)
8487-
continue;
8488+
obj = I->Obj[I->Table[a].model];
8489+
if(s >= obj->NCSet)
8490+
continue;
84888491

8489-
idx *= 3;
8490-
switch (base->code) {
8491-
case SELE_ZVLx:
8492-
idx++;
8493-
case SELE_YVLx:
8494-
idx++;
8495-
}
8492+
at = I->Table[a].atom;
8493+
cs = obj->CSet[s];
8494+
idx = cs->atmToIdx(at);
8495+
if(idx < 0)
8496+
continue;
8497+
8498+
idx *= 3;
8499+
switch (base->code) {
8500+
case SELE_ZVLx:
8501+
idx++;
8502+
case SELE_YVLx:
8503+
idx++;
8504+
case SELE_XVLx:
8505+
break;
8506+
}
84968507

8497-
base[0].sele[a] = fcmp(cs->Coord[idx], comp1, oper);
8508+
if (fcmp(cs->Coord[idx], comp1, oper)) {
8509+
base[0].sele[a] = true;
8510+
} else {
8511+
base[0].sele[a] = false;
8512+
}
8513+
}
84988514
}
84998515
}
8500-
}
8501-
break;
8502-
case SELE_PCHx:
8503-
case SELE_FCHx:
8504-
case SELE_BVLx:
8505-
case SELE_QVLx:
8506-
oper = WordKey(G, AtOper, base[1].text(), 4, ignore_case, &exact);
8507-
if(!oper) {
8508-
ok = ErrMessage(G, "Selector", "Invalid Operator.");
85098516
break;
8510-
}
8511-
switch (oper) {
8512-
case SCMP_GTHN:
8513-
case SCMP_LTHN:
8514-
case SCMP_EQAL:
8515-
if(sscanf(base[2].text(), "%f", &comp1) != 1)
8516-
ok = ErrMessage(G, "Selector", "Invalid Number");
8517+
case SELE_PCHx:
8518+
case SELE_FCHx:
8519+
case SELE_BVLx:
8520+
case SELE_QVLx:
8521+
oper = WordKey(G, AtOper, base[1].text(), 4, ignore_case, &exact);
8522+
if(!oper) {
8523+
ok = ErrMessage(G, "Selector", "Invalid Operator.");
8524+
break;
85178525
}
8518-
break;
8519-
default:
8520-
ok = ErrMessage(G, "Selector", "Invalid Operator.");
8521-
break;
8522-
}
8523-
8524-
if(ok) {
8525-
for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8526-
at1 = I->Obj[I->Table[a].model]->AtomInfo + I->Table[a].atom;
8527-
float atomValue;
8528-
switch (base->code) {
8529-
case SELE_BVLx: atomValue = at1->bval; break;
8530-
case SELE_PCHx: atomValue = at1->partialCharge; break;
8531-
case SELE_FCHx: atomValue = at1->formalCharge; break;
8532-
case SELE_QVLx: atomValue = at1->qval; break;
8533-
default: {
8534-
ErrMessage(G, "Selector", "Invalid Operand.");
8535-
return false;
8536-
}
8537-
}
8538-
8539-
if(fcmp(atomValue, comp1, oper)) {
8540-
base[0].sele[a] = true;
8541-
c++;
8542-
} else {
8543-
base[0].sele[a] = false;
8526+
switch (oper) {
8527+
case SCMP_GTHN:
8528+
case SCMP_LTHN:
8529+
case SCMP_EQAL:
8530+
case SCMP_GEQL:
8531+
case SCMP_LEQL:
8532+
if(sscanf(base[2].text(), "%f", &comp1) != 1)
8533+
ok = ErrMessage(G, "Selector", "Invalid Number");
8534+
break;
8535+
default:
8536+
ok = ErrMessage(G, "Selector", "Invalid Operator.");
8537+
break;
8538+
}
8539+
8540+
if(ok) {
8541+
for(a = cNDummyAtoms; a < I->Table.size(); a++) {
8542+
at1 = I->Obj[I->Table[a].model]->AtomInfo + I->Table[a].atom;
8543+
float atomValue;
8544+
switch (base->code) {
8545+
case SELE_BVLx: atomValue = at1->b; break;
8546+
case SELE_PCHx: atomValue = at1->partialCharge; break;
8547+
case SELE_FCHx: atomValue = at1->formalCharge; break;
8548+
case SELE_QVLx: atomValue = at1->q; break;
8549+
default: {
8550+
ErrMessage(G, "Selector", "Invalid Operand.");
8551+
return false;
8552+
}
8553+
}
8554+
8555+
if(fcmp(atomValue, comp1, oper)) {
8556+
base[0].sele[a] = true;
8557+
c++;
8558+
} else {
8559+
base[0].sele[a] = false;
8560+
}
85448561
}
85458562
}
8563+
break;
85468564
}
8547-
break;
8548-
85498565

85508566
PRINTFD(G, FB_Selector)
85518567
" %s: %d atoms selected.\n", __func__, c ENDFD;
@@ -9912,11 +9928,20 @@ std::vector<std::string> SelectorParse(PyMOLGlobals * G, const char *s)
99129928
case '|':
99139929
case '(':
99149930
case ')':
9931+
case '%':
9932+
r.emplace_back(1, *p); /* add new word */
9933+
q = &r.back();
9934+
w_flag = false;
9935+
break;
99159936
case '>':
99169937
case '<':
99179938
case '=':
9918-
case '%':
9919-
r.emplace_back(1, *p); /* add new word */
9939+
if (*(p+1) == '=') { /* handle >=, <=, == */
9940+
r.emplace_back(p, 2);
9941+
p++;
9942+
} else {
9943+
r.emplace_back(1, *p);
9944+
}
99209945
q = &r.back();
99219946
w_flag = false;
99229947
break;
@@ -9936,13 +9961,22 @@ std::vector<std::string> SelectorParse(PyMOLGlobals * G, const char *s)
99369961
case '|':
99379962
case '(':
99389963
case ')':
9939-
case '>':
9940-
case '<':
9941-
case '=':
99429964
case '%':
99439965
r.emplace_back(1, *p); /* add new word */
99449966
q = &r.back();
99459967
break;
9968+
case '>':
9969+
case '<':
9970+
case '=':
9971+
if (*(p+1) == '=') { /* handle >=, <=, == */
9972+
r.emplace_back(p, 2);
9973+
p++;
9974+
} else {
9975+
r.emplace_back(1, *p);
9976+
}
9977+
q = &r.back();
9978+
w_flag = false;
9979+
break;
99469980
case ' ':
99479981
break;
99489982
case '"':
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
from pymol import cmd
22

33

4-
def test_float_comparisons():
4+
def test_select_operators():
55
cmd.reinitialize()
66
cmd.pseudoatom(pos=[1, 2, 3], b=5)
77

8-
assert cmd.count_atoms("b > 4 & x = 1") == 1
9-
assert cmd.count_atoms("b < 6 & y < 3") == 1
10-
assert cmd.count_atoms("b = 5 & z > 2") == 1
8+
assert cmd.count_atoms("b > 4") == 1
9+
assert cmd.count_atoms("b < 6") == 1
10+
assert cmd.count_atoms("b = 5") == 1
11+
assert cmd.count_atoms("b >= 5") == 1
12+
assert cmd.count_atoms("b <= 5") == 1
13+
14+
assert cmd.count_atoms("b > 4 & x == 1") == 1
15+
assert cmd.count_atoms("b > 5 & x == 1") == 0
16+
assert cmd.count_atoms("b < 6 & y <= 3") == 1
17+
assert cmd.count_atoms("b = 5 & z >= 2") == 1

0 commit comments

Comments
 (0)