bool KoEnhancedPathCommand::execute() { /* * The parameters of the commands are in viewbox coordinates, which have * to be converted to the shapes coordinate system by calling viewboxToShape * on the enhanced path the command works on. * Parameters which resemble angles are angles corresponding to the viewbox * coordinate system. Those have to be transformed into angles corresponding * to the normal mathematically coordinate system to be used for the arcTo * drawing routine. This is done by computing (2*M_PI - angle). */ QList<QPointF> points = pointsFromParameters(); uint pointsCount = points.size(); switch( m_command.toAscii() ) { // starts new subpath at given position (x y) + case 'M': if( ! pointsCount ) return false; m_parent->moveTo( m_parent->viewboxToShape( points[0] ) ); if( pointsCount > 1 ) for( uint i = 1; i < pointsCount; i++ ) m_parent->lineTo( m_parent->viewboxToShape( points[i] ) ); break; // line from current point (x y) + case 'L': foreach( const QPointF &point, points ) m_parent->lineTo( m_parent->viewboxToShape( point ) ); break; // cubic bezier curve from current point (x1 y1 x2 y2 x y) + case 'C': for( uint i = 0; i < pointsCount; i+=3 ) m_parent->curveTo( m_parent->viewboxToShape( points[i] ), m_parent->viewboxToShape( points[i+1] ), m_parent->viewboxToShape( points[i+2] ) ); break; // closes the current subpath case 'Z': m_parent->close(); break; // ends the current set of subpaths case 'N': // N just ends the complete path break; // no fill for current set of subpaths case 'F': // TODO implement me break; // no stroke for current set of subpaths case 'S': // TODO implement me break; // segment of an ellipse (x y w h t0 t1) + case 'T': // same like T but with implied movement to starting point (x y w h t0 t1) + case 'U': { bool lineTo = m_command == 'T'; for( uint i = 0; i < pointsCount; i+=3 ) { const QPointF &radii = m_parent->viewboxToShape( points[i+1] ); const QPointF &angles = points[i+2] / rad2deg; // compute the ellipses starting point QPointF start( radii.x() * cos( angles.x() ), radii.y() * sin( angles.x() ) ); qreal sweepAngle = degSweepAngle( points[i+2].x(), points[i+2].y(), false ); if( lineTo ) m_parent->lineTo( m_parent->viewboxToShape( points[i] ) + start ); else m_parent->moveTo( m_parent->viewboxToShape( points[i] ) + start ); m_parent->arcTo( radii.x(), radii.y(), points[i+2].x(), sweepAngle ); } } break; // counter-clockwise arc (x1 y1 x2 y2 x3 y3 x y) + case 'A': // the same as A, with implied moveto to the starting point (x1 y1 x2 y2 x3 y3 x y) + case 'B': { bool lineTo = m_command == 'A'; for( uint i = 0; i < pointsCount; i+=4 ) { QRectF bbox = rectFromPoints( points[i], points[i+1] ); QPointF center = bbox.center(); qreal rx = 0.5 * m_parent->viewboxToShape( bbox.width() ); qreal ry = 0.5 * m_parent->viewboxToShape( bbox.height() ); qreal startAngle = angleFromPoint( points[i+2] - center ); qreal stopAngle = angleFromPoint( points[i+3] - center ); // we are moving counter-clockwise to the end angle qreal sweepAngle = radSweepAngle( startAngle, stopAngle, false ); // compute the starting point to draw the line to QPointF startPoint( rx * cos( startAngle ), ry * sin( 2*M_PI - startAngle ) ); if( lineTo ) m_parent->moveTo( m_parent->viewboxToShape( center ) + startPoint ); else m_parent->moveTo( m_parent->viewboxToShape( center ) + startPoint ); m_parent->arcTo( rx, ry, startAngle * rad2deg, sweepAngle * rad2deg ); } } break; // clockwise arc (x1 y1 x2 y2 x3 y3 x y) + case 'W': // the same as W, but implied moveto (x1 y1 x2 y2 x3 y3 x y) + case 'V': { bool lineTo = m_command == 'W'; for( uint i = 0; i < pointsCount; i+=4 ) { QRectF bbox = rectFromPoints( points[i], points[i+1] ); QPointF center = bbox.center(); qreal rx = 0.5 * m_parent->viewboxToShape( bbox.width() ); qreal ry = 0.5 * m_parent->viewboxToShape( bbox.height() ); qreal startAngle = angleFromPoint( points[i+2] - center ); qreal stopAngle = angleFromPoint( points[i+3] - center ); // we are moving clockwise to the end angle qreal sweepAngle = radSweepAngle( startAngle, stopAngle, true ); if( lineTo ) m_parent->lineTo( m_parent->viewboxToShape( points[i+2] ) ); else m_parent->lineTo( m_parent->viewboxToShape( points[i+2] ) ); m_parent->arcTo( rx, ry, startAngle * rad2deg, sweepAngle * rad2deg ); } } break; // elliptical quadrant (initial segment tangential to x-axis) (x y) + case 'X': { KoPathPoint * lastPoint = lastPathPoint(); foreach( QPointF point, points ) { point = m_parent->viewboxToShape( point ); qreal rx = point.x() - lastPoint->point().x(); qreal ry = point.y() - lastPoint->point().y(); qreal startAngle = ry > 0.0 ? 90.0 : 270.0; qreal sweepAngle = rx*ry < 0.0 ? 90.0 : -90.0; lastPoint = m_parent->arcTo( fabs(rx), fabs(ry), startAngle, sweepAngle ); } } break; // elliptical quadrant (initial segment tangential to y-axis) (x y) + case 'Y': { KoPathPoint * lastPoint = lastPathPoint(); foreach( QPointF point, points ) { point = m_parent->viewboxToShape( point ); qreal rx = point.x() - lastPoint->point().x(); qreal ry = point.y() - lastPoint->point().y(); qreal startAngle = rx < 0.0 ? 0.0 : 180.0; qreal sweepAngle = rx*ry > 0.0 ? 90.0 : -90.0; lastPoint = m_parent->arcTo( fabs(rx), fabs(ry), startAngle, sweepAngle ); } }
bool EnhancedPathCommand::execute() { /* * The parameters of the commands are in viewbox coordinates, which have * to be converted to the shapes coordinate system by calling viewboxToShape * on the enhanced path the command works on. * Parameters which resemble angles are angles corresponding to the viewbox * coordinate system. Those have to be transformed into angles corresponding * to the normal mathematically coordinate system to be used for the arcTo * drawing routine. This is done by computing (2*M_PI - angle). */ QList<QPointF> points = pointsFromParameters(); const int pointsCount = points.size(); switch (m_command.unicode()) { // starts new subpath at given position (x y) + case 'M': if (!pointsCount) return false; m_parent->moveTo(points[0]); if (pointsCount > 1) for (int i = 1; i < pointsCount; i++) m_parent->lineTo(points[i]); break; // line from current point (x y) + case 'L': foreach(const QPointF &point, points) m_parent->lineTo(point); break; // cubic bezier curve from current point (x1 y1 x2 y2 x y) + case 'C': for (int i = 0; i < pointsCount; i+=3) m_parent->curveTo(points[i], points[i+1], points[i+2]); break; // closes the current subpath case 'Z': m_parent->close(); break; // ends the current set of subpaths case 'N': // N just ends the complete path break; // no fill for current set of subpaths case 'F': // TODO implement me break; // no stroke for current set of subpaths case 'S': // TODO implement me break; // segment of an ellipse (x y w h t0 t1) + case 'T': // same like T but with implied movement to starting point (x y w h t0 t1) + case 'U': { bool lineTo = m_command.unicode() == 'T'; for (int i = 0; i < pointsCount; i+=3) { const QPointF &radii = points[i+1]; const QPointF &angles = points[i+2] / rad2deg; // compute the ellipses starting point QPointF start(radii.x() * cos(angles.x()), -1 * radii.y() * sin(angles.x())); qreal sweepAngle = degSweepAngle(points[i+2].x(), points[i+2].y(), false); if (lineTo) m_parent->lineTo(points[i] + start); else m_parent->moveTo(points[i] + start); m_parent->arcTo(radii.x(), radii.y(), points[i+2].x(), sweepAngle); } break; } // counter-clockwise arc (x1 y1 x2 y2 x3 y3 x y) + case 'A': // the same as A, with implied moveto to the starting point (x1 y1 x2 y2 x3 y3 x y) + case 'B': // clockwise arc (x1 y1 x2 y2 x3 y3 x y) + case 'W': // the same as W, but implied moveto (x1 y1 x2 y2 x3 y3 x y) + case 'V': { bool lineTo = ((m_command.unicode() == 'A') || (m_command.unicode() == 'W')); bool clockwise = ((m_command.unicode() == 'W') || (m_command.unicode() == 'V')); for (int i = 0; i < pointsCount; i+=4) { QRectF bbox = rectFromPoints(points[i], points[i+1]); QPointF center = bbox.center(); qreal rx = 0.5 * bbox.width(); qreal ry = 0.5 * bbox.height(); if (rx == 0) { rx = 1; } if (ry == 0) { ry = 1; } QPointF startRadialVector = points[i+2] - center; QPointF endRadialVector = points[i+3] - center; // convert from ellipse space to unit-circle space qreal x0 = startRadialVector.x() / rx; qreal y0 = startRadialVector.y() / ry; qreal x1 = endRadialVector.x() / rx; qreal y1 = endRadialVector.y() / ry; qreal startAngle = angleFromPoint(QPointF(x0,y0)); qreal stopAngle = angleFromPoint(QPointF(x1,y1)); // we are moving counter-clockwise to the end angle qreal sweepAngle = radSweepAngle(startAngle, stopAngle, clockwise); // compute the starting point to draw the line to // as the point x3 y3 is not on the ellipse, spec says the point define radial vector QPointF startPoint(rx * cos(startAngle), ry * sin(2*M_PI - startAngle)); // if A or W is first command in enhanced path // move to the starting point bool isFirstCommandInPath = (m_parent->subpathCount() == 0); bool isFirstCommandInSubpath = m_parent->isClosedSubpath( m_parent->subpathCount() - 1 ); if (lineTo && !isFirstCommandInPath && !isFirstCommandInSubpath) { m_parent->lineTo(center + startPoint); } else { m_parent->moveTo(center + startPoint); } m_parent->arcTo(rx, ry, startAngle * rad2deg, sweepAngle * rad2deg); } break; } // elliptical quadrant (initial segment tangential to x-axis) (x y) + case 'X': { KoPathPoint * lastPoint = lastPathPoint(); bool xDir = true; foreach (const QPointF &point, points) { qreal rx = point.x() - lastPoint->point().x(); qreal ry = point.y() - lastPoint->point().y(); qreal startAngle = xDir ? (ry > 0.0 ? 90.0 : 270.0) : (rx < 0.0 ? 0.0 : 180.0); qreal sweepAngle = xDir ? (rx*ry < 0.0 ? 90.0 : -90.0) : (rx*ry > 0.0 ? 90.0 : -90.0); lastPoint = m_parent->arcTo(fabs(rx), fabs(ry), startAngle, sweepAngle); xDir = !xDir; } break; } // elliptical quadrant (initial segment tangential to y-axis) (x y) + case 'Y': { KoPathPoint * lastPoint = lastPathPoint(); bool xDir = false; foreach (const QPointF &point, points) { qreal rx = point.x() - lastPoint->point().x(); qreal ry = point.y() - lastPoint->point().y(); qreal startAngle = xDir ? (ry > 0.0 ? 90.0 : 270.0) : (rx < 0.0 ? 0.0 : 180.0); qreal sweepAngle = xDir ? (rx*ry < 0.0 ? 90.0 : -90.0) : (rx*ry > 0.0 ? 90.0 : -90.0); lastPoint = m_parent->arcTo(fabs(rx), fabs(ry), startAngle, sweepAngle); xDir = !xDir; } break; }