mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
175 lines
3.9 KiB
C++
175 lines
3.9 KiB
C++
/*
|
|
* Color picking
|
|
*
|
|
* (C) Schrodinger, Inc.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <vector>
|
|
|
|
//! With shaders we have to pre-allocate picking color buffers, so
|
|
//! limit this to 2 passes.
|
|
constexpr auto SHADER_PICKING_PASSES_MAX = 2;
|
|
|
|
//! Transparency cutoff for cPickableThrough, smaller values are considered
|
|
//! opaque.
|
|
constexpr auto PICKABLE_THROUGH_CUTOFF = 0.1f;
|
|
|
|
//! Valid values for the `transparency_picking_mode` setting
|
|
enum {
|
|
cTransparencyPickingModePickable = 1,
|
|
cTransparencyPickingModeAuto = 2, // 0 has same effect
|
|
};
|
|
|
|
enum cPickable_t {
|
|
cPickableAtom = -1,
|
|
cPickableLabel = -2,
|
|
cPickableGadget = -3,
|
|
cPickableNoPick = -4,
|
|
cPickableThrough = -5,
|
|
};
|
|
|
|
/**
|
|
* Identifier for a pickable item like an atom, half-bond or label, within a
|
|
* pick context (object state).
|
|
*/
|
|
struct Pickable {
|
|
//! Primary index (e.g. atom index within object)
|
|
unsigned int index;
|
|
|
|
//! Secondary index (>= 0, e.g. bond index) or special value (::cPickable_t)
|
|
int bond;
|
|
|
|
// comparison
|
|
bool operator==(const Pickable& rhs) const
|
|
{
|
|
return index == rhs.index && bond == rhs.bond;
|
|
}
|
|
};
|
|
|
|
namespace pymol
|
|
{
|
|
struct CObject;
|
|
}
|
|
|
|
/**
|
|
* Generic object state identifier
|
|
*/
|
|
struct PickContext {
|
|
pymol::CObject* object = nullptr;
|
|
int state;
|
|
|
|
// comparison
|
|
bool operator==(const PickContext& rhs) const
|
|
{
|
|
return object == rhs.object && state == rhs.state;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Global identifier for a pickable item like an atom, half-bond or label
|
|
*/
|
|
struct Picking {
|
|
Pickable src;
|
|
PickContext context;
|
|
|
|
// comparison
|
|
bool operator==(const Picking& rhs) const
|
|
{
|
|
return src == rhs.src && context == rhs.context;
|
|
}
|
|
bool operator!=(const Picking& rhs) const { return !(*this == rhs); }
|
|
};
|
|
|
|
/**
|
|
* For picking everything within a rectangle (Shift+click drag selection)
|
|
*/
|
|
struct Multipick {
|
|
int x, y, w, h;
|
|
std::vector<Picking> picked;
|
|
};
|
|
|
|
/**
|
|
* Color picking index conversions
|
|
*/
|
|
class PickColorConverter
|
|
{
|
|
typedef unsigned char channel_t;
|
|
typedef unsigned int index_t;
|
|
|
|
// 12 bit default
|
|
unsigned char m_rgba_bits[4]{4, 4, 4, 0};
|
|
|
|
// Number of used bits plus check bits
|
|
unsigned char m_rgba_and_check_bits[4]{4, 4, 4, 0};
|
|
|
|
public:
|
|
// (only public for testing)
|
|
bool validateCheckBits(const channel_t* rgba) const;
|
|
|
|
//! Set the number of bits for each channel
|
|
void setRgbaBits(const int* rgba_bits, int max_check_bits = 0);
|
|
|
|
//! Get the sum of bits from all channels
|
|
unsigned getTotalBits() const
|
|
{
|
|
return m_rgba_bits[0] + m_rgba_bits[1] + m_rgba_bits[2] + m_rgba_bits[3];
|
|
}
|
|
|
|
//! Picking index from picked color
|
|
index_t indexFromColor(const channel_t* rgba) const;
|
|
|
|
//! RGBA color from picking index
|
|
void colorFromIndex(channel_t* rgba, index_t idx) const;
|
|
|
|
//! RGBA color for "no-pick" (blocks pick, but doesn't pick anything)
|
|
void colorNoPick(channel_t* rgba) const;
|
|
|
|
//! RGBA color for "pick-through" (doesn't block, can pick items below)
|
|
void colorPickThrough(channel_t* rgba) const;
|
|
};
|
|
|
|
class PickColorManager : public PickColorConverter
|
|
{
|
|
unsigned m_count = 0;
|
|
std::vector<Picking> m_identifiers;
|
|
|
|
public:
|
|
//! picking pass
|
|
unsigned m_pass = 0;
|
|
|
|
//! whether `m_identifiers` and `m_count` are complete
|
|
bool m_valid = false;
|
|
|
|
//! Running index while `!m_valid`, equal to `m_identifiers.size()` at the end
|
|
//! of a picking pass or if `m_valid`.
|
|
unsigned count() const { return m_count; }
|
|
|
|
//! Reset the count, but don't invalidate identifiers (they will be identical
|
|
//! in the next pass)
|
|
void resetCount()
|
|
{
|
|
m_count = 0;
|
|
m_valid = false;
|
|
}
|
|
|
|
//! Invalidate identifiers
|
|
void invalidate()
|
|
{
|
|
if (!m_valid) {
|
|
// protect against calls during rendering
|
|
return;
|
|
}
|
|
m_identifiers.clear();
|
|
m_valid = false;
|
|
}
|
|
|
|
bool pickColorsValid() const { return m_valid; }
|
|
|
|
void colorNext(unsigned char* color, const PickContext* context,
|
|
unsigned int index, int bond);
|
|
|
|
const Picking* getIdentifier(unsigned index) const;
|
|
};
|