void Trajectory::shiftIntoBox(Positions &p) { if (p.getBox().empty()) return; const vector<Vector3D> &box = p.getBox(); if (offsets.size() < p.size()) offsets.resize(p.size()); // NOTE: This method was stolen from: // gromacs-4.5.4/src/gmxlib/rmpbc.c:rm_gropbc() // With the difference that we are accumulating offsets over the whole // trajectory. for (unsigned n = 1; n < p.size(); n++) { Vector3D orig = p[n]; p[n] += offsets[n]; for (int m = 2; 0 <= m; m--) { double dist; while (0.75 * box[m][m] < fabs(dist = p[n][m] - p[n - 1][m])) { if (10 * box[m][m] < fabs(dist)) break; // Ignore unreasonable if (0 < dist) for (int d = 0; d <= m; d++) p[n][d] -= box[m][d]; else for (int d = 0; d <= m; d++) p[n][d] += box[m][d]; } } offsets[n] = p[n] - orig; LOG_DEBUG(5, "SHIFT: " << n << ' ' << p[n] << ' ' << offsets[n]); } }
static double alignment(const Positions &p1, const Positions &p2) { // Computes a metric of how aligned the two sets of positions are double d = 0; // Only consider every 8th position for better performance for (unsigned i = 1; i < p1.size(); i += 8) d += p1[i].distanceSquared(p2[i]); return sqrt(d) * 8 / p1.size(); }
void DoubleTapDetector::initGesture(const Positions& positions) { if (!_canBeDoubleTap) { // adding initial points if (positions.size() > _touchStartPos.size()) { if (_touchStartPos.empty()) _startGesture(positions); else _touchStartPos = positions; return; } // all points released, decide if potential double tap or abort if (positions.empty()) { _canBeDoubleTap = _doubleTapTimer.isActive(); if (!_canBeDoubleTap) cancelGesture(); } return; } // points pressed again, check for double tap if (positions.size() == _touchStartPos.size() && !MathUtils::hasMoved(positions, _touchStartPos, _doubleTapThresholdPx)) { emit doubleTap(MathUtils::computeCenter(_touchStartPos), _touchStartPos.size()); _doubleTapTimer.stop(); } // all points released, reset everything for next detection if (positions.empty()) cancelGesture(); }
void Trajectory::alignToLast(Positions &p) { // This function uses a random search, similar to simulated annealing, to // find a rotation of the current positions which is close to the previous. // This helps stabilize the view of the protein and improve interpolation // between frames. if (empty() || p.empty()) return; const Positions &last = *back(); double align = alignment(p, last); double start = align; double angle = 2 * M_PI; unsigned rounds = 64; const double magicStoppingPoint = 0.3; double rate = pow(4.0 / 360.0, 1.0 / rounds); // Within 4 degrees for (unsigned j = 0; magicStoppingPoint < align && j < rounds; j++) { for (unsigned l = 0; l < 2; l++) { uint8_t r = Random::instance().rand<uint8_t>(); Vector3D v(!(r & 1), !(r & 2), !(r & 4)); AxisAngleD a(angle * l ? -1 : 1, v.normalize()); // Rotate Positions tmp(p); for (unsigned i = 0; i < p.size(); i++) tmp[i] = a.rotate(tmp[i]); // Recheck double newAlign = alignment(tmp, last); if (newAlign < align) { // Accept align = newAlign; p = tmp; } } angle *= rate; } if (magicStoppingPoint <= start) LOG_DEBUG(3, "Alignment start=" << start << " end=" << align << " improved " << String::printf("%0.2f%%", (1.0 - align / start) * 100)); }