github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/randdist/graph/graph.go (about)

     1  // Copyright 2015 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package graph
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"image"
    11  	"image/color"
    12  	"image/draw"
    13  	"image/png"
    14  	"io/ioutil"
    15  	"math"
    16  	"math/big"
    17  	"net/http"
    18  	"strconv"
    19  
    20  	"github.com/golang/freetype"
    21  	"github.com/golang/freetype/raster"
    22  	"golang.org/x/image/math/fixed"
    23  )
    24  
    25  func Handler(w http.ResponseWriter, req *http.Request) {
    26  	const size = 500
    27  	c := image.NewRGBA(image.Rect(0, 0, size, size))
    28  	white := &image.Uniform{C: color.White}
    29  	draw.Draw(c, c.Bounds(), white, image.ZP, draw.Src)
    30  
    31  	p := raster.NewRGBAPainter(c)
    32  	p.SetColor(color.Black)
    33  	r := raster.NewRasterizer(500, 500)
    34  	r.UseNonZeroWinding = true
    35  	var path raster.Path
    36  	path.Start(nudge(fixed.P(50, 50)))
    37  	path.Add1(nudge(fixed.P(50, 450)))
    38  	path.Add1(nudge(fixed.P(450, 450)))
    39  	r.AddStroke(path, fixed.I(2), raster.ButtCapper, raster.BevelJoiner)
    40  	r.Rasterize(p)
    41  	r.Clear()
    42  
    43  	p.SetColor(color.Gray16{0x7FFF})
    44  	path.Clear()
    45  	r.Clear()
    46  	path.Start(nudge(fixed.P(450, 450)))
    47  	path.Add1(nudge(fixed.P(450, 50)))
    48  	path.Add1(nudge(fixed.P(50, 50)))
    49  	r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner)
    50  	r.Rasterize(p)
    51  
    52  	p.SetColor(color.Gray16{0x7FFF})
    53  	for x := 50; x <= 450; x += 50 {
    54  		path.Clear()
    55  		path.Start(nudge(fixed.P(x, 450)))
    56  		path.Add1(nudge(fixed.P(x, 460)))
    57  		r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner)
    58  	}
    59  	for x := 50; x <= 450; x += 50 {
    60  		path.Clear()
    61  		path.Start(nudge(fixed.P(50, x)))
    62  		path.Add1(nudge(fixed.P(40, x)))
    63  		r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner)
    64  	}
    65  	r.Rasterize(p)
    66  
    67  	p.SetColor(color.RGBA{0x00, 0x00, 0xFF, 0xFF})
    68  	r.Clear()
    69  	path.Clear()
    70  	path.Start(nudge(fixed.P(50, 450)))
    71  	path.Add1(nudge(fixed.P(450, 50)))
    72  	r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner)
    73  	r.Rasterize(p)
    74  
    75  	p.SetColor(color.RGBA{0xCC, 0x00, 0x00, 0xFF})
    76  	r.Clear()
    77  	window := fixed.Rectangle26_6{fixed.P(50, 450), fixed.P(450, 50)}
    78  	//	path = plotPath(ratSin, big.NewRat(0, 1), big.NewRat(-1, 1), big.NewRat(10, 1), big.NewRat(1, 1), window)
    79  
    80  	//	path = plotPath(ratProb, big.NewRat(0, 1), big.NewRat(0, 1), big.NewRat(1, 1<<57), big.NewRat(1, 1<<57), window)
    81  
    82  	var lo, hi *big.Rat
    83  	var locap, hicap, scalecap string
    84  	id, _ := strconv.Atoi(req.FormValue("x"))
    85  	switch id {
    86  	case 0:
    87  		lo = big.NewRat(1<<53-1<<3, 1<<54)
    88  		hi = big.NewRat(1<<53+1<<3, 1<<54)
    89  		locap = "1/2 - 1/2^51"
    90  		hicap = "1/2 + 1/2^51"
    91  		scalecap = "1/2^53"
    92  	case 1:
    93  		lo = big.NewRat(1<<54-1<<4, 1<<54)
    94  		hi = big.NewRat(1<<54, 1<<54)
    95  		locap = "1 - 1/2^50"
    96  		hicap = "1"
    97  		scalecap = "1/2^53"
    98  	case 2:
    99  		lo = big.NewRat(0, 1<<54)
   100  		hi = big.NewRat(1<<4, 1<<54)
   101  		locap = "0"
   102  		hicap = "1/2^50"
   103  		scalecap = "1/2^53"
   104  	case 3:
   105  		lo = big.NewRat(0, 1<<54)
   106  		hi = big.NewRat(1<<2, 1<<62)
   107  		locap = "0"
   108  		hicap = "1/2^59"
   109  		scalecap = "1/2^63"
   110  	}
   111  
   112  	mode, _ := strconv.Atoi(req.FormValue("mode"))
   113  	path = plotPath(ratProb(mode), lo, lo, hi, hi, window)
   114  	r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner)
   115  	r.Rasterize(p)
   116  
   117  	var modestr string
   118  	switch mode {
   119  	case 0:
   120  		modestr = "original behavior (can return 1)"
   121  	case 1:
   122  		modestr = "new behavior (too much 0)"
   123  	case 2:
   124  		modestr = "retry loop"
   125  	}
   126  
   127  	data, err := ioutil.ReadFile("/tmp/luxisr.ttf")
   128  	if err != nil {
   129  		panic(err)
   130  	}
   131  	tfont, err := freetype.ParseFont(data)
   132  	if err != nil {
   133  		panic(err)
   134  	}
   135  	const pt = 10
   136  	ft := freetype.NewContext()
   137  	ft.SetDst(c)
   138  	ft.SetDPI(100)
   139  	ft.SetFont(tfont)
   140  	ft.SetFontSize(float64(pt))
   141  	ft.SetSrc(image.NewUniform(color.Black))
   142  
   143  	ft.SetClip(image.Rect(0, 0, 0, 0))
   144  	wid, err := ft.DrawString(locap, freetype.Pt(0, 0))
   145  	if err != nil {
   146  		panic(err)
   147  	}
   148  	pp := freetype.Pt(50, 480)
   149  	pp.X -= wid.X / 2
   150  	ft.SetClip(c.Bounds())
   151  	ft.DrawString(locap, pp)
   152  
   153  	ft.SetClip(image.Rect(0, 0, 0, 0))
   154  	wid, err = ft.DrawString(hicap, freetype.Pt(0, 0))
   155  	if err != nil {
   156  		panic(err)
   157  	}
   158  	pp = freetype.Pt(450, 480)
   159  	pp.X -= wid.X / 2
   160  	ft.SetClip(c.Bounds())
   161  	ft.DrawString(hicap, pp)
   162  
   163  	r.Clear()
   164  	p.SetColor(color.Black)
   165  	path.Clear()
   166  	const dy = 5
   167  	path.Start(nudge(fixed.P(400, 400)))
   168  	path.Add1(nudge(fixed.P(400, 400-dy)))
   169  	path.Add1(nudge(fixed.P(400, 400+dy)))
   170  	path.Add1(nudge(fixed.P(400, 400)))
   171  	path.Add1(nudge(fixed.P(450, 400)))
   172  	path.Add1(nudge(fixed.P(450, 400-dy)))
   173  	path.Add1(nudge(fixed.P(450, 400+dy)))
   174  	path.Add1(nudge(fixed.P(450, 400)))
   175  	r.AddStroke(path, fixed.I(2), raster.ButtCapper, raster.BevelJoiner)
   176  	r.Rasterize(p)
   177  
   178  	ft.SetClip(image.Rect(0, 0, 0, 0))
   179  	wid, err = ft.DrawString(scalecap, freetype.Pt(0, 0))
   180  	if err != nil {
   181  		panic(err)
   182  	}
   183  	pp = freetype.Pt(425, 420)
   184  	pp.X -= wid.X / 2
   185  	ft.SetClip(c.Bounds())
   186  	ft.DrawString(scalecap, pp)
   187  
   188  	ft.SetClip(image.Rect(0, 0, 0, 0))
   189  	wid, err = ft.DrawString(modestr, freetype.Pt(0, 0))
   190  	if err != nil {
   191  		panic(err)
   192  	}
   193  	pp = freetype.Pt(250, 490)
   194  	pp.X -= wid.X / 2
   195  	ft.SetClip(c.Bounds())
   196  	ft.DrawString(modestr, pp)
   197  
   198  	w.Write(pngEncode(c))
   199  }
   200  
   201  func nudge(x fixed.Point26_6) fixed.Point26_6 {
   202  	return fixed.Point26_6{x.X + 1<<5, x.Y + 1<<5}
   203  }
   204  
   205  func pngEncode(c image.Image) []byte {
   206  	var b bytes.Buffer
   207  	png.Encode(&b, c)
   208  	return b.Bytes()
   209  }
   210  
   211  func plotPath(f func(*big.Rat) *big.Rat, fminx, fminy, fmaxx, fmaxy *big.Rat, r fixed.Rectangle26_6) raster.Path {
   212  	px := r.Min.X
   213  	var path raster.Path
   214  	for ; px <= r.Max.X; px += 1 << 6 {
   215  		fx := new(big.Rat).Add(fminx, new(big.Rat).Mul(new(big.Rat).Sub(fmaxx, fminx), new(big.Rat).Quo(big.NewRat(int64(px-r.Min.X), 1<<6), big.NewRat(int64(r.Max.X-r.Min.X), 1<<6))))
   216  		fy := f(fx)
   217  		fdy := new(big.Rat).Mul(new(big.Rat).Sub(fy, fminy), new(big.Rat).Quo(big.NewRat(int64(r.Max.Y-r.Min.Y), 1), new(big.Rat).Sub(fmaxy, fminy)))
   218  		dy, _ := fdy.Float64()
   219  		py := r.Min.Y + fixed.Int26_6(dy+0.5)
   220  		if len(path) == 0 {
   221  			path.Start(fixed.Point26_6{px, py})
   222  		} else {
   223  			path.Add1(fixed.Point26_6{px, py})
   224  		}
   225  	}
   226  	return path
   227  }
   228  
   229  func ratSin(x *big.Rat) *big.Rat {
   230  	fx, _ := x.Float64()
   231  	fy := math.Sin(fx)
   232  	return new(big.Rat).SetFloat64(fy)
   233  }
   234  
   235  const cutoff1 = 1<<63 - 1<<9
   236  const cutoff2 = 1<<63 - 1<<8
   237  
   238  func init() {
   239  	var f1, f2 float64
   240  	f1 = cutoff1 - 1
   241  	f1 /= 1 << 63
   242  	f2 = cutoff1
   243  	f2 /= 1 << 63
   244  	if f1 == 1 || f2 != 1 {
   245  		panic(fmt.Sprintf("BAD: %#x => %g %g", cutoff1, f1, f2))
   246  	}
   247  }
   248  
   249  var bigOne = big.NewInt(1)
   250  var big2p63 = new(big.Int).Lsh(bigOne, 63)
   251  
   252  func ratProb(mode int) func(*big.Rat) *big.Rat {
   253  	return func(x *big.Rat) *big.Rat {
   254  		lo := big.NewInt(0)
   255  		hi := new(big.Int).Set(big2p63)
   256  		n := 0
   257  		for lo.Cmp(hi) != 0 {
   258  			m := new(big.Int).Add(lo, hi)
   259  			m = m.Rsh(m, 1)
   260  			if n++; n > 100 {
   261  				fmt.Printf("??? %v %v %v\n", lo, hi, m)
   262  				break
   263  			}
   264  			v := new(big.Rat).SetFrac(m, big2p63)
   265  			f, _ := v.Float64()
   266  			v.SetFloat64(f)
   267  			if v.Cmp(x) < 0 {
   268  				lo.Add(m, bigOne)
   269  			} else {
   270  				hi.Set(m)
   271  			}
   272  		}
   273  		switch mode {
   274  		default: // case 0
   275  			return new(big.Rat).SetFrac(lo, big2p63)
   276  		case 1:
   277  			if lo.Cmp(big.NewInt(cutoff1)) <= 0 {
   278  				lo.Add(lo, big.NewInt(1<<63-cutoff1))
   279  			}
   280  			return new(big.Rat).SetFrac(lo, big2p63)
   281  		case 2:
   282  			return new(big.Rat).SetFrac(lo, big.NewInt(cutoff1))
   283  		}
   284  	}
   285  }
   286  
   287  func div2p63(x int64) *big.Rat {
   288  	return new(big.Rat).SetFrac(big.NewInt(x), big2p63)
   289  }