Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
fogleman committed Sep 23, 2016
1 parent 5cd1ae5 commit 1305a19
Show file tree
Hide file tree
Showing 13 changed files with 57 additions and 58 deletions.
Binary file removed examples/monalisa128.png
Binary file not shown.
Binary file removed examples/monalisa256.png
Binary file not shown.
Binary file removed examples/owl128.png
Binary file not shown.
Binary file removed examples/owl256.png
Binary file not shown.
Binary file removed examples/pyramids128.png
Binary file not shown.
Binary file removed examples/pyramids256.png
Binary file not shown.
6 changes: 4 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var (
InputSize int
OutputSize int
Mode int
Workers int
V, VV bool
)

Expand All @@ -35,6 +36,7 @@ func init() {
flag.IntVar(&InputSize, "r", 256, "resize large input images to this size")
flag.IntVar(&OutputSize, "s", 1024, "output image size")
flag.IntVar(&Mode, "m", 1, "0=combo 1=triangle 2=rect 3=ellipse 4=circle 5=rotatedrect")
flag.IntVar(&Workers, "j", 0, "number of parallel workers (default uses all cores)")
flag.BoolVar(&V, "v", false, "verbose")
flag.BoolVar(&VV, "vv", false, "very verbose")
}
Expand Down Expand Up @@ -102,11 +104,11 @@ func main() {
}

// run algorithm
model := primitive.NewModel(input, bg, Alpha, OutputSize, primitive.Mode(Mode))
model := primitive.NewModel(input, bg, OutputSize)
start := time.Now()
for i := 1; i <= Number; i++ {
// find optimal shape and add it to the model
model.Step()
model.Step(primitive.ShapeType(Mode), Alpha, Workers)
elapsed := time.Since(start).Seconds()
primitive.Log(1, "iteration %d, time %.3f, score %.6f\n", i, elapsed, model.Score)

Expand Down
12 changes: 0 additions & 12 deletions primitive/mode.go

This file was deleted.

77 changes: 37 additions & 40 deletions primitive/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,18 @@ type Model struct {
Buffer *image.RGBA
Context *gg.Context
Score float64
Alpha int
Size int
Mode Mode
Shapes []Shape
Colors []Color
Scores []float64
SVGs []string
}

func NewModel(target image.Image, background Color, alpha, size int, mode Mode) *Model {
func NewModel(target image.Image, background Color, size int) *Model {
model := &Model{}
model.W = target.Bounds().Size().X
model.H = target.Bounds().Size().Y
model.Background = background
model.Alpha = alpha
model.Size = size
model.Mode = mode
model.Target = imageToRGBA(target)
model.Current = uniformRGBA(target.Bounds(), background.NRGBA())
model.Buffer = uniformRGBA(target.Bounds(), background.NRGBA())
Expand Down Expand Up @@ -95,48 +90,50 @@ func (model *Model) SVG() string {
lines = append(lines, fmt.Sprintf("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"%d\" height=\"%d\">", w, h))
lines = append(lines, fmt.Sprintf("<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"#%02x%02x%02x\" />", w, h, c.R, c.G, c.B))
lines = append(lines, fmt.Sprintf("<g transform=\"scale(%f) translate(0.5 0.5)\">", scale))
lines = append(lines, model.SVGs...)
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) {
func (model *Model) Add(shape Shape, alpha int) {
lines := shape.Rasterize()
c := model.computeColor(lines, model.Alpha)
c := model.computeColor(lines, alpha)
s := model.computeScore(lines, c, model.Buffer)
Draw(model.Current, c, lines)

attrs := "fill=\"#%02x%02x%02x\" fill-opacity=\"%f\""
attrs = fmt.Sprintf(attrs, c.R, c.G, c.B, float64(c.A)/255)
svg := shape.SVG(attrs)

model.Score = s
model.Shapes = append(model.Shapes, shape)
model.Colors = append(model.Colors, c)
model.Scores = append(model.Scores, s)
model.SVGs = append(model.SVGs, svg)

model.Context.SetRGBA255(c.R, c.G, c.B, c.A)
shape.Draw(model.Context)
model.Context.Fill()
}

func (model *Model) Step() {
state := model.runWorkers(model.Mode, 100, 100, 8)
func (model *Model) Step(shapeType ShapeType, alpha, numWorkers int) {
state := model.runWorkers(shapeType, alpha, numWorkers, 100, 100, 8)
state = HillClimb(state, 1000).(*State)
model.Add(state.Shape)
model.Add(state.Shape, state.Alpha)
}

func (model *Model) runWorkers(t Mode, n, age, m int) *State {
wn := runtime.GOMAXPROCS(0)
func (model *Model) runWorkers(t ShapeType, a, wn, n, age, m int) *State {
if wn < 1 {
wn = runtime.NumCPU()
}
ch := make(chan *State, wn)
wm := m / wn
if m%wn != 0 {
wm++
}
for i := 0; i < wn; i++ {
go model.runWorker(t, n, age, wm, ch)
go model.runWorker(t, a, n, age, wm, ch)
}
var bestEnergy float64
var bestState *State
Expand All @@ -151,18 +148,18 @@ func (model *Model) runWorkers(t Mode, n, age, m int) *State {
return bestState
}

func (model *Model) runWorker(t Mode, n, age, m int, ch chan *State) {
func (model *Model) runWorker(t ShapeType, a, n, age, m int, ch chan *State) {
buffer := image.NewRGBA(model.Target.Bounds())
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
state := model.BestHillClimbState(buffer, t, n, age, m, rnd)
state := model.BestHillClimbState(buffer, t, a, n, age, m, rnd)
ch <- state
}

func (model *Model) BestHillClimbState(buffer *image.RGBA, t Mode, n, age, m int, rnd *rand.Rand) *State {
func (model *Model) BestHillClimbState(buffer *image.RGBA, t ShapeType, a, n, age, m int, rnd *rand.Rand) *State {
var bestEnergy float64
var bestState *State
for i := 0; i < m; i++ {
state := model.BestRandomState(buffer, t, n, rnd)
state := model.BestRandomState(buffer, t, a, n, rnd)
before := state.Energy()
state = HillClimb(state, age).(*State)
energy := state.Energy()
Expand All @@ -175,11 +172,11 @@ func (model *Model) BestHillClimbState(buffer *image.RGBA, t Mode, n, age, m int
return bestState
}

func (model *Model) BestRandomState(buffer *image.RGBA, t Mode, n int, rnd *rand.Rand) *State {
func (model *Model) BestRandomState(buffer *image.RGBA, t ShapeType, a, n int, rnd *rand.Rand) *State {
var bestEnergy float64
var bestState *State
for i := 0; i < n; i++ {
state := model.RandomState(buffer, t, rnd)
state := model.RandomState(buffer, t, a, rnd)
energy := state.Energy()
if i == 0 || energy < bestEnergy {
bestEnergy = energy
Expand All @@ -189,20 +186,20 @@ func (model *Model) BestRandomState(buffer *image.RGBA, t Mode, n int, rnd *rand
return bestState
}

func (model *Model) RandomState(buffer *image.RGBA, t Mode, rnd *rand.Rand) *State {
func (model *Model) RandomState(buffer *image.RGBA, t ShapeType, a int, rnd *rand.Rand) *State {
switch t {
default:
return model.RandomState(buffer, Mode(rnd.Intn(5)+1), rnd)
case ModeTriangle:
return NewState(model, buffer, NewRandomTriangle(model.W, model.H, rnd))
case ModeRectangle:
return NewState(model, buffer, NewRandomRectangle(model.W, model.H, rnd))
case ModeEllipse:
return NewState(model, buffer, NewRandomEllipse(model.W, model.H, rnd))
case ModeCircle:
return NewState(model, buffer, NewRandomCircle(model.W, model.H, rnd))
case ModeRotatedRectangle:
return NewState(model, buffer, NewRandomRotatedRectangle(model.W, model.H, rnd))
return model.RandomState(buffer, ShapeType(rnd.Intn(5)+1), a, rnd)
case ShapeTypeTriangle:
return NewState(model, buffer, a, NewRandomTriangle(model.W, model.H, rnd))
case ShapeTypeRectangle:
return NewState(model, buffer, a, NewRandomRectangle(model.W, model.H, rnd))
case ShapeTypeEllipse:
return NewState(model, buffer, a, NewRandomEllipse(model.W, model.H, rnd))
case ShapeTypeCircle:
return NewState(model, buffer, a, NewRandomCircle(model.W, model.H, rnd))
case ShapeTypeRotatedRectangle:
return NewState(model, buffer, a, NewRandomRotatedRectangle(model.W, model.H, rnd))
}
}

Expand Down Expand Up @@ -240,9 +237,9 @@ func (model *Model) computeScore(lines []Scanline, c Color, buffer *image.RGBA)
return differencePartial(model.Target, model.Current, buffer, model.Score, lines)
}

func (model *Model) Energy(shape Shape, buffer *image.RGBA) float64 {
func (model *Model) Energy(alpha int, shape Shape, buffer *image.RGBA) float64 {
lines := shape.Rasterize()
c := model.computeColor(lines, model.Alpha)
c := model.computeColor(lines, alpha)
s := model.computeScore(lines, c, buffer)
return s
}
11 changes: 11 additions & 0 deletions primitive/shape.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,14 @@ type Shape interface {
Draw(dc *gg.Context)
SVG(attrs string) string
}

type ShapeType int

const (
ShapeTypeAny ShapeType = iota
ShapeTypeTriangle
ShapeTypeRectangle
ShapeTypeEllipse
ShapeTypeCircle
ShapeTypeRotatedRectangle
)
9 changes: 5 additions & 4 deletions primitive/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import "image"
type State struct {
Model *Model
Buffer *image.RGBA
Alpha int
Shape Shape
Score float64
}

func NewState(model *Model, buffer *image.RGBA, shape Shape) *State {
return &State{model, buffer, shape, -1}
func NewState(model *Model, buffer *image.RGBA, alpha int, shape Shape) *State {
return &State{model, buffer, alpha, shape, -1}
}

func (state *State) Energy() float64 {
if state.Score < 0 {
state.Score = state.Model.Energy(state.Shape, state.Buffer)
state.Score = state.Model.Energy(state.Alpha, state.Shape, state.Buffer)
}
return state.Score
}
Expand All @@ -34,5 +35,5 @@ func (state *State) UndoMove(undo interface{}) {
}

func (state *State) Copy() Annealable {
return &State{state.Model, state.Buffer, state.Shape.Copy(), state.Score}
return &State{state.Model, state.Buffer, state.Alpha, state.Shape.Copy(), state.Score}
}
File renamed without changes.
File renamed without changes.

0 comments on commit 1305a19

Please sign in to comment.