From 2e8e508e48bed209a8a3ee6fc7b9647b0400b5bc Mon Sep 17 00:00:00 2001 From: Jozef Dochan Date: Tue, 2 Apr 2024 17:30:32 +0200 Subject: [PATCH] allow to change map colors (#11) --- config.example.yml | 13 ++++++++++ pkg/config/config.go | 43 +++++++++++++++++++++++++++++++- pkg/renderer/drawer.go | 14 +++++++---- pkg/renderer/drawer_layers.go | 6 ++--- pkg/renderer/fourcolortheorem.go | 9 +------ pkg/renderer/renderer.go | 8 ++++++ pkg/server/server.go | 27 ++++++++++++++++++++ 7 files changed, 103 insertions(+), 17 deletions(-) diff --git a/config.example.yml b/config.example.yml index 20b855c..7320359 100644 --- a/config.example.yml +++ b/config.example.yml @@ -74,3 +74,16 @@ map: start_y: end_x: end_y: + + # You can customize map colors with these + colors: + floor: "#0076ff" + obstacle: "#5d5d5d" + path: "#ffffff" + no_go_area: "#ff00004a" + virtual_wall: "#ff0000bf" + segments: + - "#19a1a1" + - "#7ac037" + - "#ff9b57" + - "#f7c841" \ No newline at end of file diff --git a/pkg/config/config.go b/pkg/config/config.go index 0568a9f..2630ae1 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -48,6 +48,14 @@ type MapConfig struct { EndX int `yaml:"end_x"` EndY int `yaml:"end_y"` } `yaml:"custom_limits"` + Colors struct { + Floor string `yaml:"floor"` + Obstacle string `yaml:"obstacle"` + Path string `yaml:"path"` + NoGoArea string `yaml:"no_go_area"` + VirtualWall string `yaml:"virtual_wall"` + Segments []string `yaml:"segments"` + } `yaml:"colors"` } type Config struct { @@ -69,7 +77,40 @@ func NewConfig(configFile string) (*Config, error) { return nil, err } - return validate(c) + c, err = validate(c) + if err != nil { + return nil, err + } + + return setDefaultColors(c) +} + +func setDefaultColors(c *Config) (*Config, error) { + if c.Map.Colors.Floor == "" { + c.Map.Colors.Floor = "#0076ffff" + } + + if c.Map.Colors.Obstacle == "" { + c.Map.Colors.Obstacle = "#5d5d5d" + } + + if c.Map.Colors.Path == "" { + c.Map.Colors.Path = "#ffffffff" + } + + if c.Map.Colors.NoGoArea == "" { + c.Map.Colors.NoGoArea = "#ff00004a" + } + + if c.Map.Colors.VirtualWall == "" { + c.Map.Colors.VirtualWall = "#ff0000bf" + } + + if len(c.Map.Colors.Segments) < 4 { + c.Map.Colors.Segments = []string{"#19a1a1ff", "#7ac037ff", "#ff9b57ff", "#f7c841ff"} + } + + return c, nil } func validate(c *Config) (*Config, error) { diff --git a/pkg/renderer/drawer.go b/pkg/renderer/drawer.go index 0fdbb98..37c569c 100644 --- a/pkg/renderer/drawer.go +++ b/pkg/renderer/drawer.go @@ -75,7 +75,7 @@ func newValetudoImage(valetudoJSON *ValetudoJSON, r *Renderer) *valetudoImage { // Load colors for each segment vi.segmentColor = make(map[string]color.RGBA) - vi.findFourColors() + vi.findFourColors(r.settings.SegmentColors) // Find map bounds within robot's coordinates system (from given layers) vi.robotCoords.minX = math.MaxInt32 @@ -140,7 +140,8 @@ func (vi *valetudoImage) DrawAll() { vi.upscaleToGGContext() // Draw path entity - vi.ggContext.SetRGB255(255, 255, 255) + col := vi.renderer.settings.PathColor + vi.ggContext.SetRGBA255(int(col.R), int(col.G), int(col.B), int(col.A)) vi.ggContext.SetLineWidth(float64(vi.renderer.settings.Scale) * 0.75) for _, e := range vi.entities["path"] { vi.drawEntityPath(e) @@ -148,7 +149,8 @@ func (vi *valetudoImage) DrawAll() { vi.ggContext.Stroke() // Draw virtual_wall entities - vi.ggContext.SetRGBA255(255, 0, 0, 192) + col = vi.renderer.settings.VirtualWallColor + vi.ggContext.SetRGBA255(int(col.R), int(col.G), int(col.B), int(col.A)) vi.ggContext.SetLineWidth(float64(vi.renderer.settings.Scale) * 1.5) vi.ggContext.SetLineCapButt() for _, e := range vi.entities["virtual_wall"] { @@ -158,13 +160,15 @@ func (vi *valetudoImage) DrawAll() { // Draw no_go_area entities lineWidth := float64(vi.renderer.settings.Scale * 0.5) noGoAreas := vi.entities["no_go_area"] - vi.ggContext.SetRGBA255(255, 0, 0, 75) + col = vi.renderer.settings.NoGoAreaColor + vi.ggContext.SetRGBA255(int(col.R), int(col.G), int(col.B), int(col.A)) vi.ggContext.SetLineWidth(0) for _, e := range noGoAreas { vi.drawEntityNoGoArea(e) } vi.ggContext.Fill() - vi.ggContext.SetRGB255(255, 0, 0) + col = vi.renderer.settings.VirtualWallColor + vi.ggContext.SetRGBA255(int(col.R), int(col.G), int(col.B), int(col.A)) vi.ggContext.SetLineWidth(lineWidth) for _, e := range noGoAreas { vi.drawEntityNoGoArea(e) diff --git a/pkg/renderer/drawer_layers.go b/pkg/renderer/drawer_layers.go index 8957237..ca75a86 100644 --- a/pkg/renderer/drawer_layers.go +++ b/pkg/renderer/drawer_layers.go @@ -28,12 +28,12 @@ func (vi *valetudoImage) drawLayers() { } // Send layers to the channel - col := color.RGBA{0, 118, 255, 255} + col := vi.renderer.settings.FloorColor for _, l := range vi.layers["floor"] { layerCh <- layerColor{l, col} } - col = color.RGBA{30, 100, 100, 255} + col = vi.renderer.settings.ObstacleColor for _, l := range vi.layers["wall"] { layerCh <- layerColor{l, col} } @@ -61,4 +61,4 @@ func (vi *valetudoImage) drawLayer(l *Layer, col color.RGBA) { vi.img.SetRGBA(x, y, col) } } -} \ No newline at end of file +} diff --git a/pkg/renderer/fourcolortheorem.go b/pkg/renderer/fourcolortheorem.go index 0c0d621..d5f8ab1 100644 --- a/pkg/renderer/fourcolortheorem.go +++ b/pkg/renderer/fourcolortheorem.go @@ -73,14 +73,7 @@ func (g *graph) colorVertices() { } } -var fourColors = []color.RGBA{ - {R: 25, G: 161, B: 161, A: 255}, // Teal - {R: 122, G: 192, B: 55, A: 255}, // Light Green - {R: 255, G: 155, B: 87, A: 255}, // Orange - {R: 247, G: 200, B: 65, A: 255}, // Light Yellow -} - -func (vi *valetudoImage) findFourColors() { +func (vi *valetudoImage) findFourColors(fourColors []color.RGBA) { g := graph{} for _, layer := range vi.layers["segment"] { diff --git a/pkg/renderer/renderer.go b/pkg/renderer/renderer.go index 4c500ef..59319d7 100644 --- a/pkg/renderer/renderer.go +++ b/pkg/renderer/renderer.go @@ -2,6 +2,7 @@ package renderer import ( "image" + "image/color" "image/png" "math" @@ -25,6 +26,13 @@ type Settings struct { // Hardcoded limits for a map within robot's coordinates system StaticStartX, StaticStartY int StaticEndX, StaticEndY int + + FloorColor color.RGBA + ObstacleColor color.RGBA + PathColor color.RGBA + NoGoAreaColor color.RGBA + VirtualWallColor color.RGBA + SegmentColors []color.RGBA } func New(s *Settings) *Renderer { diff --git a/pkg/server/server.go b/pkg/server/server.go index 7aec8cf..9fc2cbc 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -3,9 +3,11 @@ package server import ( "encoding/base64" "fmt" + "image/color" "log" "os" "os/signal" + "strconv" "sync" "syscall" "time" @@ -31,6 +33,18 @@ func Start(c *config.Config) { StaticStartY: c.Map.CustomLimits.StartY, StaticEndX: c.Map.CustomLimits.EndX, StaticEndY: c.Map.CustomLimits.EndY, + + FloorColor: HexColor(c.Map.Colors.Floor), + ObstacleColor: HexColor(c.Map.Colors.Obstacle), + PathColor: HexColor(c.Map.Colors.Path), + NoGoAreaColor: HexColor(c.Map.Colors.NoGoArea), + VirtualWallColor: HexColor(c.Map.Colors.VirtualWall), + SegmentColors: []color.RGBA{ + HexColor(c.Map.Colors.Segments[0]), + HexColor(c.Map.Colors.Segments[1]), + HexColor(c.Map.Colors.Segments[2]), + HexColor(c.Map.Colors.Segments[3]), + }, }) if c.HTTP.Enabled { @@ -102,3 +116,16 @@ func ByteCountSI(b int64) string { } return fmt.Sprintf("%.1f%cB", float64(b)/float64(div), "kMGTPE"[exp]) } + +func HexColor(hex string) color.RGBA { + red, _ := strconv.ParseUint(hex[1:3], 16, 8) + green, _ := strconv.ParseUint(hex[3:5], 16, 8) + blue, _ := strconv.ParseUint(hex[5:7], 16, 8) + alpha := uint64(255) + + if len(hex) > 8 { + alpha, _ = strconv.ParseUint(hex[7:9], 16, 8) + } + + return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(alpha)} +}