go-hep.org/x/hep@v0.38.1/hplot/vgop/ops.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  	"bytes"
     9  	"encoding/base64"
    10  	"encoding/json"
    11  	"fmt"
    12  	"image"
    13  	"image/color"
    14  	"image/png"
    15  
    16  	"gonum.org/v1/plot/font"
    17  	"gonum.org/v1/plot/vg"
    18  )
    19  
    20  // op is a vector graphics operation as defined by the vg.Canvas interface.
    21  // Each method of vg.Canvas has a corresponding vgop.op.
    22  type op interface {
    23  	op(ctx fontCtx, c vg.Canvas) error
    24  }
    25  
    26  // opSetLineWidth corresponds to the vg.Canvas.SetWidth method.
    27  type opSetLineWidth struct {
    28  	Width vg.Length `json:"width"`
    29  }
    30  
    31  func (op opSetLineWidth) op(ctx fontCtx, c vg.Canvas) error {
    32  	c.SetLineWidth(op.Width)
    33  	return nil
    34  }
    35  
    36  // opSetLineDash corresponds to the vg.Canvas.SetLineDash method.
    37  type opSetLineDash struct {
    38  	Dashes  []vg.Length `json:"dashes"`
    39  	Offsets vg.Length   `json:"offsets"`
    40  }
    41  
    42  func (op opSetLineDash) op(ctx fontCtx, c vg.Canvas) error {
    43  	c.SetLineDash(op.Dashes, op.Offsets)
    44  	return nil
    45  }
    46  
    47  // opSetColor corresponds to the vg.Canvas.SetColor method.
    48  type opSetColor struct {
    49  	Color color.Color
    50  }
    51  
    52  func (op opSetColor) MarshalJSON() ([]byte, error) {
    53  	var ctx struct {
    54  		C struct {
    55  			R uint32 `json:"r"`
    56  			G uint32 `json:"g"`
    57  			B uint32 `json:"b"`
    58  			A uint32 `json:"a"`
    59  		} `json:"color"`
    60  	}
    61  
    62  	ctx.C.R, ctx.C.G, ctx.C.B, ctx.C.A = op.Color.RGBA()
    63  	return json.Marshal(ctx)
    64  }
    65  
    66  func (op *opSetColor) UnmarshalJSON(p []byte) error {
    67  	var ctx struct {
    68  		C struct {
    69  			R uint32 `json:"r"`
    70  			G uint32 `json:"g"`
    71  			B uint32 `json:"b"`
    72  			A uint32 `json:"a"`
    73  		} `json:"color"`
    74  	}
    75  
    76  	err := json.Unmarshal(p, &ctx)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	op.Color = &color.RGBA{
    81  		R: uint8(ctx.C.R),
    82  		G: uint8(ctx.C.G),
    83  		B: uint8(ctx.C.B),
    84  		A: uint8(ctx.C.A),
    85  	}
    86  	return nil
    87  }
    88  
    89  func (op opSetColor) op(ctx fontCtx, c vg.Canvas) error {
    90  	c.SetColor(op.Color)
    91  	return nil
    92  }
    93  
    94  // opRotate corresponds to the vg.Canvas.Rotate method.
    95  type opRotate struct {
    96  	Angle float64 `json:"angle"`
    97  }
    98  
    99  func (op opRotate) op(ctx fontCtx, c vg.Canvas) error {
   100  	c.Rotate(op.Angle)
   101  	return nil
   102  }
   103  
   104  // opTranslate corresponds to the vg.Canvas.Translate method.
   105  type opTranslate struct {
   106  	Point vg.Point
   107  }
   108  
   109  func (op opTranslate) MarshalJSON() ([]byte, error) {
   110  	var v struct {
   111  		P jsonPoint `json:"point"`
   112  	}
   113  	v.P = jsonPointFrom(op.Point)
   114  
   115  	return json.Marshal(v)
   116  }
   117  
   118  func (op *opTranslate) UnmarshalJSON(p []byte) error {
   119  	var v struct {
   120  		P jsonPoint `json:"point"`
   121  	}
   122  	err := json.Unmarshal(p, &v)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	op.Point = v.P.cnv()
   127  
   128  	return nil
   129  }
   130  
   131  func (op opTranslate) op(ctx fontCtx, c vg.Canvas) error {
   132  	c.Translate(op.Point)
   133  	return nil
   134  }
   135  
   136  // opScale corresponds to the vg.Canvas.Scale method.
   137  type opScale struct {
   138  	X float64 `json:"x"`
   139  	Y float64 `json:"y"`
   140  }
   141  
   142  func (op opScale) op(ctx fontCtx, c vg.Canvas) error {
   143  	c.Scale(op.X, op.Y)
   144  	return nil
   145  }
   146  
   147  // opPush corresponds to the vg.Canvas.Push method.
   148  type opPush struct{}
   149  
   150  func (op opPush) op(ctx fontCtx, c vg.Canvas) error {
   151  	c.Push()
   152  	return nil
   153  }
   154  
   155  // opPop corresponds to the vg.Canvas.Pop method.
   156  type opPop struct{}
   157  
   158  func (op opPop) op(ctx fontCtx, c vg.Canvas) error {
   159  	c.Pop()
   160  	return nil
   161  }
   162  
   163  // opStroke corresponds to the vg.Canvas.Stroke method.
   164  type opStroke struct {
   165  	Path vg.Path
   166  }
   167  
   168  func (op opStroke) MarshalJSON() ([]byte, error) {
   169  	var v struct {
   170  		P jsonPath `json:"path"`
   171  	}
   172  	v.P = jsonPathFrom(op.Path)
   173  
   174  	return json.Marshal(v)
   175  }
   176  
   177  func (op *opStroke) UnmarshalJSON(p []byte) error {
   178  	var v struct {
   179  		P jsonPath `json:"path"`
   180  	}
   181  	err := json.Unmarshal(p, &v)
   182  	if err != nil {
   183  		return err
   184  	}
   185  	op.Path = v.P.cnv()
   186  
   187  	return nil
   188  }
   189  
   190  func (op opStroke) op(ctx fontCtx, c vg.Canvas) error {
   191  	c.Stroke(op.Path)
   192  	return nil
   193  }
   194  
   195  // opFill corresponds to the vg.Canvas.Fill method.
   196  type opFill struct {
   197  	Path vg.Path
   198  }
   199  
   200  func (op opFill) MarshalJSON() ([]byte, error) {
   201  	var v struct {
   202  		P jsonPath `json:"path"`
   203  	}
   204  	v.P = jsonPathFrom(op.Path)
   205  
   206  	return json.Marshal(v)
   207  }
   208  
   209  func (op *opFill) UnmarshalJSON(p []byte) error {
   210  	var v struct {
   211  		P jsonPath `json:"path"`
   212  	}
   213  	err := json.Unmarshal(p, &v)
   214  	if err != nil {
   215  		return err
   216  	}
   217  	op.Path = v.P.cnv()
   218  
   219  	return nil
   220  }
   221  
   222  func (op opFill) op(ctx fontCtx, c vg.Canvas) error {
   223  	c.Fill(op.Path)
   224  	return nil
   225  }
   226  
   227  // opFillString corresponds to the vg.Canvas.FillString method.
   228  type opFillString struct {
   229  	Font   font.Font
   230  	Point  vg.Point
   231  	String string
   232  }
   233  
   234  func (op opFillString) MarshalJSON() ([]byte, error) {
   235  	var v struct {
   236  		ID  fontID    `json:"font"`
   237  		Pt  jsonPoint `json:"point"`
   238  		Str string    `json:"string"`
   239  	}
   240  	v.ID = fontID(op.Font)
   241  	v.Pt = jsonPointFrom(op.Point)
   242  	v.Str = op.String
   243  
   244  	return json.Marshal(v)
   245  }
   246  
   247  func (op *opFillString) UnmarshalJSON(p []byte) error {
   248  	var v struct {
   249  		ID  fontID    `json:"font"`
   250  		Pt  jsonPoint `json:"point"`
   251  		Str string    `json:"string"`
   252  	}
   253  	err := json.Unmarshal(p, &v)
   254  	if err != nil {
   255  		return err
   256  	}
   257  	op.Font = font.Font(v.ID)
   258  	op.Point = v.Pt.cnv()
   259  	op.String = v.Str
   260  
   261  	return nil
   262  }
   263  
   264  func (op opFillString) op(ctx fontCtx, c vg.Canvas) error {
   265  	id := fontID(op.Font)
   266  	face, ok := ctx.fonts[id]
   267  	if !ok {
   268  		return fmt.Errorf("unknown font name=%q, size=%v", op.Font.Name(), op.Font.Size)
   269  	}
   270  	c.FillString(face, op.Point, op.String)
   271  	return nil
   272  }
   273  
   274  // opDrawImage corresponds to the vg.Canvas.DrawImage method
   275  type opDrawImage struct {
   276  	Rect  vg.Rectangle
   277  	Image image.Image
   278  }
   279  
   280  func (op opDrawImage) MarshalJSON() ([]byte, error) {
   281  	var v struct {
   282  		Rect struct {
   283  			Min jsonPoint `json:"min"`
   284  			Max jsonPoint `json:"max"`
   285  		}
   286  		Image []byte `json:"data"`
   287  	}
   288  	v.Rect.Min = jsonPointFrom(op.Rect.Min)
   289  	v.Rect.Max = jsonPointFrom(op.Rect.Max)
   290  
   291  	var buf bytes.Buffer
   292  	err := png.Encode(&buf, op.Image)
   293  	if err != nil {
   294  		return nil, fmt.Errorf("could not encode image to PNG: %w", err)
   295  	}
   296  	v.Image = []byte(base64.StdEncoding.EncodeToString(buf.Bytes()))
   297  
   298  	return json.Marshal(v)
   299  }
   300  
   301  func (op *opDrawImage) UnmarshalJSON(p []byte) error {
   302  	var v struct {
   303  		Rect struct {
   304  			Min jsonPoint `json:"min"`
   305  			Max jsonPoint `json:"max"`
   306  		}
   307  		Image []byte `json:"data"`
   308  	}
   309  	err := json.Unmarshal(p, &v)
   310  	if err != nil {
   311  		return err
   312  	}
   313  	op.Rect.Min = v.Rect.Min.cnv()
   314  	op.Rect.Max = v.Rect.Max.cnv()
   315  
   316  	buf, err := base64.StdEncoding.DecodeString(string(v.Image))
   317  	if err != nil {
   318  		return err
   319  	}
   320  	img, err := png.Decode(bytes.NewReader(buf))
   321  	if err != nil {
   322  		return err
   323  	}
   324  	op.Image = img
   325  
   326  	return nil
   327  }
   328  
   329  func (op opDrawImage) op(ctx fontCtx, c vg.Canvas) error {
   330  	c.DrawImage(op.Rect, op.Image)
   331  	return nil
   332  }