diff options
| author | Christine Dodrill <me@christine.website> | 2018-10-04 19:44:06 -0700 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2018-10-04 19:44:06 -0700 |
| commit | 1c1d089725dd9f98b7ac73276d07dbadb388b748 (patch) | |
| tree | b0e783f5e2d485050e0072faf3b303bdc66e3539 /vendor/github.com/fogleman/primitive | |
| parent | e36f755db2198a96e2b87781f5f5432fcee092dd (diff) | |
| download | x-1c1d089725dd9f98b7ac73276d07dbadb388b748.tar.xz x-1c1d089725dd9f98b7ac73276d07dbadb388b748.zip | |
add Dockerfile
Diffstat (limited to 'vendor/github.com/fogleman/primitive')
18 files changed, 1739 insertions, 0 deletions
diff --git a/vendor/github.com/fogleman/primitive/LICENSE.md b/vendor/github.com/fogleman/primitive/LICENSE.md new file mode 100644 index 0000000..d7b4099 --- /dev/null +++ b/vendor/github.com/fogleman/primitive/LICENSE.md @@ -0,0 +1,19 @@ +Copyright (C) 2016 Michael Fogleman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/fogleman/primitive/primitive/color.go b/vendor/github.com/fogleman/primitive/primitive/color.go new file mode 100644 index 0000000..2828957 --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/color.go @@ -0,0 +1,44 @@ +package primitive + +import ( + "fmt" + "image/color" + "strings" +) + +type Color struct { + R, G, B, A int +} + +func MakeColor(c color.Color) Color { + r, g, b, a := c.RGBA() + return Color{int(r / 257), int(g / 257), int(b / 257), int(a / 257)} +} + +func MakeHexColor(x string) Color { + x = strings.Trim(x, "#") + var r, g, b, a int + a = 255 + switch len(x) { + case 3: + fmt.Sscanf(x, "%1x%1x%1x", &r, &g, &b) + r = (r << 4) | r + g = (g << 4) | g + b = (b << 4) | b + case 4: + fmt.Sscanf(x, "%1x%1x%1x%1x", &r, &g, &b, &a) + r = (r << 4) | r + g = (g << 4) | g + b = (b << 4) | b + a = (a << 4) | a + case 6: + fmt.Sscanf(x, "%02x%02x%02x", &r, &g, &b) + case 8: + fmt.Sscanf(x, "%02x%02x%02x%02x", &r, &g, &b, &a) + } + return Color{r, g, b, a} +} + +func (c *Color) NRGBA() color.NRGBA { + return color.NRGBA{uint8(c.R), uint8(c.G), uint8(c.B), uint8(c.A)} +} diff --git a/vendor/github.com/fogleman/primitive/primitive/core.go b/vendor/github.com/fogleman/primitive/primitive/core.go new file mode 100644 index 0000000..5a0c0aa --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/core.go @@ -0,0 +1,124 @@ +package primitive + +import ( + "image" + "math" +) + +func computeColor(target, current *image.RGBA, lines []Scanline, alpha int) Color { + var rsum, gsum, bsum, count int64 + a := 0x101 * 255 / alpha + for _, line := range lines { + i := target.PixOffset(line.X1, line.Y) + for x := line.X1; x <= line.X2; x++ { + tr := int(target.Pix[i]) + tg := int(target.Pix[i+1]) + tb := int(target.Pix[i+2]) + cr := int(current.Pix[i]) + cg := int(current.Pix[i+1]) + cb := int(current.Pix[i+2]) + i += 4 + rsum += int64((tr-cr)*a + cr*0x101) + gsum += int64((tg-cg)*a + cg*0x101) + bsum += int64((tb-cb)*a + cb*0x101) + count++ + } + } + if count == 0 { + return Color{} + } + r := clampInt(int(rsum/count)>>8, 0, 255) + g := clampInt(int(gsum/count)>>8, 0, 255) + b := clampInt(int(bsum/count)>>8, 0, 255) + return Color{r, g, b, alpha} +} + +func copyLines(dst, src *image.RGBA, lines []Scanline) { + for _, line := range lines { + a := dst.PixOffset(line.X1, line.Y) + b := a + (line.X2-line.X1+1)*4 + copy(dst.Pix[a:b], src.Pix[a:b]) + } +} + +func drawLines(im *image.RGBA, c Color, lines []Scanline) { + const m = 0xffff + sr, sg, sb, sa := c.NRGBA().RGBA() + for _, line := range lines { + ma := line.Alpha + a := (m - sa*ma/m) * 0x101 + i := im.PixOffset(line.X1, line.Y) + for x := line.X1; x <= line.X2; x++ { + dr := uint32(im.Pix[i+0]) + dg := uint32(im.Pix[i+1]) + db := uint32(im.Pix[i+2]) + da := uint32(im.Pix[i+3]) + im.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8) + im.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8) + im.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8) + im.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8) + i += 4 + } + } +} + +func differenceFull(a, b *image.RGBA) float64 { + size := a.Bounds().Size() + w, h := size.X, size.Y + var total uint64 + for y := 0; y < h; y++ { + i := a.PixOffset(0, y) + for x := 0; x < w; x++ { + ar := int(a.Pix[i]) + ag := int(a.Pix[i+1]) + ab := int(a.Pix[i+2]) + aa := int(a.Pix[i+3]) + br := int(b.Pix[i]) + bg := int(b.Pix[i+1]) + bb := int(b.Pix[i+2]) + ba := int(b.Pix[i+3]) + i += 4 + dr := ar - br + dg := ag - bg + db := ab - bb + da := aa - ba + total += uint64(dr*dr + dg*dg + db*db + da*da) + } + } + return math.Sqrt(float64(total)/float64(w*h*4)) / 255 +} + +func differencePartial(target, before, after *image.RGBA, score float64, lines []Scanline) float64 { + size := target.Bounds().Size() + w, h := size.X, size.Y + total := uint64(math.Pow(score*255, 2) * float64(w*h*4)) + for _, line := range lines { + i := target.PixOffset(line.X1, line.Y) + for x := line.X1; x <= line.X2; x++ { + tr := int(target.Pix[i]) + tg := int(target.Pix[i+1]) + tb := int(target.Pix[i+2]) + ta := int(target.Pix[i+3]) + br := int(before.Pix[i]) + bg := int(before.Pix[i+1]) + bb := int(before.Pix[i+2]) + ba := int(before.Pix[i+3]) + ar := int(after.Pix[i]) + ag := int(after.Pix[i+1]) + ab := int(after.Pix[i+2]) + aa := int(after.Pix[i+3]) + i += 4 + dr1 := tr - br + dg1 := tg - bg + db1 := tb - bb + da1 := ta - ba + dr2 := tr - ar + dg2 := tg - ag + db2 := tb - ab + da2 := ta - aa + total -= uint64(dr1*dr1 + dg1*dg1 + db1*db1 + da1*da1) + total += uint64(dr2*dr2 + dg2*dg2 + db2*db2 + da2*da2) + } + } + return math.Sqrt(float64(total)/float64(w*h*4)) / 255 +} diff --git a/vendor/github.com/fogleman/primitive/primitive/ellipse.go b/vendor/github.com/fogleman/primitive/primitive/ellipse.go new file mode 100644 index 0000000..878df40 --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/ellipse.go @@ -0,0 +1,179 @@ +package primitive + +import ( + "fmt" + "math" + + "github.com/fogleman/gg" + "github.com/golang/freetype/raster" +) + +type Ellipse struct { + Worker *Worker + X, Y int + Rx, Ry int + Circle bool +} + +func NewRandomEllipse(worker *Worker) *Ellipse { + rnd := worker.Rnd + x := rnd.Intn(worker.W) + y := rnd.Intn(worker.H) + rx := rnd.Intn(32) + 1 + ry := rnd.Intn(32) + 1 + return &Ellipse{worker, x, y, rx, ry, false} +} + +func NewRandomCircle(worker *Worker) *Ellipse { + rnd := worker.Rnd + x := rnd.Intn(worker.W) + y := rnd.Intn(worker.H) + r := rnd.Intn(32) + 1 + return &Ellipse{worker, x, y, r, r, true} +} + +func (c *Ellipse) Draw(dc *gg.Context, scale float64) { + dc.DrawEllipse(float64(c.X), float64(c.Y), float64(c.Rx), float64(c.Ry)) + dc.Fill() +} + +func (c *Ellipse) SVG(attrs string) string { + return fmt.Sprintf( + "<ellipse %s cx=\"%d\" cy=\"%d\" rx=\"%d\" ry=\"%d\" />", + attrs, c.X, c.Y, c.Rx, c.Ry) +} + +func (c *Ellipse) Copy() Shape { + a := *c + return &a +} + +func (c *Ellipse) Mutate() { + w := c.Worker.W + h := c.Worker.H + rnd := c.Worker.Rnd + switch rnd.Intn(3) { + case 0: + c.X = clampInt(c.X+int(rnd.NormFloat64()*16), 0, w-1) + c.Y = clampInt(c.Y+int(rnd.NormFloat64()*16), 0, h-1) + case 1: + c.Rx = clampInt(c.Rx+int(rnd.NormFloat64()*16), 1, w-1) + if c.Circle { + c.Ry = c.Rx + } + case 2: + c.Ry = clampInt(c.Ry+int(rnd.NormFloat64()*16), 1, w-1) + if c.Circle { + c.Rx = c.Ry + } + } +} + +func (c *Ellipse) Rasterize() []Scanline { + w := c.Worker.W + h := c.Worker.H + lines := c.Worker.Lines[:0] + aspect := float64(c.Rx) / float64(c.Ry) + for dy := 0; dy < c.Ry; dy++ { + y1 := c.Y - dy + y2 := c.Y + dy + if (y1 < 0 || y1 >= h) && (y2 < 0 || y2 >= h) { + continue + } + s := int(math.Sqrt(float64(c.Ry*c.Ry-dy*dy)) * aspect) + x1 := c.X - s + x2 := c.X + s + if x1 < 0 { + x1 = 0 + } + if x2 >= w { + x2 = w - 1 + } + if y1 >= 0 && y1 < h { + lines = append(lines, Scanline{y1, x1, x2, 0xffff}) + } + if y2 >= 0 && y2 < h && dy > 0 { + lines = append(lines, Scanline{y2, x1, x2, 0xffff}) + } + } + return lines +} + +type RotatedEllipse struct { + Worker *Worker + X, Y float64 + Rx, Ry float64 + Angle float64 +} + +func NewRandomRotatedEllipse(worker *Worker) *RotatedEllipse { + rnd := worker.Rnd + x := rnd.Float64() * float64(worker.W) + y := rnd.Float64() * float64(worker.H) + rx := rnd.Float64()*32 + 1 + ry := rnd.Float64()*32 + 1 + a := rnd.Float64() * 360 + return &RotatedEllipse{worker, x, y, rx, ry, a} +} + +func (c *RotatedEllipse) Draw(dc *gg.Context, scale float64) { + dc.Push() + dc.RotateAbout(radians(c.Angle), c.X, c.Y) + dc.DrawEllipse(c.X, c.Y, c.Rx, c.Ry) + dc.Fill() + dc.Pop() +} + +func (c *RotatedEllipse) SVG(attrs string) string { + return fmt.Sprintf( + "<g transform=\"translate(%f %f) rotate(%f) scale(%f %f)\"><ellipse %s cx=\"0\" cy=\"0\" rx=\"1\" ry=\"1\" /></g>", + c.X, c.Y, c.Angle, c.Rx, c.Ry, attrs) +} + +func (c *RotatedEllipse) Copy() Shape { + a := *c + return &a +} + +func (c *RotatedEllipse) Mutate() { + w := c.Worker.W + h := c.Worker.H + rnd := c.Worker.Rnd + switch rnd.Intn(3) { + case 0: + c.X = clamp(c.X+rnd.NormFloat64()*16, 0, float64(w-1)) + c.Y = clamp(c.Y+rnd.NormFloat64()*16, 0, float64(h-1)) + case 1: + c.Rx = clamp(c.Rx+rnd.NormFloat64()*16, 1, float64(w-1)) + c.Ry = clamp(c.Ry+rnd.NormFloat64()*16, 1, float64(w-1)) + case 2: + c.Angle = c.Angle + rnd.NormFloat64()*32 + } +} + +func (c *RotatedEllipse) Rasterize() []Scanline { + var path raster.Path + const n = 16 + for i := 0; i < n; i++ { + p1 := float64(i+0) / n + p2 := float64(i+1) / n + a1 := p1 * 2 * math.Pi + a2 := p2 * 2 * math.Pi + x0 := c.Rx * math.Cos(a1) + y0 := c.Ry * math.Sin(a1) + x1 := c.Rx * math.Cos(a1+(a2-a1)/2) + y1 := c.Ry * math.Sin(a1+(a2-a1)/2) + x2 := c.Rx * math.Cos(a2) + y2 := c.Ry * math.Sin(a2) + cx := 2*x1 - x0/2 - x2/2 + cy := 2*y1 - y0/2 - y2/2 + x0, y0 = rotate(x0, y0, radians(c.Angle)) + cx, cy = rotate(cx, cy, radians(c.Angle)) + x2, y2 = rotate(x2, y2, radians(c.Angle)) + if i == 0 { + path.Start(fixp(x0+c.X, y0+c.Y)) + } + path.Add2(fixp(cx+c.X, cy+c.Y), fixp(x2+c.X, y2+c.Y)) + } + return fillPath(c.Worker, path) +} diff --git a/vendor/github.com/fogleman/primitive/primitive/heatmap.go b/vendor/github.com/fogleman/primitive/primitive/heatmap.go new file mode 100644 index 0000000..7bef5fe --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/heatmap.go @@ -0,0 +1,59 @@ +package primitive + +import ( + "image" + "image/color" + "math" +) + +type Heatmap struct { + W, H int + Count []uint64 +} + +func NewHeatmap(w, h int) *Heatmap { + count := make([]uint64, w*h) + return &Heatmap{w, h, count} +} + +func (h *Heatmap) Clear() { + for i := range h.Count { + h.Count[i] = 0 + } +} + +func (h *Heatmap) Add(lines []Scanline) { + for _, line := range lines { + i := line.Y*h.W + line.X1 + for x := line.X1; x <= line.X2; x++ { + h.Count[i] += uint64(line.Alpha) + i++ + } + } +} + +func (h *Heatmap) AddHeatmap(a *Heatmap) { + for i, x := range a.Count { + h.Count[i] += x + } +} + +func (h *Heatmap) Image(gamma float64) *image.Gray16 { + im := image.NewGray16(image.Rect(0, 0, h.W, h.H)) + var hi uint64 + for _, h := range h.Count { + if h > hi { + hi = h + } + } + i := 0 + for y := 0; y < h.H; y++ { + for x := 0; x < h.W; x++ { + p := float64(h.Count[i]) / float64(hi) + p = math.Pow(p, gamma) + im.SetGray16(x, y, color.Gray16{uint16(p * 0xffff)}) + i++ + } + } + return im +} diff --git a/vendor/github.com/fogleman/primitive/primitive/log.go b/vendor/github.com/fogleman/primitive/primitive/log.go new file mode 100644 index 0000000..d162eff --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/log.go @@ -0,0 +1,23 @@ +package primitive + +import "fmt" + +var LogLevel int + +func Log(level int, format string, a ...interface{}) { + if LogLevel >= level { + fmt.Printf(format, a...) + } +} + +func v(format string, a ...interface{}) { + Log(1, format, a...) +} + +func vv(format string, a ...interface{}) { + Log(2, " "+format, a...) +} + +func vvv(format string, a ...interface{}) { + Log(3, " "+format, a...) +} diff --git a/vendor/github.com/fogleman/primitive/primitive/model.go b/vendor/github.com/fogleman/primitive/primitive/model.go new file mode 100644 index 0000000..80f49ae --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/model.go @@ -0,0 +1,174 @@ +package primitive + +import ( + "fmt" + "image" + "strings" + + "github.com/fogleman/gg" +) + +type Model struct { + Sw, Sh int + Scale float64 + Background Color + Target *image.RGBA + Current *image.RGBA + Context *gg.Context + Score float64 + Shapes []Shape + Colors []Color + Scores []float64 + Workers []*Worker +} + +func NewModel(target image.Image, background Color, size, numWorkers int) *Model { + w := target.Bounds().Size().X + h := target.Bounds().Size().Y + aspect := float64(w) / float64(h) + var sw, sh int + var scale float64 + if aspect >= 1 { + sw = size + sh = int(float64(size) / aspect) + scale = float64(size) / float64(w) + } else { + sw = int(float64(size) * aspect) + sh = size + scale = float64(size) / float64(h) + } + + model := &Model{} + model.Sw = sw + model.Sh = sh + model.Scale = scale + model.Background = background + model.Target = imageToRGBA(target) + model.Current = uniformRGBA(target.Bounds(), background.NRGBA()) + model.Score = differenceFull(model.Target, model.Current) + model.Context = model.newContext() + for i := 0; i < numWorkers; i++ { + worker := NewWorker(model.Target) + model.Workers = append(model.Workers, worker) + } + return model +} + +func (model *Model) newContext() *gg.Context { + dc := gg.NewContext(model.Sw, model.Sh) + dc.Scale(model.Scale, model.Scale) + dc.Translate(0.5, 0.5) + dc.SetColor(model.Background.NRGBA()) + dc.Clear() + return dc +} + +func (model *Model) Frames(scoreDelta float64) []image.Image { + var result []image.Image + dc := model.newContext() + result = append(result, imageToRGBA(dc.Image())) + previous := 10.0 + for i, shape := range model.Shapes { + c := model.Colors[i] + dc.SetRGBA255(c.R, c.G, c.B, c.A) + shape.Draw(dc, model.Scale) + dc.Fill() + score := model.Scores[i] + delta := previous - score + if delta >= scoreDelta { + previous = score + result = append(result, imageToRGBA(dc.Image())) + } + } + return result +} + +func (model *Model) SVG() string { + bg := model.Background + var lines []string + lines = append(lines, fmt.Sprintf("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"%d\" height=\"%d\">", model.Sw, model.Sh)) + lines = append(lines, fmt.Sprintf("<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"#%02x%02x%02x\" />", model.Sw, model.Sh, bg.R, bg.G, bg.B)) + lines = append(lines, fmt.Sprintf("<g transform=\"scale(%f) translate(0.5 0.5)\">", model.Scale)) + for i, shape := range model.Shapes { + c := model.Colors[i] + attrs := "fill=\"#%02x%02x%02x\" fill-opacity=\"%f\"" + attrs = fmt.Sprintf(attrs, c.R, c.G, c.B, float64(c.A)/255) + lines = append(lines, shape.SVG(attrs)) + } + lines = append(lines, "</g>") + lines = append(lines, "</svg>") + return strings.Join(lines, "\n") +} + +func (model *Model) Add(shape Shape, alpha int) { + before := copyRGBA(model.Current) + lines := shape.Rasterize() + color := computeColor(model.Target, model.Current, lines, alpha) + drawLines(model.Current, color, lines) + score := differencePartial(model.Target, before, model.Current, model.Score, lines) + + model.Score = score + model.Shapes = append(model.Shapes, shape) + model.Colors = append(model.Colors, color) + model.Scores = append(model.Scores, score) + + model.Context.SetRGBA255(color.R, color.G, color.B, color.A) + shape.Draw(model.Context, model.Scale) +} + +func (model *Model) Step(shapeType ShapeType, alpha, repeat int) int { + state := model.runWorkers(shapeType, alpha, 1000, 100, 16) + // state = HillClimb(state, 1000).(*State) + model.Add(state.Shape, state.Alpha) + + for i := 0; i < repeat; i++ { + state.Worker.Init(model.Current, model.Score) + a := state.Energy() + state = HillClimb(state, 100).(*State) + b := state.Energy() + if a == b { + break + } + model.Add(state.Shape, state.Alpha) + } + + // for _, w := range model.Workers[1:] { + // model.Workers[0].Heatmap.AddHeatmap(w.Heatmap) + // } + // SavePNG("heatmap.png", model.Workers[0].Heatmap.Image(0.5)) + + counter := 0 + for _, worker := range model.Workers { + counter += worker.Counter + } + return counter +} + +func (model *Model) runWorkers(t ShapeType, a, n, age, m int) *State { + wn := len(model.Workers) + ch := make(chan *State, wn) + wm := m / wn + if m%wn != 0 { + wm++ + } + for i := 0; i < wn; i++ { + worker := model.Workers[i] + worker.Init(model.Current, model.Score) + go model.runWorker(worker, t, a, n, age, wm, ch) + } + var bestEnergy float64 + var bestState *State + for i := 0; i < wn; i++ { + state := <-ch + energy := state.Energy() + if i == 0 || energy < bestEnergy { + bestEnergy = energy + bestState = state + } + } + return bestState +} + +func (model *Model) runWorker(worker *Worker, t ShapeType, a, n, age, m int, ch chan *State) { + ch <- worker.BestHillClimbState(t, a, n, age, m) +} diff --git a/vendor/github.com/fogleman/primitive/primitive/optimize.go b/vendor/github.com/fogleman/primitive/primitive/optimize.go new file mode 100644 index 0000000..e15d2b6 --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/optimize.go @@ -0,0 +1,75 @@ +package primitive + +import ( + "math" + "math/rand" +) + +type Annealable interface { + Energy() float64 + DoMove() interface{} + UndoMove(interface{}) + Copy() Annealable +} + +func HillClimb(state Annealable, maxAge int) Annealable { + state = state.Copy() + bestState := state.Copy() + bestEnergy := state.Energy() + step := 0 + for age := 0; age < maxAge; age++ { + undo := state.DoMove() + energy := state.Energy() + if energy >= bestEnergy { + state.UndoMove(undo) + } else { + // fmt.Printf("step: %d, energy: %.6f\n", step, energy) + bestEnergy = energy + bestState = state.Copy() + age = -1 + } + step++ + } + return bestState +} + +func PreAnneal(state Annealable, iterations int) float64 { + state = state.Copy() + previous := state.Energy() + var total float64 + for i := 0; i < iterations; i++ { + state.DoMove() + energy := state.Energy() + total += math.Abs(energy - previous) + previous = energy + } + return total / float64(iterations) +} + +func Anneal(state Annealable, maxTemp, minTemp float64, steps int) Annealable { + factor := -math.Log(maxTemp / minTemp) + state = state.Copy() + bestState := state.Copy() + bestEnergy := state.Energy() + previousEnergy := bestEnergy + for step := 0; step < steps; step++ { + pct := float64(step) / float64(steps-1) + temp := maxTemp * math.Exp(factor*pct) + undo := state.DoMove() + energy := state.Energy() + change := energy - previousEnergy + if change > 0 && math.Exp(-change/temp) < rand.Float64() { + state.UndoMove(undo) + } else { + previousEnergy = energy + if energy < bestEnergy { + // pct := float64(step*100) / float64(steps) + // fmt.Printf("step: %d of %d (%.1f%%), temp: %.3f, energy: %.6f\n", + // step, steps, pct, temp, energy) + bestEnergy = energy + bestState = state.Copy() + } + } + } + return bestState +} diff --git a/vendor/github.com/fogleman/primitive/primitive/polygon.go b/vendor/github.com/fogleman/primitive/primitive/polygon.go new file mode 100644 index 0000000..dee3a6c --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/polygon.go @@ -0,0 +1,122 @@ +package primitive + +import ( + "fmt" + "strings" + + "github.com/fogleman/gg" + "github.com/golang/freetype/raster" +) + +type Polygon struct { + Worker *Worker + Order int + Convex bool + X, Y []float64 +} + +func NewRandomPolygon(worker *Worker, order int, convex bool) *Polygon { + rnd := worker.Rnd + x := make([]float64, order) + y := make([]float64, order) + x[0] = rnd.Float64() * float64(worker.W) + y[0] = rnd.Float64() * float64(worker.H) + for i := 1; i < order; i++ { + x[i] = x[0] + rnd.Float64()*40 - 20 + y[i] = y[0] + rnd.Float64()*40 - 20 + } + p := &Polygon{worker, order, convex, x, y} + p.Mutate() + return p +} + +func (p *Polygon) Draw(dc *gg.Context, scale float64) { + dc.NewSubPath() + for i := 0; i < p.Order; i++ { + dc.LineTo(p.X[i], p.Y[i]) + } + dc.ClosePath() + dc.Fill() +} + +func (p *Polygon) SVG(attrs string) string { + ret := fmt.Sprintf( + "<polygon %s points=\"", + attrs) + points := make([]string, len(p.X)) + for i := 0; i < len(p.X); i++ { + points[i] = fmt.Sprintf("%f,%f", p.X[i], p.Y[i]) + } + + return ret + strings.Join(points, ",") + "\" />" +} + +func (p *Polygon) Copy() Shape { + a := *p + a.X = make([]float64, p.Order) + a.Y = make([]float64, p.Order) + copy(a.X, p.X) + copy(a.Y, p.Y) + return &a +} + +func (p *Polygon) Mutate() { + const m = 16 + w := p.Worker.W + h := p.Worker.H + rnd := p.Worker.Rnd + for { + if rnd.Float64() < 0.25 { + i := rnd.Intn(p.Order) + j := rnd.Intn(p.Order) + p.X[i], p.Y[i], p.X[j], p.Y[j] = p.X[j], p.Y[j], p.X[i], p.Y[i] + } else { + i := rnd.Intn(p.Order) + p.X[i] = clamp(p.X[i]+rnd.NormFloat64()*16, -m, float64(w-1+m)) + p.Y[i] = clamp(p.Y[i]+rnd.NormFloat64()*16, -m, float64(h-1+m)) + } + if p.Valid() { + break + } + } +} + +func (p *Polygon) Valid() bool { + if !p.Convex { + return true + } + var sign bool + for a := 0; a < p.Order; a++ { + i := (a + 0) % p.Order + j := (a + 1) % p.Order + k := (a + 2) % p.Order + c := cross3(p.X[i], p.Y[i], p.X[j], p.Y[j], p.X[k], p.Y[k]) + if a == 0 { + sign = c > 0 + } else if c > 0 != sign { + return false + } + } + return true +} + +func cross3(x1, y1, x2, y2, x3, y3 float64) float64 { + dx1 := x2 - x1 + dy1 := y2 - y1 + dx2 := x3 - x2 + dy2 := y3 - y2 + return dx1*dy2 - dy1*dx2 +} + +func (p *Polygon) Rasterize() []Scanline { + var path raster.Path + for i := 0; i <= p.Order; i++ { + f := fixp(p.X[i%p.Order], p.Y[i%p.Order]) + if i == 0 { + path.Start(f) + } else { + path.Add1(f) + } + } + return fillPath(p.Worker, path) +} diff --git a/vendor/github.com/fogleman/primitive/primitive/quadratic.go b/vendor/github.com/fogleman/primitive/primitive/quadratic.go new file mode 100644 index 0000000..740d44a --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/quadratic.go @@ -0,0 +1,100 @@ +package primitive + +import ( + "fmt" + "strings" + + "github.com/fogleman/gg" + "github.com/golang/freetype/raster" +) + +type Quadratic struct { + Worker *Worker + X1, Y1 float64 + X2, Y2 float64 + X3, Y3 float64 + Width float64 +} + +func NewRandomQuadratic(worker *Worker) *Quadratic { + rnd := worker.Rnd + x1 := rnd.Float64() * float64(worker.W) + y1 := rnd.Float64() * float64(worker.H) + x2 := x1 + rnd.Float64()*40 - 20 + y2 := y1 + rnd.Float64()*40 - 20 + x3 := x2 + rnd.Float64()*40 - 20 + y3 := y2 + rnd.Float64()*40 - 20 + width := 1.0 / 2 + q := &Quadratic{worker, x1, y1, x2, y2, x3, y3, width} + q.Mutate() + return q +} + +func (q *Quadratic) Draw(dc *gg.Context, scale float64) { + dc.MoveTo(q.X1, q.Y1) + dc.QuadraticTo(q.X2, q.Y2, q.X3, q.Y3) + dc.SetLineWidth(q.Width * scale) + dc.Stroke() +} + +func (q *Quadratic) SVG(attrs string) string { + // TODO: this is a little silly + attrs = strings.Replace(attrs, "fill", "stroke", -1) + return fmt.Sprintf( + "<path %s fill=\"none\" d=\"M %f %f Q %f %f, %f %f\" stroke-width=\"%f\" />", + attrs, q.X1, q.Y1, q.X2, q.Y2, q.X3, q.Y3, q.Width) +} + +func (q *Quadratic) Copy() Shape { + a := *q + return &a +} + +func (q *Quadratic) Mutate() { + const m = 16 + w := q.Worker.W + h := q.Worker.H + rnd := q.Worker.Rnd + for { + switch rnd.Intn(3) { + case 0: + q.X1 = clamp(q.X1+rnd.NormFloat64()*16, -m, float64(w-1+m)) + q.Y1 = clamp(q.Y1+rnd.NormFloat64()*16, -m, float64(h-1+m)) + case 1: + q.X2 = clamp(q.X2+rnd.NormFloat64()*16, -m, float64(w-1+m)) + q.Y2 = clamp(q.Y2+rnd.NormFloat64()*16, -m, float64(h-1+m)) + case 2: + q.X3 = clamp(q.X3+rnd.NormFloat64()*16, -m, float64(w-1+m)) + q.Y3 = clamp(q.Y3+rnd.NormFloat64()*16, -m, float64(h-1+m)) + case 3: + q.Width = clamp(q.Width+rnd.NormFloat64(), 1, 16) + } + if q.Valid() { + break + } + } +} + +func (q *Quadratic) Valid() bool { + dx12 := int(q.X1 - q.X2) + dy12 := int(q.Y1 - q.Y2) + dx23 := int(q.X2 - q.X3) + dy23 := int(q.Y2 - q.Y3) + dx13 := int(q.X1 - q.X3) + dy13 := int(q.Y1 - q.Y3) + d12 := dx12*dx12 + dy12*dy12 + d23 := dx23*dx23 + dy23*dy23 + d13 := dx13*dx13 + dy13*dy13 + return d13 > d12 && d13 > d23 +} + +func (q *Quadratic) Rasterize() []Scanline { + var path raster.Path + p1 := fixp(q.X1, q.Y1) + p2 := fixp(q.X2, q.Y2) + p3 := fixp(q.X3, q.Y3) + path.Start(p1) + path.Add2(p2, p3) + width := fix(q.Width) + return strokePath(q.Worker, path, width, raster.RoundCapper, raster.RoundJoiner) +} diff --git a/vendor/github.com/fogleman/primitive/primitive/raster.go b/vendor/github.com/fogleman/primitive/primitive/raster.go new file mode 100644 index 0000000..a5d620a --- /dev/null +++ b/vendor/github.com/fogleman/primitive/primitive/raster.go @@ -0,0 +1,46 @@ +package primitive + +import ( + "github.com/golang/freetype/raster" + "golang.org/x/image/math/fixed" +) + +func fix(x float64) fixed.Int26_6 { + return fixed.Int26_6(x * 64) +} + +func fixp(x, y float64) fixed.Point26_6 { + return fixed.Point26_6{fix(x), fix(y)} +} + +type painter struct { + Lines []Scanline +} + +func (p *painter) Paint(spans []raster.Span, done bool) { + for _, span := range spans { + p.Lines = append(p.Lines, Scanline{span.Y, span.X0, span.X1 - 1, span.Alpha}) + } +} + +func fillPath(worker *Worker, path raster.Path) []Scanline { + r := worker.Rasterizer + r.Clear() + r.UseNonZeroWinding = true + r.AddPath(path) + var p painter + p.Lines = worker.Lines[:0] + r.Rasterize(&p) + return p.Lines +} + +func strokePath(worker *Worker, path raster.Path, width fixed.Int26_6, cr raster.Capper, jr raster.Joiner) []Scanline { + r := worker.Rasterizer + r.Clear() + r.UseNonZeroWinding = true + r.AddStroke(path, width, cr, jr) + var p painter + p.Lines = worker.Lines[:0] + r.Rasterize(&p) |
