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 }