Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Last active April 30, 2018 21:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/9d7ac00aaa57477d3080acc6f1965fb1 to your computer and use it in GitHub Desktop.
Save JoshCheek/9d7ac00aaa57477d3080acc6f1965fb1 to your computer and use it in GitHub Desktop.
require 'graphics'
class IDK < Graphics::Simulation
def initialize(functions, *rest)
@functions = functions
super *rest
end
def draw(n)
clear :black
sample(@functions, n, 20)
.each_cons(2) { |(x1, y1), (x2, y2)| line x1, y1, x2, y2, :white }
end
end
def sample(fns, upto, num_fn_samples)
fns = fns.dup
points = []
while 0 < upto && fns.any?
fn = fns.shift
size = [upto, num_fn_samples].min
upto -= num_fn_samples
size.times do |crnt|
points << fn[crnt.to_f/(num_fn_samples-1)]
end
end
points
end
def center(width:, height:, fns:, margin:, maintain_ratio:)
n_samples = 100
samples = fns.flat_map do |fn|
n_samples.times.map { |i| fn[i.to_f/(n_samples-1)] }
end
xs, ys = samples.transpose
minx, maxx = xs.minmax
miny, maxy = ys.minmax
scale_x = (width - 2*margin) / (maxx - minx)
scale_y = (height - 2*margin) / (maxy - miny)
scale_x = scale_y = [scale_x, scale_y].min if maintain_ratio
translate_x = width / 2
translate_y = height / 2
fns.map do |fn|
lambda do |t|
x, y = fn[t]
# require "pry"
# binding().pry if 0.5 <= t
[x * scale_x + translate_x, y * scale_y + translate_y]
end
end
end
include Math
width = 800
height = 600
functions = center(
width: width,
height: height,
margin: 20,
maintain_ratio: true,
fns: [
-> t { [cos(PI*t) , sin(PI*t) + 1] }, # top
-> t { [cos(PI*t+PI/2) - 1 , sin(PI*t+PI/2) ] }, # left
-> t { [cos(PI+PI*t) , sin(PI+PI*t) - 1] }, # bottom
-> t { [cos(PI*t+PI*3/2) + 1 , sin(PI*t+PI*3/2) ] }, # right
],
)
p sample(functions, 20*functions.length, 20).map { |x, y| [x.round, y.round] }
IDK.new(functions, width, height, 31).run
module Fourier
extend self
extend Math
include Math
def to(fx)
n = fx.length
n.times.map do |u|
real = imaginary = 0
fx.each_with_index do |(f_real, f_imaginary), k|
p = -2*PI*u*k/n
c, s = cos(p), sin(p)
real += c*f_real + s*f_imaginary
imaginary += c*f_imaginary - s*f_real
end
[real/n, imaginary/n]
end
end
def from(fu, m=fu.length)
n = fu.length
n.times.map do |k|
real = imaginary = 0
fu.take(m).each_with_index do |(f_real, f_imaginary), u|
p = 2*PI*u*k/n
c, s = cos(p), sin(p)
real += c*f_real + s*f_imaginary
imaginary += c*f_imaginary - s*f_real
end
[real, imaginary]
end
end
end
require 'graphics'
require_relative 'fourier'
class ShowFourier < Graphics::Simulation
include Math
def initialize(points, width, height, num_colours)
super width, height, num_colours
@frequencies = Fourier.to(points)
@progress = []
@x_off = w/2
@y_off = h/2
end
def draw(iteration)
clear :black
n = @frequencies.length
iteration = 1 + (iteration / 10)
# the machine to draw it all
two_pi = 2*PI
p_no_u = two_pi*iteration.pred/n
mag_and_angle = @frequencies.each_with_index.map do |(f_real, f_imag), u|
[f_real, f_imag, p_no_u*u]
end
sum_x = sum_y = 0
pts = []
mag_and_angle.each do |r, i, ø|
c, s = cos(ø), sin(ø)
sum_x += c*r + s*i
sum_y += c*i - s*r
mag = sqrt(r*r + i*i)
# omit spammy results
if 2 <= mag
center_circle sum_x, sum_y, mag, :white unless mag < 2
pts << sum_x << sum_y
end
end
last_x, last_y = pts.last(2)
draw_points pts, :green#, true
# crosshairs
center_line last_x, -h, last_x, 2*h, :red# , true
center_line -w, last_y, 2*w, last_y, :red# , true
# the drawing up to this point
p_no_u = two_pi*iteration.pred/n
real = imaginary = 0
@frequencies.each_with_index do |(f_real, f_imaginary), u|
p = p_no_u*u
c, s = cos(p), sin(p)
real += c*f_real + s*f_imaginary
imaginary += c*f_imaginary - s*f_real
end
@progress << real << imaginary
draw_points @progress, :magenta, true
end
def draw_points(points, colour, thick = false)
0.step by: 2, to: points.length-3 do |i|
center_line points[i+0], points[i+1], points[i+2], points[i+3], colour, thick
end
end
def center_circle(x, y, r, colour)
circle x+w/2, y+h/2, r, colour
end
def center_line(x1, y1, x2, y2, colour, thick=false)
x1 += @x_off
x2 += @x_off
y1 += @y_off
y2 += @y_off
line x1, y1, x2, y2, colour
if thick
line x1+1, y1, x2+1, y2, colour
line x1-1, y1, x2-1, y2, colour
line x1, y1+1, x2, y2+1, colour
line x1, y1-1, x2, y2-1, colour
end
end
end
w, h = 800, 600
# note: https://www.mobilefish.com/services/record_mouse_coordinates/record_mouse_coordinates.php
points = %w[193,47 166,125 140,204 123,193 99,189 74,196 58,213 49,237 52,261 65,279 86,292 113,295 135,282 152,258 201,95 212,127 218,150 213,168 201,185 192,200 203,214 219,205 233,191 242,170 244,149 242,131 233,111]
.map { |n|
# uhhhhh this is fkn dumb -.^ the drawing thing should either
# not mess w/ coords, or should get them correct
x, y = n.split(?,).map(&:to_i)
[x*2, h-y]
}
xmin, xmax = points.map(&:first).minmax
ymin, ymax = points.map(&:last).minmax
points = points.map do |x, y|
[ (xmax - xmin) / 2 + x - xmin - w/2,
(ymax - ymin) / 2 + y - ymin - h/2,
]
end
points = [[540, 440], [538, 463], [532, 485], [523, 507], [510, 526], [495, 543], [477, 557], [456, 568], [434, 576], [412, 580], [388, 580], [366, 576], [344, 568], [323, 557], [305, 543], [290, 526], [277, 507], [268, 485], [262, 463], [260, 440], [260, 440], [237, 438], [215, 432], [193, 423], [174, 410], [157, 395], [143, 377], [132, 356], [124, 334], [120, 312], [120, 288], [124, 266], [132, 244], [143, 223], [157, 205], [174, 190], [193, 177], [215, 168], [237, 162], [260, 160], [260, 160], [262, 137], [268, 115], [277, 93], [290, 74], [305, 57], [323, 43], [344, 32], [366, 24], [388, 20], [412, 20], [434, 24], [456, 32], [477, 43], [495, 57], [510, 74], [523, 93], [532, 115], [538, 137], [540, 160], [540, 160], [563, 162], [585, 168], [607, 177], [626, 190], [643, 205], [657, 223], [668, 244], [676, 266], [680, 288], [680, 312], [676, 334], [668, 356], [657, 377], [643, 395], [626, 410], [607, 423], [585, 432], [563, 438], [540, 440]]
.map { |x, y| [x-w/2, y-h/2] }
points.push points[0]
# Draw it
ShowFourier.new(points, w, h, 31).run
require 'graphics'
require_relative 'fourier'
class DrawFourier < Graphics::Simulation
def initialize(*)
super
@points = []
@state = [:get_points]
end
def draw(iteration)
clear :black
if @state[0] == :get_points
text "Draw with the mouse, click when done", 50, 50, :white
x, y, clicked, * = mouse
if clicked
@frequencies = Fourier.to(@points)
@state = [:transform, iteration]
at_exit { p @points }
else
point = [x, y]
@points << point unless point == @points.last
end
draw_points @points
else
n = @frequencies.length
m = n - (iteration - @state[1]) + 1
text "#{m}/#{n}", 50, 50, :white
draw_points Fourier.from(@frequencies, m)
sleep 1 unless m == n
end
end
private def draw_points(points)
points.each_cons(2) { |p1, p2| line *p1, *p2, :white }
end
end
DrawFourier.new(800, 600, 31).run
require 'graphics'
require_relative 'fourier'
class ShowFourier < Graphics::Simulation
def initialize(points, width, height, num_colours)
super width, height, num_colours
@frequencies = Fourier.to(points)
@m = @frequencies.length
@mode = :points
add_keydown_handler("j") { self.m = m - 1 }
add_keydown_handler("k") { self.m = m + 1 }
add_keydown_handler("p") { @mode = :points }
add_keydown_handler("f") { @mode = :frequencies }
end
def draw(iteration)
n = @frequencies.length
clear :black
text "#{m}/#{n}", 50, h-50, :white
frequencies = @frequencies.take(m)
case mode
when :points
points = Fourier.from frequencies
when :frequencies
points = frequencies
else raise "Wat? #{mode.inspect}"
end
points.each_cons(2) { |p1, p2| line *p1, *p2, :white }
end
private
attr_reader :m, :mode
def m=(m)
m = 2 if m < 2
m = @frequencies.length if @frequencies.length < m
@m = m
end
end
w = 800
h = 600
r = 250
n = 8
# circle
circle = n.times.map do |i|
[r*Math.cos(i*2*Math::PI/n)+w/2, r*Math.sin(i*2*Math::PI/n)+h/2]
end
# square
square =
(n/4).times.map { |i| [ w/2+r , h/2+r - r*8*i/n ] } +
(n/4).times.map { |i| [ w/2+r - r*8*i/n , h/2-r ] } +
(n/4).times.map { |i| [ w/2-r , h/2-r + r*8*i/n ] } +
(n/4).times.map { |i| [ w/2-r + r*8*i/n , h/2+r ] }
# note: https://www.mobilefish.com/services/record_mouse_coordinates/record_mouse_coordinates.php
note = %w[193,47 140,204 123,193 99,189 74,196 58,213 49,237 52,261 65,279 86,292 113,295 135,282 152,258 201,95 212,127 218,150 213,168 201,185 192,200 203,214 219,205 233,191 242,170 244,149 242,131 233,111]
.map { |n|
x, y = n.split(?,).map(&:to_i)
[x*2, h-y]
}
# Normalize the points
points = note
points = points.dup
points.push points[0]
xmin, xmax = points.map(&:first).minmax
ymin, ymax = points.map(&:last).minmax
points = points.map do |x, y|
[x - xmin + 10, y - ymin + 10]
end
# Draw it
ShowFourier.new(points, w, h, 31).run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment