66
void KarbonCalligraphyTool::paint( QPainter &painter,
67
const KoViewConverter &converter )
66
void KarbonCalligraphyTool::paint(QPainter &painter,
67
const KoViewConverter &converter)
72
painter.setRenderHints( QPainter::Antialiasing, false );
73
painter.setPen( Qt::red ); // TODO make configurable
71
painter.setRenderHints(QPainter::Antialiasing, false);
72
painter.setPen(Qt::red); // TODO make configurable
74
73
QRectF rect = m_selectedPath->boundingRect();
75
QPointF p1 = converter.documentToView( rect.topLeft() );
76
QPointF p2 = converter.documentToView( rect.bottomRight() );
77
painter.drawRect( QRectF(p1, p2) );
74
QPointF p1 = converter.documentToView(rect.topLeft());
75
QPointF p2 = converter.documentToView(rect.bottomRight());
76
painter.drawRect(QRectF(p1, p2));
86
painter.setMatrix( m_shape->absoluteTransformation(&converter) *
88
m_shape->paint( painter, converter );
85
painter.setMatrix(m_shape->absoluteTransformation(&converter) *
87
m_shape->paint(painter, converter);
93
void KarbonCalligraphyTool::mousePressEvent( KoPointerEvent *event )
92
void KarbonCalligraphyTool::mousePressEvent(KoPointerEvent *event)
98
97
m_lastPoint = event->point;
101
100
m_isDrawing = true;
102
101
m_pointCount = 0;
103
m_shape = new KarbonCalligraphicShape( m_caps );
104
m_shape->setBackground( new KoColorBackground( m_canvas->resourceProvider()->backgroundColor().toQColor() ) );
102
m_shape = new KarbonCalligraphicShape(m_caps);
103
m_shape->setBackground(new KoColorBackground(canvas()->resourceManager()->backgroundColor().toQColor()));
105
104
//addPoint( event );
108
void KarbonCalligraphyTool::mouseMoveEvent( KoPointerEvent *event )
107
void KarbonCalligraphyTool::mouseMoveEvent(KoPointerEvent *event)
116
void KarbonCalligraphyTool::mouseReleaseEvent( KoPointerEvent *event )
115
void KarbonCalligraphyTool::mouseReleaseEvent(KoPointerEvent *event)
121
if ( m_pointCount == 0 )
120
if (m_pointCount == 0) {
123
121
// handle click: select shape (if any)
124
if ( event->point == m_lastPoint )
126
KoShapeManager *shapeManager = m_canvas->shapeManager();
127
KoShape *selectedShape = shapeManager->shapeAt( event->point );
128
if ( selectedShape != 0 )
122
if (event->point == m_lastPoint) {
123
KoShapeManager *shapeManager = canvas()->shapeManager();
124
KoShape *selectedShape = shapeManager->shapeAt(event->point);
125
if (selectedShape != 0) {
130
126
shapeManager->selection()->deselectAll();
131
shapeManager->selection()->select( selectedShape );
127
shapeManager->selection()->select(selectedShape);
165
void KarbonCalligraphyTool::addPoint( KoPointerEvent *event )
156
void KarbonCalligraphyTool::addPoint(KoPointerEvent *event)
167
if ( m_pointCount == 0 )
169
if ( m_usePath && m_selectedPath )
158
if (m_pointCount == 0) {
159
if (m_usePath && m_selectedPath)
170
160
m_selectedPathOutline = m_selectedPath->outline();
171
161
m_pointCount = 1;
172
162
m_endOfPath = false;
173
163
m_followPathPosition = 0;
174
164
m_lastMousePos = event->point;
175
m_lastPoint = calculateNewPoint( event->point, &m_speed );
176
m_deviceSupportsTilt = ( event->xTilt() != 0 || event->yTilt() != 0 );
165
m_lastPoint = calculateNewPoint(event->point, &m_speed);
166
m_deviceSupportsTilt = (event->xTilt() != 0 || event->yTilt() != 0);
187
177
QPointF newSpeed;
188
QPointF newPoint= calculateNewPoint( event->point, &newSpeed );
189
qreal width = calculateWidth( event->pressure() );
190
qreal angle = calculateAngle( m_speed, newSpeed );
178
QPointF newPoint = calculateNewPoint(event->point, &newSpeed);
179
qreal width = calculateWidth(event->pressure());
180
qreal angle = calculateAngle(m_speed, newSpeed);
192
182
// add the previous point
193
m_shape->appendPoint( m_lastPoint, angle, width );
183
m_shape->appendPoint(m_lastPoint, angle, width);
195
185
m_speed = newSpeed;
196
186
m_lastPoint = newPoint;
197
m_canvas->updateCanvas( m_shape->lastPieceBoundingRect() );
187
canvas()->updateCanvas(m_shape->lastPieceBoundingRect());
199
if ( m_usePath && m_selectedPath )
189
if (m_usePath && m_selectedPath)
200
190
m_speed = QPointF(0, 0); // following path
203
void KarbonCalligraphyTool::setAngle( KoPointerEvent *event )
193
void KarbonCalligraphyTool::setAngle(KoPointerEvent *event)
207
196
m_angle = (360 - m_customAngle + 90) / 180.0 * M_PI;
211
200
// setting m_angle to the angle of the device
212
if ( event->xTilt() != 0 || event->yTilt() != 0 )
201
if (event->xTilt() != 0 || event->yTilt() != 0)
213
202
m_deviceSupportsTilt = false;
215
if ( m_deviceSupportsTilt )
217
if ( event->xTilt() == 0 && event->yTilt() == 0 )
204
if (m_deviceSupportsTilt) {
205
if (event->xTilt() == 0 && event->yTilt() == 0)
218
206
return; // leave as is
219
207
kDebug(38000) << "using tilt" << m_angle;
221
if ( event->x() == 0 )
209
if (event->x() == 0) {
227
214
// y is inverted in qt painting
228
m_angle = std::atan( static_cast<double>(-event->yTilt() / event->xTilt()) ) + M_PI/2;
232
m_angle = event->rotation() + M_PI/2;
215
m_angle = std::atan(static_cast<double>(-event->yTilt() / event->xTilt())) + M_PI / 2;
217
m_angle = event->rotation() + M_PI / 2;
233
218
kDebug(38000) << "using rotation" << m_angle;
238
QPointF KarbonCalligraphyTool::calculateNewPoint( const QPointF &mousePos,
223
QPointF KarbonCalligraphyTool::calculateNewPoint(const QPointF &mousePos,
241
if ( !m_usePath || !m_selectedPath ) // don't follow path
226
if (!m_usePath || !m_selectedPath) { // don't follow path
243
227
QPointF force = mousePos - m_lastPoint;
244
228
QPointF dSpeed = force / m_mass;
245
229
*speed = m_speed * (1.0 - m_drag) + dSpeed;
246
return m_lastPoint + *speed;
230
return m_lastPoint + *speed;
249
233
QPointF sp = mousePos - m_lastMousePos;
250
234
m_lastMousePos = mousePos;
252
236
// follow selected path
253
qreal step = QLineF(QPointF(0,0), sp).length();
237
qreal step = QLineF(QPointF(0, 0), sp).length();
254
238
m_followPathPosition += step;
257
if (m_followPathPosition >= m_selectedPathOutline.length())
241
if (m_followPathPosition >= m_selectedPathOutline.length()) {
260
243
m_endOfPath = true;
264
t = m_selectedPathOutline.percentAtLength( m_followPathPosition );
245
t = m_selectedPathOutline.percentAtLength(m_followPathPosition);
267
248
QPointF res = m_selectedPathOutline.pointAtPercent(t)
273
qreal KarbonCalligraphyTool::calculateWidth( qreal pressure )
254
qreal KarbonCalligraphyTool::calculateWidth(qreal pressure)
275
256
// calculate the modulo of the speed
276
qreal speed = std::sqrt( pow(m_speed.x(), 2) + pow(m_speed.y(), 2) );
257
qreal speed = std::sqrt(pow(m_speed.x(), 2) + pow(m_speed.y(), 2));
277
258
qreal thinning = m_thinning * (speed + 1) / 10.0; // can be negative
282
if ( ! m_usePressure )
285
266
qreal strokeWidth = m_strokeWidth * pressure * (1 - thinning);
287
268
const qreal MINIMUM_STROKE_WIDTH = 1.0;
288
if ( strokeWidth < MINIMUM_STROKE_WIDTH )
269
if (strokeWidth < MINIMUM_STROKE_WIDTH)
289
270
strokeWidth = MINIMUM_STROKE_WIDTH;
291
272
return strokeWidth;
295
qreal KarbonCalligraphyTool::calculateAngle( const QPointF &oldSpeed,
296
const QPointF &newSpeed )
276
qreal KarbonCalligraphyTool::calculateAngle(const QPointF &oldSpeed,
277
const QPointF &newSpeed)
298
279
// calculate the avarage of the speed (sum of the normalized values)
299
qreal oldLength = QLineF( QPointF(0,0), oldSpeed ).length();
300
qreal newLength = QLineF( QPointF(0,0), newSpeed ).length();
280
qreal oldLength = QLineF(QPointF(0, 0), oldSpeed).length();
281
qreal newLength = QLineF(QPointF(0, 0), newSpeed).length();
301
282
QPointF oldSpeedNorm = !qFuzzyCompare(oldLength + 1, 1) ?
302
oldSpeed/oldLength : QPointF(0, 0);
283
oldSpeed / oldLength : QPointF(0, 0);
303
284
QPointF newSpeedNorm = !qFuzzyCompare(newLength + 1, 1) ?
304
newSpeed/newLength : QPointF(0, 0);
285
newSpeed / newLength : QPointF(0, 0);
305
286
QPointF speed = oldSpeedNorm + newSpeedNorm;
307
288
// angle solely based on the speed
308
289
qreal speedAngle = 0;
309
if ( speed.x() != 0 ) // avoid division by zero
311
speedAngle = std::atan( speed.y() / speed.x() );
313
else if ( speed.y() > 0 )
318
else if ( speed.y() < 0 )
321
speedAngle = -M_PI/2;
290
if (speed.x() != 0) { // avoid division by zero
291
speedAngle = std::atan(speed.y() / speed.x());
292
} else if (speed.y() > 0) {
294
speedAngle = M_PI / 2;
295
} else if (speed.y() < 0) {
297
speedAngle = -M_PI / 2;
324
300
speedAngle += M_PI;
326
302
// move 90 degrees
327
speedAngle += M_PI/2;
303
speedAngle += M_PI / 2;
329
305
qreal fixedAngle = m_angle;
330
306
// check if the fixed angle needs to be flipped
331
307
qreal diff = fixedAngle - speedAngle;
332
while ( diff >= M_PI ) // normalize diff between -180 and 180
334
while ( diff < -M_PI )
308
while (diff >= M_PI) // normalize diff between -180 and 180
337
if ( std::abs(diff) > M_PI/2 ) // if absolute value < 90
313
if (std::abs(diff) > M_PI / 2) // if absolute value < 90
338
314
fixedAngle += M_PI; // += 180
340
316
qreal dAngle = speedAngle - fixedAngle;
342
318
// normalize dAngle between -90 and +90
343
while ( dAngle >= M_PI/2 )
319
while (dAngle >= M_PI / 2)
345
while ( dAngle < -M_PI/2 )
321
while (dAngle < -M_PI / 2)
348
qreal angle = fixedAngle + dAngle*(1.0 - m_fixation);
324
qreal angle = fixedAngle + dAngle * (1.0 - m_fixation);
353
void KarbonCalligraphyTool::activate( bool )
329
void KarbonCalligraphyTool::activate(ToolActivation, const QSet<KoShape*> &)
355
useCursor(Qt::ArrowCursor, true);
331
useCursor(Qt::ArrowCursor);
359
335
void KarbonCalligraphyTool::deactivate()
363
KoSelection *selection = m_canvas->shapeManager()->selection();
337
if (m_lastShape && canvas()->shapeManager()->shapes().contains(m_lastShape)) {
338
KoSelection *selection = canvas()->shapeManager()->selection();
364
339
selection->deselectAll();
365
selection->select( m_lastShape );
340
selection->select(m_lastShape);
371
346
// if the widget don't exists yet create it
372
347
KarbonCalligraphyOptionWidget *widget = new KarbonCalligraphyOptionWidget;
373
connect( widget, SIGNAL(usePathChanged(bool)),
348
connect(widget, SIGNAL(usePathChanged(bool)),
374
349
this, SLOT(setUsePath(bool)));
376
connect( widget, SIGNAL(usePressureChanged(bool)),
351
connect(widget, SIGNAL(usePressureChanged(bool)),
377
352
this, SLOT(setUsePressure(bool)));
379
connect( widget, SIGNAL(useAngleChanged(bool)),
354
connect(widget, SIGNAL(useAngleChanged(bool)),
380
355
this, SLOT(setUseAngle(bool)));
382
connect( widget, SIGNAL(widthChanged(double)),
383
this, SLOT(setStrokeWidth(double)));
385
connect( widget, SIGNAL(thinningChanged(double)),
386
this, SLOT(setThinning(double)));
388
connect( widget, SIGNAL(angleChanged(int)),
389
this, SLOT(setAngle(int)));
391
connect( widget, SIGNAL(fixationChanged(double)),
392
this, SLOT(setFixation(double)));
394
connect( widget, SIGNAL(capsChanged(double)),
395
this, SLOT(setCaps(double)));
397
connect( widget, SIGNAL(massChanged(double)),
398
this, SLOT(setMass(double)));
400
connect( widget, SIGNAL(dragChanged(double)),
401
this, SLOT(setDrag(double)));
403
connect( this, SIGNAL(pathSelectedChanged(bool)),
404
widget, SLOT(setUsePathEnabled(bool)) );
357
connect(widget, SIGNAL(widthChanged(double)),
358
this, SLOT(setStrokeWidth(double)));
360
connect(widget, SIGNAL(thinningChanged(double)),
361
this, SLOT(setThinning(double)));
363
connect(widget, SIGNAL(angleChanged(int)),
364
this, SLOT(setAngle(int)));
366
connect(widget, SIGNAL(fixationChanged(double)),
367
this, SLOT(setFixation(double)));
369
connect(widget, SIGNAL(capsChanged(double)),
370
this, SLOT(setCaps(double)));
372
connect(widget, SIGNAL(massChanged(double)),
373
this, SLOT(setMass(double)));
375
connect(widget, SIGNAL(dragChanged(double)),
376
this, SLOT(setDrag(double)));
378
connect(this, SIGNAL(pathSelectedChanged(bool)),
379
widget, SLOT(setUsePathEnabled(bool)));
407
KAction *action = new KAction( i18n("Calligraphy: increase width"), this );
408
action->setShortcut( Qt::Key_Right );
409
connect( action, SIGNAL(triggered()), widget, SLOT(increaseWidth()) );
410
addAction( "calligraphy_increase_width", action );
412
action = new KAction( i18n("Calligraphy: decrease width"), this );
413
action->setShortcut( Qt::Key_Left );
414
connect( action, SIGNAL(triggered()), widget, SLOT(decreaseWidth()) );
415
addAction( "calligraphy_decrease_width", action );
417
action = new KAction( i18n("Calligraphy: increase angle"), this );
418
action->setShortcut( Qt::Key_Up );
419
connect( action, SIGNAL(triggered()), widget, SLOT(increaseAngle()) );
420
addAction( "calligraphy_increase_angle", action );
422
action = new KAction( i18n("Calligraphy: decrease angle"), this );
423
action->setShortcut( Qt::Key_Down );
424
connect( action, SIGNAL(triggered()), widget, SLOT(decreaseAngle()) );
425
addAction( "calligraphy_decrease_angle", action );
382
KAction *action = new KAction(i18n("Calligraphy: increase width"), this);
383
action->setShortcut(Qt::Key_Right);
384
connect(action, SIGNAL(triggered()), widget, SLOT(increaseWidth()));
385
addAction("calligraphy_increase_width", action);
387
action = new KAction(i18n("Calligraphy: decrease width"), this);
388
action->setShortcut(Qt::Key_Left);
389
connect(action, SIGNAL(triggered()), widget, SLOT(decreaseWidth()));
390
addAction("calligraphy_decrease_width", action);
392
action = new KAction(i18n("Calligraphy: increase angle"), this);
393
action->setShortcut(Qt::Key_Up);
394
connect(action, SIGNAL(triggered()), widget, SLOT(increaseAngle()));
395
addAction("calligraphy_increase_angle", action);
397
action = new KAction(i18n("Calligraphy: decrease angle"), this);
398
action->setShortcut(Qt::Key_Down);
399
connect(action, SIGNAL(triggered()), widget, SLOT(decreaseAngle()));
400
addAction("calligraphy_decrease_angle", action);
427
402
// sync all parameters with the loaded profile
428
403
widget->emitAll();
433
void KarbonCalligraphyTool::setStrokeWidth( double width )
408
void KarbonCalligraphyTool::setStrokeWidth(double width)
435
410
m_strokeWidth = width;
438
void KarbonCalligraphyTool::setThinning( double thinning )
413
void KarbonCalligraphyTool::setThinning(double thinning)
440
415
m_thinning = thinning;
443
void KarbonCalligraphyTool::setAngle( int angle )
418
void KarbonCalligraphyTool::setAngle(int angle)
445
420
m_customAngle = angle;
448
void KarbonCalligraphyTool::setFixation( double fixation )
423
void KarbonCalligraphyTool::setFixation(double fixation)
450
425
m_fixation = fixation;
453
void KarbonCalligraphyTool::setMass( double mass )
428
void KarbonCalligraphyTool::setMass(double mass)
455
430
m_mass = mass * mass + 1;
458
void KarbonCalligraphyTool::setDrag( double drag )
433
void KarbonCalligraphyTool::setDrag(double drag)
463
void KarbonCalligraphyTool::setUsePath( bool usePath )
438
void KarbonCalligraphyTool::setUsePath(bool usePath)
465
440
m_usePath = usePath;
466
441
//if ( m_selectedPath )
467
// m_canvas->updateCanvas( m_selectedPath->boundingRect() );
442
// canvas()->updateCanvas( m_selectedPath->boundingRect() );
470
void KarbonCalligraphyTool::setUsePressure( bool usePressure )
445
void KarbonCalligraphyTool::setUsePressure(bool usePressure)
472
447
m_usePressure = usePressure;
475
void KarbonCalligraphyTool::setUseAngle(bool useAngle )
450
void KarbonCalligraphyTool::setUseAngle(bool useAngle)
477
452
m_useAngle = useAngle;
480
void KarbonCalligraphyTool::setCaps( double caps )
455
void KarbonCalligraphyTool::setCaps(double caps)