#include "toonz/tpalettehandle.h" #include "tools/toolhandle.h" #include "tools/toolcommandids.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 "historytypes.h" #include "toonzvectorbrushtool.h" // For Qt translation support #include using namespace ToolUtils; class GeometricTool; class MultiLinePrimitive; class MultiArcPrimitive; 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 GeometricSmooth("InknpaintGeometricSmooth", 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); TEnv::IntVar GeometricSnap("InknpaintGeometricSnap", 0); TEnv::IntVar GeometricSnapSensitivity("InknpaintGeometricSnapSensitivity", 0); TEnv::IntVar GeometricDrawBehind("InknpaintGeometricDrawBehind", 0); //------------------------------------------------------------------- #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" #define LOW_WSTR L"Low" #define MEDIUM_WSTR L"Medium" #define HIGH_WSTR L"High" const double SNAPPING_LOW = 5.0; const double SNAPPING_MEDIUM = 25.0; const double SNAPPING_HIGH = 100.0; //============================================================================= // Utility Functions //----------------------------------------------------------------------------- static 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]; } //----------------------------------------------------------------------------- static TPointD computeSpeed(TPointD p0, TPointD p1, double factor) { TPointD d = p1 - p0; return (d == TPointD()) ? TPointD() : d * (factor / norm(d)); } //----------------------------------------------------------------------------- static 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 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; } //----------------------------------------------------------------------------- static 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 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, false); } delete s; return rectRender; } //----------------------------------------------------------------------------- class MultilinePrimitiveUndo final : public TUndo { std::vector m_oldVertex; std::vector m_newVertex; MultiLinePrimitive *m_tool; public: MultilinePrimitiveUndo(const std::vector &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 &vertex) { m_newVertex = vertex; } int getSize() const override { return sizeof(this); } QString getToolName(); int getHistoryType() override { return HistoryType::GeometricTool; } }; //----------------------------------------------------------------------------- class MultiArcPrimitiveUndoData { private: TStroke *m_stroke; TStroke *m_strokeTemp; TPointD m_startPoint, m_endPoint, m_centralPoint; int m_clickNumber; public: MultiArcPrimitiveUndoData(TStroke *stroke, TStroke *strokeTemp, TPointD startPoint, TPointD endPoint, TPointD centralPoint, int clickNumber) : m_stroke(0) , m_strokeTemp(0) , m_startPoint(startPoint) , m_endPoint(endPoint) , m_centralPoint(centralPoint) , m_clickNumber(clickNumber) { if (stroke) { m_stroke = new TStroke(*stroke); } if (strokeTemp) { m_strokeTemp = new TStroke(*strokeTemp); } } ~MultiArcPrimitiveUndoData() { delete m_stroke; delete m_strokeTemp; } void replace(MultiArcPrimitive *tool) const; }; //----------------------------------------------------------------------------- class MultiArcPrimitiveUndo final : public TUndo { MultiArcPrimitive *m_tool; MultiArcPrimitiveUndoData m_undo; MultiArcPrimitiveUndoData *m_redo; public: MultiArcPrimitiveUndo(MultiArcPrimitive *tool, TStroke *stroke, TStroke *strokeTemp, TPointD startPoint, TPointD endPoint, TPointD centralPoint, int clickNumber) : TUndo() , m_tool(tool) , m_undo(stroke, strokeTemp, startPoint, endPoint, centralPoint, clickNumber) , m_redo(0) {} ~MultiArcPrimitiveUndo() { delete m_redo; } void setRedoData(TStroke *stroke, TStroke *strokeTemp, TPointD startPoint, TPointD endPoint, TPointD centralPoint, int clickNumber) { m_redo = new MultiArcPrimitiveUndoData(stroke, strokeTemp, startPoint, endPoint, centralPoint, clickNumber); } void undo() const override; void redo() const override; 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_smooth; TBoolProperty m_selective; TBoolProperty m_pencil; TEnumProperty m_capStyle; TEnumProperty m_joinStyle; TIntProperty m_miterJoinLimit; TBoolProperty m_snap; TEnumProperty m_snapSensitivity; TBoolProperty m_sendToBack; TPropertyGroup m_prop[2]; int m_targetType; // for snapping int m_strokeIndex1; double m_w1, m_pixelSize, m_currThickness, m_minDistance2; bool m_foundSnap = false; TPointD m_snapPoint; 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_smooth("Smooth", false) , m_selective("Selective", false) , m_pencil("Pencil Mode", false) , m_capStyle("Cap") , m_joinStyle("Join") , m_miterJoinLimit("Miter:", 0, 100, 4) , m_snap("Snap", false) , m_snapSensitivity("Sensitivity:") , m_sendToBack("Draw Under", 0) , 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); m_prop[0].bind(m_sendToBack); m_sendToBack.setId("DrawUnder"); m_prop[0].bind(m_snap); m_snap.setId("Snap"); m_prop[0].bind(m_snapSensitivity); m_snapSensitivity.addValue(LOW_WSTR); m_snapSensitivity.addValue(MEDIUM_WSTR); m_snapSensitivity.addValue(HIGH_WSTR); m_snapSensitivity.setId("SnapSensitivity"); } if (targetType & TTool::ToonzImage) { m_prop[0].bind(m_selective); m_prop[0].bind(m_pencil); m_pencil.setId("PencilMode"); } m_prop[0].bind(m_smooth); m_capStyle.addValue(BUTT_WSTR, QString::fromStdWString(BUTT_WSTR)); m_capStyle.addValue(ROUNDC_WSTR, QString::fromStdWString(ROUNDC_WSTR)); m_capStyle.addValue(PROJECTING_WSTR, QString::fromStdWString(PROJECTING_WSTR)); m_capStyle.setId("Cap"); m_joinStyle.addValue(MITER_WSTR, QString::fromStdWString(MITER_WSTR)); m_joinStyle.addValue(ROUNDJ_WSTR, QString::fromStdWString(ROUNDJ_WSTR)); m_joinStyle.addValue(BEVEL_WSTR, QString::fromStdWString(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_smooth.setId("Smooth"); m_type.setId("GeometricShape"); m_edgeCount.setId("GeometricEdge"); } void updateTranslation() { m_type.setQStringName(tr("Shape:")); m_type.setItemUIName(L"Rectangle", tr("Rectangle")); m_type.setItemUIName(L"Circle", tr("Circle")); m_type.setItemUIName(L"Ellipse", tr("Ellipse")); m_type.setItemUIName(L"Line", tr("Line")); m_type.setItemUIName(L"Polyline", tr("Polyline")); m_type.setItemUIName(L"Arc", tr("Arc")); m_type.setItemUIName(L"MultiArc", tr("MultiArc")); m_type.setItemUIName(L"Polygon", tr("Polygon")); 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_smooth.setQStringName(tr("Smooth")); m_selective.setQStringName(tr("Selective")); m_pencil.setQStringName(tr("Pencil Mode")); m_capStyle.setQStringName(tr("Cap")); m_capStyle.setItemUIName(BUTT_WSTR, tr("Butt cap")); m_capStyle.setItemUIName(ROUNDC_WSTR, tr("Round cap")); m_capStyle.setItemUIName(PROJECTING_WSTR, tr("Projecting cap")); m_joinStyle.setQStringName(tr("Join")); m_joinStyle.setItemUIName(MITER_WSTR, tr("Miter join")); m_joinStyle.setItemUIName(ROUNDJ_WSTR, tr("Round join")); m_joinStyle.setItemUIName(BEVEL_WSTR, tr("Bevel join")); m_miterJoinLimit.setQStringName(tr("Miter:")); m_snap.setQStringName(tr("Snap")); m_snapSensitivity.setQStringName(tr("")); if (m_targetType & TTool::Vectors) { m_snapSensitivity.setItemUIName(LOW_WSTR, tr("Low")); m_snapSensitivity.setItemUIName(MEDIUM_WSTR, tr("Med")); m_snapSensitivity.setItemUIName(HIGH_WSTR, tr("High")); m_sendToBack.setQStringName(tr("Draw Under")); } } }; //============================================================================= // Abstract Class Primitive //----------------------------------------------------------------------------- class Primitive { protected: bool m_isEditing, m_rasterTool, m_isPrompting, m_doSnap; 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(QKeyEvent *event) { return false; } virtual void onEnter(){}; virtual void draw(){}; virtual void onActivate(){}; virtual void onDeactivate(){}; virtual void onImageChanged(){}; TPointD calculateSnap(TPointD pos, const TMouseEvent &e); void drawSnap(); TPointD getSnap(TPointD pos); void resetSnap(); TPointD checkGuideSnapping(TPointD pos, const TMouseEvent &e); bool getSmooth() { return m_param->m_smooth.getValue(); } virtual TStroke *makeStroke() const = 0; virtual bool canTouchImageOnPreLeftClick() { return true; } }; //----------------------------------------------------------------------------- void Primitive::resetSnap() { m_param->m_strokeIndex1 = -1; m_param->m_w1 = -1; m_param->m_foundSnap = false; } //----------------------------------------------------------------------------- TPointD Primitive::calculateSnap(TPointD pos, const TMouseEvent &e) { m_param->m_foundSnap = false; if (Preferences::instance()->getVectorSnappingTarget() == 1) return pos; TVectorImageP vi(TTool::getImage(false)); TPointD snapPoint = pos; m_doSnap = (m_param->m_snap.getValue() && !(e.isCtrlPressed() && e.isShiftPressed())) || (!m_param->m_snap.getValue() && (e.isCtrlPressed() && e.isShiftPressed())); if (vi && m_doSnap) { double minDistance2 = m_param->m_minDistance2; m_param->m_strokeIndex1 = -1; int i, strokeNumber = vi->getStrokeCount(); TStroke *stroke; double distance2, outW; for (i = 0; i < strokeNumber; i++) { stroke = vi->getStroke(i); if (stroke->getNearestW(pos, outW, distance2) && distance2 < minDistance2) { minDistance2 = distance2; m_param->m_strokeIndex1 = i; if (areAlmostEqual(outW, 0.0, 1e-3)) m_param->m_w1 = 0.0; else if (areAlmostEqual(outW, 1.0, 1e-3)) m_param->m_w1 = 1.0; else m_param->m_w1 = outW; TThickPoint point1 = stroke->getPoint(m_param->m_w1); snapPoint = TPointD(point1.x, point1.y); m_param->m_foundSnap = true; m_param->m_snapPoint = snapPoint; } } } return snapPoint; } //----------------------------------------------------------------------------- TPointD Primitive::getSnap(TPointD pos) { if (m_doSnap && m_param->m_foundSnap) return m_param->m_snapPoint; else return pos; } // Primitive::drawSnap and Primitive::checkGuideSnapping are below the // Geometric Tool definition since they use the m_tool property of Primitive // but it isn't defined yet up here. //============================================================================= // 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 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(QKeyEvent *event) 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 &vertex) { m_vertex = vertex; }; void setSpeedMoved(bool speedMoved) { m_speedMoved = speedMoved; }; // Only execute touchImage when clicking the first point of the polyline bool canTouchImageOnPreLeftClick() override { return m_vertex.empty(); } }; //----------------------------------------------------------------------------- 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 mouseMove(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; }; //============================================================================= // Multi Arc Primitive Class Declaration //----------------------------------------------------------------------------- class MultiArcPrimitive : public Primitive { TStroke *m_stroke; TStroke *m_strokeTemp; TPointD m_startPoint, m_endPoint, m_centralPoint; int m_clickNumber; TPixel32 m_color; int m_undoCount; protected: bool m_isSingleArc; public: MultiArcPrimitive(PrimitiveParam *param, GeometricTool *tool, bool reasterTool) : Primitive(param, tool, reasterTool) , m_stroke(0) , m_strokeTemp(0) , m_clickNumber(0) , m_isSingleArc(false) , m_undoCount(0) {} ~MultiArcPrimitive() { delete m_stroke; } std::string getName() const override { return "MultiArc"; } TStroke *makeStroke() const override; void draw() override; void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override; void leftButtonUp(const TPointD &pos, const TMouseEvent &) override; void mouseMove(const TPointD &pos, const TMouseEvent &e) override; void leftButtonDoubleClick(const TPointD &, const TMouseEvent &e) override; bool keyDown(QKeyEvent *event) override; void onEnter() override; void onDeactivate() override { delete m_stroke; delete m_strokeTemp; m_stroke = 0; m_strokeTemp = 0; m_clickNumber = 0; TUndoManager::manager()->popUndo(m_undoCount); m_undoCount = 0; } void replaceData(TStroke *stroke, TStroke *strokeTemp, TPointD startPoint, TPointD endPoint, TPointD centralPoint, int clickNumber) { delete m_stroke; delete m_strokeTemp; m_stroke = stroke; m_strokeTemp = strokeTemp; m_startPoint = startPoint; m_endPoint = endPoint; m_centralPoint = centralPoint; m_clickNumber = clickNumber; } void decreaseUndo() { --m_undoCount; } void increaseUndo() { ++m_undoCount; } // Only execute touchImage when clicking the first point of the multi arc bool canTouchImageOnPreLeftClick() override { return m_clickNumber == 0; } }; //----------------------------------------------------------------------------- void MultiArcPrimitiveUndoData::replace(MultiArcPrimitive *tool) const { TStroke *stroke = 0; TStroke *strokeTemp = 0; if (m_stroke) { stroke = new TStroke(*m_stroke); } if (m_strokeTemp) { strokeTemp = new TStroke(*m_strokeTemp); } tool->replaceData(stroke, strokeTemp, m_startPoint, m_endPoint, m_centralPoint, m_clickNumber); } //----------------------------------------------------------------------------- void MultiArcPrimitiveUndo::undo() const { m_undo.replace(m_tool); m_tool->decreaseUndo(); TTool::getApplication()->getCurrentTool()->getTool()->invalidate(); } //----------------------------------------------------------------------------- void MultiArcPrimitiveUndo::redo() const { m_redo->replace(m_tool); m_tool->increaseUndo(); TTool::getApplication()->getCurrentTool()->getTool()->invalidate(); } //---------------------------------------------------------------------------- QString MultiArcPrimitiveUndo::getToolName() { return QString("Geometric Tool %1") .arg(QString::fromStdString(m_tool->getName())); } //============================================================================= // Arc Primitive Class Declaration //----------------------------------------------------------------------------- class ArcPrimitive final : public MultiArcPrimitive { public: ArcPrimitive(PrimitiveParam *param, GeometricTool *tool, bool reasterTool) : MultiArcPrimitive(param, tool, reasterTool) { m_isSingleArc = true; } std::string getName() const override { return "Arc"; } // _ToolOptions_ShapeArc";} }; //============================================================================= // 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; void mouseMove(const TPointD &pos, const TMouseEvent &e) override; }; //============================================================================= // Geometric Tool //----------------------------------------------------------------------------- class GeometricTool final : public TTool { protected: Primitive *m_primitive; std::map 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 MultiArcPrimitive(&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 MultiArcPrimitive(&m_param, this, false)); addPrimitive(new PolygonPrimitive(&m_param, this, false)); } } ~GeometricTool() { std::map::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::iterator it = m_primitiveTable.find(name); if (it != m_primitiveTable.end()) { if (m_primitive) m_primitive->onDeactivate(); m_primitive = it->second; } } bool preLeftButtonDown() override { if (getViewer() && getViewer()->getGuidedStrokePickerMode()) return false; if (getApplication()->getCurrentObject()->isSpline()) return true; // in the halfway through the drawing of Polyline / MultiArc primitive, OT // should not call touchImage or the m_frameCreated / m_levelCreated flags // will be reset. if (m_primitive && !m_primitive->canTouchImageOnPreLeftClick()) return true; // NEEDS to be done even if(m_active), due // to the HORRIBLE m_frameCreated / m_levelCreated // mechanism. touchImage() is the ONLY function // resetting them to false... >_< m_active = !!touchImage(); return true; } void leftButtonDown(const TPointD &p, const TMouseEvent &e) override { if (getViewer() && getViewer()->getGuidedStrokePickerMode()) { getViewer()->doPickGuideStroke(p); return; } 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(QKeyEvent *event) override { return m_primitive->keyDown(event); } 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_smooth.setValue(GeometricSmooth ? 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; m_param.m_snap.setValue(GeometricSnap); m_param.m_sendToBack.setValue(GeometricDrawBehind); if (m_targetType & TTool::Vectors) { m_param.m_snapSensitivity.setIndex(GeometricSnapSensitivity); switch (GeometricSnapSensitivity) { case 0: m_param.m_minDistance2 = SNAPPING_LOW; break; case 1: m_param.m_minDistance2 = SNAPPING_MEDIUM; break; case 2: m_param.m_minDistance2 = SNAPPING_HIGH; break; } } } m_primitive->resetSnap(); /*-- ショートカットでいきなりスタート(=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 { if (m_viewer && m_viewer->getGuidedStrokePickerMode()) return m_viewer->getGuidedStrokePickerCursor(); 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); TTool::getApplication()->refreshStatusBar(); } } 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_smooth.getName()) { GeometricSmooth = m_param.m_smooth.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(); else if (propertyName == m_param.m_sendToBack.getName()) GeometricDrawBehind = m_param.m_sendToBack.getValue(); else if (propertyName == m_param.m_snap.getName()) GeometricSnap = m_param.m_snap.getValue(); else if (propertyName == m_param.m_snapSensitivity.getName()) { GeometricSnapSensitivity = m_param.m_snapSensitivity.getIndex(); switch (GeometricSnapSensitivity) { case 0: m_param.m_minDistance2 = SNAPPING_LOW; break; case 1: m_param.m_minDistance2 = SNAPPING_MEDIUM; break; case 2: m_param.m_minDistance2 = SNAPPING_HIGH; break; } } 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) { int addedStrokeIndex = -1; bool isSpline = false; if (TTool::getApplication()->getCurrentObject()->isSpline()) { isSpline = true; // 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; //} QMutexLocker lock(vi->getMutex()); TUndo *undo = new UndoPath( getXsheet()->getStageObject(getObjectId())->getSpline()); while (vi->getStrokeCount() > 0) vi->deleteStroke(0); vi->addStroke(stroke, false); TUndoManager::manager()->add(undo); } else { int styleId = TTool::getApplication()->getCurrentLevelStyleIndex(); if (styleId >= 0) stroke->setStyle(styleId); QMutexLocker lock(vi->getMutex()); std::vector *fillInformation = new std::vector; ImageUtils::getFillingInformationOverlappingArea(vi, *fillInformation, stroke->getBBox()); addedStrokeIndex = vi->addStroke(stroke, true, m_param.m_sendToBack.getValue() > 0); TUndoManager::manager()->add(new UndoPencil( stroke, fillInformation, sl, id, m_isFrameCreated, m_isLevelCreated, m_param.m_autogroup.getValue(), m_param.m_autofill.getValue(), m_param.m_sendToBack.getValue() > 0)); if ((Preferences::instance()->getGuidedDrawingType() == 1 || Preferences::instance()->getGuidedDrawingType() == 2) && Preferences::instance()->getGuidedAutoInbetween()) { TTool *tool = TTool::getTool(T_Brush, TTool::ToolTargetType::VectorImage); ToonzVectorBrushTool *vbTool = (ToonzVectorBrushTool *)tool; if (vbTool) { vbTool->setViewer(m_viewer); vbTool->doGuidedAutoInbetween(id, vi, stroke, false, m_param.m_autogroup.getValue(), m_param.m_autofill.getValue(), false, m_param.m_sendToBack.getValue() > 0); } } } if (m_param.m_autogroup.getValue() && stroke->isSelfLoop()) { int index = vi->getStrokeCount() - 1; if (m_param.m_sendToBack.getValue() > 0 && !isSpline) index = addedStrokeIndex; 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); //------------------------------------------------------------------------------------------------------------- void Primitive::drawSnap() { // snapping if ((m_param->m_targetType & TTool::Vectors) && m_doSnap) { m_param->m_pixelSize = m_tool->getPixelSize(); double thick = 6.0 * m_param->m_pixelSize; if (m_param->m_foundSnap) { tglColor(TPixelD(0.1, 0.9, 0.1)); tglDrawCircle(m_param->m_snapPoint, thick); } } } //------------------------------------------------------------------------------------------------------------- TPointD Primitive::checkGuideSnapping(TPointD pos, const TMouseEvent &e) { if (Preferences::instance()->getVectorSnappingTarget() == 0) { if (m_param->m_foundSnap) return m_param->m_snapPoint; else return pos; } m_doSnap = (m_param->m_snap.getValue() && !(e.isCtrlPressed() && e.isShiftPressed())) || (!m_param->m_snap.getValue() && (e.isCtrlPressed() && e.isShiftPressed())); if ((m_param->m_targetType & TTool::Vectors) && m_doSnap) { int vGuideCount = 0, hGuideCount = 0; double guideDistance = sqrt(m_param->m_minDistance2); TTool::Viewer *viewer = m_tool->getViewer(); if (viewer) { vGuideCount = viewer->getVGuideCount(); hGuideCount = viewer->getHGuideCount(); } double distanceToVGuide = -1.0, distanceToHGuide = -1.0; double vGuide, hGuide; bool useGuides = false; if (vGuideCount) { for (int j = 0; j < vGuideCount; j++) { double guide = viewer->getVGuide(j); double tempDistance = std::abs(guide - pos.y); if (tempDistance < guideDistance && (distanceToVGuide < 0 || tempDistance < distanceToVGuide)) { distanceToVGuide = tempDistance; vGuide = guide; useGuides = true; } } } if (hGuideCount) { for (int j = 0; j < hGuideCount; j++) { double guide = viewer->getHGuide(j); double tempDistance = std::abs(guide - pos.x); if (tempDistance < guideDistance && (distanceToHGuide < 0 || tempDistance < distanceToHGuide)) { distanceToHGuide = tempDistance; hGuide = guide; useGuides = true; } } } if (useGuides && m_param->m_foundSnap) { double currYDistance = std::abs(m_param->m_snapPoint.y - pos.y); double currXDistance = std::abs(m_param->m_snapPoint.x - pos.x); double hypotenuse = sqrt(pow(currYDistance, 2.0) + pow(currXDistance, 2.0)); if ((distanceToVGuide >= 0 && distanceToVGuide < hypotenuse) || (distanceToHGuide >= 0 && distanceToHGuide < hypotenuse)) useGuides = true; else useGuides = false; } if (useGuides) { assert(distanceToHGuide >= 0 || distanceToVGuide >= 0); if (distanceToHGuide < 0 || (distanceToVGuide <= distanceToHGuide && distanceToVGuide >= 0)) { m_param->m_snapPoint.y = vGuide; m_param->m_snapPoint.x = pos.x; } else { m_param->m_snapPoint.y = pos.y; m_param->m_snapPoint.x = hGuide; } m_param->m_foundSnap = true; } if (m_param->m_foundSnap) return m_param->m_snapPoint; else return pos; } else return pos; } //============================================================================= // Rectangle Primitive Class Implementation //----------------------------------------------------------------------------- void RectanglePrimitive::draw() { drawSnap(); 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; TPointD newPos = getSnap(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) 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 = newPos; 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() && !e.isCtrlPressed()) { 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 = calculateSnap(realPos, e); pos = checkGuideSnapping(realPos, e); } 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 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 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(); resetSnap(); } //----------------------------------------------------------------------------- void RectanglePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { TPointD newPos = calculateSnap(pos, e); newPos = checkGuideSnapping(pos, e); m_pos = newPos; m_tool->invalidate(); } //----------------------------------------------------------------------------- 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() { drawSnap(); 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 = getSnap(pos); m_centre = m_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_pos = calculateSnap(pos, e); m_pos = checkGuideSnapping(pos, e); m_radius = tdistance(m_centre, m_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 = calculateSnap(pos, e); m_pos = checkGuideSnapping(pos, e); m_tool->invalidate(); } //----------------------------------------------------------------------------- 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(); drawSnap(); if ((m_isEditing || m_isPrompting) && size > 0) { tglColor(m_isEditing ? m_color : TPixel32::Green); std::vector 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; TPointD newPos; newPos = getSnap(pos); // Se clicco nell'ultimo vertice chiudo la linea. TPointD _pos = pos; if (m_closed) _pos = m_vertex.front(); if ((e.isShiftPressed() && !e.isCtrlPressed()) && !m_vertex.empty()) addVertex(rectify(m_vertex.back(), _pos)); else addVertex(newPos); 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(); resetSnap(); } //----------------------------------------------------------------------------- void MultiLinePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { if (m_closed) endLine(); resetSnap(); } //----------------------------------------------------------------------------- void MultiLinePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { m_ctrlDown = e.isCtrlPressed() && !e.isShiftPressed(); TPointD newPos; newPos = calculateSnap(pos, e); newPos = checkGuideSnapping(pos, e); if (m_isEditing) { if ((e.isShiftPressed() && !e.isCtrlPressed()) && !m_vertex.empty()) m_mousePosition = rectify(m_vertex.back(), newPos); else m_mousePosition = newPos; double dist = joinDistance * joinDistance; if (!m_vertex.empty() && (tdistance2(pos, m_vertex.front()) < dist * m_tool->getPixelSize())) { m_closed = true; m_mousePosition = m_vertex.front(); } else m_closed = false; } else m_mousePosition = newPos; m_tool->invalidate(); } //----------------------------------------------------------------------------- bool MultiLinePrimitive::keyDown(QKeyEvent *event) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { endLine(); return true; } if (event->key() != Qt::Key_Escape || !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 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(); drawSnap(); tglColor(TPixel32::Red); if (m_isEditing || m_isPrompting) { glBegin(GL_LINE_STRIP); tglVertex(m_vertex[0]); tglVertex(m_mousePosition); glEnd(); } } //----------------------------------------------------------------------------- void LinePrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { TPointD newPos = calculateSnap(pos, e); newPos = checkGuideSnapping(pos, e); m_tool->invalidate(); } //----------------------------------------------------------------------------- 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; TPointD newPos = getSnap(pos); m_mousePosition = newPos; TPointD _pos = newPos; 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)newPos.x, (int)newPos.y); else _pos = TPointD((int)newPos.x + 0.5, (int)newPos.y + 0.5); } if (m_vertex.size() == 0) addVertex(_pos); else { if ((e.isShiftPressed() && !e.isCtrlPressed()) && !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; TPointD newPos = calculateSnap(pos, e); newPos = checkGuideSnapping(pos, e); m_mousePosition = newPos; } //----------------------------------------------------------------------------- void LinePrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &e) { // snapping TPointD newPos = getSnap(pos); m_mousePosition = newPos; if ((e.isShiftPressed() && !e.isCtrlPressed()) && !m_vertex.empty()) m_vertex.push_back(rectify(m_vertex.back(), pos)); else m_vertex.push_back(newPos); endLine(); resetSnap(); } //----------------------------------------------------------------------------- //============================================================================= // Ellipse Primitive Class Implementation //----------------------------------------------------------------------------- void EllipsePrimitive::draw() { drawSnap(); 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; TPointD newPos = getSnap(pos); m_startPoint = newPos; m_selectingRect.x0 = newPos.x; m_selectingRect.y0 = newPos.y; m_selectingRect.x1 = newPos.x; m_selectingRect.y1 = newPos.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() && !e.isCtrlPressed()) { 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 = calculateSnap(realPos, e); pos = checkGuideSnapping(realPos, e); } 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 = calculateSnap(pos, e); m_pos = checkGuideSnapping(pos, e); m_tool->invalidate(); } //----------------------------------------------------------------------------- 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 MultiArcPrimitive::draw() { drawSnap(); double pixelSize = m_tool->getPixelSize(); switch (m_clickNumber) { case 1: tglColor(m_color); tglDrawSegment(m_startPoint, m_endPoint); if (m_stroke) { drawStrokeCenterline(*m_stroke, sqrt(tglGetPixelSize2())); TPointD firstPoint = m_stroke->getControlPoint(0); if (firstPoint == m_endPoint) { tglColor(TPixel32((m_color.r + 127) % 255, m_color.g, (m_color.b + 127) % 255, m_color.m)); } tglDrawCircle(m_stroke->getControlPoint(0), joinDistance * pixelSize); } break; case 2: tglColor(m_isPrompting ? TPixel32::Green : m_color); if (!m_isPrompting) { glLineStipple(1, 0x5555); glEnable(GL_LINE_STIPPLE); glBegin(GL_LINE_STRIP); tglVertex(m_startPoint); tglVertex(m_centralPoint); tglVertex(m_endPoint); glEnd(); glDisable(GL_LINE_STIPPLE); } if (m_stroke) drawStrokeCenterline(*m_stroke, sqrt(tglGetPixelSize2())); if (m_strokeTemp) drawStrokeCenterline(*m_strokeTemp, sqrt(tglGetPixelSize2())); if (m_stroke) { TPointD firstPoint = m_stroke->getControlPoint(0); if (firstPoint == m_endPoint) { tglColor(TPixel32((m_color.r + 127) % 255, m_color.g, (m_color.b + 127) % 255, m_color.m)); } tglDrawCircle(m_stroke->getControlPoint(0), joinDistance * pixelSize); } break; }; } //----------------------------------------------------------------------------- TStroke *MultiArcPrimitive::makeStroke() const { return new TStroke(*m_stroke); } //----------------------------------------------------------------------------- void MultiArcPrimitive::leftButtonDown(const TPointD &pos, const TMouseEvent &e) { if (m_clickNumber == 0) { TPointD newPos = calculateSnap(pos, e); newPos = checkGuideSnapping(pos, e); m_startPoint = newPos; } } //----------------------------------------------------------------------------- void MultiArcPrimitive::leftButtonUp(const TPointD &pos, const TMouseEvent &) { TTool::Application *app = TTool::getApplication(); if (!app) return; TPointD newPos = getSnap(pos); std::vector points(9); double thick = getThickness(); double dist = joinDistance * joinDistance; bool strokeAdded = false; MultiArcPrimitiveUndo *undo = new MultiArcPrimitiveUndo(this, m_stroke, m_strokeTemp, m_startPoint, m_endPoint, m_centralPoint, m_clickNumber); 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; } } switch (m_clickNumber) { case 0: m_endPoint = newPos; if (!m_isEditing) return; m_clickNumber++; break; case 1: m_centralPoint = newPos; points[0] = TThickPoint(m_startPoint, thick); points[8] = TThickPoint(m_endPoint, 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); m_strokeTemp = new TStroke(points); m_clickNumber++; break; case 2: m_startPoint = newPos; if (!m_isSingleArc) { m_clickNumber = 1; if (m_stroke) { TVectorImageP vi = new TVectorImage(); vi->addStroke(m_stroke); vi->addStroke(m_strokeTemp); m_strokeTemp = 0; vi->joinStroke(0, 1, m_stroke->getControlPointCount() - 1, 0, getSmooth()); m_stroke = new TStroke(*vi->getStroke(0)); int count = m_stroke->getControlPointCount(); TPointD firstPoint = m_stroke->getControlPoint(0); TPointD lastPoint = m_stroke->getControlPoint(count - 1); m_startPoint = lastPoint; if (firstPoint == lastPoint) { vi->joinStroke(0, 0, 0, m_stroke->getControlPointCount() - 1, getSmooth()); delete m_stroke; m_stroke = new TStroke(*vi->getStroke(0)); TUndoManager::manager()->popUndo(m_undoCount); m_undoCount = 0; m_tool->addStroke(); onDeactivate(); strokeAdded = true; } } else { m_stroke = m_strokeTemp; m_strokeTemp = 0; m_startPoint = m_endPoint; } } else { m_stroke = m_strokeTemp; m_strokeTemp = 0; TUndoManager::manager()->popUndo(m_undoCount); m_undoCount = 0; m_tool->addStroke(); onDeactivate(); strokeAdded = true; } break; } if (strokeAdded) { delete undo; } else { undo->setRedoData(m_stroke, m_strokeTemp, m_startPoint, m_endPoint, m_centralPoint, m_clickNumber); TUndoManager::manager()->add(undo); ++m_undoCount; } resetSnap(); } //----------------------------------------------------------------------------- void MultiArcPrimitive::leftButtonDoubleClick(const TPointD &, const TMouseEvent &e) { if (m_stroke) { TUndoManager::manager()->popUndo(m_undoCount); m_undoCount = 0; m_tool->addStroke(); } onDeactivate(); } //----------------------------------------------------------------------------- bool MultiArcPrimitive::keyDown(QKeyEvent *event) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { if (m_stroke) { TUndoManager::manager()->popUndo(m_undoCount); m_undoCount = 0; m_tool->addStroke(); } onDeactivate(); return true; } return false; } //----------------------------------------------------------------------------- void MultiArcPrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { TPointD newPos = calculateSnap(pos, e); newPos = checkGuideSnapping(pos, e); double dist = joinDistance * joinDistance; switch (m_clickNumber) { case 0: m_startPoint = newPos; break; case 1: if (e.isShiftPressed() && !e.isCtrlPressed()) m_endPoint = rectify(m_startPoint, pos); else m_endPoint = newPos; if (m_stroke) { TPointD firstPoint = m_stroke->getControlPoint(0); if (tdistance2(m_endPoint, firstPoint) < dist * m_tool->getPixelSize()) m_endPoint = firstPoint; } break; case 2: m_centralPoint = newPos; TThickQuadratic q(m_startPoint, TThickPoint(m_centralPoint, getThickness()), m_endPoint); 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_strokeTemp->setControlPoint(1, q00.getP1()); m_strokeTemp->setControlPoint(2, q00.getP2()); m_strokeTemp->setControlPoint(3, q01.getP1()); m_strokeTemp->setControlPoint(4, q01.getP2()); m_strokeTemp->setControlPoint(5, q10.getP1()); m_strokeTemp->setControlPoint(6, q10.getP2()); m_strokeTemp->setControlPoint(7, q11.getP1()); break; } m_tool->invalidate(); } //----------------------------------------------------------------------------- void MultiArcPrimitive::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() { drawSnap(); 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 = getSnap(pos); m_radius = 0; } //----------------------------------------------------------------------------- void PolygonPrimitive::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { if (!m_isEditing) return; TPointD newPos = calculateSnap(pos, e); newPos = checkGuideSnapping(pos, e); m_radius = tdistance(m_centre, newPos); } //----------------------------------------------------------------------------- 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 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 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(); } //----------------------------------------------------------------------------- void PolygonPrimitive::mouseMove(const TPointD &pos, const TMouseEvent &e) { TPointD newPos = calculateSnap(pos, e); newPos = checkGuideSnapping(pos, e); m_tool->invalidate(); }