mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
520 lines
15 KiB
C++
520 lines
15 KiB
C++
#include "ObjectCurve.h"
|
|
#include "CGO.h"
|
|
#include "Color.h"
|
|
#include "PConv.h"
|
|
#include "Picking.h"
|
|
#include "ShaderMgr.h"
|
|
#include "TTT.h"
|
|
|
|
ObjectCurveState::ObjectCurveState(PyMOLGlobals* G)
|
|
: CObjectState(G)
|
|
{
|
|
}
|
|
|
|
void ObjectCurveState::addDefaultBezierSpline()
|
|
{
|
|
if (splines.empty()) {
|
|
splines.emplace_back();
|
|
auto& firstSpline = splines.back();
|
|
firstSpline.addBezierPoint();
|
|
}
|
|
}
|
|
|
|
glm::vec3 ObjectCurveState::getPosition(float t) const
|
|
{
|
|
const auto& spline = splines.front();
|
|
return spline.getBezierPoint(t);
|
|
}
|
|
|
|
glm::vec3 ObjectCurveState::getNormalizedDirection(float t) const
|
|
{
|
|
const auto& spline = splines.front();
|
|
return spline.getFirstDerivative(t);
|
|
}
|
|
|
|
/**
|
|
* @param cgo bezier cgo
|
|
* @param curveState ObjectCurveState to validate
|
|
* @param handleDiameter diameter of drawn validator spheres
|
|
*/
|
|
|
|
static void ValidateCGO(
|
|
CGO& cgo, const ObjectCurveState& curveState, float handleDiameter)
|
|
{
|
|
glm::vec3 redGLM = glm::vec3(1, 1, 1);
|
|
auto red = glm::value_ptr(redGLM);
|
|
int quality = 32;
|
|
for (int i = 0; i < quality; i++) {
|
|
float t = static_cast<float>(i) / static_cast<float>(quality);
|
|
const auto pos = curveState.getPosition(t);
|
|
cgo.add<cgo::draw::color>(red);
|
|
cgo.add<cgo::draw::sphere>(glm::value_ptr(pos), handleDiameter);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws Handle for each control point
|
|
* @param cgo bezier cgo
|
|
* @param splineIndex index of spline to draw
|
|
* @param pickIndex PickIndex
|
|
* @param handleDiameter diameter of drawn handle spheres
|
|
* @param ctrlPtPtr pointer to control point position
|
|
* @param handlePointPtr pointer to handle point position
|
|
*/
|
|
|
|
static void DrawHandle(CGO& cgo, int splineIndex, int pickIndex,
|
|
float handleDiameter, const float* ctrlPtPtr, const float* handlePtPtr)
|
|
{
|
|
float handleLineRadius = 0.25f / 6.0f;
|
|
glm::vec3 redGLM = glm::vec3(1, 0, 0);
|
|
auto red = glm::value_ptr(redGLM);
|
|
glm::vec3 whiteGLM = glm::vec3(1, 1, 1);
|
|
auto white = glm::value_ptr(whiteGLM);
|
|
CGOPickColor(&cgo, pickIndex, splineIndex);
|
|
cgo.add<cgo::draw::color>(red);
|
|
cgo.add<cgo::draw::sphere>(handlePtPtr, handleDiameter);
|
|
|
|
cgo.add<cgo::draw::color>(white);
|
|
cgo.add<cgo::draw::cylinder>(
|
|
ctrlPtPtr, handlePtPtr, handleLineRadius, white, white);
|
|
}
|
|
|
|
void ObjectCurveState::updateRawCGO()
|
|
{
|
|
rawCGO = nullptr;
|
|
if (splines.empty()) {
|
|
return;
|
|
}
|
|
auto& spline = splines.front();
|
|
int splineIndex = 0; // TODO Iterate through splines
|
|
const auto& bezierPoints = spline.getBezierPoints();
|
|
rawCGO.reset(new CGO(G));
|
|
float handleDiameter = 0.25f;
|
|
for (int i = 1; i < bezierPoints.size(); ++i) {
|
|
const auto& bezierA = bezierPoints[i - 1];
|
|
const auto& bezierB = bezierPoints[i];
|
|
rawCGO->add<cgo::draw::bezier>(glm::value_ptr(bezierA.control),
|
|
glm::value_ptr(bezierA.rightHandle), glm::value_ptr(bezierB.leftHandle),
|
|
glm::value_ptr(bezierB.control));
|
|
}
|
|
const glm::vec3 greenGLM(0, 1, 0.2);
|
|
auto green = glm::value_ptr(greenGLM);
|
|
|
|
for (int i = 0; i < bezierPoints.size(); ++i) {
|
|
const auto& bezierPt = bezierPoints[i];
|
|
auto pickIdx = i * 3;
|
|
auto ctrlPt = glm::value_ptr(bezierPt.control);
|
|
|
|
// draw control point
|
|
CGOPickColor(rawCGO.get(), pickIdx, splineIndex);
|
|
rawCGO->add<cgo::draw::color>(green);
|
|
rawCGO->add<cgo::draw::sphere>(ctrlPt, handleDiameter);
|
|
|
|
// draw Left handle -- Draw for all except first
|
|
if (i != 0) {
|
|
DrawHandle(*rawCGO, splineIndex, pickIdx + 1, handleDiameter, ctrlPt,
|
|
glm::value_ptr(bezierPt.leftHandle));
|
|
}
|
|
|
|
// draw Right handle -- Draw for all except last
|
|
if (i != bezierPoints.size() - 1) {
|
|
DrawHandle(*rawCGO, splineIndex, pickIdx + 2, handleDiameter, ctrlPt,
|
|
glm::value_ptr(bezierPt.rightHandle));
|
|
}
|
|
}
|
|
|
|
#if PYMOL_CURVE_VALIDATE
|
|
ValidateCGO(*rawCGO, *this, handleDiameter);
|
|
#endif
|
|
}
|
|
|
|
static CGO* FilterCGO(PyMOLGlobals* G, const CGO* rawCGO)
|
|
{
|
|
auto optCGO = std::make_unique<CGO>(G);
|
|
CGO* allCylinders = nullptr;
|
|
CGO* allBeziers = nullptr;
|
|
CGO* allSpheres = nullptr;
|
|
CGO* convertcgo = nullptr;
|
|
if (CGOHasBezierOperations(rawCGO)) {
|
|
CGO* allButBezier = new CGO(G);
|
|
allBeziers = CGOOptimizeBezier(rawCGO);
|
|
CGOFilterOutBezierOperationsInto(rawCGO, allButBezier);
|
|
CGOStop(allButBezier);
|
|
CGOFree(convertcgo);
|
|
convertcgo = allButBezier;
|
|
}
|
|
if (CGOHasCylinderOperations(rawCGO)) {
|
|
allCylinders = CGONew(G);
|
|
CGOEnable(allCylinders, GL_CYLINDER_SHADER);
|
|
CGO* newCGO =
|
|
CGOConvertShaderCylindersToCylinderShader(rawCGO, allCylinders);
|
|
allCylinders->free_append(newCGO);
|
|
assert(newCGO == nullptr);
|
|
CGODisable(allCylinders, GL_CYLINDER_SHADER);
|
|
CGOStop(allCylinders);
|
|
CGO* allButCylinders = CGONew(G);
|
|
CGOFilterOutCylinderOperationsInto(rawCGO, allButCylinders);
|
|
CGOStop(allButCylinders);
|
|
CGOFree(convertcgo);
|
|
convertcgo = allButCylinders;
|
|
}
|
|
if (CGOHasSphereOperations(rawCGO)) {
|
|
CGO* allButSpheres = CGONew(G);
|
|
allSpheres =
|
|
CGOOptimizeSpheresToVBONonIndexed(rawCGO, 0, true, allButSpheres);
|
|
if (allSpheres) {
|
|
CGOFree(convertcgo);
|
|
CGOStop(allButSpheres);
|
|
convertcgo = allButSpheres;
|
|
} else {
|
|
CGOFree(allButSpheres);
|
|
}
|
|
}
|
|
optCGO.reset(CGOSimplify(convertcgo));
|
|
optCGO.reset(CGOOptimizeToVBONotIndexed(optCGO.get()));
|
|
|
|
if (allBeziers) {
|
|
optCGO->free_append(allBeziers);
|
|
}
|
|
if (allSpheres) {
|
|
optCGO->free_append(allSpheres);
|
|
}
|
|
if (allCylinders) {
|
|
optCGO->free_append(allCylinders);
|
|
}
|
|
return optCGO.release();
|
|
}
|
|
|
|
void ObjectCurveState::updateRenderCGO()
|
|
{
|
|
if (renderCGO) { // We've already generated the renderCGO
|
|
return;
|
|
}
|
|
if (!rawCGO) { // Raw CGO needs to be updated
|
|
updateRawCGO();
|
|
if (!rawCGO) {
|
|
return;
|
|
}
|
|
}
|
|
renderCGO.reset(FilterCGO(G, rawCGO.get()));
|
|
}
|
|
|
|
ObjectCurve::ObjectCurve(PyMOLGlobals* G)
|
|
: pymol::CObject(G)
|
|
{
|
|
type = cObjectCurve;
|
|
|
|
m_states.emplace_back(G);
|
|
auto& defaultState = m_states.back();
|
|
defaultState.addDefaultBezierSpline();
|
|
}
|
|
|
|
void ObjectCurve::render(RenderInfo* info)
|
|
{
|
|
ObjectPrepareContext(this, info);
|
|
if (!(this->visRep & cRepCGOBit)) {
|
|
return;
|
|
}
|
|
auto pass = info->pass;
|
|
auto color = ColorGet(G, this->Color);
|
|
|
|
if (info->ray) {
|
|
return;
|
|
}
|
|
|
|
if (!G->HaveGUI || !G->ValidContext) {
|
|
return;
|
|
}
|
|
|
|
for (auto state : StateIteratorV2(this, info->state)) {
|
|
if (state >= m_states.size()) {
|
|
continue;
|
|
}
|
|
auto& stateObj = m_states[state];
|
|
if (info->pick) {
|
|
PickContext context{};
|
|
context.object = this;
|
|
auto cgo = stateObj.renderCGO.get();
|
|
CGORenderPicking(cgo, info, &context, Setting.get(), nullptr);
|
|
continue;
|
|
}
|
|
if (pass != RenderPass::Antialias) {
|
|
stateObj.updateRenderCGO();
|
|
auto cgo = stateObj.renderCGO.get();
|
|
if (cgo) {
|
|
CGORender(cgo, color, this->Setting.get(), nullptr, info, nullptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ObjectCurve::update()
|
|
{
|
|
for (auto& state : m_states) {
|
|
state.renderCGO = nullptr;
|
|
}
|
|
}
|
|
|
|
pymol::CObject* ObjectCurve::clone() const
|
|
{
|
|
return new ObjectCurve(*this);
|
|
}
|
|
|
|
pymol::Result<pymol::BezierSplinePoint> ObjectCurve::getBezierPointByPick(
|
|
const Picking& pick)
|
|
{
|
|
assert(pick.context.state >= 0 && pick.context.state < m_states.size());
|
|
const auto& state = m_states[pick.context.state];
|
|
assert(pick.src.bond < state.splines.size());
|
|
const auto& spline = state.splines[pick.src.bond];
|
|
assert(pick.src.index < (spline.getBezierPoints().size() * 3));
|
|
int bezPtIdx = pick.src.index / 3;
|
|
return spline.getBezierPoints()[bezPtIdx];
|
|
}
|
|
|
|
pymol::Result<glm::vec3> ObjectCurve::getPositionByPick(const Picking& pick)
|
|
{
|
|
auto bezPt = getBezierPointByPick(pick);
|
|
if (!bezPt) {
|
|
return bezPt.error();
|
|
}
|
|
int handleIdx = pick.src.index % 3;
|
|
glm::vec3 pt;
|
|
switch (handleIdx) {
|
|
case 0:
|
|
pt = bezPt->control;
|
|
break;
|
|
case 1:
|
|
pt = bezPt->leftHandle;
|
|
break;
|
|
case 2:
|
|
pt = bezPt->rightHandle;
|
|
break;
|
|
default:
|
|
return pymol::make_error("Invalid handle index");
|
|
break;
|
|
}
|
|
if (TTTFlag) {
|
|
pt = pymol::TTT::from_pymol_2_legacy(TTT).transform(pt);
|
|
}
|
|
return pt;
|
|
}
|
|
|
|
pymol::Result<> ObjectCurve::setPositionByPick(
|
|
const Picking& pick, const glm::vec3& newPos)
|
|
{
|
|
assert(pick.context.state >= 0 && pick.context.state < m_states.size());
|
|
auto& state = m_states[pick.context.state];
|
|
assert(pick.src.bond < state.splines.size());
|
|
auto& spline = state.splines[pick.src.bond];
|
|
assert(pick.src.index < (spline.getBezierPoints().size() * 3));
|
|
|
|
int bezPtIdx = pick.src.index / 3;
|
|
int handleIdx = pick.src.index % 3;
|
|
auto& bezPt = spline.getBezierPoints()[bezPtIdx];
|
|
switch (handleIdx) {
|
|
case 0: {
|
|
auto delta = newPos - bezPt.control;
|
|
bezPt.control += delta;
|
|
bezPt.leftHandle += delta;
|
|
bezPt.rightHandle += delta;
|
|
} break;
|
|
case 1: {
|
|
bezPt.leftHandle = newPos;
|
|
auto handleVec = bezPt.leftHandle - bezPt.control;
|
|
if (bezPt.mode == pymol::BezierControlPointMode::ALIGNED) {
|
|
bezPt.rightHandle = bezPt.control - handleVec;
|
|
}
|
|
} break;
|
|
case 2: {
|
|
bezPt.rightHandle = newPos;
|
|
auto handleVec = bezPt.rightHandle - bezPt.control;
|
|
if (bezPt.mode == pymol::BezierControlPointMode::ALIGNED) {
|
|
bezPt.leftHandle = bezPt.control - handleVec;
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
state.rawCGO = nullptr;
|
|
state.renderCGO = nullptr;
|
|
return {};
|
|
}
|
|
|
|
pymol::Result<pymol::BezierSpline*> ObjectCurve::getBezierSplineByPick(
|
|
const Picking& pick)
|
|
{
|
|
assert(pick.context.state >= 0 && pick.context.state < m_states.size());
|
|
auto& state = m_states[pick.context.state];
|
|
assert(pick.src.bond < state.splines.size());
|
|
auto& spline = state.splines[pick.src.bond];
|
|
return &spline;
|
|
}
|
|
|
|
glm::vec3 ObjectCurve::getPosition(float t) const
|
|
{
|
|
int currentState = 0;
|
|
const auto& state = m_states[currentState];
|
|
auto rawPos = state.getPosition(t);
|
|
if (!TTTFlag) {
|
|
return rawPos;
|
|
}
|
|
auto ttt = pymol::TTT::from_pymol_2_legacy(TTT);
|
|
return ttt.transform(rawPos);
|
|
}
|
|
|
|
glm::vec3 ObjectCurve::getNormalizedDirection(float t) const
|
|
{
|
|
int currentState = 0;
|
|
const auto& state = m_states[currentState];
|
|
return state.getNormalizedDirection(t);
|
|
}
|
|
|
|
void ObjectCurve::invalidate(cRep_t rep, cRepInv_t level, int state)
|
|
{
|
|
for (auto& state : m_states) {
|
|
state.rawCGO = nullptr;
|
|
state.renderCGO = nullptr;
|
|
}
|
|
}
|
|
|
|
////////////////////////////
|
|
/////////SERIALIZE//////////
|
|
////////////////////////////
|
|
|
|
static PyObject* BezierPointAsPyList(const pymol::BezierSplinePoint& pt)
|
|
{
|
|
auto numFloatsPerPoint = 10; // 3 vec3s + 1 enum
|
|
auto bezierList = PyList_New(numFloatsPerPoint);
|
|
PyList_SetItem(bezierList, 0, PyFloat_FromDouble(pt.control[0]));
|
|
PyList_SetItem(bezierList, 1, PyFloat_FromDouble(pt.control[1]));
|
|
PyList_SetItem(bezierList, 2, PyFloat_FromDouble(pt.control[2]));
|
|
PyList_SetItem(bezierList, 3, PyFloat_FromDouble(pt.leftHandle[0]));
|
|
PyList_SetItem(bezierList, 4, PyFloat_FromDouble(pt.leftHandle[1]));
|
|
PyList_SetItem(bezierList, 5, PyFloat_FromDouble(pt.leftHandle[2]));
|
|
PyList_SetItem(bezierList, 6, PyFloat_FromDouble(pt.rightHandle[0]));
|
|
PyList_SetItem(bezierList, 7, PyFloat_FromDouble(pt.rightHandle[1]));
|
|
PyList_SetItem(bezierList, 8, PyFloat_FromDouble(pt.rightHandle[2]));
|
|
PyList_SetItem(bezierList, 9, PyInt_FromLong(static_cast<int>(pt.mode)));
|
|
return PConvAutoNone(bezierList);
|
|
}
|
|
|
|
static PyObject* BezierSplineAsPyList(const pymol::BezierSpline& spline)
|
|
{
|
|
const auto& bezPts = spline.getBezierPoints();
|
|
auto result = PyList_New(bezPts.size());
|
|
for (int i = 0; i < bezPts.size(); i++) {
|
|
PyList_SetItem(result, i, BezierPointAsPyList(bezPts[i]));
|
|
}
|
|
return PConvAutoNone(result);
|
|
}
|
|
|
|
PyObject* ObjectCurveState::asPyList() const
|
|
{
|
|
auto result = PyList_New(splines.size());
|
|
for (int i = 0; i < splines.size(); i++) {
|
|
PyList_SetItem(result, i, BezierSplineAsPyList(splines[i]));
|
|
}
|
|
|
|
return PConvAutoNone(result);
|
|
}
|
|
|
|
PyObject* ObjectCurve::statesAsPyList() const
|
|
{
|
|
auto result = PyList_New(m_states.size());
|
|
for (int a = 0; a < m_states.size(); a++) {
|
|
PyList_SetItem(result, a, m_states[a].asPyList());
|
|
}
|
|
return PConvAutoNone(result);
|
|
}
|
|
|
|
PyObject* ObjectCurve::asPyList() const
|
|
{
|
|
auto result = PyList_New(2);
|
|
PyList_SetItem(result, 0, ObjectAsPyList(this));
|
|
PyList_SetItem(result, 1, statesAsPyList());
|
|
return PConvAutoNone(result);
|
|
}
|
|
|
|
////////////////////////////
|
|
////////DESERIALIZE/////////
|
|
////////////////////////////
|
|
|
|
pymol::Result<pymol::BezierSplinePoint> BezierSplineFromPyList(
|
|
PyObject* serializedList)
|
|
{
|
|
pymol::BezierSplinePoint pt;
|
|
if (!PyList_Check(serializedList)) {
|
|
return pymol::make_error("BezierSplinePoint: Not a list");
|
|
}
|
|
assert(PyList_Size(serializedList) == 10); // 3 vec3s + 1 enum
|
|
auto to_float = [&](int idx) {
|
|
return static_cast<float>(
|
|
PyFloat_AsDouble(PyList_GetItem(serializedList, idx)));
|
|
};
|
|
auto to_glm_vec3 = [&](int idx) {
|
|
return glm::vec3(to_float(idx), to_float(idx + 1), to_float(idx + 2));
|
|
};
|
|
auto to_handle_mode = [&](int idx) {
|
|
return static_cast<pymol::BezierControlPointMode>(
|
|
PyInt_AsLong(PyList_GetItem(serializedList, idx)));
|
|
};
|
|
pt.control = to_glm_vec3(0);
|
|
pt.leftHandle = to_glm_vec3(3);
|
|
pt.rightHandle = to_glm_vec3(6);
|
|
pt.mode = to_handle_mode(9);
|
|
return pt;
|
|
}
|
|
|
|
ObjectCurveState::ObjectCurveState(PyMOLGlobals* G, PyObject* serializedList)
|
|
: CObjectState(G)
|
|
{
|
|
if (!PyList_Check(serializedList)) {
|
|
printf("ObjectCurveState: Could not deserialize list\n");
|
|
return;
|
|
}
|
|
int newSplinesSize = PyList_Size(serializedList);
|
|
for (int a = 0; a < newSplinesSize; a++) {
|
|
auto splinePyList = CPythonVal_PyList_GetItem(I->G, serializedList, a);
|
|
splines.emplace_back();
|
|
auto& newSpline = splines.back();
|
|
int newPointsListSize = PyList_Size(splinePyList);
|
|
for (int bp = 0; bp < newPointsListSize; bp++) {
|
|
auto newPointList = CPythonVal_PyList_GetItem(I->G, splinePyList, bp);
|
|
if (auto splineListResult = BezierSplineFromPyList(newPointList)) {
|
|
newSpline.addBezierPoint(*splineListResult);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pymol::Result<> ObjectCurve::statesFromPyList(PyObject* serializedList)
|
|
{
|
|
if (!PyList_Check(serializedList)) {
|
|
return pymol::make_error("Curve States: Invalid PyList");
|
|
}
|
|
int newStatesSize = PyList_Size(serializedList);
|
|
for (int a = 0; a < newStatesSize; a++) {
|
|
auto statePyList = CPythonVal_PyList_GetItem(I->G, serializedList, a);
|
|
m_states.emplace_back(G, statePyList);
|
|
CPythonVal_Free(statePyList);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
ObjectCurve::ObjectCurve(PyMOLGlobals* G, PyObject* serializedList)
|
|
: pymol::CObject(G)
|
|
{
|
|
bool ok = true;
|
|
if (ok) {
|
|
auto val = CPythonVal_PyList_GetItem(G, serializedList, 0);
|
|
ok = ObjectFromPyList(G, val, this);
|
|
CPythonVal_Free(val);
|
|
}
|
|
if (ok) {
|
|
auto val = CPythonVal_PyList_GetItem(G, serializedList, 1);
|
|
statesFromPyList(val);
|
|
CPythonVal_Free(val);
|
|
}
|
|
}
|