demo application for modular-spatial-index
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
5.1 KiB

package main
import (
"encoding/binary"
"fmt"
"image"
"image/color"
"math"
"math/bits"
"time"
spatial "git.sequentialread.com/forest/modular-spatial-index"
)
const dim = 512
const rainbowCount = float64(20)
const saturationFluctuationCount = float64(8)
var frames = 0
func main() {
spatialIndex, err := spatial.NewSpatialIndex2D(bits.UintSize)
if err != nil {
panic(err)
}
run_opengl_app(func() *image.RGBA {
seconds := float64(time.Now().UnixNano()) / float64(int64(time.Second))
rectX := int(float64(dim) * (float64(0.4) + math.Sin(seconds*float64(1.3))*float64(0.3)))
rectY := int(float64(dim) * (float64(0.5) + math.Cos(seconds*float64(0.3))*float64(0.2)))
rectSize := 1 + int(float64(25)*(float64(1)+math.Sin(seconds*float64(0.843))))
rectMaxX := rectX + rectSize
rectMaxY := rectY + rectSize
inputMin, inputMax := spatialIndex.GetValidInputRange()
_, outputMaxBytes := spatialIndex.GetOutputRange()
curveLength := int(binary.BigEndian.Uint64(outputMaxBytes))
//log.Printf("inputMin: %d, inputMax: %d, curveLength: %d", inputMin, inputMax, curveLength)
remappedRectXMin := int(lerp(float64(inputMin), float64(inputMax), float64(rectX)/float64(dim)))
remappedRectYMin := int(lerp(float64(inputMin), float64(inputMax), float64(rectY)/float64(dim)))
remappedRectXMax := int(lerp(float64(inputMin), float64(inputMax), float64(rectX+rectSize)/float64(dim)))
remappedRectSize := remappedRectXMax - remappedRectXMin
byteRanges, err := spatialIndex.RectangleToIndexedRanges(remappedRectXMin, remappedRectYMin, remappedRectSize, remappedRectSize, 1)
if err != nil {
panic(err)
}
ranges := make([][]int, len(byteRanges))
// log.Println("------------")
for i, byteRange := range byteRanges {
ranges[i] = []int{
int(binary.BigEndian.Uint64(byteRange.Start)),
int(binary.BigEndian.Uint64(byteRange.End)),
}
// log.Printf("Start: %x\n", byteRange.Start)
// log.Printf(" End: %x\n", byteRange.End)
// log.Printf(" Max: %x\n", outputMaxBytes)
}
// log.Println("------------")
// outBytes, _ := json.MarshalIndent(ranges, "", " ")
// log.Println("outBytes: ", string(outBytes))
rgba := image.NewRGBA(image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{dim, dim}})
queriedArea := 0
for x := 0; x < dim; x++ {
for y := 0; y < dim; y++ {
onVertical := (x == rectMaxX || x == rectX) && y >= rectY && y <= rectMaxY
onHorizontal := (y == rectMaxY || y == rectY) && x >= rectX && x <= rectMaxX
if onVertical || onHorizontal {
rgba.Set(x, y, color.White)
continue
}
remappedX := int(lerp(float64(inputMin), float64(inputMax), float64(x)/float64(dim)))
remappedY := int(lerp(float64(inputMin), float64(inputMax), float64(y)/float64(dim)))
if y > dim-20 {
found := false
xOnCurveNumberLine := int(lerp(float64(0), float64(curveLength), float64(x)/float64(dim)))
for _, curveRange := range ranges {
if xOnCurveNumberLine >= curveRange[0] && xOnCurveNumberLine <= curveRange[1] {
found = true
}
}
if found {
rgba.Set(x, y, color.White)
} else {
rgba.Set(x, y, color.Black)
}
continue
}
curvePointBytes, err := spatialIndex.GetIndexedPoint(remappedX, remappedY)
curvePoint := int(binary.BigEndian.Uint64(curvePointBytes))
if err != nil {
panic(err)
}
// if x*2 == y && !logged {
// log.Printf("[%d,%d]: %d %d", x, y, curvePoint, int((float64(curvePoint)/float64(myCurve.N*myCurve.N))*1000))
// }
curveFloat := (float64(curvePoint) / float64(math.MaxInt64))
//sat := (float64(2) + math.Sin(curveFloat*math.Pi*2*saturationFluctuationCount)) * float64(0.3333333)
sat := 0.2
// if curvePoint >= curvePoints[0] && curvePoint <= curvePoints[len(curvePoints)-1] {
// sat = 1
// }
for _, rng := range ranges {
if curvePoint >= rng[0] && curvePoint <= rng[1] {
sat = 1
queriedArea++
}
}
hue := int(curveFloat*rainbowCount*float64(3600)) % 3600
rainbow := hsvColor(float64(hue)*0.1, sat, sat)
// uvColor := color.RGBA{
// uint8((float32(x) / float32(width)) * float32(255)),
// uint8((float32(y) / float32(width)) * float32(255)),
// 255,
// 255,
// }
rgba.Set(x, y, rainbow)
}
}
if frames%10 == 0 {
fmt.Printf("range count: %d, queriedArea: %d%%\n", len(ranges), int((float64(queriedArea)/float64(rectSize*rectSize))*float64(100)))
}
frames++
return rgba
})
}
func lerp(a, b, lerp float64) float64 {
return a*(float64(1)-lerp) + b*lerp
}
func hsvColor(H, S, V float64) color.RGBA {
Hp := H / 60.0
C := V * S
X := C * (1.0 - math.Abs(math.Mod(Hp, 2.0)-1.0))
m := V - C
r, g, b := 0.0, 0.0, 0.0
switch {
case 0.0 <= Hp && Hp < 1.0:
r = C
g = X
case 1.0 <= Hp && Hp < 2.0:
r = X
g = C
case 2.0 <= Hp && Hp < 3.0:
g = C
b = X
case 3.0 <= Hp && Hp < 4.0:
g = X
b = C
case 4.0 <= Hp && Hp < 5.0:
r = X
b = C
case 5.0 <= Hp && Hp < 6.0:
r = C
b = X
}
return color.RGBA{uint8(int((m + r) * float64(255))), uint8(int((m + g) * float64(255))), uint8(int((m + b) * float64(255))), 0xff}
}