add initial randoffset mode
This commit is contained in:
		
							parent
							
								
									82f09db519
								
							
						
					
					
						commit
						2d536f21f3
					
				
							
								
								
									
										5
									
								
								main.go
								
								
								
								
							
							
						
						
									
										5
									
								
								main.go
								
								
								
								
							| 
						 | 
					@ -69,7 +69,8 @@ func main() {
 | 
				
			||||||
			// run RPC server, tasking clients to flut
 | 
								// run RPC server, tasking clients to flut
 | 
				
			||||||
			wg.Add(1)
 | 
								wg.Add(1)
 | 
				
			||||||
			r := rpc.SummonRán(*ránAddr, stopChan, &wg)
 | 
								r := rpc.SummonRán(*ránAddr, stopChan, &wg)
 | 
				
			||||||
			r.SetTask(img, offset, *address, *connections) // @incomplete
 | 
								// TODO: startup without a task, but init params
 | 
				
			||||||
 | 
								r.SetTask(img, offset, *address, *connections)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// fetch server state and save to file
 | 
								// fetch server state and save to file
 | 
				
			||||||
| 
						 | 
					@ -82,7 +83,7 @@ func main() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// local 🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊
 | 
								// local 🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊
 | 
				
			||||||
			wg.Add(1)
 | 
								wg.Add(1)
 | 
				
			||||||
			go pixelflut.Flut(img, offset, *shuffle, *address, *connections, stopChan, &wg)
 | 
								go pixelflut.Flut(img, offset, *shuffle, false, false, false, *address, *connections, stopChan, &wg)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} else if *hevringAddr != "" {
 | 
						} else if *hevringAddr != "" {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,21 +6,20 @@ import (
 | 
				
			||||||
	"image"
 | 
						"image"
 | 
				
			||||||
	"image/color"
 | 
						"image/color"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/SpeckiJ/Hochwasser/render"
 | 
						"github.com/SpeckiJ/Hochwasser/render"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var funmode = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Flut asynchronously sends the given image to pixelflut server at `address`
 | 
					// Flut asynchronously sends the given image to pixelflut server at `address`
 | 
				
			||||||
//   using `conns` connections. Pixels are sent column wise, unless `shuffle`
 | 
					//   using `conns` connections. Pixels are sent column wise, unless `shuffle`
 | 
				
			||||||
//   is set. Stops when stop is closed.
 | 
					//   is set. Stops when stop is closed.
 | 
				
			||||||
// @cleanup: use FlutTask{} as arg
 | 
					// @cleanup: use FlutTask{} as arg
 | 
				
			||||||
func Flut(img *image.NRGBA, position image.Point, shuffle bool, address string, conns int, stop chan bool, wg *sync.WaitGroup) {
 | 
					func Flut(img *image.NRGBA, position image.Point, shuffle, rgbsplit, randoffset, recreateConns bool, address string, conns int, stop chan bool, wg *sync.WaitGroup) {
 | 
				
			||||||
	var cmds commands
 | 
						var cmds commands
 | 
				
			||||||
	if funmode {
 | 
						if rgbsplit {
 | 
				
			||||||
		// do a RGB split of white
 | 
							// do a RGB split of white
 | 
				
			||||||
		imgmod := render.ImgColorFilter(img, color.NRGBA{0xff, 0xff, 0xff, 0xff}, color.NRGBA{0xff, 0, 0, 0xff})
 | 
							imgmod := render.ImgColorFilter(img, color.NRGBA{0xff, 0xff, 0xff, 0xff}, color.NRGBA{0xff, 0, 0, 0xff})
 | 
				
			||||||
		cmds = append(cmds, commandsFromImage(imgmod, image.Pt(position.X-10, position.Y-10))...)
 | 
							cmds = append(cmds, commandsFromImage(imgmod, image.Pt(position.X-10, position.Y-10))...)
 | 
				
			||||||
| 
						 | 
					@ -36,11 +35,31 @@ func Flut(img *image.NRGBA, position image.Point, shuffle bool, address string,
 | 
				
			||||||
	if shuffle {
 | 
						if shuffle {
 | 
				
			||||||
		cmds.Shuffle()
 | 
							cmds.Shuffle()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	messages := cmds.Chunk(conns)
 | 
					
 | 
				
			||||||
 | 
						var messages [][]byte
 | 
				
			||||||
 | 
						var maxX, maxY int
 | 
				
			||||||
 | 
						if randoffset {
 | 
				
			||||||
 | 
							maxX, maxY = CanvasSize(address)
 | 
				
			||||||
 | 
							messages = cmds.Chunk(1) // each connection should send the full img
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							messages = cmds.Chunk(conns)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bombWg := sync.WaitGroup{}
 | 
						bombWg := sync.WaitGroup{}
 | 
				
			||||||
	for _, msg := range messages {
 | 
						for i := 0; i < conns; i++ {
 | 
				
			||||||
 | 
							msg := messages[0]
 | 
				
			||||||
 | 
							if len(messages) > i {
 | 
				
			||||||
 | 
								msg = messages[i]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bombWg.Add(1)
 | 
							bombWg.Add(1)
 | 
				
			||||||
 | 
							if randoffset {
 | 
				
			||||||
 | 
								msg = append(OffsetCmd(
 | 
				
			||||||
 | 
									rand.Intn(maxX-img.Bounds().Canon().Dx()),
 | 
				
			||||||
 | 
									rand.Intn(maxY-img.Bounds().Canon().Dy()),
 | 
				
			||||||
 | 
								), msg...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		go bombAddress(msg, address, stop, &bombWg)
 | 
							go bombAddress(msg, address, stop, &bombWg)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	bombWg.Wait()
 | 
						bombWg.Wait()
 | 
				
			||||||
| 
						 | 
					@ -49,6 +68,23 @@ func Flut(img *image.NRGBA, position image.Point, shuffle bool, address string,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CanvasSize returns the size of the canvas as returned by the server
 | 
				
			||||||
 | 
					func CanvasSize(address string) (int, int) {
 | 
				
			||||||
 | 
						conn, err := net.Dial("tcp", address)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer conn.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn.Write([]byte("SIZE\n"))
 | 
				
			||||||
 | 
						reader := bufio.NewReader(conn)
 | 
				
			||||||
 | 
						res, err := reader.ReadSlice('\n')
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return parseXY(res[5:])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FetchImage asynchronously uses `conns` to fetch pixels within `bounds` from
 | 
					// FetchImage asynchronously uses `conns` to fetch pixels within `bounds` from
 | 
				
			||||||
//   a pixelflut server at `address`, and writes them into the returned Image.
 | 
					//   a pixelflut server at `address`, and writes them into the returned Image.
 | 
				
			||||||
func FetchImage(bounds image.Rectangle, address string, conns int, stop chan bool) (img *image.NRGBA) {
 | 
					func FetchImage(bounds image.Rectangle, address string, conns int, stop chan bool) (img *image.NRGBA) {
 | 
				
			||||||
| 
						 | 
					@ -84,15 +120,7 @@ func readPixels(target *image.NRGBA, conn net.Conn, stop chan bool) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// parse response ("PX <x> <y> <col>\n")
 | 
								// parse response ("PX <x> <y> <col>\n")
 | 
				
			||||||
			colorStart := len(res) - 7
 | 
								colorStart := len(res) - 7
 | 
				
			||||||
			xy := res[3 : colorStart-1]
 | 
								x, y := parseXY(res[3 : colorStart-1])
 | 
				
			||||||
			yStart := 0
 | 
					 | 
				
			||||||
			for yStart = len(xy) - 2; yStart >= 0; yStart-- {
 | 
					 | 
				
			||||||
				if xy[yStart] == ' ' {
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			x := asciiToInt(xy[:yStart])
 | 
					 | 
				
			||||||
			y := asciiToInt(xy[yStart+1:])
 | 
					 | 
				
			||||||
			hex.Decode(col, res[colorStart:len(res)-1])
 | 
								hex.Decode(col, res[colorStart:len(res)-1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			target.SetNRGBA(x, y, color.NRGBA{col[0], col[1], col[2], 255})
 | 
								target.SetNRGBA(x, y, color.NRGBA{col[0], col[1], col[2], 255})
 | 
				
			||||||
| 
						 | 
					@ -100,6 +128,19 @@ func readPixels(target *image.NRGBA, conn net.Conn, stop chan bool) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseXY(xy []byte) (int, int) {
 | 
				
			||||||
 | 
						last := len(xy) - 1
 | 
				
			||||||
 | 
						yStart := last - 1 // y is at least one char long
 | 
				
			||||||
 | 
						for ; yStart >= 0; yStart-- {
 | 
				
			||||||
 | 
							if xy[yStart] == ' ' {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						x := asciiToInt(xy[:yStart])
 | 
				
			||||||
 | 
						y := asciiToInt(xy[yStart+1 : last])
 | 
				
			||||||
 | 
						return x, y
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func asciiToInt(buf []byte) (v int) {
 | 
					func asciiToInt(buf []byte) (v int) {
 | 
				
			||||||
	for _, c := range buf {
 | 
						for _, c := range buf {
 | 
				
			||||||
		v = v*10 + int(c-'0')
 | 
							v = v*10 + int(c-'0')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,11 @@ func (c commands) Shuffle() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddOffset uses the OFFSET command to send the image at a specific place (not supported by all servers. example: https://github.com/TobleMiner/shoreline)
 | 
				
			||||||
 | 
					func OffsetCmd(x, y int) []byte {
 | 
				
			||||||
 | 
						return []byte(fmt.Sprintf("OFFSET %d %d\n", x, y))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommandsFromImage converts an image to the respective pixelflut commands
 | 
					// CommandsFromImage converts an image to the respective pixelflut commands
 | 
				
			||||||
func commandsFromImage(img *image.NRGBA, offset image.Point) (cmds commands) {
 | 
					func commandsFromImage(img *image.NRGBA, offset image.Point) (cmds commands) {
 | 
				
			||||||
	b := img.Bounds()
 | 
						b := img.Bounds()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,12 +30,14 @@ type Hevring struct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type FlutTask struct {
 | 
					type FlutTask struct {
 | 
				
			||||||
	Address  string
 | 
						Address    string
 | 
				
			||||||
	MaxConns int
 | 
						MaxConns   int
 | 
				
			||||||
	Img      *image.NRGBA
 | 
						Img        *image.NRGBA
 | 
				
			||||||
	Offset   image.Point
 | 
						Offset     image.Point
 | 
				
			||||||
	Shuffle  bool
 | 
						Shuffle    bool // TODO: refactor these as RenderOpts bitfield
 | 
				
			||||||
	// Effects  []string // @idea shuffle, rgbsplit, randoffset, ...
 | 
						RGBSplit   bool
 | 
				
			||||||
 | 
						RandOffset bool
 | 
				
			||||||
 | 
						NewConn    bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type FlutAck struct{ Ok bool }
 | 
					type FlutAck struct{ Ok bool }
 | 
				
			||||||
| 
						 | 
					@ -56,7 +58,7 @@ func (h *Hevring) Flut(task FlutTask, reply *FlutAck) error {
 | 
				
			||||||
	h.task = task
 | 
						h.task = task
 | 
				
			||||||
	h.taskQuit = make(chan bool)
 | 
						h.taskQuit = make(chan bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go pixelflut.Flut(task.Img, task.Offset, task.Shuffle, task.Address, task.MaxConns, h.taskQuit, nil)
 | 
						go pixelflut.Flut(task.Img, task.Offset, task.Shuffle, task.RGBSplit, task.RandOffset, task.NewConn, task.Address, task.MaxConns, h.taskQuit, nil)
 | 
				
			||||||
	reply.Ok = true
 | 
						reply.Ok = true
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								rpc/repl.go
								
								
								
								
							
							
						
						
									
										15
									
								
								rpc/repl.go
								
								
								
								
							| 
						 | 
					@ -82,6 +82,21 @@ func RunREPL(f Fluter) {
 | 
				
			||||||
				t.Shuffle = !t.Shuffle
 | 
									t.Shuffle = !t.Shuffle
 | 
				
			||||||
				f.applyTask(t)
 | 
									f.applyTask(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case "rgbsplit":
 | 
				
			||||||
 | 
									t := f.getTask()
 | 
				
			||||||
 | 
									t.RGBSplit = !t.RGBSplit
 | 
				
			||||||
 | 
									f.applyTask(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case "randoffset":
 | 
				
			||||||
 | 
									t := f.getTask()
 | 
				
			||||||
 | 
									t.RandOffset = !t.RandOffset
 | 
				
			||||||
 | 
									f.applyTask(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case "newconn":
 | 
				
			||||||
 | 
									t := f.getTask()
 | 
				
			||||||
 | 
									t.NewConn = !t.NewConn
 | 
				
			||||||
 | 
									f.applyTask(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case "txt":
 | 
								case "txt":
 | 
				
			||||||
				if len(args) > 0 {
 | 
									if len(args) > 0 {
 | 
				
			||||||
					if size, err := strconv.Atoi(args[0]); err == nil {
 | 
										if size, err := strconv.Atoi(args[0]); err == nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue