go-hep.org/x/hep@v0.38.1/hplot/vgop/canvas.go (about) 1 // Copyright ©2023 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package vgop // import "go-hep.org/x/hep/hplot/vgop" 6 7 import ( 8 "fmt" 9 "image" 10 "image/color" 11 "slices" 12 13 "gonum.org/v1/plot/font" 14 "gonum.org/v1/plot/vg" 15 ) 16 17 const ( 18 // DefaultWidth and DefaultHeight are the default canvas 19 // dimensions. 20 DefaultWidth = 4 * vg.Inch 21 DefaultHeight = 4 * vg.Inch 22 ) 23 24 var ( 25 _ vg.Canvas = (*Canvas)(nil) 26 _ vg.CanvasSizer = (*Canvas)(nil) 27 ) 28 29 // Canvas implements vg.Canvas for serialization. 30 type Canvas struct { 31 w, h vg.Length 32 33 ops []op 34 ctx fontCtx 35 } 36 37 type Option func(c *Canvas) 38 39 func WithSize(w, h vg.Length) Option { 40 return func(c *Canvas) { 41 c.w = w 42 c.h = h 43 } 44 } 45 46 // New returns a new canvas. 47 func New(opts ...Option) *Canvas { 48 c := &Canvas{ 49 w: DefaultWidth, 50 h: DefaultHeight, 51 } 52 53 for _, opt := range opts { 54 opt(c) 55 } 56 57 return c 58 } 59 60 func (c *Canvas) Size() (w, h vg.Length) { 61 return c.w, c.h 62 } 63 64 // Reset resets the canvas to the base state. 65 func (c *Canvas) Reset() { 66 c.ops = c.ops[:0] 67 68 // FIXME(sbinet): should we also reset the fonts context ? 69 } 70 71 // ReplayOn replays the set of vector graphics operations onto the 72 // destination canvas. 73 func (c *Canvas) ReplayOn(dst vg.Canvas) error { 74 if c.ctx.fonts == nil { 75 c.ctx.fonts = make(map[fontID]font.Face) 76 } 77 78 for _, op := range c.ops { 79 err := op.op(c.ctx, dst) 80 if err != nil { 81 return fmt.Errorf("could not apply op %T: %w", op, err) 82 } 83 } 84 85 return nil 86 } 87 88 func (c *Canvas) add(face font.Face) { 89 if c.ctx.fonts == nil { 90 c.ctx.fonts = make(map[fontID]font.Face) 91 } 92 93 id := fontID(face.Font) 94 c.ctx.fonts[id] = face 95 } 96 97 func (c *Canvas) append(o op) { 98 c.ops = append(c.ops, o) 99 } 100 101 // SetLineWidth implements the SetLineWidth method of the vg.Canvas interface. 102 func (c *Canvas) SetLineWidth(w vg.Length) { 103 c.append(opSetLineWidth{Width: w}) 104 } 105 106 // SetLineDash implements the SetLineDash method of the vg.Canvas interface. 107 func (c *Canvas) SetLineDash(dashes []vg.Length, offs vg.Length) { 108 c.append(opSetLineDash{ 109 Dashes: slices.Clone(dashes), 110 Offsets: offs, 111 }) 112 } 113 114 // SetColor implements the SetColor method of the vg.Canvas interface. 115 func (c *Canvas) SetColor(col color.Color) { 116 c.append(opSetColor{Color: col}) 117 } 118 119 // Rotate implements the Rotate method of the vg.Canvas interface. 120 func (c *Canvas) Rotate(a float64) { 121 c.append(opRotate{Angle: a}) 122 } 123 124 // Translate implements the Translate method of the vg.Canvas interface. 125 func (c *Canvas) Translate(pt vg.Point) { 126 c.append(opTranslate{Point: pt}) 127 } 128 129 // Scale implements the Scale method of the vg.Canvas interface. 130 func (c *Canvas) Scale(x, y float64) { 131 c.append(opScale{X: x, Y: y}) 132 } 133 134 // Push implements the Push method of the vg.Canvas interface. 135 func (c *Canvas) Push() { 136 c.append(opPush{}) 137 } 138 139 // Pop implements the Pop method of the vg.Canvas interface. 140 func (c *Canvas) Pop() { 141 c.append(opPop{}) 142 } 143 144 // Stroke implements the Stroke method of the vg.Canvas interface. 145 func (c *Canvas) Stroke(path vg.Path) { 146 c.append(opStroke{Path: slices.Clone(path)}) 147 } 148 149 // Fill implements the Fill method of the vg.Canvas interface. 150 func (c *Canvas) Fill(path vg.Path) { 151 c.append(opFill{Path: slices.Clone(path)}) 152 } 153 154 // FillString implements the FillString method of the vg.Canvas interface. 155 func (c *Canvas) FillString(font font.Face, pt vg.Point, str string) { 156 c.add(font) 157 c.append(opFillString{ 158 Font: font.Font, 159 Point: pt, 160 String: str, 161 }) 162 } 163 164 // DrawImage implements the DrawImage method of the vg.Canvas interface. 165 func (c *Canvas) DrawImage(rect vg.Rectangle, img image.Image) { 166 c.append(opDrawImage{ 167 Rect: rect, 168 Image: img, 169 }) 170 }