github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/linerendering/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "image" 6 "image/color" 7 "image/png" 8 "log" 9 "math" 10 "os" 11 ) 12 13 type float float64 14 15 const ( 16 tau = float(math.Pi * 2) 17 pixr = float(1.0) 18 pixr2 = pixr * pixr 19 pixa = pixr2 * tau / 2 20 ) 21 22 func (v float) Sqrt() float { return float(math.Sqrt(float64(v))) } 23 func (v float) Sqr() float { return v * v } 24 func (v float) Sin() float { return float(math.Sin(float64(v))) } 25 func (v float) Cos() float { return float(math.Cos(float64(v))) } 26 func (v float) Acos() float { return float(math.Acos(float64(v))) } 27 func (v float) Abs() float { 28 if v < 0 { 29 return -v 30 } 31 return v 32 } 33 34 type Point struct{ X, Y float } 35 type Line struct { 36 A, B Point 37 Radius float 38 } 39 40 func Dist2(a, b Point) float { 41 return (a.Y - b.Y).Sqr() + (a.X - b.X).Sqr() 42 } 43 44 func (line *Line) DistanceTo(p Point) (center float) { 45 v, w := line.A, line.B 46 l2 := Dist2(v, w) 47 t := ((p.X-v.X)*(w.X-v.X) + (p.Y-v.Y)*(w.Y-v.Y)) / l2 48 49 if t < 0 { 50 t = 0 51 } else if t > 1 { 52 t = 1 53 } 54 55 return Dist2(p, Point{X: v.X + t*(w.X-v.X), Y: v.Y + t*(w.Y-v.Y)}).Sqrt() 56 } 57 58 func SegmentArea(d float) float { 59 //if d >= pixr { 60 // return 0 61 //} 62 //if d <= -pixr { 63 // return pixa 64 //} 65 66 t := 2 * (d / pixr).Acos() 67 return (1 / 2) * pixr2 * (t - t.Sin()) 68 } 69 70 func (line *Line) Coverage(p Point) float { 71 d := line.DistanceTo(p) 72 if d > line.Radius+pixr { 73 return 0 74 } 75 76 min, max := d-line.Radius, d+line.Radius 77 if min <= -pixr && pixr <= max { 78 return 1 79 } 80 81 if pixr <= max { 82 fmt.Println(">", -pixr, min, d, max, pixr) 83 if min < 0 { 84 return (pixa - SegmentArea(-min)) / pixa 85 } 86 return SegmentArea(min) / pixa 87 } 88 if min <= -pixr { 89 fmt.Println("<", -pixr, min, d, max, pixr) 90 return SegmentArea(max) / pixa 91 } 92 fmt.Println("q", -pixr, min, d, max, pixr) 93 return (SegmentArea(min) - SegmentArea(max)) / pixa 94 } 95 96 func CoverageToColor(c float) uint8 { 97 if c >= 1 { 98 return 0x00 99 } else if c <= 0 { 100 return 0xFF 101 } else { 102 return 0xFF - uint8(0xFF*c) 103 } 104 } 105 106 func main() { 107 a := Line{Point{100, 100}, Point{213, 217}, 10} 108 w, h := 1024, 1024 109 110 m := image.NewRGBA(image.Rect(0, 0, w, h)) 111 for y := 0; y < h; y++ { 112 for x := 0; x < w; x++ { 113 p := Point{float(x), float(y)} 114 //c := a.Coverage(p) 115 c := 10 - a.DistanceTo(p) 116 q := CoverageToColor(c) 117 m.SetRGBA(x, y, color.RGBA{q, q, q, 0xFF}) 118 } 119 } 120 121 file, err := os.Create("image.png") 122 if err != nil { 123 log.Fatal(err) 124 } 125 defer file.Close() 126 127 if err := png.Encode(file, m); err != nil { 128 log.Fatal(err) 129 } 130 }