WIP trmnl BYOS
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Delete old code

-969
-66
internal/layout/fill.go
··· 1 - package layout 2 - 3 - import "image" 4 - 5 - type FillColor string 6 - 7 - const ( 8 - FillColorWhite FillColor = "white" 9 - FillColorBlack FillColor = "black" 10 - FillColorGray FillColor = "gray" 11 - ) 12 - 13 - const defaultFillColor = FillColorWhite 14 - 15 - type FillNode struct { 16 - color FillColor 17 - rect image.Rectangle 18 - style NodeStyle 19 - } 20 - 21 - func parseFillNode(n *rawNode, rect image.Rectangle) *FillNode { 22 - fn := &FillNode{ 23 - color: defaultFillColor, 24 - rect: rect, 25 - style: n.Style, 26 - } 27 - 28 - if col, ok := n.Attrs["color"]; ok { 29 - if c, ok := col.(string); ok { 30 - switch c { 31 - case "white": 32 - fn.color = FillColorWhite 33 - case "black": 34 - fn.color = FillColorBlack 35 - case "gray": 36 - fn.color = FillColorGray 37 - } 38 - } 39 - } 40 - 41 - return fn 42 - } 43 - 44 - func (n *FillNode) Type() NodeType { 45 - return NodeTypeFill 46 - } 47 - 48 - func (n *FillNode) Rect() image.Rectangle { 49 - return n.rect 50 - } 51 - 52 - func (n *FillNode) Style() NodeStyle { 53 - return n.style 54 - } 55 - 56 - func (n *FillNode) Color() FillColor { 57 - return n.color 58 - } 59 - 60 - func (n *FillNode) SetRect(rect image.Rectangle) { 61 - n.rect = rect 62 - } 63 - 64 - func (n *FillNode) GetNode() Node { 65 - return n 66 - }
-152
internal/layout/flex.go
··· 1 - package layout 2 - 3 - import ( 4 - "image" 5 - 6 - "github.com/go-viper/mapstructure/v2" 7 - ) 8 - 9 - type FlexDirection string 10 - 11 - const ( 12 - FlexDirectionVertical FlexDirection = "vertical" 13 - FlexDirectionHorizontal FlexDirection = "horizontal" 14 - ) 15 - 16 - const defaultFlexDirection = FlexDirectionHorizontal 17 - 18 - type FlexSeparator string 19 - 20 - const ( 21 - FlexSeparatorNone FlexSeparator = "none" 22 - FlexSeparatorSolid FlexSeparator = "solid" 23 - FlexSeparatorDashed FlexSeparator = "dashed" 24 - ) 25 - 26 - const defaultFlexSeparator = FlexSeparatorNone 27 - 28 - const defaultPadding = 0 29 - 30 - type childNode interface { 31 - Node 32 - GetNode() Node 33 - SetRect(image.Rectangle) 34 - } 35 - 36 - type FlexStyle struct { 37 - Direction FlexDirection 38 - Separator FlexSeparator 39 - } 40 - 41 - type FlexNode struct { 42 - flexStyle FlexStyle 43 - rect image.Rectangle 44 - children []childNode 45 - style NodeStyle 46 - } 47 - 48 - func parseFlexNode(n *rawNode, rect image.Rectangle) *FlexNode { 49 - fn := &FlexNode{ 50 - flexStyle: FlexStyle{ 51 - Direction: defaultFlexDirection, 52 - Separator: defaultFlexSeparator, 53 - }, 54 - rect: rect, 55 - style: n.Style, 56 - } 57 - 58 - // Parse separator 59 - if sep, ok := n.Attrs["separator"]; ok { 60 - if s, ok := sep.(string); ok { 61 - switch s { 62 - case "none": 63 - fn.flexStyle.Separator = FlexSeparatorNone 64 - case "solid": 65 - fn.flexStyle.Separator = FlexSeparatorSolid 66 - case "dashed": 67 - fn.flexStyle.Separator = FlexSeparatorDashed 68 - } 69 - } 70 - } 71 - 72 - // Parse direction 73 - if dir, ok := n.Attrs["direction"]; ok { 74 - if d, ok := dir.(string); ok { 75 - switch d { 76 - case "vertical": 77 - fn.flexStyle.Direction = FlexDirectionVertical 78 - case "horizontal": 79 - fn.flexStyle.Direction = FlexDirectionHorizontal 80 - } 81 - } 82 - } 83 - 84 - return fn 85 - } 86 - 87 - func (n *FlexNode) Type() NodeType { 88 - return NodeTypeFlex 89 - } 90 - 91 - func (n *FlexNode) Rect() image.Rectangle { 92 - return n.rect 93 - } 94 - 95 - func (n *FlexNode) Style() NodeStyle { 96 - return n.style 97 - } 98 - 99 - func (n *FlexNode) SetRect(rect image.Rectangle) { 100 - n.rect = rect 101 - } 102 - 103 - func (n *FlexNode) GetNode() Node { 104 - return n 105 - } 106 - 107 - func (n *FlexNode) FlexStyle() FlexStyle { 108 - return n.flexStyle 109 - } 110 - 111 - func (n *FlexNode) Children() []Node { 112 - ret := []Node{} 113 - for _, c := range n.children { 114 - ret = append(ret, c.GetNode()) 115 - } 116 - return ret 117 - } 118 - 119 - type flexCalculation struct { 120 - static int 121 - weighted int 122 - } 123 - 124 - func calculateChildrenWeights(dir FlexDirection, n *rawNode) (*flexCalculation, error) { 125 - if len(n.Children) < 1 { 126 - return nil, ErrNoChildren 127 - } 128 - 129 - calc := &flexCalculation{0, 0} 130 - for _, child := range n.Children { 131 - style := NodeStyle{ 132 - Weight: 1, 133 - } 134 - if st, ok := child["style"]; ok { 135 - if s, ok := st.(map[string]any); ok { 136 - if err := mapstructure.Decode(s, calc); err != nil { 137 - return nil, err 138 - } 139 - } 140 - } 141 - 142 - if dir == FlexDirectionHorizontal && style.Width > 0 { 143 - calc.static += style.Width 144 - } else if dir == FlexDirectionVertical && style.Height > 0 { 145 - calc.static += style.Height 146 - } else { 147 - calc.weighted += style.Weight 148 - } 149 - } 150 - 151 - return calc, nil 152 - }
-268
internal/layout/layout.go
··· 1 - package layout 2 - 3 - import ( 4 - "errors" 5 - "image" 6 - 7 - "github.com/dop251/goja" 8 - "github.com/go-viper/mapstructure/v2" 9 - ) 10 - 11 - type NodeType string 12 - 13 - const ( 14 - NodeTypeFlex NodeType = "flex" 15 - NodeTypeText NodeType = "text" 16 - NodeTypeFill NodeType = "fill" 17 - ) 18 - 19 - var ( 20 - ErrUnknownNodeType = errors.New("unknown node type") 21 - ErrModuleWithoutFunc = errors.New("module node without function") 22 - ErrNoChildren = errors.New("no children") 23 - ) 24 - 25 - type Node interface { 26 - Type() NodeType 27 - Rect() image.Rectangle 28 - Style() NodeStyle 29 - } 30 - 31 - type NodeStyle struct { 32 - Weight int 33 - Margin int 34 - Padding int 35 - Height int 36 - Width int 37 - CornerRadius int 38 - } 39 - 40 - type weightedNode struct { 41 - Node map[string]any 42 - Weight float32 43 - } 44 - 45 - type rawNode struct { 46 - NodeName string 47 - Style NodeStyle 48 - Attrs map[string]any 49 - Children []map[string]any 50 - } 51 - 52 - type rawModule struct { 53 - Module goja.Callable 54 - Weight int64 55 - Padding int64 56 - Attrs map[string]any 57 - } 58 - 59 - func CalculateLayout(root map[string]any, rect image.Rectangle, rt *goja.Runtime) (Node, error) { 60 - node, err := calculateLayout(root, rect, rt) 61 - if err != nil { 62 - return nil, err 63 - } 64 - 65 - return node.GetNode(), nil 66 - } 67 - 68 - func calculateLayout(root map[string]any, rect image.Rectangle, rt *goja.Runtime) (childNode, error) { 69 - t, ok := root["type"] 70 - if !ok { 71 - return nil, ErrUnknownNodeType 72 - } 73 - 74 - switch t { 75 - case "node": 76 - // Parse raw node data 77 - n := &rawNode{} 78 - if err := mapstructure.Decode(&root, n); err != nil { 79 - return nil, err 80 - } 81 - 82 - return calculateNode(n, rect, rt) 83 - 84 - case "module": 85 - // Make sure module has a function to call 86 - mod, ok := root["module"] 87 - if !ok { 88 - return nil, ErrModuleWithoutFunc 89 - } 90 - 91 - fn, ok := goja.AssertFunction(rt.ToValue(mod)) 92 - if !ok { 93 - return nil, ErrModuleWithoutFunc 94 - } 95 - 96 - weight := int64(1) 97 - if w, ok := root["weight"]; ok { 98 - weight = w.(int64) 99 - } 100 - 101 - padding := int64(defaultPadding) 102 - if p, ok := root["padding"]; ok { 103 - padding = p.(int64) 104 - } 105 - 106 - attrs := map[string]any{} 107 - if a, ok := root["attrs"]; ok { 108 - if err := rt.ExportTo(rt.ToValue(a), &attrs); err != nil { 109 - return nil, err 110 - } 111 - } 112 - 113 - return calculateModule(&rawModule{ 114 - Module: fn, 115 - Weight: weight, 116 - Padding: padding, 117 - Attrs: attrs, 118 - }, rect, rt) 119 - default: 120 - return nil, ErrUnknownNodeType 121 - } 122 - } 123 - 124 - func calculateNode(n *rawNode, rect image.Rectangle, rt *goja.Runtime) (childNode, error) { 125 - switch NodeType(n.NodeName) { 126 - case NodeTypeFlex: 127 - return calculateFlex(n, rect, rt) 128 - case NodeTypeFill: 129 - return parseFillNode(n, rect), nil 130 - case NodeTypeText: 131 - return parseTextNode(n, rect), nil 132 - default: 133 - return nil, ErrUnknownNodeType 134 - } 135 - } 136 - 137 - func calculateFlex(n *rawNode, rect image.Rectangle, rt *goja.Runtime) (childNode, error) { 138 - node := parseFlexNode(n, rect) 139 - 140 - // Calculate padded rect 141 - pRect := image.Rect( 142 - rect.Min.X+node.style.Padding, 143 - rect.Min.Y+node.style.Padding, 144 - rect.Max.X-node.style.Padding, 145 - rect.Max.Y-node.style.Padding, 146 - ) 147 - width := pRect.Max.X - pRect.Min.X 148 - height := pRect.Max.Y - pRect.Min.Y 149 - 150 - if calc, err := calculateChildrenWeights(node.flexStyle.Direction, n); err == nil { 151 - weighted := []weightedNode{} 152 - 153 - for _, child := range n.Children { 154 - style := NodeStyle{ 155 - Weight: 1, 156 - } 157 - if st, ok := child["style"]; ok { 158 - if s, ok := st.(map[string]any); ok { 159 - if err := mapstructure.Decode(s, calc); err != nil { 160 - return nil, err 161 - } 162 - } 163 - } 164 - } 165 - } 166 - 167 - if sum, err := sumChildrenWeight(n); err == nil { 168 - weighted := []weightedNode{} 169 - 170 - for _, child := range n.Children { 171 - weight := int64(1) 172 - if w, ok := child["weight"]; ok { 173 - weight = w.(int64) 174 - } 175 - 176 - w := float32(weight) / float32(sum) 177 - weighted = append(weighted, weightedNode{child, w}) 178 - } 179 - 180 - if node.Direction == FlexDirectionVertical { 181 - pos := pRect.Min.Y 182 - for _, child := range weighted { 183 - r := image.Rect( 184 - pRect.Min.X, 185 - pos, 186 - pRect.Max.X, 187 - pos+int(float32(height)*child.Weight), 188 - ) 189 - pos = r.Max.Y 190 - 191 - c, err := calculateLayout(child.Node, r, rt) 192 - if err != nil { 193 - return nil, err 194 - } 195 - 196 - node.Children = append(node.Children, c) 197 - } 198 - } else if node.Direction == FlexDirectionHorizontal { 199 - pos := pRect.Min.X 200 - for _, child := range weighted { 201 - r := image.Rect( 202 - pos, 203 - pRect.Min.Y, 204 - pos+int(float32(width)*child.Weight), 205 - pRect.Max.Y, 206 - ) 207 - pos = r.Max.X 208 - 209 - c, err := calculateLayout(child.Node, r, rt) 210 - if err != nil { 211 - return nil, err 212 - } 213 - 214 - node.Children = append(node.Children, c) 215 - } 216 - } 217 - } 218 - 219 - return node, nil 220 - } 221 - 222 - func sumChildrenWeight(n *rawNode) (int64, error) { 223 - if len(n.Children) < 1 { 224 - return 0, ErrNoChildren 225 - } 226 - 227 - sum := int64(0) 228 - for _, child := range n.Children { 229 - weight := int64(1) 230 - if w, ok := child["weight"]; ok { 231 - weight = w.(int64) 232 - } 233 - 234 - sum += weight 235 - } 236 - 237 - return sum, nil 238 - } 239 - 240 - func calculateModule(m *rawModule, rect image.Rectangle, rt *goja.Runtime) (childNode, error) { 241 - props := map[string]any{ 242 - "width": rect.Max.X - rect.Min.X, 243 - "height": rect.Max.Y - rect.Min.Y, 244 - } 245 - 246 - for k, v := range m.Attrs { 247 - // Make sure size can't be overwritten 248 - if k != "height" && k != "width" { 249 - props[k] = v 250 - } 251 - } 252 - 253 - ret, err := m.Module(goja.Undefined(), rt.ToValue(props)) 254 - if err != nil { 255 - return nil, err 256 - } 257 - 258 - root := map[string]any{} 259 - if err := rt.ExportTo(ret, &root); err != nil { 260 - return nil, err 261 - } 262 - 263 - // Set weight 264 - root["weight"] = m.Weight 265 - root["padding"] = m.Padding 266 - 267 - return calculateLayout(root, rect, rt) 268 - }
-62
internal/layout/text.go
··· 1 - package layout 2 - 3 - import "image" 4 - 5 - const defaultFontSize = 32 6 - 7 - type TextNode struct { 8 - content string 9 - size int 10 - rect image.Rectangle 11 - style NodeStyle 12 - } 13 - 14 - func parseTextNode(n *rawNode, rect image.Rectangle) *TextNode { 15 - tn := &TextNode{ 16 - size: defaultFontSize, 17 - rect: rect, 18 - style: n.Style, 19 - } 20 - 21 - if con, ok := n.Attrs["content"]; ok { 22 - if c, ok := con.(string); ok { 23 - tn.content = c 24 - } 25 - } 26 - 27 - if sz, ok := n.Attrs["size"]; ok { 28 - if s, ok := sz.(int64); ok { 29 - tn.size = int(s) 30 - } 31 - } 32 - 33 - return tn 34 - } 35 - 36 - func (n *TextNode) Type() NodeType { 37 - return NodeTypeText 38 - } 39 - 40 - func (n *TextNode) Rect() image.Rectangle { 41 - return n.rect 42 - } 43 - 44 - func (n *TextNode) Style() NodeStyle { 45 - return n.style 46 - } 47 - 48 - func (n *TextNode) Content() string { 49 - return n.content 50 - } 51 - 52 - func (n *TextNode) Size() int { 53 - return n.size 54 - } 55 - 56 - func (n *TextNode) SetRect(rect image.Rectangle) { 57 - n.rect = rect 58 - } 59 - 60 - func (n *TextNode) GetNode() Node { 61 - return n 62 - }
-48
internal/module/pregnancy/pregnancy.go
··· 1 - package pregnancy 2 - 3 - import ( 4 - "time" 5 - 6 - "github.com/arnarg/scrn/internal/module" 7 - "github.com/golang/freetype/truetype" 8 - "github.com/llgcode/draw2d" 9 - "github.com/llgcode/draw2d/draw2dimg" 10 - ) 11 - 12 - type pregnancyModule struct { 13 - tr *tracker 14 - font *truetype.Font 15 - } 16 - 17 - func NewPregnancyModule(due time.Time) module.Module { 18 - return &pregnancyModule{ 19 - tr: newTracker(due), 20 - font: draw2d.GetFont(draw2d.FontData{ 21 - Name: "inconsolata", 22 - Family: draw2d.FontFamilySans, 23 - Style: draw2d.FontStyleNormal, 24 - }), 25 - } 26 - } 27 - 28 - func (m *pregnancyModule) Draw(ctx *draw2dimg.GraphicContext) error { 29 - 30 - // ctx.SetFont(m.font) 31 - // ctx.SetFontSize(14) 32 - 33 - // ctx.StrokeStringAt("Week", 20, 20) 34 - 35 - // minX := float64(img.Bounds().Min.X) 36 - // minY := float64(img.Bounds().Min.Y) 37 - 38 - // ctx.SetFillColor(color.Black) 39 - // ctx.SetStrokeColor(color.Black) 40 - // ctx.SetLineWidth(5) 41 - 42 - // ctx.MoveTo(10, 10) 43 - // ctx.LineTo(100, 10) 44 - 45 - // ctx.FillStroke() 46 - 47 - return nil 48 - }
-62
internal/module/pregnancy/util.go
··· 1 - package pregnancy 2 - 3 - import ( 4 - "math" 5 - "time" 6 - ) 7 - 8 - const ( 9 - week = time.Hour * 24 * 7 10 - ) 11 - 12 - type progress struct { 13 - w int 14 - d int 15 - } 16 - 17 - type tracker struct { 18 - due time.Time 19 - start time.Time 20 - } 21 - 22 - func newTracker(due time.Time) *tracker { 23 - s := due.Add(week * -40) 24 - 25 - return &tracker{ 26 - due: due, 27 - start: s, 28 - } 29 - } 30 - 31 - func (t *tracker) progressAt(d time.Time) progress { 32 - days := t.daysAt(d) 33 - 34 - return progress{ 35 - w: days / 7, 36 - d: days % 7, 37 - } 38 - } 39 - 40 - func (t *tracker) progress() progress { 41 - return t.progressAt(time.Now()) 42 - } 43 - 44 - func (t *tracker) daysAt(d time.Time) int { 45 - // Calculate number of days since start date 46 - return int(d.Sub(t.start).Hours() / 24) 47 - } 48 - 49 - func (t *tracker) days() int { 50 - return t.daysAt(time.Now()) 51 - } 52 - 53 - func (t *tracker) percentageAt(d time.Time) float64 { 54 - total := t.due.Sub(t.start).Hours() 55 - current := d.Sub(t.start).Hours() 56 - 57 - return math.Round((current/total)*100) / 100 58 - } 59 - 60 - func (t *tracker) percentage() float64 { 61 - return t.percentageAt(time.Now()) 62 - }
-70
internal/module/pregnancy/util_test.go
··· 1 - package pregnancy 2 - 3 - import ( 4 - "testing" 5 - "time" 6 - ) 7 - 8 - func TestTrackerProgressAt(t *testing.T) { 9 - due, err := time.Parse(time.DateOnly, "2025-05-12") 10 - if err != nil { 11 - t.Fatalf("parsing time somehow failed: %s", err) 12 - } 13 - 14 - tracker := newTracker(due) 15 - 16 - now, err := time.Parse(time.DateOnly, "2025-01-01") 17 - if err != nil { 18 - t.Fatalf("parsing time somehow failed: %s", err) 19 - } 20 - 21 - progress := tracker.progressAt(now) 22 - 23 - if progress.w != 21 { 24 - t.Errorf("week progress is not 21, got %d", progress.w) 25 - } 26 - 27 - if progress.d != 2 { 28 - t.Errorf("day progress is not 2, got %d", progress.d) 29 - } 30 - } 31 - 32 - func TestTrackerDaysAt(t *testing.T) { 33 - due, err := time.Parse(time.DateOnly, "2025-05-12") 34 - if err != nil { 35 - t.Fatalf("parsing time somehow failed: %s", err) 36 - } 37 - 38 - tracker := newTracker(due) 39 - 40 - now, err := time.Parse(time.DateOnly, "2025-01-01") 41 - if err != nil { 42 - t.Fatalf("parsing time somehow failed: %s", err) 43 - } 44 - 45 - days := tracker.daysAt(now) 46 - 47 - if days != 149 { 48 - t.Errorf("number of days since start is not 149, got %d", days) 49 - } 50 - } 51 - 52 - func TestTrackerPercentageAt(t *testing.T) { 53 - due, err := time.Parse(time.DateOnly, "2025-05-12") 54 - if err != nil { 55 - t.Fatalf("parsing time somehow failed: %s", err) 56 - } 57 - 58 - tracker := newTracker(due) 59 - 60 - now, err := time.Parse(time.DateOnly, "2025-01-01") 61 - if err != nil { 62 - t.Fatalf("parsing time somehow failed: %s", err) 63 - } 64 - 65 - percentage := tracker.percentageAt(now) 66 - 67 - if percentage != 0.53 { 68 - t.Errorf("calculated percentage is not 0.53, got %f", percentage) 69 - } 70 - }
-17
internal/module/weather/weather.go
··· 1 - package weather 2 - 3 - import ( 4 - "github.com/arnarg/scrn/internal/module" 5 - "github.com/llgcode/draw2d/draw2dimg" 6 - ) 7 - 8 - type weatherModule struct { 9 - } 10 - 11 - func NewWeatherModule() module.Module { 12 - return &weatherModule{} 13 - } 14 - 15 - func (m *weatherModule) Draw(ctx *draw2dimg.GraphicContext) error { 16 - return nil 17 - }
-53
internal/util/painter.go
··· 1 - package util 2 - 3 - import ( 4 - "image" 5 - "image/color" 6 - 7 - "github.com/golang/freetype/raster" 8 - "github.com/llgcode/draw2d/draw2dimg" 9 - ) 10 - 11 - type palettedPainter struct { 12 - image *image.Paletted 13 - color color.Color 14 - } 15 - 16 - func NewPalettedPainter(img *image.Paletted) draw2dimg.Painter { 17 - return &palettedPainter{ 18 - image: img, 19 - color: color.White, 20 - } 21 - } 22 - 23 - func (p *palettedPainter) Paint(ss []raster.Span, done bool) { 24 - b := p.image.Bounds() 25 - rect := image.Rect(0, 0, b.Max.X-b.Min.X, b.Max.Y-b.Min.Y) 26 - for _, s := range ss { 27 - if s.Y < rect.Min.Y { 28 - continue 29 - } 30 - if s.Y >= rect.Max.Y { 31 - return 32 - } 33 - if s.X0 < rect.Min.X { 34 - s.X0 = b.Min.X 35 - } 36 - if s.X1 > rect.Max.X { 37 - s.X1 = b.Max.X 38 - } 39 - if s.X0 >= s.X1 { 40 - continue 41 - } 42 - 43 - if s.Alpha >= 0x8000 { 44 - for i := s.X0; i < s.X1; i++ { 45 - p.image.Set(b.Min.X+i, b.Min.Y+s.Y, p.color) 46 - } 47 - } 48 - } 49 - } 50 - 51 - func (p *palettedPainter) SetColor(color color.Color) { 52 - p.color = p.image.ColorModel().Convert(color) 53 - }
-109
internal/util/rounded.go
··· 1 - package util 2 - 3 - import ( 4 - "image" 5 - "image/color" 6 - "image/draw" 7 - "math" 8 - 9 - "github.com/golang/freetype/raster" 10 - "github.com/llgcode/draw2d/draw2dimg" 11 - ) 12 - 13 - type RoundedImage struct { 14 - draw.Image 15 - radius int 16 - } 17 - 18 - func NewRoundedImage(img draw.Image, radius int) *RoundedImage { 19 - return &RoundedImage{ 20 - img, 21 - radius, 22 - } 23 - } 24 - 25 - func (r *RoundedImage) Set(x, y int, c color.Color) { 26 - rad := r.radius 27 - b := r.Image.Bounds() 28 - p := image.Pt(x, y) 29 - 30 - // Check top left corner 31 - tl := image.Pt(b.Min.X+rad, b.Min.Y+rad) 32 - if x <= tl.X && y <= tl.Y && isOutside(p, tl, rad) { 33 - return 34 - } 35 - // Check top right corner 36 - tr := image.Pt(b.Max.X-rad-1, b.Min.Y+rad) 37 - if x >= tr.X && y <= tr.Y && isOutside(p, tr, rad) { 38 - return 39 - } 40 - // Check bottom left corner 41 - bl := image.Pt(b.Min.X+rad, b.Max.Y-rad-1) 42 - if x <= bl.X && y >= bl.Y && isOutside(p, bl, rad) { 43 - return 44 - } 45 - // Check bottom right corner 46 - br := image.Pt(b.Max.X-rad-1, b.Max.Y-rad-1) 47 - if x >= br.X && y >= br.Y && isOutside(p, br, rad) { 48 - return 49 - } 50 - 51 - // If we got this far we're inside the rounded 52 - // rectangle area and we just draw 53 - r.Image.Set(x, y, c) 54 - } 55 - 56 - func (r *RoundedImage) Painter() draw2dimg.Painter { 57 - return &roundedPainter{ 58 - image: r, 59 - color: color.White, 60 - } 61 - } 62 - 63 - func isOutside(p1, p2 image.Point, r int) bool { 64 - dist := calculateDistance(p1.X, p1.Y, p2.X, p2.Y) 65 - 66 - return dist > float64(r) 67 - } 68 - 69 - func calculateDistance(x1, y1, x2, y2 int) float64 { 70 - return math.Sqrt(math.Pow(float64(x2)-float64(x1), 2) + math.Pow(float64(y2)-float64(y1), 2)) 71 - 72 - } 73 - 74 - type roundedPainter struct { 75 - image *RoundedImage 76 - color color.Color 77 - } 78 - 79 - func (r *roundedPainter) Paint(ss []raster.Span, done bool) { 80 - b := r.image.Bounds() 81 - rect := image.Rect(0, 0, b.Max.X-b.Min.X, b.Max.Y-b.Min.Y) 82 - for _, s := range ss { 83 - if s.Y < rect.Min.Y { 84 - continue 85 - } 86 - if s.Y >= rect.Max.Y { 87 - return 88 - } 89 - if s.X0 < rect.Min.X { 90 - s.X0 = b.Min.X 91 - } 92 - if s.X1 > rect.Max.X { 93 - s.X1 = b.Max.X 94 - } 95 - if s.X0 >= s.X1 { 96 - continue 97 - } 98 - 99 - if s.Alpha >= 0x8000 { 100 - for i := s.X0; i < s.X1; i++ { 101 - r.image.Set(b.Min.X+i, b.Min.Y+s.Y, r.color) 102 - } 103 - } 104 - } 105 - } 106 - 107 - func (r *roundedPainter) SetColor(color color.Color) { 108 - r.color = r.image.ColorModel().Convert(color) 109 - }
-62
internal/util/uniform.go
··· 1 - package util 2 - 3 - import ( 4 - "image" 5 - "image/color" 6 - ) 7 - 8 - type patternImage struct { 9 - pix [][]color.Color 10 - size int 11 - } 12 - 13 - func (p *patternImage) At(x, y int) color.Color { 14 - return p.pix[y%p.size][x%p.size] 15 - } 16 - 17 - func (p *patternImage) Bounds() image.Rectangle { 18 - return image.Rectangle{ 19 - image.Point{-1e9, -1e9}, 20 - image.Point{1e9, 1e9}, 21 - } 22 - } 23 - 24 - func (p *patternImage) ColorModel() color.Model { 25 - return color.GrayModel 26 - } 27 - 28 - func NewLightGrayImage() image.Image { 29 - return &patternImage{ 30 - pix: [][]color.Color{ 31 - {color.White, color.White, color.White, color.Black, color.White, color.White}, 32 - {color.White, color.White, color.White, color.White, color.White, color.White}, 33 - {color.White, color.White, color.White, color.White, color.White, color.White}, 34 - {color.Black, color.White, color.White, color.White, color.White, color.White}, 35 - {color.White, color.White, color.White, color.White, color.White, color.White}, 36 - {color.White, color.White, color.White, color.White, color.White, color.White}, 37 - }, 38 - size: 6, 39 - } 40 - } 41 - 42 - func NewGrayImage() image.Image { 43 - return &patternImage{ 44 - pix: [][]color.Color{ 45 - {color.White, color.White, color.Black, color.White}, 46 - {color.White, color.White, color.White, color.White}, 47 - {color.Black, color.White, color.White, color.White}, 48 - {color.White, color.White, color.White, color.White}, 49 - }, 50 - size: 4, 51 - } 52 - } 53 - 54 - func NewDarkGrayImage() image.Image { 55 - return &patternImage{ 56 - pix: [][]color.Color{ 57 - {color.White, color.Black}, 58 - {color.Black, color.White}, 59 - }, 60 - size: 2, 61 - } 62 - }