Skip to content

Commit a96d8bf

Browse files
committed
feature: heatmap
1 parent 4207ca1 commit a96d8bf

33 files changed

+1852
-268
lines changed

cmd/heatmap/main.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"math/rand"
6+
7+
"github.com/johnfercher/maroto/v2"
8+
"github.com/johnfercher/maroto/v2/pkg/components/chart"
9+
"github.com/johnfercher/maroto/v2/pkg/components/text"
10+
"github.com/johnfercher/maroto/v2/pkg/config"
11+
)
12+
13+
func main() {
14+
cfg := config.NewBuilder().
15+
WithPageNumber().
16+
WithDebug(true).
17+
Build()
18+
19+
mrt := maroto.New(cfg)
20+
m := maroto.NewMetricsDecorator(mrt)
21+
22+
xMax := 10
23+
yMax := 10
24+
max := 10
25+
heat := buildHeat(xMax, yMax, max)
26+
27+
m.AddRows(text.NewRow(10, "HeatMap"))
28+
m.AddRows(chart.NewHeatMapRow(100, "map", heat, max))
29+
30+
document, err := m.Generate()
31+
if err != nil {
32+
log.Fatal(err.Error())
33+
}
34+
35+
err = document.Save("docs/assets/pdf/heatmap.pdf")
36+
if err != nil {
37+
log.Fatal(err.Error())
38+
}
39+
40+
err = document.GetReport().Save("docs/assets/text/heatmap.txt")
41+
if err != nil {
42+
log.Fatal(err.Error())
43+
}
44+
}
45+
46+
func buildHeat(x, y, max int) [][]int {
47+
var heat [][]int
48+
for i := 0; i < x; i++ {
49+
var line []int
50+
for j := 0; j < y; j++ {
51+
line = append(line, rand.Intn(max))
52+
}
53+
heat = append(heat, line)
54+
}
55+
return heat
56+
}

docs/assets/examples/textgrid/v2/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package main
22

33
import (
4-
"github.com/johnfercher/maroto/v2/pkg/consts/fontstyle"
54
"log"
65

6+
"github.com/johnfercher/maroto/v2/pkg/consts/fontstyle"
7+
78
"github.com/johnfercher/maroto/v2/pkg/core"
89

910
"github.com/johnfercher/maroto/v2"

docs/assets/pdf/heatmap.pdf

6.32 KB
Binary file not shown.

docs/assets/pdf/v2.pdf

14 KB
Binary file not shown.

docs/assets/text/heatmap.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
generate -> avg: 646.00μs, executions: [646.00μs]
2+
add_rows -> avg: 5854.50ns, executions: [11.58μs, 0.12μs]
3+
file_size -> 6.47Kb

docs/assets/text/v2.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
generate -> avg: 18.06ms, executions: [18.06ms]
2-
header -> avg: 356.00ns, executions: [356.00ns]
3-
footer -> avg: 71.00ns, executions: [71.00ns]
4-
add_row -> avg: 112.55ns, executions: [0.11μs, 0.15μs, 0.05μs, 0.08μs, 0.02μs, 0.02μs, 0.53μs, 0.06μs, 0.02μs, 0.07μs, 0.02μs, 0.02μs, 0.02μs, 0.06μs, 0.02μs, 0.02μs, 0.03μs, 1.41μs, 0.06μs, 0.02μs, 0.06μs, 0.02μs, 0.02μs, 0.01μs, 0.07μs, 0.02μs, 0.02μs, 0.03μs, 0.29μs, 1.74μs, 0.02μs, 0.07μs, 0.02μs, 0.02μs, 0.02μs, 0.06μs, 0.02μs, 0.02μs, 0.02μs, 0.24μs, 0.05μs, 0.02μs, 0.07μs, 0.02μs, 0.01μs, 0.02μs, 0.06μs, 0.03μs, 0.01μs, 0.01μs, 0.24μs, 0.05μs, 0.01μs, 0.05μs, 0.02μs]
5-
file_size -> 267.93Kb
1+
generate -> avg: 26.67ms, executions: [26.67ms]
2+
header -> avg: 21.00μs, executions: [21.00μs]
3+
footer -> avg: 333.00ns, executions: [333.00ns]
4+
add_rows -> avg: 157.56ns, executions: [0.29μs, 0.50μs, 0.29μs, 0.50μs, 0.08μs, 0.12μs, 1.21μs, 0.17μs, 0.04μs, 0.08μs, 0.04μs, 0.04μs, 0.04μs, 0.21μs, 0.04μs, 0.04μs, 0.04μs, 0.67μs, 0.17μs, 0.04μs, 0.12μs, 0.04μs, 0.04μs, 0.04μs, 0.17μs, 0.04μs, 0.04μs, 0.04μs, 0.50μs, 0.12μs, 0.04μs, 0.12μs, 0.04μs, 0.21μs, 0.04μs, 0.12μs, 0.04μs, 0.04μs, 0.08μs, 0.50μs, 0.17μs, 0.04μs, 0.08μs, 0.04μs, 0.04μs, 0.04μs, 0.33μs, 0.04μs, 0.04μs, 0.04μs, 0.46μs, 0.08μs, 0.04μs, 0.12μs, 0.04μs]
5+
file_size -> 282.23Kb

internal/providers/gofpdf/builder.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Dependencies struct {
2020
Code core.Code
2121
Image core.Image
2222
Line core.Line
23+
HeatMap core.HeatMap
2324
Cache cache.Cache
2425
CellWriter cellwriter.CellWriter
2526
Cfg *entity.Config
@@ -68,6 +69,7 @@ func (b *builder) Build(cfg *entity.Config, cache cache.Cache) *Dependencies {
6869
text := NewText(fpdf, math, font)
6970
image := NewImage(fpdf, math)
7071
line := NewLine(fpdf)
72+
heatMap := NewHeatMap(fpdf, math)
7173
cellWriter := cellwriter.NewBuilder().
7274
Build(fpdf)
7375

@@ -78,6 +80,7 @@ func (b *builder) Build(cfg *entity.Config, cache cache.Cache) *Dependencies {
7880
Code: code,
7981
Image: image,
8082
Line: line,
83+
HeatMap: heatMap,
8184
CellWriter: cellWriter,
8285
Cfg: cfg,
8386
Cache: cache,
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package gofpdf
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"math"
7+
8+
"github.com/johnfercher/maroto/v2/pkg/props"
9+
10+
"github.com/johnfercher/maroto/v2/internal/providers/gofpdf/gofpdfwrapper"
11+
"github.com/johnfercher/maroto/v2/pkg/core"
12+
"github.com/johnfercher/maroto/v2/pkg/core/entity"
13+
)
14+
15+
var ErrOutOfRange = errors.New("out of range")
16+
17+
type heatMap struct {
18+
pdf gofpdfwrapper.Fpdf
19+
math core.Math
20+
defaultFillColor *props.Color
21+
}
22+
23+
func NewHeatMap(pdf gofpdfwrapper.Fpdf, math core.Math) *heatMap {
24+
return &heatMap{
25+
pdf: pdf,
26+
math: math,
27+
defaultFillColor: &props.WhiteColor,
28+
}
29+
}
30+
31+
func (s heatMap) Add(heatMap [][]int, max int, cell *entity.Cell, margins *entity.Margins) {
32+
if heatMap == nil {
33+
return
34+
}
35+
36+
ySize := len(heatMap)
37+
if ySize == 0 {
38+
return
39+
}
40+
stepY := (cell.Height) / float64(ySize-1)
41+
42+
xSize := len(heatMap[0])
43+
if xSize == 0 {
44+
return
45+
}
46+
stepX := (cell.Width) / float64(xSize-1)
47+
48+
for i := 0; i < len(heatMap)-1; i++ {
49+
for j := 0; j < len(heatMap[i])-1; j++ {
50+
r, g, b := s.GetHeatColor(heatMap[i][j], max)
51+
52+
x := float64(i)*stepX + cell.X + margins.Left
53+
y := float64(j)*stepY + cell.Y + margins.Top
54+
width := stepX
55+
height := stepY
56+
57+
fmt.Println(x, y, width, height)
58+
59+
s.pdf.SetFillColor(r, g, b)
60+
s.pdf.Rect(x, y, width, height, "F")
61+
s.pdf.SetFillColor(s.defaultFillColor.Red, s.defaultFillColor.Green, s.defaultFillColor.Blue)
62+
}
63+
}
64+
}
65+
66+
func (s heatMap) GetHeatColor(i int, total int) (int, int, int) {
67+
hueMax := 160.0
68+
step := hueMax / float64(total)
69+
iStep := step * float64(i)
70+
71+
r, g, b, _ := HSVToRGB(iStep, 1.0, 1.0)
72+
return int(r), int(g), int(b)
73+
}
74+
75+
// HSVToRGB converts an HSV triple to an RGB triple.
76+
// Source: https://github.com/Crazy3lf/colorconv/blob/master/colorconv.go
77+
func HSVToRGB(h, s, v float64) (r, g, b uint8, err error) {
78+
if h < 0 || h >= 360 ||
79+
s < 0 || s > 1 ||
80+
v < 0 || v > 1 {
81+
return 0, 0, 0, ErrOutOfRange
82+
}
83+
// When 0 ≤ h < 360, 0 ≤ s ≤ 1 and 0 ≤ v ≤ 1:
84+
C := v * s
85+
X := C * (1 - math.Abs(math.Mod(h/60, 2)-1))
86+
m := v - C
87+
var Rnot, Gnot, Bnot float64
88+
switch {
89+
case 0 <= h && h < 60:
90+
Rnot, Gnot, Bnot = C, X, 0
91+
case 60 <= h && h < 120:
92+
Rnot, Gnot, Bnot = X, C, 0
93+
case 120 <= h && h < 180:
94+
Rnot, Gnot, Bnot = 0, C, X
95+
case 180 <= h && h < 240:
96+
Rnot, Gnot, Bnot = 0, X, C
97+
case 240 <= h && h < 300:
98+
Rnot, Gnot, Bnot = X, 0, C
99+
case 300 <= h && h < 360:
100+
Rnot, Gnot, Bnot = C, 0, X
101+
}
102+
r = uint8(math.Round((Rnot + m) * 255))
103+
g = uint8(math.Round((Gnot + m) * 255))
104+
b = uint8(math.Round((Bnot + m) * 255))
105+
return r, g, b, nil
106+
}

internal/providers/gofpdf/provider.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type provider struct {
2626
code core.Code
2727
image core.Image
2828
line core.Line
29+
heatMap core.HeatMap
2930
cache cache.Cache
3031
cellWriter cellwriter.CellWriter
3132
cfg *entity.Config
@@ -40,6 +41,7 @@ func New(dep *Dependencies) core.Provider {
4041
code: dep.Code,
4142
image: dep.Image,
4243
line: dep.Line,
44+
heatMap: dep.HeatMap,
4345
cellWriter: dep.CellWriter,
4446
cfg: dep.Cfg,
4547
cache: dep.Cache,
@@ -268,6 +270,10 @@ func (g *provider) SetCompression(compression bool) {
268270
g.fpdf.SetCompression(compression)
269271
}
270272

273+
func (g *provider) AddHeatMap(heatMap [][]int, max int, cell *entity.Cell) {
274+
g.heatMap.Add(heatMap, max, cell, g.cfg.Margins)
275+
}
276+
271277
func (g *provider) getBarcodeImageName(code string, prop *props.Barcode) string {
272278
if prop == nil {
273279
return code + string(barcode.Code128)

0 commit comments

Comments
 (0)