// TnzCore includes
#include "tundo.h"
#include "tgl.h"
// TnzExt includes
#include "ext/plasticdeformerstorage.h"
// TnzLib includes
#include "toonz/txshcell.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/tframehandle.h"
// tcg includes
#include "tcg/tcg_point_ops.h"
// boost includes
#include <boost/noncopyable.hpp>
#include "plastictool.h"
using namespace PlasticToolLocals;
//****************************************************************************************
// Local namespace stuff
//****************************************************************************************
namespace
{
enum { RIGID_IDX = 0,
FLEX_IDX };
} // namespace
//****************************************************************************************
// Undo definitions
//****************************************************************************************
namespace
{
class PaintRigidityUndo : public TUndo
{
TXshCell m_cell; //!< Affected image (cell == level + frame)
std::vector<std::map<int, double>> m_vertices; //!< Affected vertices
double m_paintValue; //!< Rigidity value the vertices were
//!< painted with
public:
PaintRigidityUndo(const TXshCell &cell,
const std::vector<std::map<int, double>> &vertices,
double paintValue)
: m_cell(cell), m_vertices(vertices), m_paintValue(paintValue) {}
int getSize() const { return 1 << 20; }
void redo() const
{
TXshSimpleLevel *sl = static_cast<TXshSimpleLevel *>(m_cell.m_level.getPointer());
sl->setDirtyFlag(true);
TMeshImageP mi(sl->getFrame(m_cell.m_frameId, true));
if (!mi || mi->meshes().size() != m_vertices.size())
return;
int m, mCount = int(mi->meshes().size());
for (m = 0; m != mCount; ++m) {
TTextureMesh &mesh = *mi->meshes()[m];
std::map<int, double>::const_iterator vt, vEnd(m_vertices[m].end());
for (vt = m_vertices[m].begin(); vt != vEnd; ++vt)
mesh.vertex(vt->first).P().rigidity = m_paintValue;
}
PlasticDeformerStorage::instance()->invalidateMeshImage(
mi.getPointer(), PlasticDeformerStorage::MESH);
}
void undo() const
{
TXshSimpleLevel *sl = static_cast<TXshSimpleLevel *>(m_cell.m_level.getPointer());
sl->setDirtyFlag(true);
TMeshImageP mi(sl->getFrame(m_cell.m_frameId, true));
if (!mi || mi->meshes().size() != m_vertices.size())
return;
int m, mCount = int(mi->meshes().size());
for (m = 0; m != mCount; ++m) {
TTextureMesh &mesh = *mi->meshes()[m];
std::map<int, double>::const_iterator vt, vEnd(m_vertices[m].end());
for (vt = m_vertices[m].begin(); vt != vEnd; ++vt)
mesh.vertex(vt->first).P().rigidity = vt->second;
}
PlasticDeformerStorage::instance()->invalidateMeshImage(
mi.getPointer(), PlasticDeformerStorage::MESH);
}
};
} // namespace
//****************************************************************************************
// RigidityPainter definition
//****************************************************************************************
namespace
{
class RigidityPainter : public tcg::polymorphic
{
std::vector<std::map<int, double>> m_oldRigidities; //!< The original values of painted vertices
double m_sqRadius, m_value; //!< Drawing parameters
public:
RigidityPainter() : m_sqRadius(), m_value() {}
void startPainting(double radius, int rigidIdx);
void paint(const TPointD &pos);
void commit();
private:
void reset()
{
m_sqRadius = 0.0, m_value = 0.0;
std::vector<std::map<int, double>>().swap(m_oldRigidities);
}
};
//------------------------------------------------------------------------
void RigidityPainter::startPainting(double radius, int rigidIdx)
{
m_sqRadius = sq(radius);
m_value = (rigidIdx == RIGID_IDX) ? 1e4 : 1.0;
assert(m_oldRigidities.empty());
}
//------------------------------------------------------------------------
void RigidityPainter::paint(const TPointD &pos)
{
const TXshCell &cell = ::xshCell();
TXshSimpleLevel *sl = dynamic_cast<TXshSimpleLevel *>(cell.m_level.getPointer());
if (!sl)
return;
TMeshImageP meshImg = TTool::getImage(true);
if (!meshImg)
return;
// Soil the level - schedules it for save
sl->setDirtyFlag(true);
// Paint all mesh vertices inside the circle with center pos and given radius
const std::vector<TTextureMeshP> &meshes = meshImg->meshes();
int m, mCount = int(meshImg->meshes().size());
m_oldRigidities.resize(mCount);
for (m = 0; m != mCount; ++m) {
TTextureMesh &mesh = *meshes[m];
int v, vCount = mesh.verticesCount();
for (v = 0; v != vCount; ++v) {
RigidPoint &vxPos = mesh.vertex(v).P();
if (tcg::point_ops::dist2(pos, (const TPointD &)vxPos) < m_sqRadius) {
if (!m_oldRigidities[m].count(v))
m_oldRigidities[m][v] = vxPos.rigidity;
vxPos.rigidity = m_value;
}
}
}
PlasticDeformerStorage::instance()->invalidateMeshImage(
meshImg.getPointer(), PlasticDeformerStorage::MESH);
}
//------------------------------------------------------------------------
void RigidityPainter::commit()
{
TUndoManager::manager()->add(new PaintRigidityUndo(::xshCell(), m_oldRigidities, m_value));
reset();
}
} // namespace
//****************************************************************************************
// PlasticTool functions
//****************************************************************************************
std::auto_ptr<tcg::polymorphic> PlasticTool::createRigidityPainter()
{
return std::auto_ptr<tcg::polymorphic>(new RigidityPainter);
}
//------------------------------------------------------------------------
void PlasticTool::mouseMove_rigidity(const TPointD &pos, const TMouseEvent &e)
{
// Track mouse position
m_pos = pos; // Needs to be done now - ensures m_pos is valid
invalidate();
}
//------------------------------------------------------------------------
void PlasticTool::leftButtonDown_rigidity(const TPointD &pos, const TMouseEvent &)
{
// Track mouse position
m_pressedPos = m_pos = pos;
RigidityPainter *painter = static_cast<RigidityPainter *>(m_rigidityPainter.get());
painter->startPainting(m_thickness.getValue(), m_rigidValue.getIndex());
painter->paint(m_pos);
invalidate();
}
//------------------------------------------------------------------------
void PlasticTool::leftButtonDrag_rigidity(const TPointD &pos, const TMouseEvent &)
{
// Track mouse position
m_pos = pos;
RigidityPainter *painter = static_cast<RigidityPainter *>(m_rigidityPainter.get());
painter->paint(m_pos);
invalidate();
}
//------------------------------------------------------------------------
void PlasticTool::leftButtonUp_rigidity(const TPointD &pos, const TMouseEvent &)
{
// Track mouse position
m_pos = pos;
RigidityPainter *painter = static_cast<RigidityPainter *>(m_rigidityPainter.get());
painter->commit();
}
//------------------------------------------------------------------------
void PlasticTool::addContextMenuActions_rigidity(QMenu *menu)
{
}
//------------------------------------------------------------------------
void PlasticTool::draw_rigidity()
{
if (TTool::getApplication()->getCurrentFrame()->isEditingScene()) {
// In the rigidity case, we're editing the mesh level - so the implicit
// transformation affine loaded by OpenGL gets multiplied by the level's
// dpi scale. We have to revert the scale before showing column-related data.
const TPointD &dpiScale = TTool::getViewer()->getDpiScale();
glPushMatrix();
{
tglMultMatrix(TScale(1.0 / dpiScale.x, 1.0 / dpiScale.y));
double pixelSize = sqrt(tglGetPixelSize2());
// Draw original skeleton
const PlasticSkeletonP &skeleton = this->skeleton();
if (skeleton) {
drawOnionSkinSkeletons_build(pixelSize);
drawSkeleton(*skeleton, pixelSize);
drawSelections(m_sd, *skeleton, pixelSize);
}
}
glPopMatrix();
}
// Draw a circle centered at m_pos with m_thickness radius
glColor3f(1.0f, 0.0f, 0.0f); // Red
tglDrawCircle(m_pos, m_thickness.getValue());
}