Skip to content

Commit

Permalink
feat: font cache, default gofonts; new builtin functions transform an…
Browse files Browse the repository at this point in the history
…d fontHeight
  • Loading branch information
lucasepe committed Dec 17, 2020
1 parent 5c4e1d4 commit dcbc311
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 47 deletions.
25 changes: 23 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
0.4.0 (Unreleased)
0.5.0 (Unreleased)

NEW FEATURES:

- `viewport(...)` builtin function takes two more optional parameters `xOffset` and `yOffset` to eventually relocate the viewport
- new builtin function: `fontHeight(size)` to set the current font height

BREAKING CHANGES:

- ~~`worldcoors(...)`~~ renamed to `viewport(...)`

---

0.4.1 (Dec 15, 2020)

BUG FIXES:

- fix gorelaser build

---

0.4.0 (Dec 15, 2020)

NEW FEATURES:

Expand All @@ -15,7 +36,7 @@ NEW FEATURES:
- new builtin math function: `max(n1, n2, ...n)` to calculate the maximum of a sequence


BREAKING CHANGES!
BREAKING CHANGES:

- massive code refactory
- renamed `screensize` function to `size`
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ Function | Description
`size(w,[h])` | when both _w_ and _h_ are specified creates a rectangular image otherwise creates a squared one |
`width()` | returns the main drawing image width |
`height()` | returns the main drawing image height |
`worldcoords(xMin, xMax, yMin, yMax)` | sets up user-defined coordinate system; performs a screen reset (drawings are cleared)|
`viewport(xMin, xMax, yMin, yMax, xOffset, yOffset)` | sets up user-defined coordinate system; performs a screen reset (drawings are cleared)|
`pencolor(hexcolor)` | sets the pen color to the specified _hexcolor_; example _pencolor("#ff0000")_ |
`pencolor(r, g, b, [a])` | sets the pen color to _r,g,b,a_ values - should be between 0 and 1, inclusive |
`pensize(width)` | sets the pen line thickness to the specified _width_ |
Expand All @@ -331,6 +331,7 @@ Function | Description
`triangle(x1,y1, x2,y2, x3,y3)` | draws a triangle using the provided vertices |
`text(str, x, y, [ax, ay])` | draws the specified text at the specified anchor point<br/>anchor point is _x - w * ax_, _y - h * ay_, where _w_, _h_ is the size of the text<br/>when omitted _ax=0.5_, _ay=0.5_ to center the text at the specified point |
`textWidth(str)` | returns the rendered width of the specified text given the current font face |
`fontHeight(size)` | sets the font height of the current font face |
`beginPath()` | starts a new path |
`moveTo(x, y)` | sets the begin of a new subpath starting at the specified _(x, y)_ point |
`lineTo(x, y)` | adds a line segment to the current path starting at the current point. <br/>If there is no current point, it is equivalent to MoveTo(x, y) |
Expand Down
36 changes: 19 additions & 17 deletions builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,21 @@ var Builtins = map[string]*object.Builtin{
"sqrt": &object.Builtin{Name: "sqrt", Fn: calc.Sqrt},

// Graphic Context
"size": &object.Builtin{Name: "size", Fn: graphics.Size},
"clear": &object.Builtin{Name: "clear", Fn: graphics.Clear},
"dashes": &object.Builtin{Name: "dashes", Fn: graphics.Dashes},
"pencolor": &object.Builtin{Name: "pencolor", Fn: graphics.PenColor},
"pensize": &object.Builtin{Name: "pensize", Fn: graphics.PenSize},
"xpos": &object.Builtin{Name: "xpos", Fn: graphics.GetCurrentX},
"ypos": &object.Builtin{Name: "ypos", Fn: graphics.GetCurrentY},
"snapshot": &object.Builtin{Name: "snapshot", Fn: graphics.Snapshot},
"width": &object.Builtin{Name: "width", Fn: graphics.Width},
"height": &object.Builtin{Name: "height", Fn: graphics.Height},
"push": &object.Builtin{Name: "push", Fn: graphics.Push},
"pop": &object.Builtin{Name: "pop", Fn: graphics.Pop},
"stroke": &object.Builtin{Name: "stroke", Fn: graphics.Stroke},
"fill": &object.Builtin{Name: "fill", Fn: graphics.Fill},
"worldcoords": &object.Builtin{Name: "worldcoords", Fn: graphics.WorldCoords},
"size": &object.Builtin{Name: "size", Fn: graphics.Size},
"clear": &object.Builtin{Name: "clear", Fn: graphics.Clear},
"dashes": &object.Builtin{Name: "dashes", Fn: graphics.Dashes},
"pencolor": &object.Builtin{Name: "pencolor", Fn: graphics.PenColor},
"pensize": &object.Builtin{Name: "pensize", Fn: graphics.PenSize},
"xpos": &object.Builtin{Name: "xpos", Fn: graphics.GetCurrentX},
"ypos": &object.Builtin{Name: "ypos", Fn: graphics.GetCurrentY},
"snapshot": &object.Builtin{Name: "snapshot", Fn: graphics.Snapshot},
"width": &object.Builtin{Name: "width", Fn: graphics.Width},
"height": &object.Builtin{Name: "height", Fn: graphics.Height},
"push": &object.Builtin{Name: "push", Fn: graphics.Push},
"pop": &object.Builtin{Name: "pop", Fn: graphics.Pop},
"stroke": &object.Builtin{Name: "stroke", Fn: graphics.Stroke},
"fill": &object.Builtin{Name: "fill", Fn: graphics.Fill},
"viewport": &object.Builtin{Name: "viewport", Fn: graphics.Viewport},

// Path
"beginPath": &object.Builtin{Name: "beginPath", Fn: graphics.BeginPath},
Expand All @@ -78,6 +78,7 @@ var Builtins = map[string]*object.Builtin{
"scale": &object.Builtin{Name: "scale", Fn: graphics.ScaleAbout},
"translate": &object.Builtin{Name: "translate", Fn: graphics.Translate},
"identity": &object.Builtin{Name: "identity", Fn: graphics.Identity},
"transform": &object.Builtin{Name: "transform", Fn: graphics.Transform},

// 2D Primitives
"arc": &object.Builtin{Name: "arc", Fn: graphics.Arc},
Expand All @@ -90,8 +91,9 @@ var Builtins = map[string]*object.Builtin{
"triangle": &object.Builtin{Name: "triangle", Fn: graphics.Triangle},

// Text
"text": &object.Builtin{Name: "text", Fn: graphics.Text},
"textWidth": &object.Builtin{Name: "textWidth", Fn: graphics.TextWidth},
"text": &object.Builtin{Name: "text", Fn: graphics.Text},
"textWidth": &object.Builtin{Name: "textWidth", Fn: graphics.TextWidth},
"fontHeight": &object.Builtin{Name: "fontHeight", Fn: graphics.FontHeight},

// Images
"loadPNG": &object.Builtin{Name: "loadPNG", Fn: graphics.LoadPNG},
Expand Down
30 changes: 20 additions & 10 deletions builtins/graphics/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,41 +310,51 @@ func Fill(env *object.Environment, args ...object.Object) object.Object {
return &object.Null{}
}

// WorldCoords sets up user-defined coordinate system.
// Viewport sets up user-defined coordinate system.
// This performs a screen reset, all drawings are cleared.
func WorldCoords(env *object.Environment, args ...object.Object) object.Object {
if err := typing.Check("worldcoords", args, typing.ExactArgs(4)); err != nil {
func Viewport(env *object.Environment, args ...object.Object) object.Object {
if err := typing.Check("viewport", args, typing.RangeOfArgs(4, 6)); err != nil {
return object.NewError(err.Error())
}

xMin, err := typing.ToFloat(args[0])
if err != nil {
return object.NewError("TypeError: worldcoords() argument #1 `xMin` %s", err.Error())
return object.NewError("TypeError: viewport() argument #1 `xMin` %s", err.Error())
}

xMax, err := typing.ToFloat(args[1])
if err != nil {
return object.NewError("TypeError: worldcoords() argument #2 `xMax` %s", err.Error())
return object.NewError("TypeError: viewport() argument #2 `xMax` %s", err.Error())
}

if xMax <= xMin {
return object.NewError("RangeError: worldcoords() xMax must be greater then xMin")
return object.NewError("RangeError: viewport() xMax must be greater then xMin")
}

yMin, err := typing.ToFloat(args[2])
if err != nil {
return object.NewError("TypeError: worldcoords() argument #3 `yMin` %s", err.Error())
return object.NewError("TypeError: viewport() argument #3 `yMin` %s", err.Error())
}

yMax, err := typing.ToFloat(args[3])
if err != nil {
return object.NewError("TypeError: worldcoords() argument #4 `yMax` %s", err.Error())
return object.NewError("TypeError: viewport() argument #4 `yMax` %s", err.Error())
}

if yMax <= yMin {
return object.NewError("RangeError: worldcoords() yMax must be greater then yMin")
return object.NewError("RangeError: viewport() yMax must be greater then yMin")
}

env.Canvas().Value.SetWorldCoordinates(xMin, xMax, yMin, yMax)
xOffset := 0.0
if len(args) == 5 {
xOffset, _ = typing.ToFloat(args[4])
}

yOffset := 0.0
if len(args) == 6 {
yOffset, _ = typing.ToFloat(args[5])
}

env.Canvas().Value.SetWorldCoordinates(xMin, xMax, yMin, yMax, xOffset, yOffset)
return &object.Null{}
}
15 changes: 15 additions & 0 deletions builtins/graphics/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,18 @@ func TextWidth(env *object.Environment, args ...object.Object) object.Object {
}
*/
}

// FontHeight sets the size of the current font face.
func FontHeight(env *object.Environment, args ...object.Object) object.Object {
if err := typing.Check("fontHeight", args, typing.ExactArgs(1)); err != nil {
return object.NewError(err.Error())
}

size, err := typing.ToFloat(args[0])
if err != nil {
return object.NewError("TypeError: fontHeight() argument #1 %s", err.Error())
}

env.Canvas().Value.SetFontHeight(size)
return &object.Null{}
}
26 changes: 26 additions & 0 deletions builtins/graphics/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,29 @@ func Identity(env *object.Environment, args ...object.Object) object.Object {
env.Canvas().Value.Identity()
return &object.Null{}
}

// Transform multiplies the specified point by the current matrix,
// returning a transformed position.
func Transform(env *object.Environment, args ...object.Object) object.Object {
if err := typing.Check("transform", args, typing.ExactArgs(2)); err != nil {
return object.NewError(err.Error())
}

x, err := typing.ToFloat(args[0])
if err != nil {
return object.NewError("TypeError: transform() argument #1 `x` %s", err.Error())
}

y, err := typing.ToFloat(args[1])
if err != nil {
return object.NewError("TypeError: transform() argument #2 `y` %s", err.Error())
}

tx, ty := env.Canvas().Value.TransformPoint(x, y)
return &object.Array{
Elements: []object.Object{
&object.Float{Value: tx},
&object.Float{Value: ty},
},
}
}
52 changes: 35 additions & 17 deletions gg/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strings"

"github.com/golang/freetype/raster"
"github.com/golang/freetype/truetype"
"golang.org/x/image/draw"
"golang.org/x/image/font"
"golang.org/x/image/font/basicfont"
Expand Down Expand Up @@ -73,10 +74,11 @@ type Context struct {
lineCap LineCap
lineJoin LineJoin
fillRule FillRule
fontFace font.Face
fontHeight float64
matrix Matrix
stack []*Context
//fontFace font.Face
fontCache FontCache
fontHeight float64
matrix Matrix
stack []*Context
}

// NewContext creates a new image.RGBA with the specified width and height
Expand Down Expand Up @@ -107,14 +109,15 @@ func NewContextForRGBA(im *image.RGBA) *Context {
strokePattern: defaultStrokeStyle,
lineWidth: 1,
fillRule: FillRuleWinding,
fontFace: basicfont.Face7x13,
fontHeight: 13,
matrix: Identity(),
//fontFace: basicfont.Face7x13,
fontCache: GetGlobalFontCache(),
fontHeight: 13,
matrix: Identity(),
}

/*
if font, err := truetype.Parse(goregular.TTF); err == nil {
res.fontHeight = 13
if font, err := truetype.Parse(gomono.TTF); err == nil {
res.fontHeight = 14
res.fontFace = truetype.NewFace(font, &truetype.Options{Size: res.fontHeight})
}
*/
Expand Down Expand Up @@ -844,7 +847,7 @@ func (dc *Context) DrawImageAnchored(im image.Image, x, y int, ax, ay float64) {
}

// Text Functions

/*
func (dc *Context) SetFontFace(fontFace font.Face) {
dc.fontFace = fontFace
dc.fontHeight = float64(fontFace.Metrics().Height) / 64
Expand All @@ -858,17 +861,31 @@ func (dc *Context) LoadFontFace(path string, points float64) error {
}
return err
}

*/
// FontHeight returns font's size
func (dc *Context) FontHeight() float64 {
return dc.fontHeight
}

// SetFontHeight sets current font's size
func (dc *Context) SetFontHeight(size float64) {
dc.fontHeight = size
}

func (dc *Context) fontFace() font.Face {
fnt, err := dc.fontCache.Load(FontData{Name: "gomono"})
if err == nil {
return truetype.NewFace(fnt, &truetype.Options{Size: dc.fontHeight})
}

return basicfont.Face7x13
}

func (dc *Context) drawString(im *image.RGBA, s string, x, y float64) {
d := &font.Drawer{
Dst: im,
Src: image.NewUniform(dc.color),
Face: dc.fontFace,
Face: dc.fontFace(), // dc.fontFace,
Dot: fixp(x, y),
}
// based on Drawer.DrawString() in golang.org/x/image/font/font.go
Expand Down Expand Up @@ -956,7 +973,7 @@ func (dc *Context) MeasureMultilineString(s string, lineSpacing float64) (width,
height -= (lineSpacing - 1) * dc.fontHeight

d := &font.Drawer{
Face: dc.fontFace,
Face: dc.fontFace(),
}

// max width from lines
Expand All @@ -975,7 +992,7 @@ func (dc *Context) MeasureMultilineString(s string, lineSpacing float64) (width,
// given the current font face.
func (dc *Context) MeasureString(s string) (w, h float64) {
d := &font.Drawer{
Face: dc.fontFace,
Face: dc.fontFace(),
}
a := d.MeasureString(s)
return float64(a >> 6), dc.fontHeight
Expand Down Expand Up @@ -1084,8 +1101,9 @@ func (dc *Context) Pop() {
// xMax: x-coordinate of upper right corner of canvas.
// yMin: y-coordinate of lower left corner of canvas.
// yMax: y-coordinate of upper right corner of canvas.
func (dc *Context) SetWorldCoordinates(xMin, xMax, yMin, yMax float64) {
w, h := float64(dc.Width()), float64(dc.Height())
func (dc *Context) SetWorldCoordinates(xMin, xMax, yMin, yMax float64, xOffset, yOffset float64) {
w := float64(dc.Width()) - 2*xOffset
h := float64(dc.Height()) - 2*yOffset

displayAspect := math.Abs(h / w)
windowAspect := math.Abs((yMax - yMin) / (xMax - xMin))
Expand All @@ -1105,7 +1123,7 @@ func (dc *Context) SetWorldCoordinates(xMin, xMax, yMin, yMax float64) {
sx, sy := w/(xMax-xMin), h/(yMin-yMax)
tx, ty := -xMin, -yMax

dc.Identity()
dc.Translate(xOffset, yOffset)
dc.Scale(sx, sy)
dc.Translate(tx, ty)
}
Loading

0 comments on commit dcbc311

Please sign in to comment.