# One-point perspective box drawing demo
# kevin conner
# connerk@gmail.com
# version 1, 23 February 2008
# this code is free, do what you like with it!

$width, $height = 700, 400

def rand_height
	return 1.1 + rand(3) * 0.15
end

$boxes = [
	[-200, -150, -150, -50, rand_height, 1.0],
	[-100, 50, -150, -50, rand_height, 1.0],
	[100, 200, -150, -100, rand_height, 1.0],
	[150, 250, -50, 0, rand_height, 1.0],
	[150, 250, 50, 150, rand_height, 1.0],
	[50, 100, 0, 150, rand_height, 1.0],
	[-50, 0, 0, 100, rand_height, 1.0],
	[-100, 0, 150, 200, rand_height, 1.0],
	[-200, -100, 0, 100, rand_height, 1.0],
	[80, 120, -70, -30, rand_height, 1.0]
]

class Opp
	@offset_x, @offset_y = 0, 0
	@following = false
	
	def self.update_scene
		$app.clear do
			stroke_color = $app.red(0.9)
			fill_color = $app.red(0.2)
			
			if @following
				unused, x, y = $app.mouse
				@offset_x, @offset_y = x - @center_x, y - @center_y
			end
			
			$app.background $app.black
			@center_x, @center_y = $app.width / 2, $app.height / 2
			
			$boxes.each do |box|
				draw_opp_box box, stroke_color, fill_color
			end
		end
	end
	
	def self.set_following bool
		@following = bool
	end
	
	# here "front" and "back" push the rect into and out of the window.
	# 1.0 means your x and y units are pixels on the surface.
	# greater than that brings the box closer.  less pushes it back.  0.0 => infinity.
	# the front will be filled but the rest is wireframe only.
	def self.draw_opp_box box, stroke_color, fill_color
		left, right, top, bottom, front, back = box
		
		# project the near and far faces
		near_left, near_right, near_top, near_bottom = [left, right].collect { |x| @center_x + front * (x - @offset_x) } +
			[top, bottom].collect { |y| @center_y + front * (y - @offset_y) }
		far_left, far_right, far_top, far_bottom = [left, right].collect { |x| @center_x + back * (x - @offset_x) } +
			[top, bottom].collect { |y| @center_y + back * (y - @offset_y) }
		
		# determine which sides of the box are visible
		draw_left = @center_x < near_left
		draw_right = near_right < @center_x
		draw_top = @center_y < near_top
		draw_bottom = near_bottom < @center_y
		
		$app.stroke stroke_color
		$app.fill fill_color
		
		# draw lines for the back edges
		$app.line far_left, far_top, far_right, far_top if draw_top
		$app.line far_left, far_bottom, far_right, far_bottom if draw_bottom
		$app.line far_left, far_top, far_left, far_bottom if draw_left
		$app.line far_right, far_top, far_right, far_bottom if draw_right
		
		# draw lines to connect the front and back
		$app.line near_left, near_top, far_left, far_top if draw_left or draw_top
		$app.line near_right, near_top, far_right, far_top if draw_right or draw_top
		$app.line near_left, near_bottom, far_left, far_bottom if draw_left or draw_bottom
		$app.line near_right, near_bottom, far_right, far_bottom if draw_right or draw_bottom
		
		# draw the front, filled
		$app.rect near_left, near_top, near_right - near_left, near_bottom - near_top
	end
end

Shoes.app :width => $width, :height => $height do
	$app = self
	
	click do |button, x, y|
		Opp.set_following true if button == 1
	end
	release do |button, x, y|
		Opp.set_following false if button == 1
	end
	
	animate(60) { Opp.update_scene }
end
