Skip to content

Commit

Permalink
Merge pull request #10 from Baggerone/develop
Browse files Browse the repository at this point in the history
Improved process to mark cells as done and added binaries
  • Loading branch information
Baggerone authored Sep 11, 2017
2 parents 09fe348 + b80bc0c commit b56e18d
Show file tree
Hide file tree
Showing 17 changed files with 267 additions and 64 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ You can also copy *.png files into the `examples` folder and include their names

That will create corresponding *.html files.

## Binaries ##
The dist folder includes binaries for different operating systems.
They allow you to convert *.png files to svg by including the png file name(s) as command line arguments.
Binary file added dist/darwin/386/pixel2svg
Binary file not shown.
Binary file added dist/darwin/amd64/pixel2svg
Binary file not shown.
Binary file added dist/freebsd/386/pixel2svg
Binary file not shown.
Binary file added dist/freebsd/amd64/pixel2svg
Binary file not shown.
Binary file added dist/freebsd/arm/pixel2svg
Binary file not shown.
Binary file added dist/linux/386/pixel2svg
Binary file not shown.
Binary file added dist/linux/amd64/pixel2svg
Binary file not shown.
Binary file added dist/linux/arm/pixel2svg
Binary file not shown.
Binary file added dist/netbsd/386/pixel2svg
Binary file not shown.
Binary file added dist/netbsd/amd64/pixel2svg
Binary file not shown.
Binary file added dist/netbsd/arm/pixel2svg
Binary file not shown.
Binary file added dist/openbsd/386/pixel2svg
Binary file not shown.
Binary file added dist/openbsd/amd64/pixel2svg
Binary file not shown.
55 changes: 42 additions & 13 deletions examples/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,23 @@ func convertToUint8(rgbTone uint32) uint8 {
return uint8(rgbTone / 0x101)
}

func ReadPNGPixels(filePath string) [][][4]uint8 {
func ReadPNGPixels(filePath string) ([][][4]uint8, error) {

var infile *os.File
var err error
var src image.Image

// fmt.Println("\nReading \n", filePath)
infile, err := os.Open(filePath)
if err != nil {
// replace this with good error handling
panic(err)
if infile, err = os.Open(filePath); err != nil {
return nil, err
}

defer infile.Close()

// Decode will figure out what type of image is in the file on its own.
// We just have to be sure all the image packages we want are imported.
src, _, err := image.Decode(infile)
if err != nil {
// replace this with good error handling
panic(err)
if src, _, err = image.Decode(infile); err != nil {
return nil, err
}

colorGrid := [][][4]uint8{}
Expand All @@ -114,7 +115,14 @@ func ReadPNGPixels(filePath string) [][][4]uint8 {
colorGrid = append(colorGrid, newCol)
}

return colorGrid
return colorGrid, nil
}

func addError(errors *[]string, summary string, err error) {
*errors = append(
*errors,
strings.Join([]string{summary, err.Error()}, " "),
)
}

/*
Expand All @@ -126,20 +134,41 @@ func ReadPNGPixels(filePath string) [][][4]uint8 {
* That will create corresponding *.html files.
*/
func main() {
errors := []string{}
var s pixels2svg.ShapeExtractor
var colorGrid [][][4]uint8
var err error

s.Init(sailboat())
s.WriteSVGToFile("example_sailboat.html")

s.Init(ReadPNGPixels("test1.png"))
s.WriteSVGToFile("example_test1.html")
if colorGrid, err = ReadPNGPixels("test1.png"); err == nil {
s.Init(colorGrid)
s.WriteSVGToFile("example_test1.html")
}

args := os.Args[1:]
if len(args) <= 0 {
println("\n To create from other png files, just add their names as command line arguments.")
}

for _, nextInput := range args {
if strings.HasSuffix(nextInput, ".png") {
s.Init(ReadPNGPixels(nextInput))
colorGrid, err := ReadPNGPixels(nextInput)
if err != nil {
addError(&errors, strings.Join([]string{" Error: ", nextInput, " ... "}, ""), err)
continue
}
s.Init(colorGrid)
newName := strings.TrimSuffix(nextInput, ".png") + ".html"
s.WriteSVGToFile(newName)
}
}

if len(errors) > 0 {
println("\nRan into error(s) ...")
for _, nextErr := range errors {
println(nextErr)
}
}
}
131 changes: 115 additions & 16 deletions pixels2svg/pixels2svg.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type ShapeExtractor struct {
ColCount int
RowCount int
neighborEvaluators [8]evaluatorFunc
cellQueue [][2]int
}

func (s *ShapeExtractor) showAlreadyDone() {
Expand Down Expand Up @@ -215,10 +216,41 @@ func (s *ShapeExtractor) getLeftDirection(direction int) int {
* get the direction slightly to its right.
*/
func (s *ShapeExtractor) getAngledRightDirection(direction int) int {
if direction >= 7 {
return (direction + 1) % 8
}

/*
* Assuming an outline walker is headed a certain direction,
* get the direction to the right (90 degrees).
*/
func (s *ShapeExtractor) getRight90Direction(direction int) int {
return (direction + 2) % 8
}

func (s *ShapeExtractor) getLatestDirection(colX1, rowY1, colX2, rowY2 int) int {
colDiff := colX2 - colX1
rowDiff := rowY2 - rowY1

switch {
case (colDiff == 0 && rowDiff < 0):
return 0
case (colDiff > 0 && rowDiff < 0):
return 1
case (colDiff > 0 && rowDiff == 0):
return 2
case (colDiff > 0 && rowDiff > 0):
return 3
case (colDiff == 0 && rowDiff > 0):
return 4
case (colDiff < 0 && rowDiff > 0):
return 5
case (colDiff < 0 && rowDiff == 0):
return 6
case (colDiff < 0 && rowDiff < 0):
return 7
}
return direction + 1

return 0
}

/*
Expand Down Expand Up @@ -424,6 +456,43 @@ func (s *ShapeExtractor) GetPolygonsFromCell(
return allPolygons
}

/*
* Check the neighboring cells. If they are the
* same color and not yet done, mark them as done and add them to the queue.
*/
func (s *ShapeExtractor) addNeighborsToQueue(colX, rowY int, color [4]uint8) {

for direction := 0; direction <= 7; direction += 1 {

evaluator := s.neighborEvaluators[direction]
isGood := evaluator(colX, rowY, color)

if isGood {
nextCol, nextRow := s.getCellInDirection(colX, rowY, direction)
s.alreadyDone[nextCol][nextRow] = true
s.cellQueue = append(s.cellQueue, [2]int{nextCol, nextRow})
}
}
}

/*
* For each cell in the queue, mark it as already done
* and remove it from the queue, but also
* add its neighbors to the queue if they have the same color
* and aren't done yet
*/
func (s *ShapeExtractor) markCellQueueDone(color [4]uint8) {
if len(s.cellQueue) < 1 {
return
}

colX, rowY := split2Int(s.cellQueue[0])
s.addNeighborsToQueue(colX, rowY, color)
s.cellQueue = s.cellQueue[1:]

s.markCellQueueDone(color)
}

/*
* Given a polygon with an outline starting at a certain cell, mark
* all its points (outline and internal) as "alreadyDone".
Expand All @@ -432,31 +501,61 @@ func (s *ShapeExtractor) GetPolygonsFromCell(
* Assumes first point is the highest row of the outline and the
* left-most cell of that row.
*
* From each point on the outline of the polygon, marks it and all cells
* below it as already done until it runs into another cell of the outline
* or a cell of a different color
* Marks each point on the outline as already done and also adds the
* cell to their "right" and "angled right" to a queue for continuing
* the process of marking as done.
*
*/
func (s *ShapeExtractor) markPolygonAlreadyDone(polygonOutline [][2]int) {
firstCol, firstRow := split2Int(polygonOutline[0])
color := s.grid[firstCol][firstRow]

for _, nextPoint := range polygonOutline {
if len(polygonOutline) < 3 {
return
}

s.setNeighborEvaluators()
s.cellQueue = [][2]int{}

// deal with first cell on its own
prevCol, prevRow := split2Int(polygonOutline[0])
color := s.grid[prevCol][prevRow]
s.alreadyDone[prevCol][prevRow] = true

// Walk through outline and add cells to the right to the queue
// of cells to mark as already done
for _, nextPoint := range polygonOutline[1:] {
nextCol, nextRow := split2Int(nextPoint)
direction := s.getLatestDirection(prevCol, prevRow, nextCol, nextRow)
s.alreadyDone[nextCol][nextRow] = true

for lowerRow := nextRow + 1; lowerRow < s.RowCount; lowerRow++ {
if color != s.grid[nextCol][lowerRow] {
break
}
// Get its cell to the "right" and if necessary, add to the queue
innerDirection := s.getRight90Direction(direction)

if IsPointIn2IntArray(nextCol, lowerRow, polygonOutline) {
break
}
evaluator := s.neighborEvaluators[innerDirection]
isGood := evaluator(nextCol, nextRow, color)
if isGood {
innerCol, innerRow := s.getCellInDirection(nextCol, nextRow, innerDirection)
s.alreadyDone[innerCol][innerRow] = true

s.alreadyDone[nextCol][lowerRow] = true
s.cellQueue = append(s.cellQueue, [2]int{innerCol, innerRow})
}

// Get its cell slightly to the "right" and if necessary, add to the queue
innerDirection = s.getAngledRightDirection(direction)

evaluator = s.neighborEvaluators[innerDirection]
isGood = evaluator(nextCol, nextRow, color)
if isGood {
innerCol, innerRow := s.getCellInDirection(nextCol, nextRow, innerDirection)
s.alreadyDone[innerCol][innerRow] = true

s.cellQueue = append(s.cellQueue, [2]int{innerCol, innerRow})
}

prevCol = nextCol
prevRow = nextRow
}

s.markCellQueueDone(color)
// s.showAlreadyDone()
// println("\n")
}
Expand Down
Loading

0 comments on commit b56e18d

Please sign in to comment.