diff options
Diffstat (limited to 'chalk/plugins/tools/tool_curves/kis_tool_moutline.cc')
-rw-r--r-- | chalk/plugins/tools/tool_curves/kis_tool_moutline.cc | 809 |
1 files changed, 809 insertions, 0 deletions
diff --git a/chalk/plugins/tools/tool_curves/kis_tool_moutline.cc b/chalk/plugins/tools/tool_curves/kis_tool_moutline.cc new file mode 100644 index 00000000..4b7e51d4 --- /dev/null +++ b/chalk/plugins/tools/tool_curves/kis_tool_moutline.cc @@ -0,0 +1,809 @@ +/* + * kis_tool_moutline.cc -- part of Chalk + * + * Copyright (c) 2006 Emanuele Tamponi <[email protected]> + * + * 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 <set> + +#include <tqpainter.h> +#include <tqlayout.h> +#include <tqrect.h> +#include <tqlabel.h> +#include <tqpushbutton.h> +#include <tqslider.h> + +#include <kaction.h> +#include <kdebug.h> +#include <klocale.h> +#include <kdebug.h> +#include <knuminput.h> + +#include "kis_global.h" +#include "kis_iterators_pixel.h" +#include "kis_colorspace.h" +#include "kis_channelinfo.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_convolution_painter.h" + +#include "kis_tool_moutline.h" + +using namespace std; + +#define RMS(a, b) (sqrt ((a) * (a) + (b) * (b))) +#define ROUND(x) ((int) ((x) + 0.5)) + +const int NOEDGE = 0x0000; + +const int ORTHOGONAL_COST = 10; // 1*10 +const int DIAGONAL_COST = 14; // sqrt(2)*10 +const int MALUS = 20; // This applies to NOEDGE nodes + +const int DEFAULTDIST = 40; // Default distance between two automatic pivots +const int MAXDIST = 55; // Max distance +const int MINDIST = 15; +const int PAGESTEP = 5; + +class Node { + + TQPoint m_pos; + int m_gCost; + int m_hCost; + int m_tCost; + bool m_malus; + TQPoint m_parent; + +public: + + Node() + { + m_pos = m_parent = TQPoint(-1,-1); + m_gCost = m_hCost = m_tCost = 0; + m_malus = false; + } + + Node(const Node& node) + { + m_pos = node.pos(); + m_gCost = node.gCost(); + m_hCost = node.hCost(); + m_tCost = node.tCost(); + m_malus = node.malus(); + m_parent = node.tqparent(); + } + + Node(const TQPoint& tqparent, const TQPoint& pos, int g, int h, bool malus) + : m_pos(pos), m_hCost(h), m_malus(malus) + { + setGCost(g); + m_parent = tqparent; + } + ~Node () + { + } + + int gCost () const {return m_gCost;} + int hCost () const {return m_hCost;} + int tCost () const {return m_tCost;} + bool malus () const {return m_malus;} + TQPoint pos () const {return m_pos;} + int col () const {return m_pos.x();} + int row () const {return m_pos.y();} + TQPoint tqparent () const {return m_parent;} + + void setGCost (int g) + { + m_gCost = g+(m_malus?MALUS:0); + m_tCost = m_gCost+m_hCost; + } + void setHCost (int h) + { + m_hCost = h; + m_tCost = m_gCost+m_hCost; + } + void setPos (const TQPoint& pos) + { + m_pos = pos; + } + void setMalus (bool malus) + { + m_malus = malus; + } + void clear () + { + m_pos = TQPoint(-1,-1); + } + + bool operator== (const Node& n2) const + { + return m_pos == n2.pos(); + } + bool operator!= (const Node& n2) const + { + return m_pos != n2.pos(); + } + bool operator== (const TQPoint& n2) const + { + return m_pos == n2; + } + bool operator!= (const TQPoint& n2) const + { + return m_pos != n2; + } + bool operator< (const Node& n2) const + { + return m_tCost < n2.tCost(); + } + bool operator> (const Node& n2) const + { + return m_tCost > n2.tCost(); + } + + TQValueList<Node> getNeighbor(const GrayMatrix& src, const Node& end) + { + TQPoint tmpdist; + TQValueList<Node> temp; + int dcol, drow; + int g, h; + bool malus; + int x[8] = { 1, 1, 0,-1,-1,-1, 0, 1}, + y[8] = { 0,-1,-1,-1, 0, 1, 1, 1}; + + for (int i = 0; i < 8; i++) { + dcol = m_pos.x() + x[i]; + drow = m_pos.y() + y[i]; + tmpdist = TQPoint(dcol,drow) - end.pos(); + // I use src[0] here because all cols have same number of rows + if (dcol == (int)src.count() || dcol < 0 || + drow == (int)src[0].count() || drow < 0) + continue; + if (src[dcol][drow]) + malus = false; + else + malus = true; + if (i%2) + g = m_gCost + DIAGONAL_COST; + else + g = m_gCost + ORTHOGONAL_COST; + h = ORTHOGONAL_COST * (abs(tmpdist.x()) + abs(tmpdist.y())); + temp.append(Node(m_pos,TQPoint(dcol,drow),g,h,malus)); + } + return temp; + } + +}; + +KisKernelSP createKernel( TQ_INT32 i0, TQ_INT32 i1, TQ_INT32 i2, + TQ_INT32 i3, TQ_INT32 i4, TQ_INT32 i5, + TQ_INT32 i6, TQ_INT32 i7, TQ_INT32 i8, + TQ_INT32 factor, TQ_INT32 offset ) +{ + KisKernelSP kernel = new KisKernel(); + kernel->width = 3; + kernel->height = 3; + + kernel->factor = factor; + kernel->offset = offset; + + kernel->data = new TQ_INT32[9]; + kernel->data[0] = i0; + kernel->data[1] = i1; + kernel->data[2] = i2; + kernel->data[3] = i3; + kernel->data[4] = i4; + kernel->data[5] = i5; + kernel->data[6] = i6; + kernel->data[7] = i7; + kernel->data[8] = i8; + + return kernel; +} + +KisCurveMagnetic::KisCurveMagnetic (KisToolMagnetic *tqparent) + : m_parent(tqparent) +{ + m_standardkeepselected = false; +} + +KisCurveMagnetic::~KisCurveMagnetic () +{ + +} + +KisCurve::iterator KisCurveMagnetic::addPivot (KisCurve::iterator it, const KisPoint& point) +{ + return iterator(*this,m_curve.insert(it.position(), CurvePoint(point,true,false,LINEHINT))); +} + +KisCurve::iterator KisCurveMagnetic::pushPivot (const KisPoint& point) +{ + iterator it; + + it = pushPoint(point,true,false,LINEHINT); +// if (count() == 1 && !m_parent->editingMode()) +// addPoint(it,point,true,false,LINEHINT); + + return selectPivot(it); +} + +void KisCurveMagnetic::calculateCurve (KisCurve::iterator p1, KisCurve::iterator p2, KisCurve::iterator it) +{ + if (p1 == m_curve.end() || p2 == m_curve.end()) // It happens sometimes, for example on the first click + return; + if (m_parent->editingMode()) + return; + TQPoint start = (*p1).point().roundTQPoint(); + TQPoint end = (*p2).point().roundTQPoint(); + TQRect rc = TQRect(start,end).normalize(); + rc.setTopLeft(rc.topLeft()+TQPoint(-8,-8)); // Enlarge the view, so problems with gaussian blur can be removed + rc.setBottomRight(rc.bottomRight()+TQPoint(8,8)); // and we are able to find paths that go beyond the rect. + + KisPaintDeviceSP src = m_parent->m_currentImage->activeDevice(); + GrayMatrix dst = GrayMatrix(rc.width(),GrayCol(rc.height())); + + detectEdges (rc, src, dst); + reduceMatrix (rc, dst, 3, 3, 3, 3); + + Node startNode, endNode; + multiset<Node> openSet; + NodeMatrix openMatrix = NodeMatrix(rc.width(),NodeCol(rc.height())); + NodeMatrix closedMatrix = NodeMatrix(rc.width(),NodeCol(rc.height())); + + TQPoint tl(rc.topLeft().x(),rc.topLeft().y()); + start -= tl; // Relative to the matrix + end -= tl; // Relative to the matrix + + findEdge (start.x(), start.y(), dst, startNode); + openMatrix[startNode.col()][startNode.row()] = *openSet.insert(startNode); + endNode.setPos(end); + + while (!openSet.empty()) { + Node current = *openSet.begin(); + + openSet.erase(openSet.begin()); + openMatrix[current.col()][current.row()].clear(); + + TQValueList<Node> successors = current.getNeighbor(dst,endNode); + for (TQValueList<Node>::iterator i = successors.begin(); i != successors.end(); i++) { + int col = (*i).col(); + int row = (*i).row(); + if ((*i) == endNode) { + while (current.tqparent() != TQPoint(-1,-1)) { + it = addPoint(it,KisPoint(tl+current.pos()),false,false,LINEHINT); + current = closedMatrix[current.tqparent().x()][current.tqparent().y()]; + } + return; + } + Node *openNode = &openMatrix[col][row]; + if (*openNode != TQPoint(-1,-1)) { + if (*i > *openNode) + continue; + else { + openSet.erase(tqFind(openSet.begin(),openSet.end(),*openNode)); + openNode->clear(); // Clear the Node + } + } + Node *closedNode = &closedMatrix[col][row]; + if (*closedNode != TQPoint(-1,-1)) { + if ((*i) > (*closedNode)) + continue; + else { + openMatrix[col][row] = *openSet.insert(*closedNode); + closedNode->clear(); // Clear the Node + continue; + } + } + openMatrix[col][row] = *openSet.insert(*i); + } + closedMatrix[current.col()][current.row()] = current; + } +} + +void KisCurveMagnetic::findEdge (int col, int row, const GrayMatrix& src, Node& node) +{ + int x = -1; + int y = -1; + + // tmpdist out of range + KisVector2D mindist(5.0,5.0), tmpdist(1000.0,1000.0); + for (int i = -5; i < 6; i++) { + for (int j = -5; j < 6; j++) { + if (src[col+i][row+j] != NOEDGE) { + tmpdist = KisVector2D(i,j); + if (tmpdist.length() < mindist.length()) + mindist = tmpdist; + } + } + } + if (tmpdist.x() == 1000.0) + mindist = KisVector2D(0.0,0.0); + + x = (int)(col + mindist.x()); + y = (int)(row + mindist.y()); + + node.setPos(TQPoint(x,y)); +} + +void KisCurveMagnetic::reduceMatrix (TQRect& rc, GrayMatrix& m, int top, int right, int bottom, int left) +{ + TQPoint topleft(top, left); + TQPoint bottomright(bottom, right); + + rc.setTopLeft(rc.topLeft()+topleft); + rc.setBottomRight(rc.bottomRight()-bottomright); + + if (left) + m.erase(m.begin(),m.begin()+left); + if (right) + m.erase(m.end()-right,m.end()); + if (top) { + for (uint i = 0; i < m.count(); i++) + m[i].erase(m[i].begin(),m[i].begin()+top); + } + if (bottom) { + for (uint i = 0; i < m.count(); i++) + m[i].erase(m[i].end()-bottom,m[i].end()); + } +} + +void KisCurveMagnetic::detectEdges (const TQRect & rect, KisPaintDeviceSP src, GrayMatrix& dst) +{ + GrayMatrix graysrc(rect.width(),GrayCol(rect.height())); + GrayMatrix xdeltas(rect.width(),GrayCol(rect.height())); + GrayMatrix ydeltas(rect.width(),GrayCol(rect.height())); + GrayMatrix magnitude(rect.width(),GrayCol(rect.height())); + KisPaintDeviceSP smooth = new KisPaintDevice(src->colorSpace()); + + gaussianBlur(rect, src, smooth); + toGrayScale(rect, smooth, graysrc); + getDeltas(graysrc, xdeltas, ydeltas); + getMagnitude(xdeltas, ydeltas, magnitude); + nonMaxSupp(magnitude, xdeltas, ydeltas, dst); +} + +void KisCurveMagnetic::gaussianBlur (const TQRect& rect, KisPaintDeviceSP src, KisPaintDeviceSP dst) +{ + int grectx = rect.x(); + int grecty = rect.y(); + int grectw = rect.width(); + int grecth = rect.height(); + if (dst != src) { + KisPainter gc(dst); + gc.bitBlt(grectx, grecty, COMPOSITE_COPY, src, grectx, grecty, grectw, grecth); + gc.end(); + } + + KisConvolutionPainter painter( dst ); + // FIXME createKernel could create dynamic gaussian kernels having sigma as argument + KisKernelSP kernel = createKernel( 1, 1, 1, 1, 24, 1, 1, 1, 1, 32, 0); + painter.applyMatrix(kernel, grectx, grecty, grectw, grecth, BORDER_AVOID); +} + +void KisCurveMagnetic::toGrayScale (const TQRect& rect, KisPaintDeviceSP src, GrayMatrix& dst) +{ + int grectx = rect.x(); + int grecty = rect.y(); + int grectw = rect.width(); + int grecth = rect.height(); + TQColor c; + KisColorSpace *cs = src->colorSpace(); + + for (int row = 0; row < grecth; row++) { + KisHLineIteratorPixel srcIt = src->createHLineIterator(grectx, grecty+row, grectw, false); + for (int col = 0; col < grectw; col++) { + cs->toTQColor(srcIt.rawData(),&c); + dst[col][row] = tqGray(c.rgb()); + ++srcIt; + } + } +} + +void KisCurveMagnetic::getDeltas (const GrayMatrix& src, GrayMatrix& xdelta, GrayMatrix& ydelta) +{ + uint start = 1, xend = src[0].count()-1, yend = src.count()-1; + TQ_INT16 deri; + for (uint col = 0; col < src.count(); col++) { + for (uint row = 0; row < src[col].count(); row++) { + if (row >= start && row < xend) { + deri = src[col][row+1] - src[col][row-1]; + xdelta[col][row] = deri; + } else + xdelta[col][row] = 0; + if (col >= start && col < yend) { + deri = src[col+1][row] - src[col-1][row]; + ydelta[col][row] = deri; + } else + ydelta[col][row] = 0; + } + } +} + +void KisCurveMagnetic::getMagnitude (const GrayMatrix& xdelta, const GrayMatrix& ydelta, GrayMatrix& gradient) +{ + for (uint col = 0; col < xdelta.count(); col++) { + for (uint row = 0; row < xdelta[col].count(); row++) + gradient[col][row] = (TQ_INT16)(ROUND(RMS(xdelta[col][row],ydelta[col][row]))); + } +} + +void KisCurveMagnetic::nonMaxSupp (const GrayMatrix& magnitude, const GrayMatrix& xdelta, const GrayMatrix& ydelta, GrayMatrix& nms) +{ + // Directions: + // 1: 0 - 22.5 degrees + // 2: 22.5 - 67.5 degrees + // 3: 67.5 - 90 degrees + // Second direction is relative to a quadrant. The quadrant is known by looking at x and y derivatives + // First quadrant: Gx < 0 & Gy >= 0 + // Second quadrant: Gx < 0 & Gy < 0 + // Third quadrant: Gx >= 0 & Gy < 0 + // Fourth quadrant: Gx >= 0 & Gy >= 0 + // For this reason: first direction is relative to Gy only and third direction to Gx only + + double theta; // theta = invtan (|Gy| / |Gx|) This give the direction relative to a quadrant + TQ_INT16 mag; // Current magnitude + TQ_INT16 lmag; // Magnitude at the left (So this pixel is "more internal" than the current + TQ_INT16 rmag; // Magnitude at the right (So this pixel is "more external") + double xdel; // Current xdelta + double ydel; // Current ydelta + TQ_INT16 result; + + for (uint col = 0; col < magnitude.count(); col++) { + for (uint row = 0; row < magnitude[col].count(); row++) { + mag = magnitude[col][row]; + if (!mag || row == 0 || row == (magnitude[col].count()-1) || + col == 0 || col == (magnitude.count()-1)) + { + result = NOEDGE; + } else { + xdel = (double)xdelta[col][row]; + ydel = (double)ydelta[col][row]; + theta = atan(fabs(ydel)/fabs(xdel)); + if (theta < 0) + theta = fabs(theta)+M_PI_2; + theta = (theta * 360.0) / (2.0*M_PI); // Radians -> degrees + if (theta >= 0 && theta < 22.5) { // .0 - .3926990816 + if (ydel >= 0) { + lmag = magnitude[col][row-1]; + rmag = magnitude[col][row+1]; + } else { + lmag = magnitude[col][row+1]; + rmag = magnitude[col][row-1]; + } + } + if (theta >= 22.5 && theta < 67.5) { // .3926990816 - 1.1780972449 + if (xdel >= 0) { + if (ydel >= 0) { + lmag = magnitude[col-1][row-1]; + rmag = magnitude[col+1][row+1]; + } else { + lmag = magnitude[col+1][row-1]; + rmag = magnitude[col-1][row+1]; + } + } else { + if (ydel >= 0) { + lmag = magnitude[col-1][row+1]; + rmag = magnitude[col+1][row-1]; + } else { + lmag = magnitude[col+1][row+1]; + rmag = magnitude[col-1][row-1]; + } + } + } + if (theta >= 67.5 && theta <= 90.0) { // 1.1780972449 - 1.5707963266 + if (xdel >= 0) { + lmag = magnitude[col+1][row]; + rmag = magnitude[col-1][row]; + } else { + lmag = magnitude[col-1][row]; + rmag = magnitude[col+1][row]; + } + } + + if ((mag < lmag) || (mag < rmag)) { + result = NOEDGE; + } else { + if (rmag == mag) // If the external magnitude is equal to the current, suppress current. + result = NOEDGE; + else + result = (mag > 255) ? 255 : mag; + } + } + nms[col][row] = result; + } + } +} + +KisToolMagnetic::KisToolMagnetic () + : super("Magnetic Outline Tool") +{ + setName("tool_moutline"); + setCursor(KisCursor::load("tool_moutline_cursor.png", 6, 6)); + + m_editingMode = false; + m_editingCursor = m_draggingCursor = false; + + m_mode = 0; + m_curve = m_derived = 0; + m_current = m_previous = 0; + + m_distance = DEFAULTDIST; + + m_transactionMessage = i18n("Magnetic Outline Selection"); +} + +KisToolMagnetic::~KisToolMagnetic () +{ + m_curve = 0; + delete m_derived; +} + +void KisToolMagnetic::update (KisCanvasSubject *subject) +{ + super::update(subject); +} + +void KisToolMagnetic::activate () +{ + super::activate(); + if (!m_derived) { + m_derived = new KisCurveMagnetic(this); + m_curve = m_derived; + } +} + +void KisToolMagnetic::deactivate () +{ + m_curve->endActionOptions(); + m_actionOptions = NOOPTIONS; + m_dragging = false; + m_drawPivots = true; +} + +void KisToolMagnetic::keyPress(TQKeyEvent *event) +{ + if (event->key() == TQt::Key_Control) { + draw(false); + if (m_editingMode) { + m_editingMode = false; + if (m_current != 0) + m_curve->selectPivot(m_current,false); + m_mode->setText(i18n("Automatic Mode")); + } else { + m_editingMode = true; + m_mode->setText(i18n("Manual Mode")); + } + draw(false); + } else if (event->key() == TQt::Key_Delete && m_curve->count()) { + draw(false); + m_dragging = false; + if (m_curve->pivots().count() == 2) + m_curve->clear(); + else { + if ((*m_current) == m_curve->last() && !(m_editingMode)) { + m_curve->deletePivot(m_current.previousPivot()); + m_previous = m_current.previousPivot(); + } else { + m_editingMode = false; + m_curve->deletePivot(m_current); + m_previous = m_current = m_curve->selectPivot(m_curve->lastIterator()); + m_editingMode = true; + } + } + draw(false); + } else + super::keyPress(event); +} + +void KisToolMagnetic::buttonRelease(KisButtonReleaseEvent *event) +{ + if (m_editingMode) { + draw(m_current); + m_editingMode = false; + if (!m_curve->isEmpty()) + m_curve->movePivot(m_current, m_currentPoint); + m_editingMode = true; + draw(m_current); + } + super::buttonRelease(event); +} + +void KisToolMagnetic::buttonPress(KisButtonPressEvent *event) +{ + updateOptions(event->state()); + if (!m_currentImage) + return; + if (event->button() == Qt::LeftButton) { + m_dragging = true; + m_currentPoint = event->pos(); + PointPair temp(m_curve->end(),false); + if (m_editingMode) + temp = pointUnderMouse (m_subject->canvasController()->windowToView(event->pos().toTQPoint())); + if (temp.first == m_curve->end() && !(m_actionOptions)) { + if (m_editingMode) { + draw(true, true); + m_curve->selectAll(false); + draw(true, true); + } + draw(m_curve->end()); + if (!m_curve->isEmpty()) { + m_previous = m_current; + m_current = m_curve->pushPivot(event->pos()); + } else { + m_previous = m_current = m_curve->pushPivot(event->pos()); + } + if (m_curve->pivots().count() > 1) + m_curve->calculateCurve(m_previous,m_current,m_current); + if (m_editingMode) + draw(); + else { + if ((*m_previous).point() == (*m_current).point()) + draw(m_curve->end()); + else + draw(); + } + } else if (temp.first != m_curve->end() && m_editingMode) { + if (temp.second) { + draw(true, true); + m_current = m_curve->selectPivot(temp.first); + draw(true, true); + } else { + draw(false); + m_current = selectByMouse(temp.first); + draw(false); + } + if (!(*m_current).isSelected()) + m_dragging = false; + } + } +} + +void KisToolMagnetic::move(KisMoveEvent *event) +{ + updateOptions(event->state()); + if (m_currentPoint == event->pos().floorTQPoint()) + return; + if (m_editingMode) { + PointPair temp = pointUnderMouse(m_subject->canvasController()->windowToView(event->pos().toTQPoint())); + if (temp.first == m_curve->end() && !m_dragging) { + if (m_editingCursor || m_draggingCursor) { + setCursor(KisCursor::load("tool_moutline_cursor.png", 6, 6)); + m_editingCursor = m_draggingCursor = false; + } + } else { + if (!m_draggingCursor && temp.second) { + setCursor(KisCursor::load("tool_moutline_dragging.png", 6, 6)); + m_editingCursor = false; + m_draggingCursor = true; + } + if (!m_editingCursor && !temp.second) { + setCursor(KisCursor::load("tool_moutline_editing.png", 6, 6)); + m_editingCursor = true; + m_draggingCursor = false; + } + } + if (!m_dragging) + return; + } else { + if (m_editingCursor || m_draggingCursor) { + setCursor(KisCursor::load("tool_moutline_cursor.png", 6, 6)); + m_editingCursor = m_draggingCursor = false; + } + } + if (m_curve->selectedPivots().isEmpty()) + return; + + KisPoint trans = event->pos() - m_currentPoint; + KisPoint dist; + dist = (*m_current).point() - (*m_current.previousPivot()).point(); + if ((m_distance >= MINDIST && (fabs(dist.x()) + fabs(dist.y())) > m_distance && !(m_editingMode)) + || m_curve->pivots().count() == 1) { + draw(m_curve->end()); + m_previous = m_current; + m_current = m_curve->pushPivot(event->pos()); + } else if ((*m_previous).point() == (*m_current).point() && (*m_previous).point() == m_curve->last().point()) + draw(m_curve->end()); + else + draw(m_current); + m_curve->movePivot(m_current,event->pos()); + m_currentPoint = event->pos().floorTQPoint(); + draw(m_current); +} + +KisCurve::iterator KisToolMagnetic::selectByMouse(KisCurve::iterator it) +{ + KisCurve::iterator currPivot = m_curve->selectPivot(m_curve->addPivot(it, KisPoint(0,0))); + m_curve->movePivot(currPivot,(*it).point()); + + return currPivot; +} + +void KisToolMagnetic::slotCommitCurve () +{ + if (!m_curve->isEmpty()) + commitCurve(); +} + +void KisToolMagnetic::slotSetDistance (int dist) +{ + m_distance = dist; +} + +TQWidget* KisToolMagnetic::createOptionWidget(TQWidget* tqparent) +{ + m_optWidget = super::createOptionWidget(tqparent); + TQVBoxLayout * l = dynamic_cast<TQVBoxLayout*>(m_optWidget->tqlayout()); + TQGridLayout *box = new TQGridLayout(l, 2, 2, 3); + box->setColStretch(0, 1); + box->setColStretch(1, 1); + Q_CHECK_PTR(box); + + m_mode = new TQLabel(i18n("Automatic mode"), m_optWidget); + m_lbDistance = new TQLabel(i18n("Distance: "), m_optWidget); + TQPushButton *finish = new TQPushButton(i18n("To Selection"), m_optWidget); + m_slDistance = new TQSlider(MINDIST, MAXDIST, PAGESTEP, m_distance, Qt::Horizontal, m_optWidget); + + connect(m_slDistance, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(slotSetDistance(int))); + connect(finish, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotCommitCurve())); + + box->addWidget(m_lbDistance, 0, 0); + box->addWidget(m_slDistance, 0, 1); + box->addWidget(m_mode, 1, 0); + box->addWidget(finish, 1, 1); + + return m_optWidget; +} + +void KisToolMagnetic::setup(KActionCollection *collection) +{ + m_action = static_cast<KRadioAction *>(collection->action(name())); + + if (m_action == 0) { + KShortcut shortcut(TQt::Key_Plus); + shortcut.append(KShortcut(TQt::Key_F9)); + m_action = new KRadioAction(i18n("Magnetic Outline"), + "tool_moutline", + shortcut, + this, + TQT_SLOT(activate()), + collection, + name()); + Q_CHECK_PTR(m_action); + + m_action->setToolTip(i18n("Magnetic Selection: move around an edge to select it. Hit Ctrl to enter/quit manual mode, and double click to finish.")); + m_action->setExclusiveGroup("tools"); + m_ownAction = true; + } +} + +#include "kis_tool_moutline.moc" |