offset returns the rhs and lhs paths from offsetting a path (must not have subpaths). It closes rhs and lhs when p is closed as well.
(halfWidth float64, cr Capper, jr Joiner, strokeOpen bool, tolerance float64)
| 444 | |
| 445 | // offset returns the rhs and lhs paths from offsetting a path (must not have subpaths). It closes rhs and lhs when p is closed as well. |
| 446 | func (p *Path) offset(halfWidth float64, cr Capper, jr Joiner, strokeOpen bool, tolerance float64) (*Path, *Path) { |
| 447 | // only non-empty paths are evaluated |
| 448 | closed := false |
| 449 | states := []pathStrokeState{} |
| 450 | var start, end Point |
| 451 | for i := 0; i < len(p.d); { |
| 452 | cmd := p.d[i] |
| 453 | switch cmd { |
| 454 | case MoveToCmd: |
| 455 | end = Point{p.d[i+1], p.d[i+2]} |
| 456 | case LineToCmd: |
| 457 | end = Point{p.d[i+1], p.d[i+2]} |
| 458 | n := end.Sub(start).Rot90CW().Norm(halfWidth) |
| 459 | states = append(states, pathStrokeState{ |
| 460 | cmd: LineToCmd, |
| 461 | p0: start, |
| 462 | p1: end, |
| 463 | n0: n, |
| 464 | n1: n, |
| 465 | r0: math.NaN(), |
| 466 | r1: math.NaN(), |
| 467 | }) |
| 468 | case QuadToCmd, CubeToCmd: |
| 469 | var cp1, cp2 Point |
| 470 | if cmd == QuadToCmd { |
| 471 | cp := Point{p.d[i+1], p.d[i+2]} |
| 472 | end = Point{p.d[i+3], p.d[i+4]} |
| 473 | cp1, cp2 = quadraticToCubicBezier(start, cp, end) |
| 474 | } else { |
| 475 | cp1 = Point{p.d[i+1], p.d[i+2]} |
| 476 | cp2 = Point{p.d[i+3], p.d[i+4]} |
| 477 | end = Point{p.d[i+5], p.d[i+6]} |
| 478 | } |
| 479 | n0 := cubicBezierNormal(start, cp1, cp2, end, 0.0, halfWidth) |
| 480 | n1 := cubicBezierNormal(start, cp1, cp2, end, 1.0, halfWidth) |
| 481 | r0 := cubicBezierCurvatureRadius(start, cp1, cp2, end, 0.0) |
| 482 | r1 := cubicBezierCurvatureRadius(start, cp1, cp2, end, 1.0) |
| 483 | states = append(states, pathStrokeState{ |
| 484 | cmd: CubeToCmd, |
| 485 | p0: start, |
| 486 | p1: end, |
| 487 | n0: n0, |
| 488 | n1: n1, |
| 489 | r0: r0, |
| 490 | r1: r1, |
| 491 | cp1: cp1, |
| 492 | cp2: cp2, |
| 493 | }) |
| 494 | case ArcToCmd: |
| 495 | rx, ry, phi := p.d[i+1], p.d[i+2], p.d[i+3] |
| 496 | large, sweep := toArcFlags(p.d[i+4]) |
| 497 | end = Point{p.d[i+5], p.d[i+6]} |
| 498 | _, _, theta0, theta1 := ellipseToCenter(start.X, start.Y, rx, ry, phi, large, sweep, end.X, end.Y) |
| 499 | n0 := ellipseNormal(rx, ry, phi, sweep, theta0, halfWidth) |
| 500 | n1 := ellipseNormal(rx, ry, phi, sweep, theta1, halfWidth) |
| 501 | r0 := ellipseCurvatureRadius(rx, ry, sweep, theta0) |
| 502 | r1 := ellipseCurvatureRadius(rx, ry, sweep, theta1) |
| 503 | states = append(states, pathStrokeState{ |
no test coverage detected