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