Sketches below show an unadulterated grid, a partial grid with fish eye 'filter' and some text with fish eye 'filter'. Unfortunately the mouse was doing two things (adjusting view and selecting image for 'the gimp') so actual captured image was a bit hit and miss. View the vanilla processing version at
openprocessing.
require 'beauty'
class Fish_Eye < Processing::App
load_library :control_panel
attr_reader :b_pass, :fish_eye_filter, :aperture, :options, :some_text, :img
def setup()
size(300, 300)
setup_control
text_font(load_font("Ubuntu-Regular-48.vlw"))
@some_text = "Whatever it is,\n it was inside the Sphere.\n Now it's out, free to act. "
@img = load_image("pattern.png")
@fish_eye_filter = false
@b_pass = BeautyPass.new
smooth
end
def draw()
background(0)
case options
when 'grid'
draw_grid(20)
when 'text'
draw_text
when 'image'
display_image
when 'noise'
draw_noise
else
draw_grid(20)
end
if fish_eye_filter
fisheye
end
end
def setup_control
control_panel do |c|
c.title = "Control Panel"
c.button :t_fisheye
c.menu(:options, ['grid', 'text', 'image', 'noise' ], 'grid')
c.slider :aperture, 0.5..Math::PI, 3.142
end
end
def t_fisheye
@fish_eye_filter = !fish_eye_filter
end
def draw_grid(row)
stroke(255)
stroke_weight(2)
for i in 0...row
line(0, i * height / row, width, i * height / row)
line(i * height / row, 0, i * height / row, height)
end
end
def draw_text
fill(0, 255, 0)
text(some_text,sin(frame_count * 0.1)*(width - text_width(some_text)), 180+Math.sin(Math::PI/2 + frame_count*0.01)*(height-170))
end
def display_image
image(img, 0, 0, width, height)
end
def draw_noise
push_style
color_mode(RGB,1.0)
load_pixels
(0...width).each do |i|
(0...height).each do |j|
pixels[i+j*width] = color(noise((frame_count+i)*0.1,j*0.1),noise((frame_count+i)*0.1,j*0.1,0.3),noise((frame_count+i)*0.05,j*0.05,0.6))
end
end
update_pixels
pop_style
end
def fisheye
half_aperture = aperture / 2
cam_x = (2.0 * mouse_x) / (width - 1)
cam_y = (2.0 * mouse_y) / (width - 1)
load_pixels
(0...width).each do |i|
x = (2.0 * i) / (width - 1)
(0...height).each do |j|
y = (2.0 * j) / (height - 1)
r = dist(cam_x, cam_y, x, y)
if (r.abs > 1)
next
end
phi = Math.atan2(y - cam_y, x - cam_x)
theta = half_aperture * r
dx = map(Math.sin(theta) * Math.cos(phi), -1.0, 1, 0, width)
dy = map(Math.sin(theta) * Math.sin(phi), -1.0, 1, 0, height)
NOTE
fx = dx.round
fy = dy.round
@b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
fx = dx.round
fy = dy.floor
@b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
fx = dx.round
fy = dy.ceil
@b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
fx = dx.floor
fy = dy.floor
@b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
fx = dx.floor
fy = dy.round
@b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
fx = dx.floor
fy = dy.ceil
@b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
fx = dx.ceil
fy = dy.ceil
@b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
fx = dx.ceil
fy = dy.round
@b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
fx = dx.ceil
fy = dy.floor
@b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
end
end
@b_pass.render
@b_pass.reset
end
end
class BeautyPass
include Processing::Proxy
attr_reader :counts, :rSums, :gSums, :bSums
def initialize()
reset()
end
def reset()
@counts = Array.new(width * height, 0)
@rSums = Array.new(width * height, 0)
@gSums = Array.new(width * height, 0)
@bSums = Array.new(width * height, 0)
end
def add_color(i, c)
if (i>0 && i< width * height)
@rSums[i] += c >> 16 & 0xFF
@gSums[i] += c >> 8 & 0xFF
@bSums[i] += c & 0xFF
@counts[i] += 1
end
end
def render()
load_pixels()
(0...width * height).each do |i|
if (counts[i]>0)
pixels[i] = color(rSums[i] / counts[i], gSums[i] / counts[i], bSums[i] / counts[i])
else
pixels[i] = color(0)
end
end
update_pixels()
end
end