204 |
213 |
205 endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); |
214 endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); |
206 } |
215 } |
207 } |
216 } |
208 |
217 |
|
218 void QTriangulatingStroker::moveTo(const qreal *pts) |
|
219 { |
|
220 m_cx = pts[0]; |
|
221 m_cy = pts[1]; |
|
222 |
|
223 float x2 = pts[2]; |
|
224 float y2 = pts[3]; |
|
225 normalVector(m_cx, m_cy, x2, y2, &m_nvx, &m_nvy); |
|
226 |
|
227 |
|
228 // To acheive jumps we insert zero-area tringles. This is done by |
|
229 // adding two identical points in both the end of previous strip |
|
230 // and beginning of next strip |
|
231 bool invisibleJump = m_vertices.size(); |
|
232 |
|
233 switch (m_cap_style) { |
|
234 case Qt::FlatCap: |
|
235 if (invisibleJump) { |
|
236 m_vertices.add(m_cx + m_nvx); |
|
237 m_vertices.add(m_cy + m_nvy); |
|
238 } |
|
239 break; |
|
240 case Qt::SquareCap: { |
|
241 float sx = m_cx - m_nvy; |
|
242 float sy = m_cy + m_nvx; |
|
243 if (invisibleJump) { |
|
244 m_vertices.add(sx + m_nvx); |
|
245 m_vertices.add(sy + m_nvy); |
|
246 } |
|
247 emitLineSegment(sx, sy, m_nvx, m_nvy); |
|
248 break; } |
|
249 case Qt::RoundCap: { |
|
250 QVarLengthArray<float> points; |
|
251 arcPoints(m_cx, m_cy, m_cx + m_nvx, m_cy + m_nvy, m_cx - m_nvx, m_cy - m_nvy, points); |
|
252 m_vertices.resize(m_vertices.size() + points.size() + 2 * int(invisibleJump)); |
|
253 int count = m_vertices.size(); |
|
254 int front = 0; |
|
255 int end = points.size() / 2; |
|
256 while (front != end) { |
|
257 m_vertices.at(--count) = points[2 * end - 1]; |
|
258 m_vertices.at(--count) = points[2 * end - 2]; |
|
259 --end; |
|
260 if (front == end) |
|
261 break; |
|
262 m_vertices.at(--count) = points[2 * front + 1]; |
|
263 m_vertices.at(--count) = points[2 * front + 0]; |
|
264 ++front; |
|
265 } |
|
266 |
|
267 if (invisibleJump) { |
|
268 m_vertices.at(count - 1) = m_vertices.at(count + 1); |
|
269 m_vertices.at(count - 2) = m_vertices.at(count + 0); |
|
270 } |
|
271 break; } |
|
272 default: break; // ssssh gcc... |
|
273 } |
|
274 emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); |
|
275 } |
|
276 |
209 void QTriangulatingStroker::cubicTo(const qreal *pts) |
277 void QTriangulatingStroker::cubicTo(const qreal *pts) |
210 { |
278 { |
211 const QPointF *p = (const QPointF *) pts; |
279 const QPointF *p = (const QPointF *) pts; |
212 QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]); |
280 QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]); |
213 |
281 |
241 |
309 |
242 m_nvx = vx; |
310 m_nvx = vx; |
243 m_nvy = vy; |
311 m_nvy = vy; |
244 } |
312 } |
245 |
313 |
246 |
314 void QTriangulatingStroker::join(const qreal *pts) |
|
315 { |
|
316 // Creates a join to the next segment (m_cx, m_cy) -> (pts[0], pts[1]) |
|
317 normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy); |
|
318 |
|
319 switch (m_join_style) { |
|
320 case Qt::BevelJoin: |
|
321 break; |
|
322 case Qt::SvgMiterJoin: |
|
323 case Qt::MiterJoin: { |
|
324 // Find out on which side the join should be. |
|
325 int count = m_vertices.size(); |
|
326 float prevNvx = m_vertices.at(count - 2) - m_cx; |
|
327 float prevNvy = m_vertices.at(count - 1) - m_cy; |
|
328 float xprod = prevNvx * m_nvy - prevNvy * m_nvx; |
|
329 float px, py, qx, qy; |
|
330 |
|
331 // If the segments are parallel, use bevel join. |
|
332 if (qFuzzyIsNull(xprod)) |
|
333 break; |
|
334 |
|
335 // Find the corners of the previous and next segment to join. |
|
336 if (xprod < 0) { |
|
337 px = m_vertices.at(count - 2); |
|
338 py = m_vertices.at(count - 1); |
|
339 qx = m_cx - m_nvx; |
|
340 qy = m_cy - m_nvy; |
|
341 } else { |
|
342 px = m_vertices.at(count - 4); |
|
343 py = m_vertices.at(count - 3); |
|
344 qx = m_cx + m_nvx; |
|
345 qy = m_cy + m_nvy; |
|
346 } |
|
347 |
|
348 // Find intersection point. |
|
349 float pu = px * prevNvx + py * prevNvy; |
|
350 float qv = qx * m_nvx + qy * m_nvy; |
|
351 float ix = (m_nvy * pu - prevNvy * qv) / xprod; |
|
352 float iy = (prevNvx * qv - m_nvx * pu) / xprod; |
|
353 |
|
354 // Check that the distance to the intersection point is less than the miter limit. |
|
355 if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= m_miter_limit * m_miter_limit) { |
|
356 m_vertices.add(ix); |
|
357 m_vertices.add(iy); |
|
358 m_vertices.add(ix); |
|
359 m_vertices.add(iy); |
|
360 } |
|
361 // else |
|
362 // Do a plain bevel join if the miter limit is exceeded or if |
|
363 // the lines are parallel. This is not what the raster |
|
364 // engine's stroker does, but it is both faster and similar to |
|
365 // what some other graphics API's do. |
|
366 |
|
367 break; } |
|
368 case Qt::RoundJoin: { |
|
369 QVarLengthArray<float> points; |
|
370 int count = m_vertices.size(); |
|
371 float prevNvx = m_vertices.at(count - 2) - m_cx; |
|
372 float prevNvy = m_vertices.at(count - 1) - m_cy; |
|
373 if (m_nvx * prevNvy - m_nvy * prevNvx < 0) { |
|
374 arcPoints(0, 0, m_nvx, m_nvy, -prevNvx, -prevNvy, points); |
|
375 for (int i = points.size() / 2; i > 0; --i) |
|
376 emitLineSegment(m_cx, m_cy, points[2 * i - 2], points[2 * i - 1]); |
|
377 } else { |
|
378 arcPoints(0, 0, -prevNvx, -prevNvy, m_nvx, m_nvy, points); |
|
379 for (int i = 0; i < points.size() / 2; ++i) |
|
380 emitLineSegment(m_cx, m_cy, points[2 * i + 0], points[2 * i + 1]); |
|
381 } |
|
382 break; } |
|
383 default: break; // gcc warn-- |
|
384 } |
|
385 |
|
386 emitLineSegment(m_cx, m_cy, m_nvx, m_nvy); |
|
387 } |
|
388 |
|
389 void QTriangulatingStroker::endCap(const qreal *) |
|
390 { |
|
391 switch (m_cap_style) { |
|
392 case Qt::FlatCap: |
|
393 break; |
|
394 case Qt::SquareCap: |
|
395 emitLineSegment(m_cx + m_nvy, m_cy - m_nvx, m_nvx, m_nvy); |
|
396 break; |
|
397 case Qt::RoundCap: { |
|
398 QVarLengthArray<float> points; |
|
399 int count = m_vertices.size(); |
|
400 arcPoints(m_cx, m_cy, m_vertices.at(count - 2), m_vertices.at(count - 1), m_vertices.at(count - 4), m_vertices.at(count - 3), points); |
|
401 int front = 0; |
|
402 int end = points.size() / 2; |
|
403 while (front != end) { |
|
404 m_vertices.add(points[2 * end - 2]); |
|
405 m_vertices.add(points[2 * end - 1]); |
|
406 --end; |
|
407 if (front == end) |
|
408 break; |
|
409 m_vertices.add(points[2 * front + 0]); |
|
410 m_vertices.add(points[2 * front + 1]); |
|
411 ++front; |
|
412 } |
|
413 break; } |
|
414 default: break; // to shut gcc up... |
|
415 } |
|
416 } |
|
417 |
|
418 void QTriangulatingStroker::arcPoints(float cx, float cy, float fromX, float fromY, float toX, float toY, QVarLengthArray<float> &points) |
|
419 { |
|
420 float dx1 = fromX - cx; |
|
421 float dy1 = fromY - cy; |
|
422 float dx2 = toX - cx; |
|
423 float dy2 = toY - cy; |
|
424 |
|
425 // while more than 180 degrees left: |
|
426 while (dx1 * dy2 - dx2 * dy1 < 0) { |
|
427 float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; |
|
428 float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; |
|
429 dx1 = tmpx; |
|
430 dy1 = tmpy; |
|
431 points.append(cx + dx1); |
|
432 points.append(cy + dy1); |
|
433 } |
|
434 |
|
435 // while more than 90 degrees left: |
|
436 while (dx1 * dx2 + dy1 * dy2 < 0) { |
|
437 float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; |
|
438 float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; |
|
439 dx1 = tmpx; |
|
440 dy1 = tmpy; |
|
441 points.append(cx + dx1); |
|
442 points.append(cy + dy1); |
|
443 } |
|
444 |
|
445 // while more than 0 degrees left: |
|
446 while (dx1 * dy2 - dx2 * dy1 > 0) { |
|
447 float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta; |
|
448 float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta; |
|
449 dx1 = tmpx; |
|
450 dy1 = tmpy; |
|
451 points.append(cx + dx1); |
|
452 points.append(cy + dy1); |
|
453 } |
|
454 |
|
455 // remove last point which was rotated beyond [toX, toY]. |
|
456 if (!points.isEmpty()) |
|
457 points.resize(points.size() - 2); |
|
458 } |
247 |
459 |
248 static void qdashprocessor_moveTo(qreal x, qreal y, void *data) |
460 static void qdashprocessor_moveTo(qreal x, qreal y, void *data) |
249 { |
461 { |
250 ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y); |
462 ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y); |
251 } |
463 } |