stroke.go raw
1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 package clip
4
5 import (
6 "encoding/binary"
7 "math"
8
9 "github.com/p9c/p9/pkg/gel/gio/internal/opconst"
10 "github.com/p9c/p9/pkg/gel/gio/op"
11 )
12
13 // Stroke represents a stroked path.
14 type Stroke struct {
15 Path PathSpec
16 Style StrokeStyle
17
18 // Dashes specify the dashes of the stroke.
19 // The empty value denotes no dashes.
20 Dashes DashSpec
21 }
22
23 // Op returns a clip operation representing the stroke.
24 func (s Stroke) Op() Op {
25 return Op{
26 path: s.Path,
27 stroke: s.Style,
28 dashes: s.Dashes,
29 }
30 }
31
32 // StrokeStyle describes how a path should be stroked.
33 type StrokeStyle struct {
34 Width float32 // Width of the stroked path.
35
36 // Miter is the limit to apply to a miter joint.
37 // The zero Miter disables the miter joint; setting Miter to +∞
38 // unconditionally enables the miter joint.
39 Miter float32
40 Cap StrokeCap // Cap describes the head or tail of a stroked path.
41 Join StrokeJoin // Join describes how stroked paths are collated.
42 }
43
44 // StrokeCap describes the head or tail of a stroked path.
45 type StrokeCap uint8
46
47 const (
48 // RoundCap caps stroked paths with a round cap, joining the right-hand and
49 // left-hand sides of a stroked path with a half disc of diameter the
50 // stroked path's width.
51 RoundCap StrokeCap = iota
52
53 // FlatCap caps stroked paths with a flat cap, joining the right-hand
54 // and left-hand sides of a stroked path with a straight line.
55 FlatCap
56
57 // SquareCap caps stroked paths with a square cap, joining the right-hand
58 // and left-hand sides of a stroked path with a half square of length
59 // the stroked path's width.
60 SquareCap
61 )
62
63 // StrokeJoin describes how stroked paths are collated.
64 type StrokeJoin uint8
65
66 const (
67 // RoundJoin joins path segments with a round segment.
68 RoundJoin StrokeJoin = iota
69
70 // BevelJoin joins path segments with sharp bevels.
71 BevelJoin
72 )
73
74 // Dash records dashes' lengths and phase for a stroked path.
75 type Dash struct {
76 ops *op.Ops
77 macro op.MacroOp
78 phase float32
79 size uint8 // size of the pattern
80 }
81
82 func (d *Dash) Begin(ops *op.Ops) {
83 d.ops = ops
84 d.macro = op.Record(ops)
85 // Write the TypeAux opcode
86 data := ops.Write(opconst.TypeAuxLen)
87 data[0] = byte(opconst.TypeAux)
88 }
89
90 func (d *Dash) Phase(v float32) {
91 d.phase = v
92 }
93
94 func (d *Dash) Dash(length float32) {
95 if d.size == math.MaxUint8 {
96 panic("clip: dash pattern too large")
97 }
98 data := d.ops.Write(4)
99 bo := binary.LittleEndian
100 bo.PutUint32(data[0:], math.Float32bits(length))
101 d.size++
102 }
103
104 func (d *Dash) End() DashSpec {
105 c := d.macro.Stop()
106 return DashSpec{
107 spec: c,
108 phase: d.phase,
109 size: d.size,
110 }
111 }
112
113 // DashSpec describes a dashed pattern.
114 type DashSpec struct {
115 spec op.CallOp
116 phase float32
117 size uint8 // size of the pattern
118 }
119