tahoma2d/toonz/sources/tnztools/geometrictool.cpp
Shinya Kitaoka d1f6c4e95b REFACTORING: Add final specifiers (#537)
* add final specifiers

* apply clang-format

* fix for macOS
2016-06-29 15:17:12 +09:00

2082 lines
70 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "toonz/tpalettehandle.h"
#include "tools/toolhandle.h"
#include "toonz/tobjecthandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/tframehandle.h"
#include "tools/tool.h"
#include "tcolorstyles.h"
#include "tpalette.h"
#include "tools/toolutils.h"
#include "tstroke.h"
#include "tmathutil.h"
#include "tools/cursors.h"
#include "tproperty.h"
#include "ttoonzimage.h"
#include "drawutil.h"
#include "tvectorimage.h"
#include "tenv.h"
#include "bluredbrush.h"
#include "toonz/ttileset.h"
#include "toonz/toonzimageutils.h"
#include "toonz/tstageobject.h"
#include "toonz/tstageobjectspline.h"
#include "toonzqt/imageutils.h"
#include "toonzqt/dvdialog.h"
#include "toonz/trasterimageutils.h"
#include "toonz/preferences.h"
#include "tw/keycodes.h"
#include "historytypes.h"
// For Qt translation support
#include <QCoreApplication>
using namespace ToolUtils;
class GeometricTool;
class MultiLinePrimitive;
TEnv::DoubleVar GeometricSize("InknpaintGeometricSize", 1);
TEnv::DoubleVar GeometricRasterSize("InknpaintGeometricRasterSize", 1);
TEnv::StringVar GeometricType("InknpaintGeometricType", "Rectangle");
TEnv::IntVar GeometricEdgeCount("InknpaintGeometricEdgeCount", 3);
TEnv::IntVar GeometricSelective("InknpaintGeometricSelective", 0);
TEnv::IntVar GeometricGroupIt("InknpaintGeometricGroupIt", 0);
TEnv::IntVar GeometricAutofill("InknpaintGeometricAutofill", 0);
TEnv::IntVar GeometricPencil("InknpaintGeometricPencil", 0);
TEnv::DoubleVar GeometricBrushHardness("InknpaintGeometricHardness", 100);
TEnv::DoubleVar GeometricOpacity("InknpaintGeometricOpacity", 100);
TEnv::IntVar GeometricCapStyle("InknpaintGeometricCapStyle", 0);
TEnv::IntVar GeometricJoinStyle("InknpaintGeometricJoinStyle", 0);
TEnv::IntVar GeometricMiterValue("InknpaintGeometricMiterValue", 4);
//-------------------------------------------------------------------
#define ROUNDC_WSTR L"round_cap"
#define BUTT_WSTR L"butt_cap"
#define PROJECTING_WSTR L"projecting_cap"
#define ROUNDJ_WSTR L"round_join"
#define BEVEL_WSTR L"bevel_join"
#define MITER_WSTR L"miter_join"
//=============================================================================
// Utility Functions
//-----------------------------------------------------------------------------
TPointD rectify(const TPointD &oldPos, const TPointD &pos) {
const double h = sqrt(2.0) / 2.0;
const TPointD directions[] = {TPointD(1, 0), TPointD(h, h), TPointD(0, 1),
TPointD(-h, h), TPointD(-1, 0), TPointD(-h, -h),
TPointD(0, -1), TPointD(h, -h)};
TPointD v = pos - oldPos;
int j = 0;
double bestValue = v * directions[j];
for (int k = 1; k < 8; k++) {
double value = v * directions[k];
if (value > bestValue) {
bestValue = value;
j = k;
}
}
return oldPos + bestValue * directions[j];
}
//-----------------------------------------------------------------------------
TPointD computeSpeed(TPointD p0, TPointD p1, double factor) {
TPointD d = p1 - p0;
return (d == TPointD()) ? TPointD() : d * (factor / norm(d));
}
//-----------------------------------------------------------------------------
TRect drawBluredBrush(const TRasterImageP &ri, TStroke *stroke, int thick,
double hardness, double opacity) {
TStroke *s = new TStroke(*stroke);
TPointD riCenter = ri->getRaster()->getCenterD();
s->transform(TTranslation(riCenter));
int styleId = s->getStyle();
TPalette *plt = ri->getPalette();
TPixel32 color = plt->getStyle(styleId)->getMainColor();
TRectD bbox = s->getBBox();
TRect bboxI = convert(bbox).enlarge(1);
TRect rectRender = bboxI * ri->getRaster()->getBounds();
if (rectRender.isEmpty()) return TRect();
TRaster32P workRas(ri->getRaster()->getSize());
TRaster32P backupRas = ri->getRaster()->clone();
workRas->clear();
QRadialGradient brushPad = ToolUtils::getBrushPad(thick, hardness);
BluredBrush brush(workRas, thick, brushPad, false);
int i, chunkCount = s->getChunkCount();
for (i = 0; i < chunkCount; i++) {
const TThickQuadratic *q = s->getChunk(i);
std::vector<TThickPoint> points;
points.push_back(q->getThickP0());
points.push_back(q->getThickP1());
points.push_back(q->getThickP2());
// i punti contenuti nello stroke sembra che haano la tickness pari al
// raggio e non al diametro del punto!
points[0].thick *= 2;
points[1].thick *= 2;
points[2].thick *= 2;
TRect chunkBox = brush.getBoundFromPoints(points);
brush.addArc(points[0], points[1], points[2], 1, 1);
brush.updateDrawing(ri->getRaster(), backupRas, color, chunkBox, opacity);
}
delete s;
return rectRender;
}
//-----------------------------------------------------------------------------
TRect drawBluredBrush(const TToonzImageP &ti, TStroke *stroke, int thick,
double hardness, bool selective) {
TStroke *s = new TStroke(*stroke);
TPointD riCenter = ti->getRaster()->getCenterD();
s->transform(TTranslation(riCenter));
int styleId = s->getStyle();
TRectD bbox = s->getBBox();
TRect bboxI = convert(bbox).enlarge(1);
TRect rectRender = bboxI * ti->getRaster()->getBounds();
if (rectRender.isEmpty()) return TRect();
TRaster32P workRas(ti->getRaster()->getSize());
TRasterCM32P backupRas = ti->getRaster()->clone();
workRas->clear();
QRadialGradient brushPad = ToolUtils::getBrushPad(thick, hardness);
BluredBrush brush(workRas, thick, brushPad, false);
int i, chunkCount = s->getChunkCount();
for (i = 0; i < chunkCount; i++) {
const TThickQuadratic *q = s->getChunk(i);
std::vector<TThickPoint> points;
points.push_back(q->getThickP0());
points.push_back(q->getThickP1());
points.push_back(q->getThickP2());
// i punti contenuti nello stroke sembra che haano la tickness pari al
// raggio e non al diametro del punto!
points[0].thick *= 2;
points[1].thick *= 2;
points[2].thick *= 2;
TRect chunkBox = brush.getBoundFromPoints(points);
brush.addArc(points[0], points[1], points[2], 1, 1);
brush.updateDrawing(ti->getRaster(), backupRas, chunkBox, styleId,
selective);
}
delete s;
return rectRender;
}
//-----------------------------------------------------------------------------
class MultilinePrimitiveUndo final : public TUndo {
std::vector<TPointD> m_oldVertex;
std::vector<TPointD> m_newVertex;
MultiLinePrimitive *m_tool;
public:
MultilinePrimitiveUndo(const std::vector<TPointD> &vertex,
MultiLinePrimitive *tool)
: TUndo(), m_oldVertex(vertex), m_tool(tool), m_newVertex() {}
~MultilinePrimitiveUndo() {}
void undo() const override;
void redo() const override;
void setNewVertex(const std::vector<TPointD> &vertex) {
m_newVertex = vertex;
}
int getSize() const override { return sizeof(this); }
QString getToolName();
int getHistoryType() override { return HistoryType::GeometricTool; }
};
//-----------------------------------------------------------------------------
class FullColorBluredPrimitiveUndo final : public UndoFullColorPencil {
int m_thickness;
double m_hardness;
public:
FullColorBluredPrimitiveUndo(TXshSimpleLevel *level, const TFrameId &frameId,
TStroke *stroke, int thickness, double hardness,
double opacity, bool doAntialias,
bool createdFrame, bool createdLevel)
: UndoFullColorPencil(level, frameId, stroke, opacity, doAntialias,
createdFrame, createdLevel)
, m_thickness(thickness)
, m_hardness(hardness) {
TRasterP raster = getImage()->getRaster();
TDimension d = raster->getSize();
m_tiles = new TTileSetFullColor(d);
TRect rect = convert(stroke->getBBox()) +
TPoint((int)(d.lx * 0.5), (int)(d.ly * 0.5));
m_tiles->add(raster, rect.enlarge(2));
m_stroke = new TStroke(*stroke);
}
~FullColorBluredPrimitiveUndo() {}
void redo() const override {
insertLevelAndFrameIfNeeded();
TRasterImageP ri = getImage();
if (!ri) return;
drawBluredBrush(ri, m_stroke, m_thickness, m_hardness, m_opacity);
TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
notifyImageChanged();
}
int getSize() const override {
return UndoFullColorPencil::getSize() + sizeof(this);
}
};
//-----------------------------------------------------------------------------
/*-- Hardness<100 のときの GeometricToolのUndo --*/
class CMBluredPrimitiveUndo final : public UndoRasterPencil {
int m_thickness;
double m_hardness;
bool m_selective;
public:
CMBluredPrimitiveUndo(TXshSimpleLevel *level, const TFrameId &frameId,
TStroke *stroke, int thickness, double hardness,
bool selective, bool doAntialias, bool createdFrame,
bool createdLevel, std::string primitiveName)
: UndoRasterPencil(level, frameId, stroke, selective, false, doAntialias,
createdFrame, createdLevel, primitiveName)
, m_thickness(thickness)
, m_hardness(hardness)
, m_selective(selective) {
TRasterCM32P raster = getImage()->getRaster();
TDimension d = raster->getSize();
m_tiles = new TTileSetCM32(d);
TRect rect = convert(stroke->getBBox()) +
TPoint((int)(d.lx * 0.5), (int)(d.ly * 0.5));
m_tiles->add(raster, rect.enlarge(2));
m_stroke = new TStroke(*stroke);
}
~CMBluredPrimitiveUndo() {}
void redo() const override {
insertLevelAndFrameIfNeeded();
TToonzImageP ti = getImage();
if (!ti) return;
drawBluredBrush(ti, m_stroke, m_thickness, m_hardness, m_selective);
TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
notifyImageChanged();
}
int getSize() const override {
return UndoRasterPencil::getSize() + sizeof(this);
}
};
//-----------------------------------------------------------------------------
class PrimitiveParam {
Q_DECLARE_TR_FUNCTIONS(PrimitiveParam)
public:
TDoubleProperty m_toolSize;
TIntProperty m_rasterToolSize;
TDoubleProperty m_opacity;
TDoubleProperty m_hardness;
TEnumProperty m_type;
TIntProperty m_edgeCount;
TBoolProperty m_autogroup;
TBoolProperty m_autofill;
TBoolProperty m_selective;
TBoolProperty m_pencil;
TEnumProperty m_capStyle;
TEnumProperty m_joinStyle;
TIntProperty m_miterJoinLimit;
TPropertyGroup m_prop[2];
int m_targetType;
PrimitiveParam(int targetType)
: m_type("Shape:") // "W_ToolOptions_ShapeType")
, m_toolSize("Size:", 0, 100,
1) // "W_ToolOptions_ShapeThickness", 0,30,1)
, m_rasterToolSize("Size:", 1, 100, 1)
, m_opacity("Opacity:", 0, 100, 100)
, m_hardness("Hardness:", 0, 100, 100)
, m_edgeCount("Polygon Sides:", 3, 15, 3)
, m_autogroup("Auto Group", false)
, m_autofill("Auto Fill", false)
, m_selective("Selective", false)
, m_pencil("Pencil Mode", false)
, m_capStyle("Cap")
, m_joinStyle("Join")
, m_miterJoinLimit("Miter:", 0, 100, 4)
, m_targetType(targetType) {
if (targetType & TTool::Vectors) m_prop[0].bind(m_toolSize);
if (targetType & TTool::ToonzImage || targetType & TTool::RasterImage) {
m_prop[0].bind(m_rasterToolSize);
m_prop[0].bind(m_hardness);
}
if (targetType & TTool::RasterImage) m_prop[0].bind(m_opacity);
m_prop[0].bind(m_type);
m_prop[0].bind(m_edgeCount);
if (targetType & TTool::Vectors) {
m_prop[0].bind(m_autogroup);
m_prop[0].bind(m_autofill);
}
if (targetType & TTool::ToonzImage) {
m_prop[0].bind(m_selective);
m_prop[0].bind(m_pencil);
m_pencil.setId("PencilMode");
}
m_capStyle.addValue(BUTT_WSTR);
m_capStyle.addValue(ROUNDC_WSTR);
m_capStyle.addValue(PROJECTING_WSTR);
m_capStyle.setId("Cap");
m_joinStyle.addValue(MITER_WSTR);
m_joinStyle.addValue(ROUNDJ_WSTR);
m_joinStyle.addValue(BEVEL_WSTR);
m_joinStyle.setId("Join");
m_miterJoinLimit.setId("Miter");
m_prop[1].bind(m_capStyle);
m_prop[1].bind(m_joinStyle);
m_prop[1].bind(m_miterJoinLimit);
m_selective.setId("Selective");
m_autogroup.setId("AutoGroup");
m_autofill.setId("Autofill");
m_type.setId("GeometricShape");
m_edgeCount.setId("GeometricEdge");
}
void updateTranslation() {
m_type.setQStringName(tr("Shape:"));
m_toolSize.setQStringName(tr("Size:"));
m_rasterToolSize.setQStringName(tr("Thickness:"));
m_opacity.setQStringName(tr("Opacity:"));
m_hardness.setQStringName(tr("Hardness:"));
m_edgeCount.setQStringName(tr("Polygon Sides:"));
m_autogroup.setQStringName(tr("Auto Group"));
m_autofill.setQStringName(tr("Auto Fill"));
m_selective.setQStringName(tr("Selective"));
m_pencil.setQStringName(tr("Pencil Mode"));
m_capStyle.setQStringName(tr("Cap"));
m_joinStyle.setQStringName(tr("Join"));
m_miterJoinLimit.setQStringName(tr("Miter:"));
}
};
//=============================================================================
// Abstract Class Primitive
//-----------------------------------------------------------------------------
class Primitive {
protected:
bool m_isEditing, m_rasterTool, m_isPrompting;
GeometricTool *m_tool;
PrimitiveParam *m_param;
public:
Primitive(PrimitiveParam *param, GeometricTool *tool, bool rasterTool)
: m_param(param)
, m_tool(tool)
, m_isEditing(false)
, m_rasterTool(rasterTool)
, m_isPrompting(false) {}
double getThickness() const {
if (m_rasterTool)
return m_param->m_rasterToolSize.getValue() * 0.5;
else
return m_param->m_toolSize.getValue() * 0.5;
}
void setIsPrompting(bool value) { m_isPrompting = value; }
virtual std::string getName() const = 0;
virtual ~Primitive() {}
virtual void leftButtonDown(const TPointD &p, const TMouseEvent &e){};
virtual void leftButtonDrag(const TPointD &p, const TMouseEvent &e){};
virtual void leftButtonUp(const TPointD &p, const TMouseEvent &e){};
virtual void leftButtonDoubleClick(const TPointD &, const TMouseEvent &e){};
virtual void rightButtonDown(const TPointD &p, const TMouseEvent &e){};
virtual void mouseMove(const TPointD &p, const TMouseEvent &e){};
virtual bool keyDown(int key, const TPoint &point) { return false; }
virtual void onEnter(){};
virtual void draw(){};
virtual void onActivate(){};
virtual void onDeactivate(){};
virtual void onImageChanged(){};
virtual TStroke *makeStroke() const = 0;
};
//=============================================================================
// Rectangle Primitive Class Declaration
//-----------------------------------------------------------------------------
class RectanglePrimitive final : public Primitive {
TRectD m_selectingRect;
TPointD m_pos;
TPointD m_startPoint;
TPixel32 m_color;
public:
RectanglePrimitive(PrimitiveParam *param, GeometricTool *tool,
bool reasterTool)
: Primitive(param, tool, reasterTool) {}
std::string getName() const override {
return "Rectangle";
} // W_ToolOptions_ShapeRect"; }
TStroke *makeStroke() const override;
void draw() override;
void leftButtonDown(const TPointD &pos, const TMouseEvent &) override;
void leftButtonDrag(const TPointD &realPos, const TMouseEvent &e) override;
void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
void onEnter() override;
};
//=============================================================================
// Circle Primitive Class Declaration
//-----------------------------------------------------------------------------
class CirclePrimitive final : public Primitive {
TPointD m_centre;
TPointD m_pos;
double m_radius;
TPixel32 m_color;
public:
CirclePrimitive(PrimitiveParam *param, GeometricTool *tool, bool reasterTool)
: Primitive(param, tool, reasterTool) {}
std::string getName() const override {
return "Circle";
} // W_ToolOptions_ShapeCircle";}
void draw() override;
void leftButtonDown(const TPointD &pos, const TMouseEvent &) override;
void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override;
TStroke *makeStroke() const override;
void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
void onEnter() override;
};
//=============================================================================
// MultiLine Primitive Class Declaration
//-----------------------------------------------------------------------------
const double joinDistance = 5.0;
class MultiLinePrimitive : public Primitive {
protected:
std::vector<TPointD> m_vertex;
TPointD m_mousePosition;
TPixel32 m_color;
bool m_closed, m_isSingleLine;
bool m_speedMoved; //!< True after moveSpeed(), false after addVertex()
bool m_beforeSpeedMoved; //!< Old value of m_speedMoved
bool m_ctrlDown; //!< Whether ctrl is hold down
MultilinePrimitiveUndo *m_undo;
public:
MultiLinePrimitive(PrimitiveParam *param, GeometricTool *tool,
bool reasterTool)
: Primitive(param, tool, reasterTool)
, m_closed(false)
, m_isSingleLine(false)
, m_speedMoved(false)
, m_beforeSpeedMoved(false)
, m_ctrlDown(false) {}
std::string getName() const override {
return "Polyline";
} // W_ToolOptions_ShapePolyline";}
void addVertex(const TPointD &pos);
void moveSpeed(const TPointD &delta);
void draw() override;
void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override;
void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override;
void leftButtonDoubleClick(const TPointD &, const TMouseEvent &e) override;
void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
bool keyDown(int key, const TPoint &point) override;
TStroke *makeStroke() const override;
void endLine();
void onActivate() override;
void onDeactivate() override {
m_vertex.clear();
m_speedMoved = false;
m_beforeSpeedMoved = false;
}
void onEnter() override;
void onImageChanged() override;
void setVertexes(const std::vector<TPointD> &vertex) { m_vertex = vertex; };
void setSpeedMoved(bool speedMoved) { m_speedMoved = speedMoved; };
};
//-----------------------------------------------------------------------------
void MultilinePrimitiveUndo::undo() const {
m_tool->setVertexes(m_oldVertex);
int count = m_oldVertex.size();
bool speedMoved = (count != 0 && count % 4 != 1);
m_tool->setSpeedMoved(speedMoved);
TTool::getApplication()->getCurrentTool()->getTool()->invalidate();
}
//-----------------------------------------------------------------------------
void MultilinePrimitiveUndo::redo() const {
m_tool->setVertexes(m_newVertex);
int count = m_newVertex.size();
bool speedMoved = (count != 0 && count % 4 != 1);
m_tool->setSpeedMoved(speedMoved);
TTool::getApplication()->getCurrentTool()->getTool()->invalidate();
}
//----------------------------------------------------------------------------
QString MultilinePrimitiveUndo::getToolName() {
return QString("Geometric Tool %1")
.arg(QString::fromStdString(m_tool->getName()));
}
//=============================================================================
// Line Primitive Class Declaration
//-----------------------------------------------------------------------------
class LinePrimitive final : public MultiLinePrimitive {
public:
LinePrimitive(PrimitiveParam *param, GeometricTool *tool, bool reasterTool)
: MultiLinePrimitive(param, tool, reasterTool) {
m_isSingleLine = true;
}
std::string getName() const override {
return "Line";
} // W_ToolOptions_ShapePolyline";}
void draw() override;
void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override;
void leftButtonUp(const TPointD &pos, const TMouseEvent &e) override;
void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override;
void leftButtonDoubleClick(const TPointD &, const TMouseEvent &e) override {}
};
//=============================================================================
// Ellipse Primitive Class Declaration
//-----------------------------------------------------------------------------
class EllipsePrimitive final : public Primitive {
TPointD m_pos;
TRectD m_selectingRect;
TPointD m_startPoint;
TPixel32 m_color;
public:
EllipsePrimitive(PrimitiveParam *param, GeometricTool *tool, bool reasterTool)
: Primitive(param, tool, reasterTool) {}
std::string getName() const override {
return "Ellipse";
} // W_ToolOptions_ShapeEllipse";}
void draw() override;
void leftButtonDown(const TPointD &pos, const TMouseEvent &) override;
void leftButtonDrag(const TPointD &realPos, const TMouseEvent &e) override;
TStroke *makeStroke() const override;
void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
void onEnter() override;
};
//=============================================================================
// Arc Primitive Class Declaration
//-----------------------------------------------------------------------------
class ArcPrimitive final : public Primitive {
TStroke *m_stroke;
TPointD m_startPoint, m_endPoint, m_centralPoint;
int m_clickNumber;
TPixel32 m_color;
public:
ArcPrimitive(PrimitiveParam *param, GeometricTool *tool, bool reasterTool)
: Primitive(param, tool, reasterTool), m_stroke(0), m_clickNumber(0) {}
~ArcPrimitive() {
if (m_stroke) delete m_stroke;
}
std::string getName() const override {
return "Arc";
} // _ToolOptions_ShapeArc";}
TStroke *makeStroke() const override;
void draw() override;
void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
void onEnter() override;
};
//=============================================================================
// Polygon Primitive Class Declaration
//-----------------------------------------------------------------------------
class PolygonPrimitive final : public Primitive {
TPointD m_startPoint, m_centre;
double m_radius;
TPixel32 m_color;
public:
PolygonPrimitive(PrimitiveParam *param, GeometricTool *tool, bool reasterTool)
: Primitive(param, tool, reasterTool) {}
std::string getName() const override {
return "Polygon";
} // W_ToolOptions_ShapePolygon";}
TStroke *makeStroke() const override;
void draw() override;
void leftButtonDown(const TPointD &pos, const TMouseEvent &) override;
void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override;
void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
};
//=============================================================================
// Geometric Tool
//-----------------------------------------------------------------------------
class GeometricTool final : public TTool {
protected:
Primitive *m_primitive;
std::map<std::wstring, Primitive *> m_primitiveTable;
PrimitiveParam m_param;
std::wstring m_typeCode;
bool m_active;
bool m_firstTime;
public:
GeometricTool(int targetType)
: TTool("T_Geometric")
, m_primitive(0)
, m_param(targetType)
, m_active(false)
, m_firstTime(true) {
bind(targetType);
if ((targetType & TTool::RasterImage) || (targetType & TTool::ToonzImage)) {
addPrimitive(new RectanglePrimitive(&m_param, this, true));
addPrimitive(new CirclePrimitive(&m_param, this, true));
addPrimitive(new EllipsePrimitive(&m_param, this, true));
addPrimitive(new LinePrimitive(&m_param, this, true));
addPrimitive(new MultiLinePrimitive(&m_param, this, true));
addPrimitive(new ArcPrimitive(&m_param, this, true));
addPrimitive(new PolygonPrimitive(&m_param, this, true));
} else // targetType == 1
{
// vector
addPrimitive(m_primitive = new RectanglePrimitive(&m_param, this, false));
addPrimitive(new CirclePrimitive(&m_param, this, false));
addPrimitive(new EllipsePrimitive(&m_param, this, false));
addPrimitive(new LinePrimitive(&m_param, this, false));
addPrimitive(new MultiLinePrimitive(&m_param, this, false));
addPrimitive(new ArcPrimitive(&m_param, this, false));
addPrimitive(new PolygonPrimitive(&m_param, this, false));
}
}
~GeometricTool() {
std::map<std::wstring, Primitive *>::iterator it;
for (it = m_primitiveTable.begin(); it != m_primitiveTable.end(); ++it)
delete it->second;
}
ToolType getToolType() const override { return TTool::LevelWriteTool; }
void updateTranslation() override { m_param.updateTranslation(); }
void addPrimitive(Primitive *p) {
// TODO: aggiungere il controllo per evitare nomi ripetuti
std::wstring name = ::to_wstring(p->getName());
// wstring name = TStringTable::translate(p->getName());
m_primitiveTable[name] = p;
m_param.m_type.addValue(name);
}
void changeType(std::wstring name) {
std::map<std::wstring, Primitive *>::iterator it =
m_primitiveTable.find(name);
if (it != m_primitiveTable.end()) m_primitive = it->second;
}
void leftButtonDown(const TPointD &p, const TMouseEvent &e) override {
/* m_active = getApplication()->getCurrentObject()->isSpline() ||
(bool) getImage(true);*/
m_active = touchImage(); // NEEDS to be done even if(m_active), due
if (!m_active) // to the HORRIBLE m_frameCreated / m_levelCreated
return; // mechanism. touchImage() is the ONLY function
// resetting them to false... >_<
if (m_primitive) m_primitive->leftButtonDown(p, e);
invalidate();
}
void leftButtonDrag(const TPointD &p, const TMouseEvent &e) override {
if (!m_active) return;
if (m_primitive) m_primitive->leftButtonDrag(p, e);
invalidate();
}
void leftButtonUp(const TPointD &p, const TMouseEvent &e) override {
if (!m_active) return;
if (m_primitive) m_primitive->leftButtonUp(p, e);
invalidate();
}
void leftButtonDoubleClick(const TPointD &p, const TMouseEvent &e) override {
if (!m_active) return;
if (m_primitive) m_primitive->leftButtonDoubleClick(p, e);
invalidate();
}
bool keyDown(int key, TUINT32 b, const TPoint &point) override {
return m_primitive->keyDown(key, point);
}
void onImageChanged() override {
if (m_primitive) m_primitive->onImageChanged();
invalidate();
}
void rightButtonDown(const TPointD &p, const TMouseEvent &e) override {
if (m_primitive) m_primitive->rightButtonDown(p, e);
invalidate();
}
void mouseMove(const TPointD &p, const TMouseEvent &e) override {
if (m_primitive) m_primitive->mouseMove(p, e);
}
void onActivate() override {
if (m_firstTime) {
m_param.m_toolSize.setValue(GeometricSize);
m_param.m_rasterToolSize.setValue(GeometricRasterSize);
m_param.m_opacity.setValue(GeometricOpacity);
m_param.m_hardness.setValue(GeometricBrushHardness);
m_param.m_selective.setValue(GeometricSelective ? 1 : 0);
m_param.m_autogroup.setValue(GeometricGroupIt ? 1 : 0);
m_param.m_autofill.setValue(GeometricAutofill ? 1 : 0);
std::wstring typeCode = ::to_wstring(GeometricType.getValue());
m_param.m_type.setValue(typeCode);
GeometricType = ::to_string(typeCode);
m_typeCode = typeCode;
changeType(typeCode);
m_param.m_edgeCount.setValue(GeometricEdgeCount);
m_param.m_pencil.setValue(GeometricPencil ? 1 : 0);
m_param.m_capStyle.setIndex(GeometricCapStyle);
m_param.m_joinStyle.setIndex(GeometricJoinStyle);
m_param.m_miterJoinLimit.setValue(GeometricMiterValue);
m_firstTime = false;
}
/*--
ショートカットでいきなりスタートonEnterを通らない場合のとき、
LineToolが反応しないことがある対策 --*/
m_active = (getImage(false) != 0 ||
Preferences::instance()->isAutoCreateEnabled());
if (m_primitive) m_primitive->onActivate();
}
void onDeactivate() override {
if (m_primitive) m_primitive->onDeactivate();
}
void onEnter() override {
m_active = getImage(false) != 0;
if (m_active && m_primitive) m_primitive->onEnter();
}
void draw() override {
if (m_primitive) m_primitive->draw();
}
int getCursorId() const override { return ToolCursor::PenCursor; }
int getColorClass() const { return 1; }
TPropertyGroup *getProperties(int idx) override {
return &m_param.m_prop[idx];
}
bool onPropertyChanged(std::string propertyName) override {
/*--- 変更されたPropertyごとに処理を分ける。
注意m_toolSizeとm_rasterToolSizeは同じName(="Size:")なので、
扱っている画像がラスタかどうかで区別する ---*/
if (propertyName == m_param.m_toolSize.getName()) {
TImageP img = getImage(false);
TToonzImageP ri(img); /*-- ラスタかどうかの判定 --*/
if (ri)
GeometricRasterSize = m_param.m_rasterToolSize.getValue();
else
GeometricSize = m_param.m_toolSize.getValue();
} else if (propertyName == m_param.m_type.getName()) {
std::wstring typeCode = m_param.m_type.getValue();
GeometricType = ::to_string(typeCode);
if (typeCode != m_typeCode) {
m_typeCode = typeCode;
changeType(typeCode);
}
} else if (propertyName == m_param.m_edgeCount.getName())
GeometricEdgeCount = m_param.m_edgeCount.getValue();
else if (propertyName == m_param.m_autogroup.getName()) {
if (!m_param.m_autogroup.getValue()) {
m_param.m_autofill.setValue(false);
// this is ugly: it's needed to refresh the GUI of the toolbar after
// having set to false the autofill...
TTool::getApplication()->getCurrentTool()->setTool(
""); // necessary, otherwise next setTool is ignored...
TTool::getApplication()->getCurrentTool()->setTool(
QString::fromStdString(getName()));
}
GeometricGroupIt = m_param.m_autogroup.getValue();
} else if (propertyName == m_param.m_autofill.getName()) {
if (m_param.m_autofill.getValue()) {
m_param.m_autogroup.setValue(true);
// this is ugly: it's needed to refresh the GUI of the toolbar after
// having set to false the autofill...
TTool::getApplication()->getCurrentTool()->setTool(
""); // necessary, otherwise next setTool is ignored...
TTool::getApplication()->getCurrentTool()->setTool(
QString::fromStdString(getName()));
}
GeometricGroupIt = m_param.m_autofill.getValue();
} else if (propertyName == m_param.m_selective.getName())
GeometricSelective = m_param.m_selective.getValue();
else if (propertyName == m_param.m_pencil.getName())
GeometricPencil = m_param.m_pencil.getValue();
else if (propertyName == m_param.m_hardness.getName())
GeometricBrushHardness = m_param.m_hardness.getValue();
else if (propertyName == m_param.m_opacity.getName())
GeometricOpacity = m_param.m_opacity.getValue();
else if (propertyName == m_param.m_capStyle.getName())
GeometricCapStyle = m_param.m_capStyle.getIndex();
else if (propertyName == m_param.m_joinStyle.getName())
GeometricJoinStyle = m_param.m_joinStyle.getIndex();
else if (propertyName == m_param.m_miterJoinLimit.getName())
GeometricMiterValue = m_param.m_miterJoinLimit.getValue();
return false;
}
void addStroke() {
if (!m_primitive) return;
TStroke *stroke = m_primitive->makeStroke();
if (!stroke) return;
TStroke::OutlineOptions &options = stroke->outlineOptions();
options.m_capStyle = m_param.m_capStyle.getIndex();
options.m_joinStyle = m_param.m_joinStyle.getIndex();
options.m_miterUpper = m_param.m_miterJoinLimit.getValue();
TImage *image = getImage(true);
TToonzImageP ti(image);
TVectorImageP vi(image);
TRasterImageP ri(image);
TXshSimpleLevel *sl =
TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
TFrameId id = getCurrentFid();
/*-- ToonzImageの場合 --*/
if (ti) {
int styleId = TTool::getApplication()->getCurrentLevelStyleIndex();
bool selective = m_param.m_selective.getValue();
bool filled = false;
stroke->setStyle(styleId);
double hardness = m_param.m_hardness.getValue() * 0.01;
TRect savebox;
if (hardness == 1 || m_param.m_pencil.getValue()) {
TUndoManager::manager()->add(new UndoRasterPencil(
sl, id, stroke, selective, filled, !m_param.m_pencil.getValue(),
m_isFrameCreated, m_isLevelCreated, m_primitive->getName()));
savebox = ToonzImageUtils::addInkStroke(ti, stroke, styleId, selective,
filled, TConsts::infiniteRectD,
!m_param.m_pencil.getValue());
} else {
int thickness = m_param.m_rasterToolSize.getValue();
TUndoManager::manager()->add(new CMBluredPrimitiveUndo(
sl, id, stroke, thickness, hardness, selective, false,
m_isFrameCreated, m_isLevelCreated, m_primitive->getName()));
savebox = drawBluredBrush(ti, stroke, thickness, hardness, selective);
}
ToolUtils::updateSaveBox();
delete stroke;
}
/*-- VectorImageの場合 --*/
else if (vi) {
if (TTool::getApplication()->getCurrentObject()->isSpline()) {
if (!ToolUtils::isJustCreatedSpline(vi.getPointer())) {
m_primitive->setIsPrompting(true);
QString question("Are you sure you want to replace the motion path?");
int ret =
DVGui::MsgBox(question, QObject::tr("Yes"), QObject::tr("No"), 0);
m_primitive->setIsPrompting(false);
if (ret == 2 || ret == 0) return;
}
TUndo *undo = new UndoPath(
getXsheet()->getStageObject(getObjectId())->getSpline());
while (vi->getStrokeCount() > 0) vi->deleteStroke(0);
vi->addStroke(stroke, false);
notifyImageChanged();
TUndoManager::manager()->add(undo);
} else {
int styleId = TTool::getApplication()->getCurrentLevelStyleIndex();
if (styleId >= 0) stroke->setStyle(styleId);
QMutexLocker lock(vi->getMutex());
std::vector<TFilledRegionInf> *fillInformation =
new std::vector<TFilledRegionInf>;
ImageUtils::getFillingInformationOverlappingArea(vi, *fillInformation,
stroke->getBBox());
vi->addStroke(stroke);
TUndoManager::manager()->add(new UndoPencil(
vi->getStroke(vi->getStrokeCount() - 1), fillInformation, sl, id,
m_isFrameCreated, m_isLevelCreated, m_param.m_autogroup.getValue(),
m_param.m_autofill.getValue()));
}
if (m_param.m_autogroup.getValue() && stroke->isSelfLoop()) {
int index = vi->getStrokeCount() - 1;
vi->group(index, 1);
if (m_param.m_autofill.getValue()) {
// to avoid filling other strokes, I enter into the new stroke group
int currentGroup = vi->exitGroup();
vi->enterGroup(index);
vi->selectFill(stroke->getBBox().enlarge(1, 1), 0, stroke->getStyle(),
false, true, false);
if (currentGroup != -1)
vi->enterGroup(currentGroup);
else
vi->exitGroup();
}
}
}
/*-- RasterImageの場合 --*/
else if (ri) {
int styleId = TTool::getApplication()->getCurrentLevelStyleIndex();
stroke->setStyle(styleId);
double opacity = m_param.m_opacity.getValue() * 0.01;
double hardness = m_param.m_hardness.getValue() * 0.01;
TRect savebox;
if (hardness == 1) {
TUndoManager::manager()->add(new UndoFullColorPencil(
sl, id, stroke, opacity, true, m_isFrameCreated, m_isLevelCreated));
savebox = TRasterImageUtils::addStroke(ri, stroke, TRectD(), opacity);
} else {
int thickness = m_param.m_rasterToolSize.getValue();
TUndoManager::manager()->add(new FullColorBluredPrimitiveUndo(
sl, id, stroke, thickness, hardness, opacity, true,
m_isFrameCreated, m_isLevelCreated));
savebox = drawBluredBrush(ri, stroke, thickness, hardness, opacity);
}
ToolUtils::updateSaveBox();
delete stroke;
}
notifyImageChanged();
m_active = false;
}
};
GeometricTool GeometricVectorTool(TTool::Vectors | TTool::EmptyTarget);
GeometricTool GeometricRasterTool(TTool::ToonzImage | TTool::EmptyTarget);
GeometricTool GeometricRasterFullColorTool(TTool::RasterImage |
TTool::EmptyTarget);
//=============================================================================
// Rectangle Primitive Class Implementation
//-----------------------------------------------------------------------------
void RectanglePrimitive::draw() {
if (m_isEditing || m_isPrompting ||
areAlmostEqual(m_selectingRect.x0, m_selectingRect.x1) ||
areAlmostEqual(m_selectingRect.y0, m_selectingRect.y1)) {
tglColor(m_isEditing ? m_color : TPixel32::Green);
glBegin(GL_LINE_LOOP);
tglVertex(m_selectingRect.getP00());
tglVertex(m_selectingRect.getP01());
tglVertex(m_selectingRect.getP11());
tglVertex(m_selectingRect.getP10());
glEnd();
}
}
//-----------------------------------------------------------------------------
void RectanglePrimitive::leftButtonDown(const TPointD &pos,
const TMouseEvent &) {
TTool::Application *app = TTool::getApplication();
if (app->getCurrentObject()->isSpline()) {
m_color = TPixel32::Red;
m_isEditing = true;
} else {
// app->getCurrentTool()->getTool()->touchImage();
const TColorStyle *style = app->getCurrentLevelStyle();
m_isEditing = style != 0 && style->isStrokeStyle();
m_color = (style) ? style->getAverageColor() : TPixel32::Black;
}
if (!m_isEditing) return;
if (m_param->m_pencil.getValue() &&
(m_param->m_targetType & TTool::ToonzImage ||
m_param->m_targetType & TTool::RasterImage)) {
if (m_param->m_rasterToolSize.getValue() % 2 != 0)
m_startPoint = TPointD((int)pos.x, (int)pos.y);
else
m_startPoint = TPointD((int)pos.x + 0.5, (int)pos.y + 0.5);
} else
m_startPoint = pos;
m_selectingRect.x0 = m_startPoint.x;
m_selectingRect.y0 = m_startPoint.y;
m_selectingRect.x1 = m_startPoint.x;
m_selectingRect.y1 = m_startPoint.y;
}
//-----------------------------------------------------------------------------
void RectanglePrimitive::leftButtonDrag(const TPointD &realPos,
const TMouseEvent &e) {
if (!m_isEditing) return;
TPointD pos;
if (e.isShiftPressed()) {
double distance = tdistance(realPos, m_startPoint) * M_SQRT1_2;
pos.x = (realPos.x > m_startPoint.x) ? m_startPoint.x + distance
: m_startPoint.x - distance;
pos.y = (realPos.y > m_startPoint.y) ? m_startPoint.y + distance
: m_startPoint.y - distance;
} else {
pos = realPos;
}
if (m_param->m_pencil.getValue() &&
(m_param->m_targetType & TTool::ToonzImage ||
m_param->m_targetType & TTool::RasterImage)) {
if (m_param->m_rasterToolSize.getValue() % 2 != 0)
pos = TPointD((int)pos.x, (int)pos.y);
else
pos = TPointD((int)pos.x + 0.5, (int)pos.y + 0.5);
}
m_selectingRect.x1 = pos.x;
m_selectingRect.y1 = pos.y;
if (!e.isAltPressed()) {
m_selectingRect.x0 = m_startPoint.x;
m_selectingRect.y0 = m_startPoint.y;
} else {
m_selectingRect.x0 = m_startPoint.x + m_startPoint.x - pos.x;
m_selectingRect.y0 = m_startPoint.y + m_startPoint.y - pos.y;
}
}
//-----------------------------------------------------------------------------
TStroke *RectanglePrimitive::makeStroke() const {
if (areAlmostEqual(m_selectingRect.x0, m_selectingRect.x1) ||
areAlmostEqual(m_selectingRect.y0, m_selectingRect.y1))
return 0;
TRectD selArea;
selArea.x0 = std::min(m_selectingRect.x0, m_selectingRect.x1);
selArea.y0 = std::min(m_selectingRect.y0, m_selectingRect.y1);
selArea.x1 = std::max(m_selectingRect.x0, m_selectingRect.x1);
selArea.y1 = std::max(m_selectingRect.y0, m_selectingRect.y1);
double thick = getThickness();
TStroke *stroke = 0;
if (m_param->m_targetType & TTool::Vectors) {
std::vector<TThickPoint> points(17);
points[0] = TThickPoint(selArea.x1, selArea.y1, thick);
points[1] = TThickPoint(selArea.x1, selArea.y1, thick) + TPointD(-0.01, 0);
points[3] = TThickPoint(selArea.x0, selArea.y1, thick) + TPointD(0.01, 0);
points[4] = TThickPoint(selArea.x0, selArea.y1, thick);
points[5] = TThickPoint(selArea.x0, selArea.y1, thick) + TPointD(0, -0.01);
points[7] = TThickPoint(selArea.x0, selArea.y0, thick) + TPointD(0, 0.01);
points[8] = TThickPoint(selArea.x0, selArea.y0, thick);
points[9] = TThickPoint(selArea.x0, selArea.y0, thick) + TPointD(0.01, 0);
points[11] = TThickPoint(selArea.x1, selArea.y0, thick) + TPointD(-0.01, 0);
points[12] = TThickPoint(selArea.x1, selArea.y0, thick);
points[13] = TThickPoint(selArea.x1, selArea.y0, thick) + TPointD(0, 0.01);
points[15] = points[0] + TPointD(0, -0.01);
points[16] = points[0];
points[2] = 0.5 * (points[1] + points[3]);
points[6] = 0.5 * (points[5] + points[7]);
points[10] = 0.5 * (points[9] + points[11]);
points[14] = 0.5 * (points[13] + points[15]);
stroke = new TStroke(points);
} else if (m_param->m_targetType & TTool::ToonzImage ||
m_param->m_targetType & TTool::RasterImage) {
std::vector<TThickPoint> points(9);
double middleX = (selArea.x0 + selArea.x1) * 0.5;
double middleY = (selArea.y0 + selArea.y1) * 0.5;
points[0] = TThickPoint(selArea.x1, selArea.y1, thick);
points[1] = TThickPoint(middleX, selArea.y1, thick);
points[2] = TThickPoint(selArea.x0, selArea.y1, thick);
points[3] = TThickPoint(selArea.x0, middleY, thick);
points[4] = TThickPoint(selArea.x0, selArea.y0, thick);
points[5] = TThickPoint(middleX, selArea.y0, thick);
points[6] = TThickPoint(selArea.x1, selArea.y0, thick);
points[7] = TThickPoint(selArea.x1, middleY, thick);
points[8] = points[0];
stroke = new TStroke(points);
}
stroke->setSelfLoop();
return stroke;
}
//-----------------------------------------------------------------------------
void RectanglePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
if (!m_isEditing) return;
m_isEditing = false;
m_tool->addStroke();
}
//-----------------------------------------------------------------------------
void RectanglePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) {
m_pos = pos;
}
//-----------------------------------------------------------------------------
void RectanglePrimitive::onEnter() {
TTool::Application *app = TTool::getApplication();
if (!app) return;
if (app->getCurrentObject()->isSpline())
m_color = TPixel32::Red;
else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
}
}
//=============================================================================
// Circle Primitive Class Implementation
//-----------------------------------------------------------------------------
void CirclePrimitive::draw() {
if (m_isEditing || m_isPrompting) {
tglColor(m_isEditing ? m_color : TPixel32::Green);
tglDrawCircle(m_centre, m_radius);
}
}
//-----------------------------------------------------------------------------
void CirclePrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
m_pos = pos;
m_centre = pos;
TTool::Application *app = TTool::getApplication();
if (!app) return;
if (app->getCurrentObject()->isSpline()) {
m_color = TPixel32::Red;
m_isEditing = true;
} else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) {
m_isEditing = style->isStrokeStyle();
m_color = style->getAverageColor();
} else {
m_isEditing = false;
m_color = TPixel32::Black;
}
}
if (!m_isEditing) return;
}
//-----------------------------------------------------------------------------
void CirclePrimitive::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
if (!m_isEditing) return;
m_pos = pos;
m_radius = tdistance(m_centre, pos);
}
//-----------------------------------------------------------------------------
TStroke *CirclePrimitive::makeStroke() const {
return makeEllipticStroke(getThickness(), m_centre, m_radius, m_radius);
}
//-----------------------------------------------------------------------------
void CirclePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
if (!m_isEditing) return;
m_isEditing = false;
if (isAlmostZero(m_radius)) return;
m_tool->addStroke();
m_radius = 0;
}
//-----------------------------------------------------------------------------
void CirclePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) {
m_pos = pos;
}
//-----------------------------------------------------------------------------
void CirclePrimitive::onEnter() {
TTool::Application *app = TTool::getApplication();
if (!app) return;
if (app->getCurrentObject()->isSpline())
m_color = TPixel32::Red;
else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
}
}
//=============================================================================
// MultiLine Primitive Class Implementation
//-----------------------------------------------------------------------------
void MultiLinePrimitive::addVertex(const TPointD &pos) {
int count = m_vertex.size();
// Inserisco il primo punto
if (count == 0) {
m_vertex.push_back(pos);
return;
}
TPointD &vertex = m_vertex[count - 1];
// Caso particolare in cui inizio una curva e la chiudo subito cliccando sul
// punto di pertenza
if (count == 1 && pos == vertex) {
m_vertex.push_back(pos);
m_vertex.push_back(pos);
m_vertex.push_back(pos);
return;
}
// Calcolo lo speedOut
TPointD speedOutPoint;
if (!m_speedMoved) // Se non e' stato mosso lo speedOut devo calcolarlo e
// inserirlo
{
speedOutPoint = vertex + computeSpeed(vertex, pos, 0.01);
m_vertex.push_back(speedOutPoint);
} else {
if (m_ctrlDown)
vertex =
m_vertex[count - 2] + computeSpeed(m_vertex[count - 2], pos, 0.01);
speedOutPoint = vertex;
}
// Calcolo lo speedIn
TPointD speedInPoint = pos + computeSpeed(pos, speedOutPoint, 0.01);
// Calcolo il "punto di mezzo" e lo inserisco
TPointD middlePoint = 0.5 * (speedInPoint + speedOutPoint);
// Inserisco il "punto di mezzo"
m_vertex.push_back(middlePoint);
// Inserisco lo speedIn
m_vertex.push_back(speedInPoint);
// Inserisco il nuovo punto
m_vertex.push_back(pos);
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::moveSpeed(const TPointD &delta) {
int count = m_vertex.size();
assert(count > 0);
TPointD lastPoint = m_vertex[count - 1];
TPointD newSpeedOutPoint = lastPoint - delta;
if (m_speedMoved)
m_vertex[count - 1] = newSpeedOutPoint;
else {
m_vertex.push_back(newSpeedOutPoint);
++count;
}
if (count < 5) {
assert(count == 2);
return;
}
TPointD vertex = m_vertex[count - 2];
TPointD v(0, 0);
if (newSpeedOutPoint != vertex) v = normalize(newSpeedOutPoint - vertex);
double speedOut = tdistance(newSpeedOutPoint, vertex);
TPointD newSpeedInPoint = vertex - TPointD(speedOut * v.x, speedOut * v.y);
m_vertex[count - 3] = newSpeedInPoint;
if (tdistance(m_vertex[count - 5], m_vertex[count - 6]) <= 0.02)
// see ControlPointEditorStroke::isSpeedOutLinear() from
// controlpointselection.cpp
m_vertex[count - 5] =
m_vertex[count - 6] +
computeSpeed(m_vertex[count - 6], m_vertex[count - 3], 0.01);
m_vertex[count - 4] = 0.5 * (m_vertex[count - 3] + m_vertex[count - 5]);
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::draw() {
UINT size = m_vertex.size();
if ((m_isEditing || m_isPrompting) && size > 0) {
tglColor(m_isEditing ? m_color : TPixel32::Green);
std::vector<TPointD> points;
points.assign(m_vertex.begin(), m_vertex.end());
int count = points.size();
if (count % 4 == 1) {
// No speedOut
points.push_back(points[count - 1]);
count++;
} else if (m_ctrlDown)
points[count - 1] = points[count - 2];
points.push_back(0.5 * (m_mousePosition + points[count - 1]));
points.push_back(m_mousePosition);
points.push_back(m_mousePosition);
double pixelSize = m_tool->getPixelSize();
TStroke *stroke = new TStroke(points);
drawStrokeCenterline(*stroke, pixelSize);
delete stroke;
if (m_vertex.size() > 1) {
tglColor(TPixel(79, 128, 255));
int index = (count < 5) ? count - 1 : count - 5;
// Disegno lo speedOut precedente (che e' quello corrente solo nel caso in
// cui count < 5)
TPointD p0 = m_vertex[index];
TPointD p1 = m_vertex[index - 1];
if (tdistance(p0, p1) > 0.1) {
tglDrawSegment(p0, p1);
tglDrawDisk(p0, 2 * pixelSize);
tglDrawDisk(p1, 4 * pixelSize);
}
// Disegno lo speedIn/Out corrente nel caso in cui count > 5
if (m_speedMoved && count > 5) {
TPointD p0 = m_vertex[count - 1];
TPointD p1 = m_vertex[count - 2];
TPointD p2 = m_vertex[count - 3];
tglDrawSegment(p0, p2);
tglDrawDisk(p0, 2 * pixelSize);
tglDrawDisk(p1, 4 * pixelSize);
tglDrawDisk(p2, 2 * pixelSize);
}
}
if (m_closed)
tglColor(TPixel32((m_color.r + 127) % 255, m_color.g,
(m_color.b + 127) % 255, m_color.m));
else
tglColor(m_color);
tglDrawCircle(m_vertex[0], joinDistance * pixelSize);
}
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::leftButtonDown(const TPointD &pos,
const TMouseEvent &e) {
TTool::Application *app = TTool::getApplication();
if (!app) return;
if (app->getCurrentObject()->isSpline()) {
m_color = TPixel32::Red;
m_isEditing = true;
} else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) {
m_isEditing = style->isStrokeStyle();
m_color = style->getAverageColor();
} else {
m_isEditing = false;
m_color = TPixel32::Black;
}
}
if (!m_isEditing) return;
m_undo = new MultilinePrimitiveUndo(m_vertex, this);
TUndoManager::manager()->add(m_undo);
m_mousePosition = pos;
// Se clicco nell'ultimo vertice chiudo la linea.
TPointD _pos = pos;
if (m_closed) _pos = m_vertex.front();
if (e.isShiftPressed() && !m_vertex.empty())
addVertex(rectify(m_vertex.back(), _pos));
else
addVertex(_pos);
m_undo->setNewVertex(m_vertex);
m_beforeSpeedMoved = m_speedMoved;
m_speedMoved = false;
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::leftButtonDrag(const TPointD &pos,
const TMouseEvent &e) {
if (m_vertex.size() == 0 || m_isSingleLine) return;
if (m_speedMoved ||
tdistance2(m_vertex[m_vertex.size() - 1], pos) >
sq(7.0 * m_tool->getPixelSize())) {
moveSpeed(m_mousePosition - pos);
m_speedMoved = true;
m_undo->setNewVertex(m_vertex);
m_mousePosition = pos;
}
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::leftButtonDoubleClick(const TPointD &,
const TMouseEvent &e) {
endLine();
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
if (m_closed) endLine();
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) {
m_ctrlDown = e.isCtrlPressed();
if (m_isEditing) {
if (e.isShiftPressed() && !m_vertex.empty())
m_mousePosition = rectify(m_vertex.back(), pos);
else
m_mousePosition = pos;
double dist = joinDistance * joinDistance;
if (!m_vertex.empty() && (tdistance2(m_mousePosition, m_vertex.front()) <
dist * m_tool->getPixelSize())) {
m_closed = true;
m_mousePosition = m_vertex.front();
} else
m_closed = false;
} else
m_mousePosition = pos;
m_tool->invalidate();
}
//-----------------------------------------------------------------------------
bool MultiLinePrimitive::keyDown(int key, const TPoint &point) {
if (key != TwConsts::TK_Esc || !m_isEditing) return false;
UINT size = m_vertex.size();
if (size <= 1) return 0;
if (!m_isSingleLine) TUndoManager::manager()->popUndo((size - 1) / 4 + 1);
m_isEditing = false;
m_speedMoved = false;
m_beforeSpeedMoved = false;
m_closed = false;
m_vertex.clear();
return true;
}
//-----------------------------------------------------------------------------
TStroke *MultiLinePrimitive::makeStroke() const {
double thick = getThickness();
/*---
* Pencilの場合は、線幅を減らす。Thickness1の線を1ピクセルにするため。thick
* = 0 になる)---*/
if (m_param->m_pencil.getValue()) thick -= 1.0;
UINT size = m_vertex.size();
if (size <= 1) return 0;
if (!m_isSingleLine) TUndoManager::manager()->popUndo((size - 1) / 4 + 1);
TStroke *stroke = 0;
std::vector<TThickPoint> points;
int i;
for (i = 0; i < (int)size; i++) {
TPointD vertex = m_vertex[i];
points.push_back(TThickPoint(vertex, thick));
}
stroke = new TStroke(points);
if (m_closed) stroke->setSelfLoop();
return stroke;
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::endLine() {
if (!m_isEditing) return;
m_isEditing = false;
m_speedMoved = false;
m_beforeSpeedMoved = false;
if (!m_isSingleLine && !m_vertex.empty() &&
m_vertex.size() % 4 != 1 /* && !m_rasterTool*/) {
m_vertex.erase(--m_vertex.end());
assert(m_vertex.size() == 3 || m_vertex.size() % 4 == 1);
}
m_tool->addStroke();
if (m_closed) m_closed = false;
m_vertex.clear();
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::onActivate() {
m_isEditing = false;
m_closed = false;
m_vertex.clear();
m_speedMoved = false;
m_beforeSpeedMoved = false;
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::onEnter() {
TTool::Application *app = TTool::getApplication();
if (!app) return;
if (app->getCurrentObject()->isSpline())
m_color = TPixel32::Red;
else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
}
}
//-----------------------------------------------------------------------------
void MultiLinePrimitive::onImageChanged() { onActivate(); }
//=============================================================================
// Line Primitive Class Implementation
//-----------------------------------------------------------------------------
void LinePrimitive::draw() {
UINT size = m_vertex.size();
tglColor(TPixel32::Red);
if (m_isEditing || m_isPrompting) {
glBegin(GL_LINE_STRIP);
tglVertex(m_vertex[0]);
tglVertex(m_mousePosition);
glEnd();
}
}
//-----------------------------------------------------------------------------
void LinePrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
TTool::Application *app = TTool::getApplication();
if (!app) return;
if (app->getCurrentObject()->isSpline()) {
m_color = TPixel32::Red;
m_isEditing = true;
} else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) {
m_isEditing = style->isStrokeStyle();
m_color = style->getAverageColor();
} else {
m_isEditing = false;
m_color = TPixel32::Black;
}
}
if (!m_isEditing) return;
m_mousePosition = pos;
TPointD _pos = pos;
if (m_param->m_pencil.getValue() &&
(m_param->m_targetType & TTool::ToonzImage ||
m_param->m_targetType & TTool::RasterImage)) {
if (m_param->m_rasterToolSize.getValue() % 2 != 0)
_pos = TPointD((int)pos.x, (int)pos.y);
else
_pos = TPointD((int)pos.x + 0.5, (int)pos.y + 0.5);
}
if (m_vertex.size() == 0)
addVertex(_pos);
else {
if (e.isShiftPressed() && !m_vertex.empty())
addVertex(rectify(m_vertex.back(), _pos));
else
addVertex(_pos);
endLine();
}
}
//-----------------------------------------------------------------------------
void LinePrimitive::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
if (!m_isEditing) return;
m_mousePosition = pos;
}
//-----------------------------------------------------------------------------
void LinePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &e) {
m_mousePosition = pos;
if (e.isShiftPressed() && !m_vertex.empty())
m_vertex.push_back(rectify(m_vertex.back(), pos));
else
m_vertex.push_back(pos);
endLine();
}
//-----------------------------------------------------------------------------
//=============================================================================
// Ellipse Primitive Class Implementation
//-----------------------------------------------------------------------------
void EllipsePrimitive::draw() {
if (m_isEditing || m_isPrompting ||
areAlmostEqual(m_selectingRect.x0, m_selectingRect.x1) ||
areAlmostEqual(m_selectingRect.y0, m_selectingRect.y1)) {
tglColor(m_isEditing ? m_color : TPixel32::Green);
TPointD centre = TPointD((m_selectingRect.x0 + m_selectingRect.x1) * 0.5,
(m_selectingRect.y0 + m_selectingRect.y1) * 0.5);
glPushMatrix();
tglMultMatrix(TScale(centre, m_selectingRect.x1 - m_selectingRect.x0,
m_selectingRect.y1 - m_selectingRect.y0));
tglDrawCircle(centre, 0.5);
glPopMatrix();
drawRect(m_selectingRect, m_color, 0x5555, true);
}
}
//-----------------------------------------------------------------------------
void EllipsePrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
TTool::Application *app = TTool::getApplication();
if (!app) return;
m_startPoint = pos;
m_selectingRect.x0 = pos.x;
m_selectingRect.y0 = pos.y;
m_selectingRect.x1 = pos.x;
m_selectingRect.y1 = pos.y;
if (app->getCurrentObject()->isSpline()) {
m_isEditing = true;
m_color = TPixel32::Red;
} else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) {
m_isEditing = style->isStrokeStyle();
m_color = style->getAverageColor();
} else {
m_isEditing = false;
m_color = TPixel32::Black;
}
}
if (!m_isEditing) return;
}
//-----------------------------------------------------------------------------
void EllipsePrimitive::leftButtonDrag(const TPointD &realPos,
const TMouseEvent &e) {
if (!m_isEditing) return;
TPointD pos;
if (e.isShiftPressed()) {
double distance = tdistance(realPos, m_startPoint) * M_SQRT1_2;
pos.x = (realPos.x > m_startPoint.x) ? m_startPoint.x + distance
: m_startPoint.x - distance;
pos.y = (realPos.y > m_startPoint.y) ? m_startPoint.y + distance
: m_startPoint.y - distance;
} else {
pos = realPos;
}
m_pos = pos;
m_selectingRect.x1 = pos.x;
m_selectingRect.y1 = pos.y;
if (!e.isAltPressed()) {
m_selectingRect.x0 = m_startPoint.x;
m_selectingRect.y0 = m_startPoint.y;
} else {
m_selectingRect.x0 = m_startPoint.x + m_startPoint.x - pos.x;
m_selectingRect.y0 = m_startPoint.y + m_startPoint.y - pos.y;
}
}
//-----------------------------------------------------------------------------
TStroke *EllipsePrimitive::makeStroke() const {
if (areAlmostEqual(m_selectingRect.x0, m_selectingRect.x1) ||
areAlmostEqual(m_selectingRect.y0, m_selectingRect.y1))
return 0;
return makeEllipticStroke(
getThickness(), TPointD(0.5 * (m_selectingRect.x0 + m_selectingRect.x1),
0.5 * (m_selectingRect.y0 + m_selectingRect.y1)),
fabs(0.5 * (m_selectingRect.x1 - m_selectingRect.x0)),
fabs(0.5 * (m_selectingRect.y1 - m_selectingRect.y0)));
}
//-----------------------------------------------------------------------------
void EllipsePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
if (!m_isEditing) return;
m_isEditing = false;
m_tool->addStroke();
}
//-----------------------------------------------------------------------------
void EllipsePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) {
m_pos = pos;
}
//-----------------------------------------------------------------------------
void EllipsePrimitive::onEnter() {
TTool::Application *app = TTool::getApplication();
if (!app) return;
if (app->getCurrentObject()->isSpline()) {
m_color = TPixel32::Red;
} else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
}
}
//=============================================================================
// Arc Primitive Class Implementation
//-----------------------------------------------------------------------------
void ArcPrimitive::draw() {
switch (m_clickNumber) {
case 1:
tglColor(m_color);
tglDrawSegment(m_startPoint, m_endPoint);
break;
case 2:
assert(m_stroke);
if (m_stroke) {
tglColor(m_isPrompting ? TPixel32::Green : m_color);
if (!m_isPrompting) {
glLineStipple(1, 0x5555);
glEnable(GL_LINE_STIPPLE);
glBegin(GL_LINE_STRIP);
tglVertex(m_stroke->getControlPoint(0));
tglVertex(m_centralPoint);
tglVertex(m_stroke->getControlPoint(8));
glEnd();
glDisable(GL_LINE_STIPPLE);
}
drawStrokeCenterline(*m_stroke, sqrt(tglGetPixelSize2()));
}
break;
};
}
//-----------------------------------------------------------------------------
TStroke *ArcPrimitive::makeStroke() const { return new TStroke(*m_stroke); }
//-----------------------------------------------------------------------------
void ArcPrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
TTool::Application *app = TTool::getApplication();
if (!app) return;
std::vector<TThickPoint> points(9);
double thick = getThickness();
switch (m_clickNumber) {
case 0:
if (app->getCurrentObject()->isSpline()) {
m_isEditing = true;
m_color = TPixel32::Red;
} else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) {
m_isEditing = style->isStrokeStyle();
m_color = style->getAverageColor();
} else {
m_isEditing = false;
m_color = TPixel32::Black;
}
}
if (!m_isEditing) return;
m_endPoint = m_startPoint = pos;
m_clickNumber++;
break;
case 1:
points[0] = TThickPoint(m_startPoint, thick);
points[8] = TThickPoint(pos, thick);
points[4] = TThickPoint(0.5 * (points[0] + points[8]), thick);
points[2] = TThickPoint(0.5 * (points[0] + points[4]), thick);
points[6] = TThickPoint(0.5 * (points[4] + points[8]), thick);
points[1] = TThickPoint(0.5 * (points[0] + points[2]), thick);
points[3] = TThickPoint(0.5 * (points[2] + points[4]), thick);
points[5] = TThickPoint(0.5 * (points[4] + points[6]), thick);
points[7] = TThickPoint(0.5 * (points[6] + points[8]), thick);
if (m_stroke) delete m_stroke;
m_stroke = new TStroke(points);
m_clickNumber++;
break;
case 2:
m_tool->addStroke();
m_stroke = 0;
m_clickNumber = 0;
break;
}
}
//-----------------------------------------------------------------------------
void ArcPrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) {
switch (m_clickNumber) {
case 1:
if (e.isShiftPressed())
m_endPoint = rectify(m_startPoint, pos);
else
m_endPoint = pos;
m_tool->invalidate();
break;
case 2:
m_centralPoint = TThickPoint(pos, getThickness());
TThickQuadratic q(m_stroke->getControlPoint(0), m_centralPoint,
m_stroke->getControlPoint(8));
TThickQuadratic q0, q1, q00, q01, q10, q11;
q.split(0.5, q0, q1);
q0.split(0.5, q00, q01);
q1.split(0.5, q10, q11);
assert(q00.getP2() == q01.getP0());
assert(q01.getP2() == q10.getP0());
assert(q10.getP2() == q11.getP0());
m_stroke->setControlPoint(0, q00.getP0());
m_stroke->setControlPoint(1, q00.getP1());
m_stroke->setControlPoint(2, q00.getP2());
m_stroke->setControlPoint(3, q01.getP1());
m_stroke->setControlPoint(4, q01.getP2());
m_stroke->setControlPoint(5, q10.getP1());
m_stroke->setControlPoint(6, q10.getP2());
m_stroke->setControlPoint(7, q11.getP1());
m_stroke->setControlPoint(8, q11.getP2());
m_tool->invalidate();
break;
}
}
//-----------------------------------------------------------------------------
void ArcPrimitive::onEnter() {
TTool::Application *app = TTool::getApplication();
if (!app) return;
if (app->getCurrentObject()->isSpline())
m_color = TPixel32::Red;
else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
}
}
//=============================================================================
// Polygon Primitive Class Declaration
//-----------------------------------------------------------------------------
void PolygonPrimitive::draw() {
if (!m_isEditing && !m_isPrompting) return;
tglColor(m_isEditing ? m_color : TPixel32::Green);
int edgeCount = m_param->m_edgeCount.getValue();
if (edgeCount == 0) return;
double angleDiff = M_2PI / edgeCount;
double angle = (3 * M_PI + angleDiff) * 0.5;
glBegin(GL_LINE_LOOP);
for (int i = 0; i < edgeCount; i++) {
tglVertex(m_centre + TPointD(cos(angle) * m_radius, sin(angle) * m_radius));
angle += angleDiff;
}
glEnd();
}
//-----------------------------------------------------------------------------
void PolygonPrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
TTool::Application *app = TTool::getApplication();
if (!app) return;
if (app->getCurrentObject()->isSpline()) {
m_isEditing = true;
m_color = TPixel32::Red;
} else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) {
m_isEditing = style->isStrokeStyle();
m_color = style->getAverageColor();
} else {
m_isEditing = false;
m_color = TPixel32::Black;
}
}
if (!m_isEditing) return;
m_centre = pos;
m_radius = 0;
}
//-----------------------------------------------------------------------------
void PolygonPrimitive::leftButtonDrag(const TPointD &pos,
const TMouseEvent &e) {
if (!m_isEditing) return;
m_radius = tdistance(m_centre, pos);
}
//-----------------------------------------------------------------------------
TStroke *PolygonPrimitive::makeStroke() const {
double thick = getThickness();
int edgeCount = m_param->m_edgeCount.getValue();
if (edgeCount == 0) return 0;
double angleDiff = M_2PI / (double)edgeCount;
double angle = (3 * M_PI + angleDiff) * 0.5;
TStroke *stroke = 0;
if (m_param->m_targetType & TTool::Vectors) {
std::vector<TThickPoint> points(4 * edgeCount + 1);
int i;
// Posiziono gli angoli
for (i = 0; i <= (int)points.size(); i += 4) {
points[i] = TThickPoint(
m_centre + TPointD(cos(angle) * m_radius, sin(angle) * m_radius),
thick);
angle += angleDiff;
}
// posiziono i punti medi e i punti per gestire la linearita'
for (i = 0; i < (int)points.size() - 1; i += 4) {
TPointD vertex = convert(points[i]);
TPointD nextVertex = convert(points[i + 4]);
TPointD speed = computeSpeed(vertex, nextVertex, 0.01);
TPointD speedOutPoint = vertex + speed;
TPointD speedInNextPoint = nextVertex - speed;
TPointD middlePoint = 0.5 * (speedOutPoint + speedInNextPoint);
points[i + 1] = TThickPoint(speedOutPoint, thick);
points[i + 2] = TThickPoint(middlePoint, thick);
points[i + 3] = TThickPoint(speedInNextPoint, thick);
}
stroke = new TStroke(points);
} else if (m_param->m_targetType & TTool::ToonzImage ||
m_param->m_targetType & TTool::RasterImage) {
std::vector<TThickPoint> points(edgeCount + edgeCount + 1);
points[0] = TThickPoint(
m_centre + TPointD(cos(angle) * m_radius, sin(angle) * m_radius),
thick);
for (int i = 1; i <= edgeCount; i++) {
angle += angleDiff;
points[i + i] = TThickPoint(
m_centre + TPointD(cos(angle) * m_radius, sin(angle) * m_radius),
thick);
points[i + i - 1] = (points[i + i - 2] + points[i + i]) * 0.5;
}
stroke = new TStroke(points);
}
stroke->setSelfLoop();
return stroke;
}
//-----------------------------------------------------------------------------
void PolygonPrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
if (!m_isEditing) return;
m_isEditing = false;
m_tool->addStroke();
}