Skip to content

Instantly share code, notes, and snippets.

@gugray
Created November 9, 2022 20:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gugray/7fad069724bdc957ac1b40ce03a47a03 to your computer and use it in GitHub Desktop.
Save gugray/7fad069724bdc957ac1b40ce03a47a03 to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"fmt"
"math"
"os"
"strconv"
"strings"
"time"
)
const pixelFileName = "pixels.txt"
const lineFileName = "lines.txt"
const w = 1480
const h = 1050
const rf = 2
const segLen = 2
var img image
var lines []inputLine
var visibleLines []outputLine
type pixel struct {
r, g, b byte
}
type point struct {
x, y float64
}
type pixelRow []pixel
type image struct {
rows []pixelRow
}
type inputLine struct {
pt1, pt2 point
clr1, clr2 pixel
}
type outputLine struct {
pt1, pt2 point
}
func main() {
readLines()
readImage()
tStart := time.Now()
var res []outputLine
for i, _ := range lines {
ln := lines[i]
res = getVisibleLines(res, ln)
visibleLines = append(visibleLines, res...)
}
elapsed := time.Since(tStart)
fmt.Printf("Elapsed: %v\n", elapsed)
fmt.Printf("Kept %v lines\n", len(visibleLines))
}
func getVisibleLines(visibleLines []outputLine, ln inputLine) []outputLine {
visibleLines = visibleLines[:0]
delta := point{ln.pt2.x - ln.pt1.x, ln.pt2.y - ln.pt1.y}
lineLength := math.Sqrt(delta.x*delta.x + delta.y*delta.y)
nSegs := int(math.Max(2, math.Round(lineLength/segLen)))
orto := point{-delta.y / lineLength, delta.x / lineLength}
gotFirstVisible := false
var firstVisible, pt, prevPt point
for i := 0; i <= nSegs; i++ {
pt.x = ln.pt1.x + float64(i)/float64(nSegs)*delta.x
pt.y = ln.pt1.y + float64(i)/float64(nSegs)*delta.y
clrHere := getPixel(pt)
isVisible := clrEq(clrHere, ln.clr1)
if !isVisible {
isVisible = clrEq(clrHere, ln.clr2)
}
if !isVisible {
clrHere = getPixel(point{pt.x + orto.x, pt.y + orto.y})
isVisible = clrEq(clrHere, ln.clr1)
if !isVisible {
isVisible = clrEq(clrHere, ln.clr2)
}
}
if !isVisible {
clrHere = getPixel(point{pt.x - orto.x, pt.y - orto.y})
isVisible = clrEq(clrHere, ln.clr1)
if !isVisible {
isVisible = clrEq(clrHere, ln.clr2)
}
}
if isVisible && !gotFirstVisible {
firstVisible = pt
gotFirstVisible = true
} else if !isVisible && gotFirstVisible {
if firstVisible != prevPt {
visibleLines = append(visibleLines, outputLine{firstVisible, prevPt})
}
gotFirstVisible = false
}
prevPt = pt
}
if gotFirstVisible && firstVisible != ln.pt2 {
visibleLines = append(visibleLines, outputLine{firstVisible, ln.pt2})
}
return visibleLines
}
func getPixel(pt point) pixel {
x := int(math.Round(pt.x * rf))
y := h*rf - int(math.Round(pt.y*rf))
if x < 0 || x >= w*rf || y < 0 || y >= h*rf {
return pixel{}
}
return img.rows[y][x]
}
func clrEq(a, b pixel) bool {
return areClose(a.r, b.r) && areClose(a.g, b.g) && areClose(a.b, b.b)
}
func areClose(a, b byte) bool {
if a == b {
return true
}
if a > b && a == b+1 {
return true
}
if b > a && b == a+1 {
return true
}
return false
}
func readLines() {
file, err := os.Open(lineFileName)
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
vals := strings.Fields(scanner.Text())
var ln inputLine
var err error
if ln.pt1.x, err = strconv.ParseFloat(vals[0], 64); err != nil {
panic(err)
}
if ln.pt1.y, err = strconv.ParseFloat(vals[1], 64); err != nil {
panic(err)
}
if ln.pt2.x, err = strconv.ParseFloat(vals[2], 64); err != nil {
panic(err)
}
if ln.pt2.y, err = strconv.ParseFloat(vals[3], 64); err != nil {
panic(err)
}
var r, g, b int
if r, err = strconv.Atoi(vals[4]); err != nil {
panic(err)
}
if g, err = strconv.Atoi(vals[5]); err != nil {
panic(err)
}
if b, err = strconv.Atoi(vals[6]); err != nil {
panic(err)
}
ln.clr1 = pixel{byte(r), byte(g), byte(b)}
if r, err = strconv.Atoi(vals[7]); err != nil {
panic(err)
}
if g, err = strconv.Atoi(vals[8]); err != nil {
panic(err)
}
if b, err = strconv.Atoi(vals[9]); err != nil {
panic(err)
}
ln.clr2 = pixel{byte(r), byte(g), byte(b)}
lines = append(lines, ln)
}
if err := scanner.Err(); err != nil {
panic(err)
}
fmt.Printf("Parsed %d lines\n", len(lines))
}
func readImage() {
file, err := os.Open(pixelFileName)
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
ln := scanner.Text()
vals := strings.Fields(ln)
if len(vals)/4 != w*rf {
panic(fmt.Sprintf("Expected %d pixels in row, got %d", w*rf, len(vals)/4))
}
var row pixelRow
for i := 0; i < len(vals)/4; i++ {
var r, g, b int
var err error
if r, err = strconv.Atoi(vals[i*4]); err != nil {
panic(err)
}
if g, err = strconv.Atoi(vals[i*4+1]); err != nil {
panic(err)
}
if b, err = strconv.Atoi(vals[i*4+2]); err != nil {
panic(err)
}
px := pixel{byte(r), byte(g), byte(b)}
row = append(row, px)
}
img.rows = append(img.rows, row)
}
if err := scanner.Err(); err != nil {
panic(err)
}
if len(img.rows) != h*rf {
panic(fmt.Sprintf("Expected %d rows in image, got %d", h*rf, len(img.rows)))
}
fmt.Printf("Parsed %d x %d image\n", w, h)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment