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  }