mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
802 lines
22 KiB
C++
802 lines
22 KiB
C++
|
|
/*
|
|
A* -------------------------------------------------------------------
|
|
B* This file contains source code for the PyMOL computer program
|
|
C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
|
|
D* -------------------------------------------------------------------
|
|
E* It is unlawful to modify or remove this copyright notice.
|
|
F* -------------------------------------------------------------------
|
|
G* Please see the accompanying LICENSE file for further information.
|
|
H* -------------------------------------------------------------------
|
|
I* Additional authors of this source file include:
|
|
-*
|
|
-*
|
|
-*
|
|
Z* -------------------------------------------------------------------
|
|
*/
|
|
#include "os_python.h"
|
|
|
|
#include "os_gl.h"
|
|
#include "os_predef.h"
|
|
#include "os_std.h"
|
|
|
|
#include "Base.h"
|
|
#include "CGO.h"
|
|
#include "Color.h"
|
|
#include "Err.h"
|
|
#include "Feedback.h"
|
|
#include "MemoryDebug.h"
|
|
#include "ObjectCGO.h"
|
|
#include "PConv.h"
|
|
#include "Scene.h"
|
|
#include "Setting.h"
|
|
#include "ShaderMgr.h"
|
|
#include "VFont.h"
|
|
#include "main.h"
|
|
|
|
static PyObject* ObjectCGOStateAsPyList(ObjectCGOState* I)
|
|
{
|
|
PyObject* result = nullptr;
|
|
|
|
result = PyList_New(1);
|
|
if (I->origCGO)
|
|
PyList_SetItem(result, 0, CGOAsPyList(I->origCGO.get()));
|
|
else
|
|
PyList_SetItem(result, 0, PConvAutoNone(nullptr));
|
|
return (PConvAutoNone(result));
|
|
}
|
|
|
|
static PyObject* ObjectCGOAllStatesAsPyList(ObjectCGO* I)
|
|
{
|
|
PyObject* result = nullptr;
|
|
int a;
|
|
result = PyList_New(I->State.size());
|
|
for (a = 0; a < I->State.size(); a++) {
|
|
PyList_SetItem(result, a, ObjectCGOStateAsPyList(&I->State[a]));
|
|
}
|
|
return (PConvAutoNone(result));
|
|
}
|
|
|
|
static int ObjectCGOStateFromPyList(
|
|
PyMOLGlobals* G, ObjectCGOState* I, PyObject* list, int version)
|
|
{
|
|
int ok = true;
|
|
int ll, pl = 0;
|
|
PyObject* tmp;
|
|
if (ok)
|
|
ok = (list != nullptr);
|
|
if (ok)
|
|
ok = PyList_Check(list);
|
|
if (ok)
|
|
ll = PyList_Size(list);
|
|
I->origCGO = nullptr;
|
|
/* TO SUPPORT BACKWARDS COMPATIBILITY...
|
|
Always check ll when adding new PyList_GetItem's */
|
|
if (ok && ll == 2) {
|
|
tmp = PyList_GetItem(list, 0);
|
|
if (CPythonVal_IsNone(tmp))
|
|
I->origCGO = nullptr;
|
|
else {
|
|
I->origCGO.reset(CGONewFromPyList(G, tmp, version, 1));
|
|
ok = I->origCGO != nullptr;
|
|
}
|
|
pl++;
|
|
}
|
|
if (ok && !I->origCGO) {
|
|
tmp = PyList_GetItem(list, pl);
|
|
if (CPythonVal_IsNone(tmp))
|
|
I->origCGO = nullptr;
|
|
else {
|
|
I->origCGO.reset(CGONewFromPyList(G, tmp, version, 0));
|
|
ok = I->origCGO != nullptr;
|
|
}
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
static int ObjectCGOAllStatesFromPyList(
|
|
ObjectCGO* I, PyObject* list, int version)
|
|
{
|
|
int ok = true;
|
|
int a;
|
|
if (ok)
|
|
ok = PyList_Check(list);
|
|
if (ok) {
|
|
I->State.resize(PyList_Size(list), ObjectCGOState(I->G));
|
|
for (a = 0; a < I->State.size(); a++) {
|
|
CPythonVal* val = CPythonVal_PyList_GetItem(I->G, list, a);
|
|
ok = ObjectCGOStateFromPyList(I->G, &I->State[a], val, version);
|
|
CPythonVal_Free(val);
|
|
if (!ok)
|
|
break;
|
|
}
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
int ObjectCGONewFromPyList(
|
|
PyMOLGlobals* G, PyObject* list, ObjectCGO** result, int version)
|
|
{
|
|
int ok = true;
|
|
ObjectCGO* I = nullptr;
|
|
(*result) = nullptr;
|
|
if (ok)
|
|
ok = (list != nullptr);
|
|
if (ok)
|
|
ok = PyList_Check(list);
|
|
|
|
I = new ObjectCGO(G);
|
|
if (ok)
|
|
ok = (I != nullptr);
|
|
|
|
if (ok) {
|
|
CPythonVal* val = CPythonVal_PyList_GetItem(G, list, 0);
|
|
ok = ObjectFromPyList(G, val, I);
|
|
CPythonVal_Free(val);
|
|
}
|
|
if (ok) {
|
|
CPythonVal* val = CPythonVal_PyList_GetItem(G, list, 2);
|
|
ok = ObjectCGOAllStatesFromPyList(I, val, version);
|
|
CPythonVal_Free(val);
|
|
}
|
|
if (ok) {
|
|
(*result) = I;
|
|
ObjectCGORecomputeExtent(I);
|
|
} else {
|
|
/* cleanup? */
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
PyObject* ObjectCGOAsPyList(ObjectCGO* I)
|
|
{
|
|
PyObject* result = nullptr;
|
|
|
|
result = PyList_New(3);
|
|
PyList_SetItem(result, 0, ObjectAsPyList(I));
|
|
PyList_SetItem(result, 1, PyInt_FromLong(I->State.size()));
|
|
PyList_SetItem(result, 2, ObjectCGOAllStatesAsPyList(I));
|
|
|
|
return (PConvAutoNone(result));
|
|
}
|
|
|
|
void ObjectCGORecomputeExtent(ObjectCGO* I)
|
|
{
|
|
float mx[3], mn[3];
|
|
int extent_flag = false;
|
|
int has_normals = 0;
|
|
CGO* cgo;
|
|
for (auto& state : I->State) {
|
|
cgo = state.origCGO.get();
|
|
if (!cgo) {
|
|
cgo = state.renderCGO.get();
|
|
}
|
|
if (cgo) {
|
|
if (CGOGetExtent(cgo, mn, mx)) {
|
|
if (!extent_flag) {
|
|
extent_flag = true;
|
|
copy3f(mx, I->ExtentMax);
|
|
copy3f(mn, I->ExtentMin);
|
|
} else {
|
|
max3f(mx, I->ExtentMax, I->ExtentMax);
|
|
min3f(mn, I->ExtentMin, I->ExtentMin);
|
|
}
|
|
}
|
|
if (!has_normals && cgo && CGOHasNormals(cgo)) {
|
|
has_normals = 1;
|
|
}
|
|
}
|
|
}
|
|
I->ExtentFlag = extent_flag;
|
|
SettingCheckHandle(I->G, I->Setting);
|
|
SettingSet_i(I->Setting.get(), cSetting_cgo_lighting, has_normals);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ObjectCGO::invalidate(cRep_t rep, cRepInv_t level, int state)
|
|
{
|
|
auto I = this;
|
|
ObjectCGOState* sobj = nullptr;
|
|
|
|
if (state < 0) {
|
|
for (auto& state : I->State) {
|
|
sobj = &state;
|
|
sobj->renderCGO = nullptr;
|
|
}
|
|
} else {
|
|
if ((state >= 0) && (state < I->State.size())) {
|
|
// I->State[state].valid = false;
|
|
sobj = &I->State[state];
|
|
sobj->renderCGO = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
void ObjectCGO::update()
|
|
{
|
|
for (auto& state : State) {
|
|
state.renderCGO = nullptr;
|
|
}
|
|
SceneInvalidate(G); /* needed ? */
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
int ObjectCGO::getNFrame() const
|
|
{
|
|
return State.size();
|
|
}
|
|
|
|
static void ObjectCGORenderState(PyMOLGlobals* G, RenderPass pass, CRay* ray,
|
|
ObjectCGO* I, RenderInfo* info, ObjectCGOState* sobj, const float* color,
|
|
ObjectGadgetRamp* ramp, int use_shader, bool cgo_lighting)
|
|
{
|
|
if (ray) {
|
|
if (sobj) {
|
|
if (sobj->origCGO) {
|
|
CGO *cgo = sobj->origCGO.get(), *cgo_copy = nullptr;
|
|
if (cgo_lighting && CGOHasAnyTriangleVerticesWithoutNormals(cgo)) {
|
|
cgo = cgo_copy = CGOGenerateNormalsForTriangles(cgo);
|
|
}
|
|
CGORenderRay(cgo, ray, info, color, ramp, I->Setting.get(), nullptr);
|
|
CGOFree(cgo_copy);
|
|
}
|
|
}
|
|
} else if (G->HaveGUI && G->ValidContext) {
|
|
if (info->pick) {
|
|
PickContext context;
|
|
context.object = I;
|
|
context.state = sobj - I->State.data();
|
|
CGORenderPicking(
|
|
sobj->renderCGO.get(), info, &context, I->Setting.get(), nullptr);
|
|
} else if (pass != RenderPass::Antialias) {
|
|
bool pass_is_opaque = pass == RenderPass::Opaque;
|
|
if (sobj && ((sobj->hasTransparency ^ pass_is_opaque) ||
|
|
(sobj->hasOpaque == pass_is_opaque))) {
|
|
{
|
|
CShaderPrg* shaderPrg;
|
|
int two_sided_lighting = SettingGet_i(
|
|
G, I->Setting.get(), nullptr, cSetting_two_sided_lighting);
|
|
bool backface_cull = SettingGet_i(
|
|
G, I->Setting.get(), nullptr, cSetting_backface_cull);
|
|
if (two_sided_lighting < 0) {
|
|
two_sided_lighting = !cgo_lighting;
|
|
}
|
|
two_sided_lighting &= cgo_lighting; // only set two_sided_lighting if
|
|
// cgo_lighting is set
|
|
#ifndef PURE_OPENGL_ES_2
|
|
if (cgo_lighting) {
|
|
glEnable(GL_LIGHTING);
|
|
} else {
|
|
glDisable(GL_LIGHTING);
|
|
}
|
|
if (two_sided_lighting) {
|
|
if (use_shader)
|
|
glEnable(GL_VERTEX_PROGRAM_TWO_SIDE);
|
|
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
|
|
} else {
|
|
if (use_shader)
|
|
glDisable(GL_VERTEX_PROGRAM_TWO_SIDE);
|
|
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
|
|
}
|
|
#endif
|
|
if (backface_cull) {
|
|
glCullFace(GL_BACK);
|
|
glEnable(GL_CULL_FACE);
|
|
}
|
|
|
|
if (use_shader) {
|
|
shaderPrg = G->ShaderMgr->Enable_DefaultShader(info->pass);
|
|
if (!shaderPrg)
|
|
return;
|
|
shaderPrg->SetLightingEnabled(cgo_lighting);
|
|
shaderPrg->Set1i("two_sided_lighting_enabled", two_sided_lighting);
|
|
sobj->renderCGO->use_shader = use_shader;
|
|
sobj->renderCGO->debug = SettingGetGlobal_i(G, cSetting_cgo_debug);
|
|
CGORender(sobj->renderCGO.get(), color, I->Setting.get(), nullptr,
|
|
info, nullptr);
|
|
shaderPrg->Disable();
|
|
} else {
|
|
sobj->renderCGO->use_shader = use_shader;
|
|
sobj->renderCGO->debug = SettingGetGlobal_i(G, cSetting_cgo_debug);
|
|
CGORender(sobj->renderCGO.get(), color, I->Setting.get(), nullptr,
|
|
info, nullptr);
|
|
}
|
|
if (backface_cull) {
|
|
glDisable(GL_CULL_FACE);
|
|
}
|
|
#ifndef PURE_OPENGL_ES_2
|
|
if (two_sided_lighting) {
|
|
if (use_shader)
|
|
glDisable(GL_VERTEX_PROGRAM_TWO_SIDE);
|
|
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
|
|
}
|
|
if (!cgo_lighting) {
|
|
glEnable(GL_LIGHTING);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ObjectCGOGenerateCGO(PyMOLGlobals* G, ObjectCGO* I,
|
|
ObjectCGOState* sobj, bool use_shader, bool cgo_lighting,
|
|
const float* color, ObjectGadgetRamp* ramp, int state)
|
|
{
|
|
if (sobj->renderCGO && ((use_shader ^ sobj->renderWithShaders) ||
|
|
(cgo_lighting ^ sobj->cgo_lighting))) {
|
|
// if renderWithShaders doesn't match use_shader, clear CGO and re-generate
|
|
sobj->renderCGO = nullptr;
|
|
}
|
|
if (!sobj->renderCGO) {
|
|
float colorWithA[4];
|
|
short someLinesWithoutNormals = 0;
|
|
if (color) {
|
|
colorWithA[0] = color[0];
|
|
colorWithA[1] = color[1];
|
|
colorWithA[2] = color[2];
|
|
} else {
|
|
colorWithA[0] = 1.f;
|
|
colorWithA[1] = 1.f;
|
|
colorWithA[2] = 1.f;
|
|
}
|
|
colorWithA[3] = 1.f - SettingGet_f(G, I->Setting.get(), nullptr,
|
|
cSetting_cgo_transparency);
|
|
|
|
bool hasTransparency =
|
|
(colorWithA[3] < 1.f || CGOHasTransparency(sobj->origCGO.get()));
|
|
bool hasOpaque =
|
|
(colorWithA[3] == 1.f || CGOHasOpaque(sobj->origCGO.get()));
|
|
|
|
CGO* allCylinders = nullptr;
|
|
CGO* allSpheres = nullptr;
|
|
CGO* allBeziers = nullptr;
|
|
pymol::cache_ptr<CGO> preOpt;
|
|
|
|
{
|
|
std::unique_ptr<CGO> inputWithLighting;
|
|
const CGO* inputCGO = sobj->origCGO.get();
|
|
|
|
if (cgo_lighting) {
|
|
if (CGOHasAnyTriangleVerticesWithoutNormals(inputCGO)) {
|
|
// we only need normals if cgo_lighting is on
|
|
inputWithLighting.reset(CGOGenerateNormalsForTriangles(inputCGO));
|
|
inputCGO = inputWithLighting.get();
|
|
}
|
|
someLinesWithoutNormals = CGOHasAnyLineVerticesWithoutNormals(inputCGO);
|
|
if (!use_shader && someLinesWithoutNormals) {
|
|
// if some lines without normals, turn lighting off on lines
|
|
inputWithLighting.reset(
|
|
CGOTurnLightingOnLinesOff(inputCGO, use_shader));
|
|
inputCGO = inputWithLighting.get();
|
|
}
|
|
}
|
|
|
|
CGO* convertcgo = CGONew(G);
|
|
CGOPickColor(convertcgo, 0, cPickableGadget);
|
|
CGOColorv(convertcgo, colorWithA);
|
|
CGOAlpha(convertcgo, colorWithA[3]);
|
|
CGOAppend(convertcgo, inputCGO);
|
|
inputWithLighting.reset();
|
|
|
|
if (use_shader) {
|
|
bool t_mode_3 = SettingGetGlobal_i(G, cSetting_transparency_mode) == 3;
|
|
if ((t_mode_3 || !hasTransparency) &&
|
|
G->ShaderMgr->Get_DefaultSphereShader(RenderPass::Antialias) &&
|
|
G->ShaderMgr->Get_CylinderShader(RenderPass::Antialias)) {
|
|
if (CGOHasCylinderOperations(convertcgo)) {
|
|
allCylinders = CGONew(G);
|
|
CGOEnable(allCylinders, GL_CYLINDER_SHADER);
|
|
CGO* newCGO = CGOConvertShaderCylindersToCylinderShader(
|
|
convertcgo, allCylinders);
|
|
allCylinders->free_append(newCGO);
|
|
assert(newCGO == nullptr);
|
|
CGODisable(allCylinders, GL_CYLINDER_SHADER);
|
|
CGOStop(allCylinders);
|
|
CGO* allButCylinders = CGONew(G);
|
|
CGOFilterOutCylinderOperationsInto(convertcgo, allButCylinders);
|
|
CGOStop(allButCylinders);
|
|
CGOFree(convertcgo);
|
|
convertcgo = allButCylinders;
|
|
}
|
|
if (CGOHasOperationsOfType(convertcgo, CGO_SPHERE)) {
|
|
CGO* allButSpheres = CGONew(G);
|
|
allSpheres = CGOOptimizeSpheresToVBONonIndexed(
|
|
convertcgo, 0, true, allButSpheres);
|
|
if (allSpheres) {
|
|
CGOFree(convertcgo);
|
|
CGOStop(allButSpheres);
|
|
convertcgo = allButSpheres;
|
|
} else {
|
|
CGOFree(allButSpheres);
|
|
}
|
|
}
|
|
if (CGOHasBezierOperations(convertcgo)) {
|
|
CGO* allButBezier = new CGO(G);
|
|
allBeziers = CGOOptimizeBezier(convertcgo);
|
|
CGOFilterOutBezierOperationsInto(convertcgo, allButBezier);
|
|
CGOStop(allButBezier);
|
|
CGOFree(convertcgo);
|
|
convertcgo = allButBezier;
|
|
}
|
|
preOpt.reset(CGOSimplify(convertcgo, 0));
|
|
} else {
|
|
preOpt.reset(CGOSimplifyNoCompress(convertcgo, 0));
|
|
}
|
|
} else {
|
|
preOpt.reset(CGOSimplifyNoCompress(convertcgo, 0));
|
|
}
|
|
CGOFree(convertcgo);
|
|
}
|
|
|
|
if (ramp) {
|
|
preOpt.reset(
|
|
CGOColorByRamp(G, preOpt.get(), ramp, state, I->Setting.get()));
|
|
}
|
|
|
|
sobj->hasTransparency = hasTransparency;
|
|
sobj->hasOpaque = hasOpaque;
|
|
|
|
if (use_shader) {
|
|
if (hasTransparency) {
|
|
preOpt.reset(CGOOptimizeToVBOIndexedWithColorEmbedTransparentInfo(
|
|
preOpt.get(), 0, colorWithA, false));
|
|
|
|
if (someLinesWithoutNormals) {
|
|
// if some lines without normals, turn lighting off on lines
|
|
CGO* convertcgo = preOpt.release();
|
|
preOpt.reset(CGOTurnLightingOnLinesOff(convertcgo, use_shader));
|
|
CGOFreeWithoutVBOs(convertcgo);
|
|
}
|
|
} else {
|
|
preOpt.reset(CGOOptimizeToVBONotIndexed(preOpt.get(), 0));
|
|
}
|
|
|
|
if (allCylinders) {
|
|
preOpt->free_append(allCylinders);
|
|
}
|
|
|
|
if (allSpheres) {
|
|
preOpt->free_append(allSpheres);
|
|
}
|
|
|
|
if (allBeziers) {
|
|
preOpt->free_append(allBeziers);
|
|
}
|
|
|
|
sobj->renderCGO = std::move(preOpt);
|
|
} else {
|
|
assert(sobj->hasTransparency == CGOHasTransparency(preOpt.get()));
|
|
assert(sobj->hasOpaque == CGOHasOpaque(preOpt.get()));
|
|
|
|
if (sobj->hasTransparency) {
|
|
sobj->renderCGO.reset(CGOConvertTrianglesToAlpha(preOpt.get()));
|
|
sobj->renderCGO->render_alpha = 2;
|
|
} else {
|
|
sobj->renderCGO.reset(CGOSimplify(preOpt.get(), 0));
|
|
}
|
|
}
|
|
|
|
assert(allCylinders == nullptr);
|
|
assert(allSpheres == nullptr);
|
|
assert(allBeziers == nullptr);
|
|
|
|
sobj->renderWithShaders = use_shader;
|
|
sobj->cgo_lighting = cgo_lighting;
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
void ObjectCGO::render(RenderInfo* info)
|
|
{
|
|
auto I = this;
|
|
int state = info->state;
|
|
CRay* ray = info->ray;
|
|
const RenderPass pass = info->pass;
|
|
ObjectCGOState* sobj = nullptr;
|
|
const float* color = nullptr;
|
|
bool use_shader = false, cgo_lighting = false;
|
|
ObjectGadgetRamp* ramp = nullptr;
|
|
|
|
use_shader = SettingGetGlobal_b(G, cSetting_cgo_use_shader) &
|
|
SettingGetGlobal_b(G, cSetting_use_shaders);
|
|
cgo_lighting =
|
|
SettingGet_i(G, I->Setting.get(), nullptr, cSetting_cgo_lighting);
|
|
|
|
ObjectPrepareContext(I, info);
|
|
ramp = ColorGetRamp(G, I->Color);
|
|
color = ColorGet(G, I->Color);
|
|
|
|
if (I->State.empty())
|
|
return;
|
|
|
|
if (pass != RenderPass::Antialias || info->ray || info->pick) {
|
|
if ((I->visRep & cRepCGOBit)) {
|
|
for (StateIterator iter(G, I->Setting.get(), state, I->State.size());
|
|
iter.next();) {
|
|
sobj = &I->State[iter.state];
|
|
if (!sobj->origCGO)
|
|
continue;
|
|
if (!ray)
|
|
ObjectCGOGenerateCGO(
|
|
G, I, sobj, use_shader, cgo_lighting, color, ramp, iter.state);
|
|
ObjectCGORenderState(
|
|
G, pass, ray, I, info, sobj, color, ramp, use_shader, cgo_lighting);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
ObjectCGO::ObjectCGO(PyMOLGlobals* G)
|
|
: pymol::CObject(G)
|
|
{
|
|
type = cObjectCGO;
|
|
}
|
|
|
|
ObjectCGO::ObjectCGO(const ObjectCGO& other)
|
|
: pymol::CObject(other)
|
|
, State(other.State)
|
|
{
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
ObjectCGOState::ObjectCGOState(PyMOLGlobals* G)
|
|
: G(G)
|
|
{
|
|
}
|
|
|
|
ObjectCGOState::ObjectCGOState(const ObjectCGOState& other)
|
|
: G(other.G)
|
|
, renderCGO(other.renderCGO)
|
|
, renderWithShaders(other.renderWithShaders)
|
|
, hasTransparency(other.hasTransparency)
|
|
, hasOpaque(other.hasOpaque)
|
|
, cgo_lighting(other.cgo_lighting)
|
|
{
|
|
if (other.origCGO) {
|
|
this->origCGO.reset(new CGO(other.G));
|
|
this->origCGO->append(*other.origCGO);
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
static CGO* ObjectCGOPyListFloatToCGO(PyMOLGlobals* G, PyObject* list)
|
|
{
|
|
CGO* cgo = nullptr;
|
|
int len;
|
|
int ok = true;
|
|
int result;
|
|
float* raw = nullptr;
|
|
|
|
if (PyList_Check(list)) {
|
|
len = PConvPyListToFloatArray(list, &raw);
|
|
if (len < 0)
|
|
len = 0;
|
|
if (raw) {
|
|
if (ok) {
|
|
cgo = CGONewSized(G, len);
|
|
if (cgo) {
|
|
result = CGOFromFloatArray(cgo, raw, len);
|
|
if (result) {
|
|
PRINTF " FloatToCGO: error encountered on element %d\n",
|
|
result ENDF(G);
|
|
}
|
|
CGOStop(cgo);
|
|
}
|
|
}
|
|
FreeP(raw);
|
|
}
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
static CGO* ObjectCGOFloatArrayToCGO(
|
|
PyMOLGlobals* G, float* raw, int len, int quiet)
|
|
{
|
|
CGO* cgo = nullptr;
|
|
int ok = true;
|
|
int result;
|
|
|
|
if (raw) {
|
|
if (ok) {
|
|
cgo = CGONewSized(G, len);
|
|
if (cgo) {
|
|
result = CGOFromFloatArray(cgo, raw, len);
|
|
if (result && !quiet) {
|
|
PRINTF " FloatToCGO: error encountered on element %d\n",
|
|
result ENDF(G);
|
|
}
|
|
CGOStop(cgo);
|
|
}
|
|
}
|
|
}
|
|
return (cgo);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
ObjectCGO* ObjectCGOFromCGO(
|
|
PyMOLGlobals* G, ObjectCGO* obj, CGO* cgo, int state)
|
|
{
|
|
ObjectCGO* I = nullptr;
|
|
|
|
int size_n;
|
|
|
|
if (!obj) {
|
|
I = new ObjectCGO(G);
|
|
size_n = 0;
|
|
} else {
|
|
size_n = obj->State.size();
|
|
I = obj;
|
|
}
|
|
if (state < 0)
|
|
state = I->State.size();
|
|
if (I->State.size() <= state) {
|
|
I->State.resize(size_n + 1, ObjectCGOState(G));
|
|
}
|
|
|
|
I->State[state].renderCGO = nullptr;
|
|
I->State[state].origCGO = nullptr;
|
|
I->State[state].origCGO.reset(cgo);
|
|
|
|
if (I) {
|
|
ObjectCGORecomputeExtent(I);
|
|
}
|
|
SceneChanged(G);
|
|
SceneCountFrames(G);
|
|
return (I);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
ObjectCGO* ObjectCGONewVFontTest(PyMOLGlobals* G, const char* text, float* pos)
|
|
{
|
|
|
|
ObjectCGO* I = nullptr;
|
|
int font_id;
|
|
CGO* cgo = nullptr;
|
|
float scale[2] = {1.0, 1.0};
|
|
|
|
font_id = VFontLoad(G, 1, 1, 1, true);
|
|
cgo = CGONew(G);
|
|
VFontWriteToCGO(G, font_id, cgo, text, pos, scale, nullptr, nullptr);
|
|
I = ObjectCGOFromCGO(G, nullptr, cgo, 0);
|
|
|
|
return (I);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
/**
|
|
* @pre GIL
|
|
*/
|
|
ObjectCGO* ObjectCGODefine(
|
|
PyMOLGlobals* G, ObjectCGO* obj, PyObject* pycgo, int state)
|
|
{
|
|
assert(PyGILState_Check());
|
|
|
|
ObjectCGO* I = nullptr;
|
|
|
|
CGO *cgo, *font_cgo;
|
|
int est;
|
|
|
|
int size_n;
|
|
|
|
if (obj) {
|
|
if (obj->type != cObjectCGO) { /* TODO: handle this */
|
|
obj = nullptr;
|
|
}
|
|
size_n = obj->State.size();
|
|
}
|
|
if (!obj) {
|
|
I = new ObjectCGO(G);
|
|
size_n = 0;
|
|
} else {
|
|
I = obj;
|
|
}
|
|
if (state < 0) {
|
|
state = I->State.size();
|
|
}
|
|
if (I->State.size() <= state) {
|
|
I->State.resize(state + 1, ObjectCGOState(G));
|
|
}
|
|
|
|
I->State[state].origCGO = nullptr;
|
|
|
|
if (PyList_Check(pycgo)) {
|
|
if (PyList_Size(pycgo)) {
|
|
if (PyFloat_Check(PyList_GetItem(pycgo, 0))) {
|
|
cgo = ObjectCGOPyListFloatToCGO(G, pycgo);
|
|
if (cgo) {
|
|
est = CGOCheckForText(cgo);
|
|
if (est) {
|
|
CGOPreloadFonts(cgo);
|
|
font_cgo = CGODrawText(cgo, est, nullptr);
|
|
CGOFree(cgo);
|
|
cgo = font_cgo;
|
|
}
|
|
est = CGOCheckComplex(cgo);
|
|
I->State[state].origCGO.reset(cgo);
|
|
} else {
|
|
ErrMessage(G, "ObjectCGO", "could not parse CGO List.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (I) {
|
|
ObjectCGORecomputeExtent(I);
|
|
}
|
|
SceneChanged(G);
|
|
SceneCountFrames(G);
|
|
return (I);
|
|
}
|
|
|
|
ObjectCGO* ObjectCGOFromFloatArray(PyMOLGlobals* G, ObjectCGO* obj,
|
|
float* array, int size, int state, int quiet)
|
|
{
|
|
ObjectCGO* I = nullptr;
|
|
|
|
CGO *cgo, *font_cgo;
|
|
int est;
|
|
|
|
int size_n;
|
|
|
|
if (obj) {
|
|
if (obj->type != cObjectCGO) { /* TODO: handle this */
|
|
obj = nullptr;
|
|
}
|
|
size_n = obj->State.size();
|
|
}
|
|
if (!obj) {
|
|
I = new ObjectCGO(G);
|
|
size_n = 0;
|
|
} else {
|
|
I = obj;
|
|
}
|
|
if (state < 0)
|
|
state = I->State.size();
|
|
if (I->State.size() <= state) {
|
|
I->State.resize(size_n + 1, ObjectCGOState(G));
|
|
}
|
|
|
|
I->State[state].renderCGO = nullptr;
|
|
I->State[state].origCGO = nullptr;
|
|
|
|
cgo = ObjectCGOFloatArrayToCGO(G, array, size, quiet);
|
|
if (cgo) {
|
|
est = CGOCheckForText(cgo);
|
|
if (est) {
|
|
CGOPreloadFonts(cgo);
|
|
font_cgo = CGODrawText(cgo, est, nullptr);
|
|
CGOFree(cgo);
|
|
cgo = font_cgo;
|
|
}
|
|
est = CGOCheckComplex(cgo);
|
|
I->State[state].origCGO.reset(cgo);
|
|
} else if (!quiet) {
|
|
ErrMessage(G, "ObjectCGO", "could not parse CGO.");
|
|
}
|
|
if (I) {
|
|
ObjectCGORecomputeExtent(I);
|
|
}
|
|
SceneChanged(G);
|
|
SceneCountFrames(G);
|
|
return (I);
|
|
}
|
|
|
|
pymol::CObject* ObjectCGO::clone() const
|
|
{
|
|
return new ObjectCGO(*this);
|
|
}
|