Initial commit.
This commit is contained in:
		
							
								
								
									
										124
									
								
								cmd/demo/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								cmd/demo/main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,124 @@ | ||||
| // Creates an animation of several renditions of "Munching Square". | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"image" | ||||
| 	"image/color" | ||||
| 	"image/draw" | ||||
| 	"log" | ||||
| 	"math" | ||||
|  | ||||
| 	"smariot.com/animate" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	const ( | ||||
| 		gridSize     = 32 | ||||
| 		pixelSize    = 12 | ||||
| 		pixelRadius  = pixelSize / 2. | ||||
| 		subImageSize = gridSize * pixelSize | ||||
| 		frameRate    = 10 | ||||
| 		filename     = "demo.webp" | ||||
| 	) | ||||
|  | ||||
| 	// create a single animation large enough to contain four sub-animations. | ||||
| 	anim, err := animate.New(image.Rect(0, 0, subImageSize*2, subImageSize*2), frameRate, filename) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// create the sub images | ||||
| 	// (note that b and d are swapped because I think it looks better visually, but as for logical progression, the letter order makes more sense) | ||||
| 	a := anim.SubImage(image.Rect(0, 0, subImageSize, subImageSize)).(*image.NRGBA) | ||||
| 	b := anim.SubImage(image.Rect(0, 0, subImageSize, subImageSize).Add(image.Pt(subImageSize, subImageSize))).(*image.NRGBA) | ||||
| 	c := anim.SubImage(image.Rect(0, 0, subImageSize, subImageSize).Add(image.Pt(0, subImageSize))).(*image.NRGBA) | ||||
| 	d := anim.SubImage(image.Rect(0, 0, subImageSize, subImageSize).Add(image.Pt(subImageSize, 0))).(*image.NRGBA) | ||||
|  | ||||
| 	// I don't want to deal with the sub images all having weird rectangles that don't start at (0, 0), so copy the rectangle from sub-image a for | ||||
| 	// all of them. | ||||
| 	b.Rect, c.Rect, d.Rect = a.Rect, a.Rect, a.Rect | ||||
|  | ||||
| 	// a pixel sized circle mask, used for the PDP-1 version. | ||||
| 	// scaled by √2 so that the circles touch diagonally. | ||||
| 	// need to use floor in the inset because the result is negative, | ||||
| 	// and integers normally round toward zero. | ||||
| 	dot := image.NewAlpha(image.Rect(0, 0, pixelSize, pixelSize).Inset(int(math.Floor(pixelRadius - pixelRadius*math.Sqrt2)))) | ||||
|  | ||||
| 	for y := dot.Rect.Min.Y; y < dot.Rect.Max.Y; y++ { | ||||
| 		for x := dot.Rect.Min.X; x < dot.Rect.Max.X; x++ { | ||||
| 			dx := float64(x) + 0.5 - pixelRadius | ||||
| 			dy := float64(y) + 0.5 - pixelRadius | ||||
| 			a := uint8(max(0, min(1, pixelRadius*math.Sqrt2+0.5-math.Sqrt(dx*dx+dy*dy)))*255 + 0.5) | ||||
| 			dot.SetAlpha(x, y, color.Alpha{A: a}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pixel := image.Rect(0, 0, pixelSize, pixelSize) | ||||
| 	black := image.NewUniform(color.Black) | ||||
| 	white := image.NewUniform(color.White) | ||||
|  | ||||
| 	for t := range gridSize { | ||||
| 		// clear the frame. | ||||
| 		draw.Draw(anim, anim.Rect, black, image.Point{}, draw.Src) | ||||
|  | ||||
| 		// the classing PDP-1 version. | ||||
| 		for x := range gridSize { | ||||
| 			y := x ^ t | ||||
| 			draw.DrawMask(a, dot.Rect.Add(image.Pt(x*pixelSize, y*pixelSize)), white, image.Point{}, dot, dot.Rect.Min, draw.Over) | ||||
| 		} | ||||
|  | ||||
| 		// an alternate version | ||||
| 		for y := range gridSize { | ||||
| 			for x := range gridSize { | ||||
| 				if (x^y+t)%gridSize <= gridSize/2 { | ||||
| 					draw.Draw(b, pixel.Add(image.Pt(x*pixelSize, y*pixelSize)), white, image.Point{}, draw.Src) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// an alternate alternate version - with color! | ||||
| 		for y := range gridSize { | ||||
| 			for x := range gridSize { | ||||
| 				r := uint8((x ^ y + t) % gridSize * 255 / gridSize) | ||||
| 				g := uint8((x ^ y + t + gridSize/3) % gridSize * 255 / gridSize) | ||||
| 				b := uint8((x ^ y + t + gridSize*2/3) % gridSize * 255 / gridSize) | ||||
|  | ||||
| 				draw.Draw(c, pixel.Add(image.Pt(x*pixelSize, y*pixelSize)), image.NewUniform(color.NRGBA{r, g, b, 255}), image.Point{}, draw.Src) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for y := range subImageSize { | ||||
| 			for x := range subImageSize { | ||||
| 				// distance, as 0 in the center and 1 at the corners. | ||||
| 				dx := float64(x) + 0.5 - subImageSize/2. | ||||
| 				dy := float64(y) + 0.5 - subImageSize/2. | ||||
| 				dist := math.Sqrt(dx*dx+dy*dy) / (math.Sqrt2 * subImageSize / 2.) | ||||
|  | ||||
| 				// distort time using a sine wave, based on distance and angle. | ||||
| 				t := (t + int((math.Sin(math.Pi*2.*dist+math.Atan2(dy, dx))*.5+.5)*gridSize)) % gridSize | ||||
|  | ||||
| 				// re-quantize space, but also remember the original pixel position for drawing the pixel. | ||||
| 				px, py := x, y | ||||
| 				x, y := x/pixelSize, y/pixelSize | ||||
|  | ||||
| 				// for extra contrast with the RGB version, let's use CMY. | ||||
| 				cyan := uint8((x ^ y + t) % gridSize * 255 / gridSize) | ||||
| 				magenta := uint8((x ^ y + t + gridSize/3) % gridSize * 255 / gridSize) | ||||
| 				yellow := uint8((x ^ y + t + gridSize*2/3) % gridSize * 255 / gridSize) | ||||
|  | ||||
| 				d.Set(px, py, color.NRGBA{255 - cyan, 255 - magenta, 255 - yellow, 255}) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// emit the frame. | ||||
| 		if err := anim.EmitFrame(); err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// and finally finish the animation by closing it. | ||||
| 	if err := anim.Close(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user