summaryrefslogtreecommitdiffstats
path: root/chalk/plugins/tools/tool_curves/kis_tool_curve.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chalk/plugins/tools/tool_curves/kis_tool_curve.cpp')
-rw-r--r--chalk/plugins/tools/tool_curves/kis_tool_curve.cpp593
1 files changed, 593 insertions, 0 deletions
diff --git a/chalk/plugins/tools/tool_curves/kis_tool_curve.cpp b/chalk/plugins/tools/tool_curves/kis_tool_curve.cpp
new file mode 100644
index 000000000..ea1f9a773
--- /dev/null
+++ b/chalk/plugins/tools/tool_curves/kis_tool_curve.cpp
@@ -0,0 +1,593 @@
+/*
+ * kis_tool_curve.cpp -- part of Chalk
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <emanuele@valinor.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <math.h>
+#include <limits.h>
+
+#include <tqapplication.h>
+#include <tqpainter.h>
+#include <tqlayout.h>
+#include <tqrect.h>
+
+#include <tdeaction.h>
+#include <kdebug.h>
+#include <tdelocale.h>
+#include <kdebug.h>
+
+#include "kis_global.h"
+#include "kis_doc.h"
+#include "kis_painter.h"
+#include "kis_point.h"
+#include "kis_canvas_subject.h"
+#include "kis_canvas_controller.h"
+#include "kis_button_press_event.h"
+#include "kis_button_release_event.h"
+#include "kis_move_event.h"
+#include "kis_canvas.h"
+#include "kis_canvas_painter.h"
+#include "kis_cursor.h"
+#include "kis_tool_controller.h"
+#include "kis_vec.h"
+#include "kis_selection.h"
+#include "kis_selection_options.h"
+#include "kis_selected_transaction.h"
+#include "kis_paintop_registry.h"
+
+#include "kis_curve_framework.h"
+#include "kis_tool_curve.h"
+
+TQRect KisToolCurve::pivotRect (const TQPoint& pos)
+{
+ return TQRect (pos-TQPoint(4,4),pos+TQPoint(4,4));
+}
+
+TQRect KisToolCurve::selectedPivotRect (const TQPoint& pos)
+{
+ return TQRect (pos-TQPoint(5,5),pos+TQPoint(5,5));
+}
+
+KisToolCurve::KisToolCurve(const TQString& UIName)
+ : super(UIName)
+{
+ m_UIName = UIName;
+ m_currentImage = 0;
+ m_optWidget = 0;
+
+ m_curve = 0;
+
+ m_dragging = false;
+ m_draggingCursor = false;
+ m_drawPivots = true;
+ m_drawingPen = TQPen(TQt::white, 0, TQt::SolidLine);
+ m_pivotPen = TQPen(TQt::gray, 0, TQt::SolidLine);
+ m_selectedPivotPen = TQPen(TQt::yellow, 0, TQt::SolidLine);
+ m_pivotRounding = m_selectedPivotRounding = 55;
+
+ m_actionOptions = NOOPTIONS;
+ m_supportMinimalDraw = true;
+ m_selectAction = SELECTION_ADD;
+}
+
+KisToolCurve::~KisToolCurve()
+{
+
+}
+
+void KisToolCurve::update (KisCanvasSubject *subject)
+{
+ super::update(subject);
+ if (m_subject)
+ m_currentImage = m_subject->currentImg();
+}
+
+void KisToolCurve::deactivate()
+{
+ draw(false);
+ if (m_curve) {
+ m_curve->clear();
+ m_curve->endActionOptions();
+ }
+
+ m_actionOptions = NOOPTIONS;
+ m_dragging = false;
+ m_drawPivots = true;
+}
+
+void KisToolCurve::buttonPress(KisButtonPressEvent *event)
+{
+ updateOptions(event->state());
+ if (!m_currentImage)
+ return;
+ if (event->button() == Qt::LeftButton) {
+ m_dragging = true;
+ m_currentPoint = event->pos();
+ PointPair temp = pointUnderMouse (m_subject->canvasController()->windowToView(event->pos().toTQPoint()));
+ if (temp.first == m_curve->end() && !(m_actionOptions)) {
+ draw(true, true);
+ m_curve->selectAll(false);
+ draw(true, true);
+ draw(m_curve->end());
+ m_previous = m_curve->find(m_curve->last());
+ m_current = m_curve->pushPivot(event->pos());
+ if (m_curve->pivots().count() > 1)
+ m_curve->calculateCurve(m_previous,m_current,m_current);
+ draw(m_current);
+ } else {
+ draw(true, true);
+ if (temp.second)
+ m_current = m_curve->selectPivot(temp.first);
+ else
+ m_current = selectByMouse(temp.first);
+
+ if (!(*m_current).isSelected())
+ m_dragging = false;
+ draw(true, true);
+ }
+ }
+}
+
+void KisToolCurve::keyPress(TQKeyEvent *event)
+{
+ if (event->key() == TQt::Key_Return) {
+ m_dragging = false;
+ commitCurve();
+ } else
+ if (event->key() == TQt::Key_Escape) {
+ m_dragging = false;
+ draw(false);
+ m_curve->clear();
+ } else
+ if (event->key() == TQt::Key_Delete) {
+ draw(false);
+ m_dragging = false;
+ m_curve->deleteSelected();
+ m_current = m_curve->find(m_curve->last());
+ m_previous = m_curve->selectPivot(m_current);
+ draw(false);
+ }
+}
+
+void KisToolCurve::keyRelease(TQKeyEvent *)
+{
+
+}
+
+void KisToolCurve::buttonRelease(KisButtonReleaseEvent *event)
+{
+ updateOptions(event->state());
+ m_dragging = false;
+}
+
+void KisToolCurve::doubleClick(KisDoubleClickEvent *)
+{
+ commitCurve();
+}
+
+void KisToolCurve::move(KisMoveEvent *event)
+{
+ updateOptions(event->state());
+ PointPair temp = pointUnderMouse(m_subject->canvasController()->windowToView(event->pos().toTQPoint()));
+ if (temp.first == m_curve->end() && !m_dragging) {
+ if (m_draggingCursor) {
+ setCursor(KisCursor::load(m_cursor, 6, 6));
+ m_draggingCursor = false;
+ }
+ } else {
+ setCursor(KisCursor::load("tool_curve_dragging.png", 6, 6));
+ m_draggingCursor = true;
+ }
+ if (m_dragging) {
+ draw();
+ KisPoint trans = event->pos() - m_currentPoint;
+ m_curve->moveSelected(trans);
+ m_currentPoint = event->pos();
+ draw();
+ }
+}
+
+double pointToSegmentDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1)
+{
+ double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y()));
+ double distance = 0;
+ KisVector2D v0(l0), v1(l1), v(p), seg(v0-v1), dist0(v0-p), dist1(v1-p);
+
+ if (seg.length() < dist0.length() ||
+ seg.length() < dist1.length()) // the point doesn't perpendicolarly intersecate the segment (or it's too far from the segment)
+ return (double)INT_MAX;
+
+ if (lineLength > DBL_EPSILON) {
+ distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength;
+ distance = fabs(distance);
+ }
+
+ return distance;
+}
+
+PointPair KisToolCurve::pointUnderMouse(const TQPoint& pos)
+{
+ KisCurve::iterator it, next;
+ TQPoint pos1, pos2;
+ it = handleUnderMouse(pos);
+ if (it != m_curve->end())
+ return PointPair(it,true);
+
+ for (it = m_curve->begin(); it != m_curve->end(); it++) {
+ next = it.next();
+ if (next == m_curve->end() || it == m_curve->end())
+ return PointPair(m_curve->end(),false);
+ if ((*it).hint() > LINEHINT || (*next).hint() > LINEHINT)
+ continue;
+ pos1 = m_subject->canvasController()->windowToView((*it).point().toTQPoint());
+ pos2 = m_subject->canvasController()->windowToView((*next).point().toTQPoint());
+ if (pos1 == pos2)
+ continue;
+ if (pointToSegmentDistance(pos,pos1,pos2) <= MAXDISTANCE)
+ break;
+ }
+
+ return PointPair(it,false);
+}
+
+KisCurve::iterator KisToolCurve::handleUnderMouse(const TQPoint& pos)
+{
+ KisCurve pivs = m_curve->pivots(), inHandle;
+ KisCurve::iterator it;
+ for (it = pivs.begin(); it != pivs.end(); it++) {
+ if (pivotRect(m_subject->canvasController()->windowToView((*it).point().toTQPoint())).contains(pos))
+ inHandle.pushPoint((*it));
+ }
+ if (inHandle.isEmpty())
+ return m_curve->end();
+ return m_curve->find(inHandle.last());
+}
+
+KisCurve::iterator KisToolCurve::selectByMouse(KisCurve::iterator it)
+{
+ KisCurve::iterator prevPivot, nextPivot;
+
+ if ((*it).isPivot())
+ prevPivot = it;
+ else
+ prevPivot = it.previousPivot();
+ nextPivot = it.nextPivot();
+
+ m_curve->selectPivot(prevPivot);
+ (*nextPivot).setSelected(true);
+
+ return prevPivot;
+}
+
+int KisToolCurve::updateOptions(int key)
+{
+ int options = 0x0000;
+
+ if (key & TQt::ControlButton)
+ options |= CONTROLOPTION;
+
+ if (key & TQt::ShiftButton)
+ options |= SHIFTOPTION;
+
+ if (key & TQt::AltButton)
+ options |= ALTOPTION;
+
+ if (options != m_actionOptions) {
+ draw(false);
+ m_actionOptions = options;
+ m_curve->setActionOptions(m_actionOptions);
+ draw(false);
+ }
+
+ return m_actionOptions;
+}
+
+void KisToolCurve::draw(bool m, bool o)
+{
+ draw(KisCurve::iterator(), o, m);
+}
+
+void KisToolCurve::draw(KisCurve::iterator inf, bool pivotonly, bool minimal)
+{
+ if (m_curve->isEmpty())
+ return;
+ KisCanvasPainter *gc;
+ KisCanvasController *controller;
+ KisCanvas *canvas;
+ if (m_subject && m_currentImage) {
+ controller = m_subject->canvasController();
+ canvas = controller->kiscanvas();
+ gc = new KisCanvasPainter(canvas);
+ } else
+ return;
+
+ gc->setPen(m_drawingPen);
+ gc->setRasterOp(TQt::XorROP);
+
+ KisCurve::iterator it, finish;
+
+ if (minimal && m_supportMinimalDraw) {
+ if (pivotonly) {
+ KisCurve p = m_curve->pivots();
+ for (KisCurve::iterator i = p.begin(); i != p.end(); i++)
+ drawPivotHandle (*gc, i);
+ delete gc;
+ return;
+ }
+ if (inf.target() != 0) {
+ if (inf != m_curve->end()) {
+ it = inf.previousPivot();
+ finish = inf.nextPivot();
+ } else {
+ it = --m_curve->end();
+ finish = m_curve->end();
+ }
+ } else {
+ KisCurve sel = m_curve->selectedPivots();
+ if (sel.isEmpty()) {
+ delete gc;
+ return;
+ }
+ for (KisCurve::iterator i = sel.begin(); i != sel.end(); i++) {
+ it = m_curve->find(*i).previousPivot();
+ finish = m_curve->find(*i).nextPivot();
+ if ((*finish).isSelected())
+ finish = finish.previousPivot();
+ while (it != finish) {
+ if ((*it).isPivot())
+ drawPivotHandle (*gc, it);
+ it = drawPoint (*gc, it);
+ }
+ }
+ delete gc;
+ return;
+ }
+ } else {
+ it = m_curve->begin();
+ finish = m_curve->end();
+ }
+ while (it != finish) {
+ if ((*it).isPivot())
+ drawPivotHandle (*gc, it);
+ it = drawPoint (*gc, it);
+ }
+
+ delete gc;
+}
+
+KisCurve::iterator KisToolCurve::drawPoint(KisCanvasPainter& gc, KisCurve::iterator point)
+{
+ KisCanvasController *controller = m_subject->canvasController();
+
+ TQPoint pos1, pos2;
+ pos1 = controller->windowToView((*point).point().toTQPoint());
+
+ switch ((*point).hint()) {
+ case POINTHINT:
+ gc.drawPoint(pos1);
+ point += 1;
+ break;
+ case LINEHINT:
+ gc.drawPoint(pos1);
+ if (++point != m_curve->end() && (*point).hint() <= LINEHINT) {
+ pos2 = controller->windowToView((*point).point().toTQPoint());
+ gc.drawLine(pos1,pos2);
+ }
+ break;
+ default:
+ point += 1;
+ }
+
+ return point;
+}
+
+void KisToolCurve::drawPivotHandle(KisCanvasPainter& gc, KisCurve::iterator point)
+{
+ KisCanvasController *controller = m_subject->canvasController();
+
+ if (m_drawPivots) {
+ TQPoint pos = controller->windowToView((*point).point().toTQPoint());
+ if ((*point).isSelected()) {
+ gc.setPen(m_selectedPivotPen);
+ gc.drawRoundRect(selectedPivotRect(pos),m_selectedPivotRounding,m_selectedPivotRounding);
+ } else {
+ gc.setPen(m_pivotPen);
+ gc.drawRoundRect(pivotRect(pos),m_pivotRounding,m_pivotRounding);
+ }
+ gc.setPen(m_drawingPen);
+ }
+}
+
+void KisToolCurve::paint(KisCanvasPainter&)
+{
+ draw(false);
+}
+
+void KisToolCurve::paint(KisCanvasPainter&, const TQRect&)
+{
+ draw(false);
+}
+
+void KisToolCurve::commitCurve()
+{
+ if (toolType() == TOOL_SHAPE || toolType() == TOOL_FREEHAND)
+ paintCurve();
+ else if (toolType() == TOOL_SELECT)
+ selectCurve();
+ else
+ kdDebug(0) << "NO SUPPORT FOR THIS TYPE OF TOOL" << endl;
+
+ m_curve->clear();
+ m_curve->endActionOptions();
+}
+
+void KisToolCurve::paintCurve()
+{
+ KisPaintDeviceSP device = m_currentImage->activeDevice ();
+ if (!device) return;
+
+ KisPainter painter (device);
+ if (m_currentImage->undo()) painter.beginTransaction (m_transactionMessage);
+
+ painter.setPaintColor(m_subject->fgColor());
+ painter.setBrush(m_subject->currentBrush());
+ painter.setOpacity(m_opacity);
+ painter.setCompositeOp(m_compositeOp);
+ KisPaintOp * op = KisPaintOpRegistry::instance()->paintOp(m_subject->currentPaintop(), m_subject->currentPaintopSettings(), &painter);
+ painter.setPaintOp(op); // Painter takes ownership
+
+// Call paintPoint
+ KisCurve::iterator it = m_curve->begin();
+ while (it != m_curve->end())
+ it = paintPoint(painter,it);
+// Finish
+
+ device->setDirty( painter.dirtyRect() );
+ notifyModified();
+
+ if (m_currentImage->undo()) {
+ m_currentImage->undoAdapter()->addCommand(painter.endTransaction());
+ }
+
+ draw(false);
+}
+
+KisCurve::iterator KisToolCurve::paintPoint (KisPainter& painter, KisCurve::iterator point)
+{
+ KisCurve::iterator next = point; next+=1;
+ switch ((*point).hint()) {
+ case POINTHINT:
+ painter.paintAt((*point++).point(), PRESSURE_DEFAULT, 0, 0);
+ break;
+ case LINEHINT:
+ if (next != m_curve->end() && (*next).hint() <= LINEHINT)
+ painter.paintLine((*point++).point(), PRESSURE_DEFAULT, 0, 0, (*next).point(), PRESSURE_DEFAULT, 0, 0);
+ else
+ painter.paintAt((*point++).point(), PRESSURE_DEFAULT, 0, 0);
+ break;
+ default:
+ point += 1;
+ }
+
+ return point;
+}
+
+TQValueVector<KisPoint> KisToolCurve::convertCurve()
+{
+ TQValueVector<KisPoint> points;
+
+ for (KisCurve::iterator i = m_curve->begin(); i != m_curve->end(); i++)
+ if ((*i).hint() != NOHINTS)
+ points.append((*i).point());
+
+ return points;
+}
+
+void KisToolCurve::selectCurve()
+{
+ TQApplication::setOverrideCursor(KisCursor::waitCursor());
+ KisPaintDeviceSP dev = m_currentImage->activeDevice();
+ bool hasSelection = dev->hasSelection();
+ KisSelectedTransaction *t = 0;
+ if (m_currentImage->undo()) t = new KisSelectedTransaction(m_transactionMessage, dev);
+ KisSelectionSP selection = dev->selection();
+
+ if (!hasSelection) {
+ selection->clear();
+ }
+
+ KisPainter painter(selection.data());
+
+ painter.setPaintColor(KisColor(TQt::black, selection->colorSpace()));
+ painter.setFillStyle(KisPainter::FillStyleForegroundColor);
+ painter.setStrokeStyle(KisPainter::StrokeStyleNone);
+ painter.setBrush(m_subject->currentBrush());
+ painter.setOpacity(OPACITY_OPAQUE);
+ KisPaintOp * op = KisPaintOpRegistry::instance()->paintOp("paintbrush", 0, &painter);
+ painter.setPaintOp(op); // And now the painter owns the op and will destroy it.
+
+ switch (m_selectAction) {
+ case SELECTION_ADD:
+ painter.setCompositeOp(COMPOSITE_OVER);
+ break;
+ case SELECTION_SUBTRACT:
+ painter.setCompositeOp(COMPOSITE_SUBTRACT);
+ break;
+ default:
+ break;
+ }
+
+ painter.paintPolygon(convertCurve());
+
+
+ if(hasSelection) {
+ TQRect dirty(painter.dirtyRect());
+ dev->setDirty(dirty);
+ dev->emitSelectionChanged(dirty);
+ } else {
+ dev->setDirty();
+ dev->emitSelectionChanged();
+ }
+
+ if (m_currentImage->undo())
+ m_currentImage->undoAdapter()->addCommand(t);
+
+ TQApplication::restoreOverrideCursor();
+
+ draw(false);
+}
+
+TQWidget* KisToolCurve::createOptionWidget(TQWidget* parent)
+{
+ if (toolType() == TOOL_SHAPE || toolType() == TOOL_FREEHAND)
+ return super::createOptionWidget(parent);
+ else if (toolType() == TOOL_SELECT)
+ return createSelectionOptionWidget(parent);
+ else
+ kdDebug(0) << "NO SUPPORT FOR THIS TOOL TYPE" << endl;
+ return 0;
+}
+
+void KisToolCurve::slotSetAction(int action) {
+ if (action >= SELECTION_ADD && action <= SELECTION_SUBTRACT)
+ m_selectAction =(enumSelectionMode)action;
+}
+
+TQWidget* KisToolCurve::createSelectionOptionWidget(TQWidget* parent)
+{
+ m_optWidget = new KisSelectionOptions(parent, m_subject);
+ TQ_CHECK_PTR(m_optWidget);
+ m_optWidget->setCaption(m_UIName);
+
+ connect (m_optWidget, TQT_SIGNAL(actionChanged(int)), this, TQT_SLOT(slotSetAction(int)));
+
+ TQVBoxLayout * l = dynamic_cast<TQVBoxLayout*>(m_optWidget->layout());
+ l->addItem(new TQSpacerItem(1, 1, TQSizePolicy::Fixed, TQSizePolicy::Expanding));
+
+ return m_optWidget;
+}
+
+TQWidget* KisToolCurve::optionWidget()
+{
+ if (toolType() == TOOL_SELECT)
+ return m_optWidget;
+ else
+ return super::optionWidget();
+}
+
+#include "kis_tool_curve.moc"