package graphics import ( "fmt" "math/rand" "image/color" rl "github.com/gen2brain/raylib-go/raylib" ) type Graphics struct { Style Style Bounds Rect } func CreateGraphics(bounds Rect) *Graphics { return &Graphics { Bounds: bounds, Style: Style { Fill: false, FillColor: rl.RayWhite, Stroke: true, StrokeColor: rl.Black, StrokeWeight: 1.0, }, } } type Style struct { StrokeColor, FillColor color.RGBA StrokeWeight float32 Stroke, Fill bool } func BeginDrawing() { rl.BeginDrawing() } func EndDrawing() { rl.EndDrawing() } func (g *Graphics) Center() Point { return g.Bounds.Center() } func (g *Graphics) Begin() { g.PushMatrix() g.BeginClip(g.Bounds) g.Translate(g.Bounds.UL()) } func (g *Graphics) BeginPremultiplyBlend() { rl.BeginBlendMode(rl.BlendAlphaPremultiply) } func (g *Graphics) EndPremultiplyBlend() { rl.EndBlendMode() } func (g *Graphics) End() { g.PopMatrix() g.EndClip() } func (g *Graphics) Clear() { rl.ClearBackground(rl.Blank) } func (g *Graphics) Background(c color.RGBA) { rl.ClearBackground(c) } func (g *Graphics) Width() float32 { return g.Bounds.Width } func (g *Graphics) Height() float32 { return g.Bounds.Height } func (g *Graphics) WidthInt32() int32 { return int32(g.Bounds.Width) } func (g *Graphics) HeightInt32() int32 { return int32(g.Bounds.Height) } func (g *Graphics) PushStyle() { } func (g *Graphics) PopStyle() { } func (g *Graphics) SetStrokeColor(c color.RGBA) { g.Style.StrokeColor = color.RGBA { R: c.R, G: c.G, B: c.B, A: c.A } } func (g *Graphics) SetFillColor(c color.RGBA) { g.Style.FillColor = color.RGBA { R: c.R, G: c.G, B: c.B, A: c.A } } func (g *Graphics) SetStrokeWeight(w float32) { g.Style.StrokeWeight = w } func (g *Graphics) SetStroke(b bool) { g.Style.Stroke = b } func (g *Graphics) SetFill(b bool) { g.Style.Fill = b } func (g *Graphics) PushMatrix() { rl.PushMatrix() } func (g *Graphics) Translate(p Point) { rl.Translatef(p.X, p.Y, 0) } func (g *Graphics) PopMatrix() { rl.PopMatrix() } func (g *Graphics) BeginClip(r Rect) { rlRect := r.ToRL() rint := (&rlRect).ToInt32() rl.BeginScissorMode(rint.X, rint.Y, rint.Width, rint.Height) } func (g *Graphics) EndClip() { rl.EndScissorMode() } func (g *Graphics) BeginAdditiveBlend() { rl.BeginBlendMode(rl.BlendAdditive) } func (g *Graphics) EndBlend() { rl.EndBlendMode() } func (g *Graphics) BeginTexture(t rl.RenderTexture2D) { rl.BeginTextureMode(t) } func (g *Graphics) DrawTexture(t rl.Texture2D, p Point, c color.RGBA) { rl.DrawTexture(t, int32(p.X), int32(p.Y), c) } func (g *Graphics) TransferTexture(t rl.Texture2D, src Rect, dst Rect, tint color.RGBA) { // flip source since textures are upside down on GPU //src.Y += src.Height src.Height = -src.Height rl.DrawTexturePro(t, src.ToRL(), dst.ToRL(), rl.Vector2{}, 0, tint) } func (g *Graphics) EndTexture() { rl.EndTextureMode() } func (g *Graphics) DrawRect(r Rect) { if g.Style.Fill { rl.DrawRectangleRec(r.ToRL(), g.Style.FillColor) } if g.Style.Stroke { saveLineWidth := rl.GetLineWidth() rl.SetLineWidth(g.Style.StrokeWeight) rl.DrawRectangleLines( int32(r.X), int32(r.Y), int32(r.Width), int32(r.Height), g.Style.StrokeColor, ) rl.SetLineWidth(saveLineWidth) } } func (g *Graphics) DrawLine(a, b Point) { saveLineWidth := rl.GetLineWidth() rl.SetLineWidth(g.Style.StrokeWeight) rl.DrawLineV(a.ToRL(), b.ToRL(), g.Style.StrokeColor) rl.SetLineWidth(saveLineWidth) } type HSBA struct { H uint S, B float32 A uint8 } var Origin = Point { X: 0, Y: 0, Z: 0 } type Point rl.Vector3 type Vec rl.Vector3 type Rect rl.Rectangle func (r *Rect) Center() Point { return Point { X: r.X + r.Width / 2, Y: r.Y + r.Height / 2, } } func (r *Rect) ContractByAbs(px float32) Rect { return Rect { X: r.X + px, Y: r.Y + px, Width: r.Width - 2*px, Height: r.Height - 2*px, } } func (p *Point) Add(v Vec) Point { return Point { X: p.X + v.X, Y: p.Y + v.Y, Z: p.Z + v.Z } } func (p Point) ToRL() rl.Vector2 { return rl.Vector2 { X: p.X, Y: p.Y } } func (p Point) ToRL3() rl.Vector3 { return rl.Vector3 { X: p.X, Y: p.Y, Z: p.Z } } /** * scale the given rect down to the target rect * maintaining the aspect ratio of the original rect */ func (r Rect) UL() Point { return Point { X: r.X, Y: r.Y } } func (r Rect) ToRL() rl.Rectangle { return rl.Rectangle { X: r.X, Y: r.Y, Width: r.Width, Height: r.Height } } func (r Rect) ScaleTo(tgt Rect) Rect { outputWidth := tgt.Width outputHeight := tgt.Height aspect := r.Width / r.Height tgtAspect := outputWidth / outputHeight if aspect < tgtAspect { // source is relatively taller than the target // so we set the output height to the target height // and calculate the width based on source aspect and center outputWidth = float32(outputHeight) * aspect } else { // source is relatively wider than the target // so we set the output width to the target width // and calculate the height based on source aspect and center outputHeight = float32(outputWidth) / aspect } // output width and height are correct -- center within TargetBounds x := tgt.X + tgt.Width / 2.0 - outputWidth / 2.0 y := tgt.Y + tgt.Height / 2.0 - outputHeight / 2.0 return Rect { X: x, Y: y, Width: outputWidth, Height: outputHeight, } } var ( FlourescentHues = []float32{ 0, // hot magenta-red 30, // neon orange 60, // acid yellow 120, // laser green 180, // cyan 210, // electric blue 270, // ultraviolet purple } FlourescentColors = makeFlourescentColors() ) func Clamp(c color.RGBA, min uint8, max uint8) rl.Color { return rl.NewColor( uint8(rl.Clamp(float32(c.R), float32(min), float32(max))), uint8(rl.Clamp(float32(c.G), float32(min), float32(max))), uint8(rl.Clamp(float32(c.B), float32(min), float32(max))), uint8(rl.Clamp(float32(c.A), float32(min), float32(max))), ) } func makeFlourescentColors() []rl.Color { fc := make([]rl.Color, len(FlourescentHues)) for i, hue := range(FlourescentHues) { fc[i] = rl.ColorFromHSV(hue, 1.0, 1.0) } fmt.Printf("flourescent colors --> %v\n", fc) return fc } type ColorCycle interface { Next() rl.Color } type ArrayBackedColorCycle struct { colors []rl.Color index int } func NewFixedColorCycle(colors []rl.Color) *ArrayBackedColorCycle { return &ArrayBackedColorCycle { colors: colors, index: 0, } } func (c ArrayBackedColorCycle) Shuffle(seed int64) *ArrayBackedColorCycle { r := rand.New(rand.NewSource(seed)) cprime := &ArrayBackedColorCycle { colors: make([]rl.Color, len(c.colors)), index: 0, } copy(cprime.colors, c.colors) r.Shuffle(len(c.colors), func(i, j int) { cprime.colors[i], cprime.colors[j] = cprime.colors[j], cprime.colors[i] }) fmt.Printf("shuffled colors --> %v\n", cprime.colors) return cprime } func (c *ArrayBackedColorCycle) Next() rl.Color { color := c.colors[c.index] c.index = (c.index + 1) % len(c.colors) return color }