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  }