Skip to content

Commit fd19af9

Browse files
committed
pure PiCrate lenny explorer
1 parent 8ec272f commit fd19af9

File tree

7 files changed

+175
-0
lines changed

7 files changed

+175
-0
lines changed
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# frozen_string_literal: true
2+
require 'picrate'
3+
# After 'The Explorer' by Leander Herzog, and a toxiclibs version by Karsten
4+
# Schmidt. This is a pure PiCrate version by Martin Prout
5+
class LennyExplorer < Processing::App
6+
load_library :lenny
7+
attr_reader :path
8+
9+
def settings
10+
size(600, 600)
11+
end
12+
13+
def setup
14+
sketch_title 'Lenny Explorer'
15+
no_fill
16+
# @path = Path.new(
17+
# Boundary.new(
18+
# Rect.new(
19+
# Vec2D.new(10, 10),
20+
# Vec2D.new(width - 20, height - 20)
21+
# )
22+
# ),
23+
# 10,
24+
# 0.03,
25+
# 3_000
26+
# )
27+
@path = Path.new(
28+
Boundary.new(Circle.new(Vec2D.new(width / 2, height / 2), 250)),
29+
10,
30+
0.03,
31+
3_000
32+
)
33+
end
34+
35+
def draw
36+
background(255)
37+
50.times { path.grow }
38+
path.render(g)
39+
end
40+
41+
def mouse_pressed
42+
save_frame data_path('lenny.png')
43+
end
44+
end
45+
46+
LennyExplorer.new
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
require_relative 'lib/rect'
2+
require_relative 'lib/circle'
3+
require_relative 'lib/boundary'
4+
require_relative 'lib/path'
5+
require_relative 'lib/line2d'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Duck typing Boundary
2+
class Boundary
3+
attr_reader :bounds
4+
def initialize(bounds)
5+
@bounds = bounds
6+
end
7+
8+
def contains?(vec)
9+
bounds.contains?(vec)
10+
end
11+
12+
def centroid
13+
bounds.centroid
14+
end
15+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# For use with Boundary
2+
class Circle
3+
attr_reader :centroid, :radius
4+
def initialize(center, radius)
5+
@centroid = center
6+
@radius = radius
7+
end
8+
9+
def contains?(vec)
10+
centroid.dist(vec) < radius
11+
end
12+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Use to detect whether instances of line intersect
2+
class Line2D
3+
attr_reader :a, :b
4+
def initialize(a, b)
5+
@a = a
6+
@b = b
7+
end
8+
9+
def intersecting?(line)
10+
denom = (line.b.y - line.a.y) * (b.x - a.x) - (line.b.x - line.a.x) * (b.y - a.y)
11+
na = (line.b.x - line.a.x) * (a.y - line.a.y) - (line.b.y - line.a.y) * (a.x - line.a.x)
12+
nb = (b.x - a.x) * (a.y - line.a.y) - (b.y - a.y) * (a.x - line.a.x)
13+
return false if denom.zero?
14+
15+
ua = na / denom
16+
ub = nb / denom
17+
(ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0)
18+
end
19+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Stores and manipulates the path
2+
class Path
3+
attr_reader :path, :last, :bounds, :cut, :theta, :delta, :speed, :searches
4+
5+
def initialize(bounds, speed, delta, history)
6+
@bounds = bounds
7+
@speed = speed
8+
@delta = delta
9+
@theta = 0
10+
@path = (0..history).map { bounds.centroid.copy }
11+
@searches = 0
12+
@last = Vec2D.new(path.first)
13+
end
14+
15+
def grow
16+
@delta = rand(-0.2..0.2) if rand < 0.1
17+
if !intersecting?
18+
move
19+
else
20+
search
21+
end
22+
end
23+
24+
def move
25+
@last = path.first
26+
path.pop
27+
@theta += delta
28+
path.unshift last + Vec2D.new(speed * Math.cos(theta), speed * Math.sin(theta))
29+
@searches = 0
30+
end
31+
32+
def search
33+
@theta += delta
34+
path[0] = last + Vec2D.new(speed * Math.cos(theta), speed * Math.sin(theta))
35+
@searches += 1
36+
end
37+
38+
def render(gfx)
39+
gfx.begin_shape
40+
path.map { |vec| gfx.curve_vertex(vec.x, vec.y) }
41+
gfx.end_shape
42+
end
43+
44+
def intersecting?
45+
return true unless bounds.contains?(path.first)
46+
47+
if searches < 100
48+
a = Line2D.new(path[0], path[1])
49+
(3...path.length).each do |i|
50+
b = Line2D.new(path[i], path[i - 1])
51+
return true if a.intersecting?(b)
52+
end
53+
end
54+
false
55+
end
56+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# For use with Boundary
2+
class Rect
3+
attr_reader :vec1, :vec2
4+
def initialize(vec1, vec2)
5+
@vec1 = vec1
6+
@vec2 = vec2
7+
end
8+
9+
def centroid
10+
(vec1 + vec2) / 2
11+
end
12+
13+
def contains?(vec)
14+
xminmax = [vec1.x, vec2.x]
15+
yminmax = [vec1.y, vec2.y]
16+
return false if vec.x < xminmax[0]
17+
return false if vec.x > xminmax[1]
18+
return false if vec.y < yminmax[0]
19+
20+
vec.y < yminmax[1]
21+
end
22+
end

0 commit comments

Comments
 (0)