soby 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/soby/cam.rb +103 -0
- data/lib/soby/presentation.rb +342 -0
- data/lib/soby/slide.rb +166 -0
- data/lib/soby/transforms.rb +53 -0
- data/lib/soby.rb +403 -0
- metadata +76 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 269d82eee352fce114a0120f71fccaab7f25d02a
|
4
|
+
data.tar.gz: 06534754ac6193202a319cabaed39b3b4d9ca93d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bd29c3af78d994f2d0ed175a37cd272a0f8af53dac5579724b24b958e50f2df3a2de374210ea14a0b0307bc7ec4da919980d54adc93c33600c2235d1ae23ed74
|
7
|
+
data.tar.gz: d2eb41a775fe04d13157045f9550f23069002b6b91eb2467f4334648935aefce550fb18a6112022c44e768187f32b81b418838e0aad0e7fc6ed745f786434974
|
data/lib/soby/cam.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
|
2
|
+
class Cam
|
3
|
+
|
4
|
+
include_package 'processing.core'
|
5
|
+
|
6
|
+
attr_accessor :mat, :angle, :translation, :scale, :pre_translation, :post_translation
|
7
|
+
attr_accessor :quat
|
8
|
+
|
9
|
+
@@interp = Processing::PMatrix3D.new
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@scale = 1
|
13
|
+
@angle = 0;
|
14
|
+
@translation = PVector.new
|
15
|
+
@mat = PMatrix3D.new
|
16
|
+
@pre_translation = PVector.new
|
17
|
+
@post_translation = PVector.new
|
18
|
+
|
19
|
+
@is_computed = false
|
20
|
+
end
|
21
|
+
|
22
|
+
## TODO: check modif at
|
23
|
+
def compute_mat
|
24
|
+
|
25
|
+
puts "Warning Camera already computed " if @is_computed
|
26
|
+
return if @is_computed
|
27
|
+
|
28
|
+
@is_computed = true
|
29
|
+
|
30
|
+
@mat.reset
|
31
|
+
|
32
|
+
@mat.translate(@post_translation.x, @post_translation.y)
|
33
|
+
@mat.scale(@scale)
|
34
|
+
|
35
|
+
@mat.translate(@translation.x, @translation.y)
|
36
|
+
@mat.rotate(@angle)
|
37
|
+
|
38
|
+
@mat.translate(@pre_translation.x, @pre_translation.y)
|
39
|
+
|
40
|
+
@mat
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
# Value = 0 : self, value = 1 : cam2
|
46
|
+
def lerp(cam2, value)
|
47
|
+
|
48
|
+
complement = 1 - value
|
49
|
+
|
50
|
+
# puts "Value #{value} "
|
51
|
+
# puts "complement #{complement} "
|
52
|
+
|
53
|
+
interp = @@interp
|
54
|
+
interp.reset
|
55
|
+
|
56
|
+
interp.m00 = @mat.m00 * complement + cam2.mat.m00 * value
|
57
|
+
interp.m01 = @mat.m01 * complement + cam2.mat.m01 * value
|
58
|
+
interp.m02 = @mat.m02 * complement + cam2.mat.m02 * value
|
59
|
+
interp.m03 = @mat.m03 * complement + cam2.mat.m03 * value
|
60
|
+
|
61
|
+
interp.m10 = @mat.m10 * complement + cam2.mat.m10 * value
|
62
|
+
interp.m11 = @mat.m11 * complement + cam2.mat.m11 * value
|
63
|
+
interp.m12 = @mat.m12 * complement + cam2.mat.m12 * value
|
64
|
+
interp.m13 = @mat.m13 * complement + cam2.mat.m13 * value
|
65
|
+
|
66
|
+
interp.m20 = @mat.m20 * complement + cam2.mat.m20 * value
|
67
|
+
interp.m21 = @mat.m21 * complement + cam2.mat.m21 * value
|
68
|
+
interp.m22 = @mat.m22 * complement + cam2.mat.m22 * value
|
69
|
+
interp.m23 = @mat.m23 * complement + cam2.mat.m23 * value
|
70
|
+
|
71
|
+
interp.m30 = @mat.m30 * complement + cam2.mat.m30 * value
|
72
|
+
interp.m31 = @mat.m31 * complement + cam2.mat.m31 * value
|
73
|
+
interp.m32 = @mat.m32 * complement + cam2.mat.m32 * value
|
74
|
+
interp.m33 = @mat.m33 * complement + cam2.mat.m33 * value
|
75
|
+
|
76
|
+
interp
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def create_mat4x4(mat)
|
83
|
+
Matrix4x4.new(mat.m00, mat.m01, mat.m02, mat.m03,\
|
84
|
+
mat.m10, mat.m11, mat.m12, mat.m13,\
|
85
|
+
mat.m20, mat.m21, mat.m22, mat.m23,\
|
86
|
+
mat.m30, mat.m31, mat.m32, mat.m33)
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_pmatrix(mat)
|
90
|
+
PMatrix3D.new(mat[0][0], mat[0][1], mat[0][2], mat[0][3],\
|
91
|
+
mat[1][0], mat[1][1], mat[1][2], mat[1][3],\
|
92
|
+
mat[2][0], mat[2][1], mat[2][2], mat[2][3],\
|
93
|
+
mat[3][0], mat[3][1], mat[3][2], mat[3][3])
|
94
|
+
end
|
95
|
+
|
96
|
+
def set_pmatrix(mat, pmatrix)
|
97
|
+
pmatrix.set(mat[0][0], mat[0][1], mat[0][2], mat[0][3],\
|
98
|
+
mat[1][0], mat[1][1], mat[1][2], mat[1][3],\
|
99
|
+
mat[2][0], mat[2][1], mat[2][2], mat[2][3],\
|
100
|
+
mat[3][0], mat[3][1], mat[3][2], mat[3][3])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,342 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
class Presentation
|
5
|
+
|
6
|
+
include_package 'processing.core'
|
7
|
+
|
8
|
+
include Soby
|
9
|
+
|
10
|
+
attr_accessor :geometry, :slides, :pshape, :svg, :transform
|
11
|
+
attr_accessor :width, :height, :matrix, :videos
|
12
|
+
attr_reader :nb_slides, :debug
|
13
|
+
|
14
|
+
attr_accessor :graphics
|
15
|
+
|
16
|
+
def initialize (app, url)
|
17
|
+
@app = app
|
18
|
+
xml = app.loadXML(url)
|
19
|
+
@pshape = PShapeSVG.new(xml)
|
20
|
+
@svg = Nokogiri::XML(open(url)).children[1];
|
21
|
+
|
22
|
+
@graphics = @app.g
|
23
|
+
build_internal
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def width ; @pshape.getWidth; end
|
28
|
+
def height ; @pshape.getHeight; end
|
29
|
+
|
30
|
+
|
31
|
+
def build_internal
|
32
|
+
|
33
|
+
# Create the frames..
|
34
|
+
@slides = {}
|
35
|
+
@nb_slides = 0
|
36
|
+
@playing_videos = []
|
37
|
+
|
38
|
+
puts "Svg null, look for the error... !" if @svg == nil
|
39
|
+
return if @svg == nil
|
40
|
+
|
41
|
+
load_frames
|
42
|
+
load_videos
|
43
|
+
load_animations
|
44
|
+
end
|
45
|
+
|
46
|
+
def draw
|
47
|
+
@graphics.shape pshape, 0, 0
|
48
|
+
# display_videos
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def load_frames
|
53
|
+
|
54
|
+
puts "Loading the frames..."
|
55
|
+
@svg.children.each do |child|
|
56
|
+
|
57
|
+
next unless child.name =~ /frame/
|
58
|
+
|
59
|
+
## ignore if it references nothing.
|
60
|
+
attr = child.attributes
|
61
|
+
next if attr["refid"] == nil
|
62
|
+
|
63
|
+
slide = Slide.new(child)
|
64
|
+
|
65
|
+
# get the element associated with each slide...
|
66
|
+
@svg.search("[id=" + slide.refid + "]").each do |elem|
|
67
|
+
|
68
|
+
case elem.name
|
69
|
+
when "rect", "image"
|
70
|
+
add_rect_or_image_frame(elem, slide)
|
71
|
+
when "g"
|
72
|
+
add_group(elem, slide)
|
73
|
+
else
|
74
|
+
puts "Slide type not supported ! " + elem.name
|
75
|
+
end # end case
|
76
|
+
end # search
|
77
|
+
end # frame
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_slide(new_slide)
|
81
|
+
|
82
|
+
puts "Add slide, id " << new_slide.title << " numero " << new_slide.sequence
|
83
|
+
@slides[new_slide.sequence] = new_slide
|
84
|
+
@slides[new_slide.title] = new_slide
|
85
|
+
@nb_slides = @nb_slides+1
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def add_rect_or_image_frame(element, slide)
|
90
|
+
id = slide.title
|
91
|
+
refid = slide.refid
|
92
|
+
|
93
|
+
add_slide(slide)
|
94
|
+
|
95
|
+
# set the geometry & transformation
|
96
|
+
transform = get_global_transform element
|
97
|
+
@slides[id].set_geometry(element, transform[0])
|
98
|
+
pshape_element = @pshape.getChild(refid)
|
99
|
+
|
100
|
+
# hide the element
|
101
|
+
if pshape_element == nil
|
102
|
+
puts "Error: rect or Image ID: #{refid} not found. CHECK THE PROCESSING VERSION."
|
103
|
+
else
|
104
|
+
pshape_element.setVisible(!@slides[id].hide) if element.name == "rect"
|
105
|
+
end
|
106
|
+
|
107
|
+
puts "Slide #{id} created from a rect : #{refid}" if element.name == "rect"
|
108
|
+
puts "Slide #{id} created from an image: #{refid}" if element.name == "image"
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def add_group(group, slide)
|
113
|
+
|
114
|
+
id = slide.title
|
115
|
+
refid = slide.refid
|
116
|
+
add_slide(slide)
|
117
|
+
|
118
|
+
# TODO: Find the bounding box of any group..
|
119
|
+
# TODO: Hide the whole group ?
|
120
|
+
surf_max = 0
|
121
|
+
biggest_rect = nil
|
122
|
+
|
123
|
+
## biggest_rect surface..
|
124
|
+
group.css("rect").each do |rect|
|
125
|
+
# (group.css("rect") + group.css("image")).each do |rect|
|
126
|
+
|
127
|
+
if biggest_rect == nil
|
128
|
+
biggest_rect = rect
|
129
|
+
end
|
130
|
+
|
131
|
+
surf_rect = rect.attributes["width"].value.to_i \
|
132
|
+
* rect.attributes["height"].value.to_i
|
133
|
+
|
134
|
+
if(surf_rect > surf_max)
|
135
|
+
biggest_rect = rect
|
136
|
+
surf_max = surf_rect
|
137
|
+
end
|
138
|
+
end # rect.each
|
139
|
+
|
140
|
+
rect_id = biggest_rect.attributes["id"].value
|
141
|
+
transform = get_global_transform biggest_rect
|
142
|
+
@slides[id].set_geometry(biggest_rect, transform[0])
|
143
|
+
|
144
|
+
# The description (code to excute) might be in the group and not in the rect.
|
145
|
+
## TODO: check if it should really be the first ?
|
146
|
+
desc = group.css("desc").first
|
147
|
+
title = group.css("title").first
|
148
|
+
|
149
|
+
if desc != nil
|
150
|
+
if title == nil || (title.text.match(/animation/) == nil and title.text.match(/video/) == nil)
|
151
|
+
puts "Group Description read #{desc.text}"
|
152
|
+
|
153
|
+
@slides[id].description = desc.text
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
e = @pshape.getChild(rect_id)
|
158
|
+
# hide the rect
|
159
|
+
if e == nil
|
160
|
+
puts "Error: rect ID: #{rect_id} not found "
|
161
|
+
else
|
162
|
+
@pshape.getChild(rect_id).setVisible(!@slides[id].hide)
|
163
|
+
end
|
164
|
+
|
165
|
+
puts "Slide #{id} created from a group, and the rectangle: #{rect_id}"
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
|
171
|
+
|
172
|
+
def load_videos
|
173
|
+
# Load the videos...
|
174
|
+
puts "Loading the videos..."
|
175
|
+
|
176
|
+
@svg.css("rect").each do |rect|
|
177
|
+
|
178
|
+
return if rect.attributes["id"] == nil
|
179
|
+
id = rect.attributes["id"].value #get the id
|
180
|
+
|
181
|
+
title = rect.css("title")
|
182
|
+
next if title == nil
|
183
|
+
|
184
|
+
is_video = title.text.match(/video/) != nil
|
185
|
+
next unless is_video
|
186
|
+
|
187
|
+
t = rect.css("desc").text.split("\n")
|
188
|
+
slide_id = t[0]
|
189
|
+
path = t[1]
|
190
|
+
|
191
|
+
puts ("Loading the video : " + path)
|
192
|
+
|
193
|
+
# Get the transformation
|
194
|
+
tr = get_global_transform rect
|
195
|
+
|
196
|
+
video = MyVideo.new(path, @slides[slide_id], tr[0], tr[1], tr[2])
|
197
|
+
|
198
|
+
if @slides[slide_id] == nil
|
199
|
+
puts "Error -> The video #{id} is linked to the slide #{slide_id} which is not found !"
|
200
|
+
else
|
201
|
+
|
202
|
+
@slides[slide_id].add_video(video)
|
203
|
+
puts "Video #{id} loaded with #{slide_id}"
|
204
|
+
end
|
205
|
+
|
206
|
+
# Hide the rect, and ... TODO replace it with a special object
|
207
|
+
# @pshape.getChild(id).setVisible(false)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
def load_animations
|
214
|
+
|
215
|
+
puts "Loading the animations..."
|
216
|
+
|
217
|
+
@svg.css("*").each do |elem|
|
218
|
+
|
219
|
+
valid = false
|
220
|
+
elem.children.each do |child|
|
221
|
+
valid = true if child.name == "title" && child.text.match(/animation/)
|
222
|
+
end
|
223
|
+
|
224
|
+
next unless valid
|
225
|
+
|
226
|
+
id = elem.attributes["id"].value
|
227
|
+
|
228
|
+
# Get the animation information
|
229
|
+
t = elem.css("desc").text.split("\n")
|
230
|
+
|
231
|
+
# 1. which slide
|
232
|
+
# 2. when
|
233
|
+
slide_id = t[0]
|
234
|
+
anim_id = t[1].to_i
|
235
|
+
|
236
|
+
animation = OpenStruct.new
|
237
|
+
animation.pshape_elem = @pshape.getChild(id)
|
238
|
+
|
239
|
+
puts "Animation found on slide #{slide_id} with element #{animation.pshape_elem}"
|
240
|
+
|
241
|
+
if @slides[slide_id] == nil
|
242
|
+
puts "Error -> The animation #{id} is linked to the slide #{slide_id} which is not found !"
|
243
|
+
else
|
244
|
+
# add the animation
|
245
|
+
@slides[slide_id].add_animation(anim_id, animation)
|
246
|
+
|
247
|
+
# hide the group !
|
248
|
+
animation.pshape_elem.setVisible(false) unless animation.pshape_elem == nil
|
249
|
+
puts "Animation #{id} loaded with #{slide_id}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
def display_videos
|
256
|
+
if not @app.is_moving
|
257
|
+
|
258
|
+
slide_no = @app.current_slide_no
|
259
|
+
|
260
|
+
# # Display the videos
|
261
|
+
if slide_no > 0 and @slides[slide_no].has_videos?
|
262
|
+
|
263
|
+
|
264
|
+
# draw the object
|
265
|
+
@graphics.imageMode(Processing::App::CORNER)
|
266
|
+
|
267
|
+
@slides[slide_no].videos.each do |my_video|
|
268
|
+
@graphics.push_matrix
|
269
|
+
@graphics.modelview.apply(my_video.matrix)
|
270
|
+
|
271
|
+
# when the video is loaded it is saved... so that the memory can
|
272
|
+
# hopefully be freed
|
273
|
+
@playing_videos << my_video if my_video.play
|
274
|
+
|
275
|
+
# my_video.video.read if my_video.video.available?
|
276
|
+
# my_video.video.read
|
277
|
+
@graphics.image(my_video.video, 0, 0, my_video.width, my_video.height)
|
278
|
+
|
279
|
+
|
280
|
+
@graphics.pop_matrix
|
281
|
+
end # videos.each
|
282
|
+
|
283
|
+
else # has_videos
|
284
|
+
|
285
|
+
## no video, free some memory
|
286
|
+
@playing_videos.each do |my_video|
|
287
|
+
puts "Saving memory. "
|
288
|
+
my_video.video.stop
|
289
|
+
my_video.video = nil
|
290
|
+
System.gc
|
291
|
+
end
|
292
|
+
@playing_videos = []
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
|
299
|
+
class MyVideo
|
300
|
+
|
301
|
+
include_package 'processing.video'
|
302
|
+
include_package 'processing.video.Movie'
|
303
|
+
include_package 'org.gestreamer.elements'
|
304
|
+
|
305
|
+
attr_reader :matrix, :width, :height, :slide
|
306
|
+
attr_accessor :video
|
307
|
+
|
308
|
+
def initialize(path, slide, matrix, width, height)
|
309
|
+
@path = path
|
310
|
+
@width = width
|
311
|
+
@height = height
|
312
|
+
@matrix = matrix
|
313
|
+
@slide = slide
|
314
|
+
end
|
315
|
+
|
316
|
+
def play
|
317
|
+
if @video == nil
|
318
|
+
|
319
|
+
absolute_path = $app.sketchPath "" << @path
|
320
|
+
|
321
|
+
puts ("loading the video : " + absolute_path)
|
322
|
+
vid = Movie.new($app, absolute_path)
|
323
|
+
|
324
|
+
# vid = Movie.new($app, @path)
|
325
|
+
puts "Loaded "
|
326
|
+
puts vid, vid.width, vid.height
|
327
|
+
vid.play
|
328
|
+
@video = vid
|
329
|
+
true
|
330
|
+
else
|
331
|
+
false
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
|
337
|
+
|
338
|
+
def to_s
|
339
|
+
"Slides #{@slides.size}"
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
data/lib/soby/slide.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
class Slide
|
2
|
+
|
3
|
+
attr_accessor :next, :previous, :refid, :sequence, :hide, :matrix, :description
|
4
|
+
attr_reader :x, :y, :width, :height, :transform, :title ,:id
|
5
|
+
|
6
|
+
# transition states
|
7
|
+
attr_reader :transition_duration_ms, :timeout_ms, :timeout_enable, :transition_profile
|
8
|
+
attr_reader :videos, :animations
|
9
|
+
|
10
|
+
|
11
|
+
def initialize (element)
|
12
|
+
|
13
|
+
attr = element.attributes
|
14
|
+
|
15
|
+
## Zoom & Path -> not used...
|
16
|
+
# @transition_path_hide = attr["transition-path-hide"].value
|
17
|
+
# @transition_zoom_percent = attr["transition-zoom-percent"].value
|
18
|
+
|
19
|
+
#Not always set ! We use the title instead
|
20
|
+
# @id = attr["id"].value
|
21
|
+
|
22
|
+
@transition_profile = attr["transition-profile"].value
|
23
|
+
@transition_duration_ms = attr["transition-duration-ms"].value.to_f
|
24
|
+
@timeout_ms = attr["timeout-ms"].value.to_f
|
25
|
+
@timeout_enable = attr["timeout-enable"].value == "true"
|
26
|
+
# @show_in_frame_list = attr["show-in-frame-list"].value
|
27
|
+
|
28
|
+
@clip = attr["clip"].value
|
29
|
+
@hide = attr["hide"].value == "true"
|
30
|
+
@sequence = attr["sequence"].value.to_i
|
31
|
+
@title = attr["title"].value
|
32
|
+
@refid = attr["refid"].value
|
33
|
+
|
34
|
+
init_transition_profile
|
35
|
+
|
36
|
+
@videos = []
|
37
|
+
@animations = []
|
38
|
+
@current_animation = 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def is_hidden?
|
42
|
+
@hide
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def init_transition_profile
|
47
|
+
case @transition_profile
|
48
|
+
when "linear"
|
49
|
+
@transition = Proc.new { |x| x }
|
50
|
+
when "accelerate"
|
51
|
+
@transition = Proc.new { |x| x**3 }
|
52
|
+
when "strong-accelerate"
|
53
|
+
@transition = Proc.new { |x| x**5 }
|
54
|
+
when "decelerate"
|
55
|
+
@transition = Proc.new { |x| 1 - Math.pow(1 - x, 3) }
|
56
|
+
when "strong-decelerate"
|
57
|
+
@transition = Proc.new { |x| 1 - Math.pow(1 - x, 5) }
|
58
|
+
when "accelerate-decelerate"
|
59
|
+
@transition = Proc.new { |x|
|
60
|
+
xs = x <= 0.5 ? x : 1 - x
|
61
|
+
y = Math.pow(2 * xs, 3) / 2
|
62
|
+
x <= 0.5 ? y : 1 - y
|
63
|
+
}
|
64
|
+
when "strong-decelerate-accelerate"
|
65
|
+
@transition = Proc.new { |x|
|
66
|
+
xs = x <= 0.5 ? x : 1 - x
|
67
|
+
y = Math.pow(2 * xs, 5) / 2
|
68
|
+
x <= 0.5 ? y : 1 - y
|
69
|
+
}
|
70
|
+
when "immediate-beginning"
|
71
|
+
@transition = Proc.new { |x| 1 }
|
72
|
+
when "immediate-end"
|
73
|
+
@transition = Proc.new { |x| x === 1 ? 1 : 0}
|
74
|
+
when "immediate-middle"
|
75
|
+
@transition = Proc.new { |x| x >= 0.5 ? 1 : 0}
|
76
|
+
|
77
|
+
else
|
78
|
+
@transition = Proc.new {|x| x }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def add_video (video)
|
84
|
+
@videos << video
|
85
|
+
end
|
86
|
+
|
87
|
+
def has_videos?()
|
88
|
+
@videos.size() != 0
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def add_animation (id, animation)
|
93
|
+
@animations[id] = animation
|
94
|
+
end
|
95
|
+
|
96
|
+
def has_next_animation?()
|
97
|
+
return false if @animations.size == 0
|
98
|
+
return @current_animation < @animations.size
|
99
|
+
end
|
100
|
+
|
101
|
+
def next_animation
|
102
|
+
@current_animation = @current_animation + 1
|
103
|
+
@animations[@current_animation -1]
|
104
|
+
end
|
105
|
+
|
106
|
+
def has_previous_animation?()
|
107
|
+
return false if @animations.size == 0
|
108
|
+
return @current_animation > 0
|
109
|
+
end
|
110
|
+
|
111
|
+
def previous_animation
|
112
|
+
@current_animation = @current_animation - 1
|
113
|
+
@animations[@current_animation]
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def set_geometry (element, matrix)
|
118
|
+
|
119
|
+
@matrix = matrix
|
120
|
+
attr = element.attributes
|
121
|
+
|
122
|
+
# @label = attr["label"].value
|
123
|
+
# @id = attr["id"].value
|
124
|
+
@x = attr["x"].value.to_f
|
125
|
+
@y = attr["y"].value.to_f
|
126
|
+
@width = attr["width"].value.to_f
|
127
|
+
@height = attr["height"].value.to_f
|
128
|
+
|
129
|
+
@description = element.css("desc").text if element.css("desc").size > 0
|
130
|
+
@title = element.css("title").text if element.css("title").size > 0
|
131
|
+
|
132
|
+
## TODO : check if title != animation and video
|
133
|
+
|
134
|
+
# Animation & Video are not ruby Code !
|
135
|
+
if @title != nil
|
136
|
+
if @title.match(/animation/) or @title.match(/video/)
|
137
|
+
@description = nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
#eval @description if @description
|
142
|
+
end
|
143
|
+
|
144
|
+
def transition x
|
145
|
+
@transition.call x
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def to_s
|
150
|
+
["id ",@id.to_s ," ",
|
151
|
+
"transition_path_hide ",@transition_path_hide.to_s ," ",
|
152
|
+
"transition_profile ",@transition_profile.to_s ," ",
|
153
|
+
"transition_zoom_percent ",@transition_zoom_percent.to_s ," ",
|
154
|
+
"transition_duration_ms ",@transition_duration_ms.to_s ," ",
|
155
|
+
"timeout_ms ",@timeout_ms.to_s ," ",
|
156
|
+
"timeout_enable ",@timeout_enable.to_s ," ",
|
157
|
+
"show_in_frame_list ",@show_in_frame_list.to_s ," ",
|
158
|
+
"clip ",@clip.to_s ," ",
|
159
|
+
"hide ",@hide.to_s ," ",
|
160
|
+
"sequence ",@sequence.to_s ," ",
|
161
|
+
"title ",@title.to_s ," ",
|
162
|
+
"refid ",@refid.to_s ," " ].join
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
module Soby
|
3
|
+
|
4
|
+
include_package 'processing.core'
|
5
|
+
|
6
|
+
def read_transform (text)
|
7
|
+
if(text.match("matrix"))
|
8
|
+
text = text.split(/[,\(]/) # split with , or (
|
9
|
+
return PMatrix2D.new(text[1].to_f, text[3].to_f, text[5].to_f, \
|
10
|
+
text[2].to_f, text[4].to_f, text[6].to_f) if(text.size == 7)
|
11
|
+
end
|
12
|
+
|
13
|
+
if(text.match("translate"))
|
14
|
+
text = text.split(/[,\(]/)
|
15
|
+
return PMatrix2D.new(1, 0, text[1].to_f, 0, 1, text[2].to_f)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def get_global_transform element
|
21
|
+
e = element
|
22
|
+
global_transform = PMatrix3D.new
|
23
|
+
|
24
|
+
attr = e.attributes
|
25
|
+
|
26
|
+
global_transform.translate(attr["x"].value.to_f, attr["y"].value.to_f)
|
27
|
+
|
28
|
+
while e.class != Nokogiri::XML::Document do
|
29
|
+
t = e.attribute("transform")
|
30
|
+
if (t != nil)
|
31
|
+
tr = read_transform(t.value)
|
32
|
+
global_transform.preApply(tr)
|
33
|
+
end
|
34
|
+
e = e.parent
|
35
|
+
end
|
36
|
+
|
37
|
+
[global_transform , attr["width"].value.to_f, attr["height"].value.to_f]
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_angle (mat)
|
41
|
+
Math.atan2(mat.m01, mat.m00)
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def create_mat4x4(mat)
|
46
|
+
Matrix4x4.new(mat.m00, mat.m01, mat.m02, mat.m03,\
|
47
|
+
mat.m10, mat.m11, mat.m12, mat.m13,\
|
48
|
+
mat.m20, mat.m21, mat.m22, mat.m23,\
|
49
|
+
mat.m30, mat.m31, mat.m32, mat.m33)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
data/lib/soby.rb
ADDED
@@ -0,0 +1,403 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'nokogiri' # for XML.
|
4
|
+
require 'ruby-processing'
|
5
|
+
require 'java'
|
6
|
+
|
7
|
+
# For the other files, we need to load the libraries
|
8
|
+
Processing::App::load_library 'video', 'toxiclibscore'
|
9
|
+
|
10
|
+
require_relative 'soby/transforms'
|
11
|
+
require_relative 'soby/presentation'
|
12
|
+
require_relative 'soby/slide'
|
13
|
+
require_relative 'soby/cam'
|
14
|
+
|
15
|
+
class SobyPlayer < Processing::App
|
16
|
+
|
17
|
+
include_package 'processing.core'
|
18
|
+
include_package 'toxi.geom'
|
19
|
+
|
20
|
+
import 'toxi.geom.Matrix4x4'
|
21
|
+
|
22
|
+
attr_accessor :prez, :prev_cam, :next_cam, :slides
|
23
|
+
attr_reader :is_moving, :current_slide_no
|
24
|
+
|
25
|
+
# attr_accessor :background_max_color, :background_min_color, :background_constrain
|
26
|
+
attr_accessor :cam
|
27
|
+
|
28
|
+
|
29
|
+
def running? () @is_running end
|
30
|
+
|
31
|
+
TRANSITION_DURATION = 1000
|
32
|
+
|
33
|
+
# no Border
|
34
|
+
def init
|
35
|
+
super
|
36
|
+
removeFrameBorder
|
37
|
+
end
|
38
|
+
|
39
|
+
def removeFrameBorder
|
40
|
+
frame.removeNotify
|
41
|
+
frame.setUndecorated true
|
42
|
+
frame.addNotify
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def setup
|
47
|
+
@ready = false
|
48
|
+
size 1024, 768, OPENGL
|
49
|
+
|
50
|
+
## Some bugs with this.
|
51
|
+
## frame.setResizable true if frame != nil
|
52
|
+
|
53
|
+
init_player
|
54
|
+
|
55
|
+
@custom_setup_done = true
|
56
|
+
@ready = true
|
57
|
+
end
|
58
|
+
|
59
|
+
def ready?
|
60
|
+
@ready
|
61
|
+
end
|
62
|
+
|
63
|
+
def init_player
|
64
|
+
@prez = nil
|
65
|
+
@current_slide_no = 0
|
66
|
+
@is_running = false
|
67
|
+
init_cameras
|
68
|
+
end
|
69
|
+
|
70
|
+
def init_cameras
|
71
|
+
# current camera matrix
|
72
|
+
@cam = PMatrix3D.new
|
73
|
+
|
74
|
+
# Cameras for movement.
|
75
|
+
@prev_cam = Cam.new
|
76
|
+
@next_cam = Cam.new
|
77
|
+
@is_moving = false
|
78
|
+
@current_ratio = 0
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
## To be overriden by the Presentation Code.
|
83
|
+
def custom_setup
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
def custom_pre_draw
|
88
|
+
background(255)
|
89
|
+
end
|
90
|
+
|
91
|
+
def custom_post_draw
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def draw
|
97
|
+
|
98
|
+
if not @custom_setup_done
|
99
|
+
custom_setup
|
100
|
+
@custom_setup_done = true
|
101
|
+
end
|
102
|
+
|
103
|
+
custom_pre_draw
|
104
|
+
|
105
|
+
smooth(8)
|
106
|
+
|
107
|
+
shapeMode(CORNER)
|
108
|
+
imageMode(CORNER)
|
109
|
+
|
110
|
+
if(running?)
|
111
|
+
|
112
|
+
push_matrix
|
113
|
+
|
114
|
+
update_cam
|
115
|
+
self.g.modelview.apply(@cam)
|
116
|
+
@prez.draw
|
117
|
+
@prez.display_videos
|
118
|
+
pop_matrix
|
119
|
+
|
120
|
+
run_slide_code
|
121
|
+
display_slide_number
|
122
|
+
end
|
123
|
+
|
124
|
+
custom_post_draw
|
125
|
+
end
|
126
|
+
|
127
|
+
def run_slide_code
|
128
|
+
translate 0, 0, 1
|
129
|
+
if not @is_moving and @current_slide_no != 0
|
130
|
+
desc = @prez.slides[@current_slide_no].description
|
131
|
+
if(desc != nil)
|
132
|
+
# puts "EVAL #{desc}"
|
133
|
+
eval desc
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def display_slide_number
|
140
|
+
# Slide number
|
141
|
+
push_matrix
|
142
|
+
translate(@width - 40, @height - 45)
|
143
|
+
fill(30)
|
144
|
+
strokeWeight(3)
|
145
|
+
stroke(190)
|
146
|
+
ellipseMode(CENTER)
|
147
|
+
ellipse(18, 22, 35, 35)
|
148
|
+
fill(255)
|
149
|
+
noStroke
|
150
|
+
textSize(20)
|
151
|
+
text(@current_slide_no.to_s, 10, 30)
|
152
|
+
pop_matrix
|
153
|
+
end
|
154
|
+
|
155
|
+
alias :default_display_slide_number :display_slide_number
|
156
|
+
|
157
|
+
|
158
|
+
def key_pressed
|
159
|
+
|
160
|
+
if key == 'g'
|
161
|
+
puts "Garbage"
|
162
|
+
Java::JavaLang::System.gc
|
163
|
+
end
|
164
|
+
|
165
|
+
return if @prez == nil
|
166
|
+
|
167
|
+
if keyCode == LEFT
|
168
|
+
prev_slide
|
169
|
+
end
|
170
|
+
|
171
|
+
if keyCode == RIGHT
|
172
|
+
next_slide
|
173
|
+
end
|
174
|
+
|
175
|
+
# puts "slide #{@current_slide_no} "
|
176
|
+
end
|
177
|
+
|
178
|
+
def mouse_dragged
|
179
|
+
if not @is_moving
|
180
|
+
tr = PMatrix3D.new
|
181
|
+
tr.translate(mouse_x - pmouse_x, mouse_y - pmouse_y)
|
182
|
+
|
183
|
+
@cam.preApply(tr)
|
184
|
+
@next_cam.mat.preApply(tr)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def mouseWheel(event)
|
189
|
+
e = event.getAmount()
|
190
|
+
if not @is_moving
|
191
|
+
tr = PMatrix3D.new
|
192
|
+
tr.translate(mouse_x, mouse_y)
|
193
|
+
tr.scale(e < 0 ? 1.05 : 1 / 1.05)
|
194
|
+
tr.translate(-mouse_x, -mouse_y)
|
195
|
+
@cam.preApply(tr)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
def set_prez (prez)
|
201
|
+
# PShape.loadedImages.clear
|
202
|
+
@prez = prez
|
203
|
+
@slides = prez.slides
|
204
|
+
goto_slide 0
|
205
|
+
@is_running = true
|
206
|
+
@prez_middle = PVector.new(@prez.width / 2.0, @prez.height / 2.0)
|
207
|
+
|
208
|
+
puts "Presentation size "
|
209
|
+
puts @prez.width
|
210
|
+
puts @prez.height
|
211
|
+
|
212
|
+
@custom_setup_done = false
|
213
|
+
end
|
214
|
+
|
215
|
+
def compute_view(view, slide_number)
|
216
|
+
# view = createGraphics(@width, @height, P3D)
|
217
|
+
# @next_view = createGraphics(@width, @height)
|
218
|
+
view.beginDraw
|
219
|
+
|
220
|
+
cam = slide_view slide_number
|
221
|
+
view.g.modelview.apply(cam)
|
222
|
+
view.shapeMode(CORNER)
|
223
|
+
view.shape(@prez.pshape, 0, 0)
|
224
|
+
view.endDraw
|
225
|
+
|
226
|
+
view
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
def next_slide
|
231
|
+
is_last_slide = @current_slide_no >= @prez.nb_slides
|
232
|
+
is_slide_zero = current_slide_no == 0
|
233
|
+
|
234
|
+
# Gloal view
|
235
|
+
if is_slide_zero
|
236
|
+
goto_slide(@current_slide_no + 1)
|
237
|
+
return
|
238
|
+
end
|
239
|
+
|
240
|
+
# animation
|
241
|
+
if @slides[@current_slide_no].has_next_animation?
|
242
|
+
puts "Animation Next "
|
243
|
+
anim = @slides[@current_slide_no].next_animation
|
244
|
+
anim.pshape_elem.setVisible(true)
|
245
|
+
else
|
246
|
+
goto_slide(@current_slide_no + 1) unless is_last_slide
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
def prev_slide
|
252
|
+
return if @current_slide_no <= 0
|
253
|
+
|
254
|
+
if current_slide_no == 0
|
255
|
+
goto_slide(@current_slide_no - 1)
|
256
|
+
return
|
257
|
+
end
|
258
|
+
|
259
|
+
if @slides[@current_slide_no].has_previous_animation?
|
260
|
+
puts "Animation Previous "
|
261
|
+
anim = @slides[@current_slide_no].previous_animation
|
262
|
+
anim.pshape_elem.setVisible(false)
|
263
|
+
else
|
264
|
+
goto_slide(@current_slide_no - 1)
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
def goto_slide (next_slide)
|
270
|
+
|
271
|
+
current_slide = @current_slide_no
|
272
|
+
|
273
|
+
use_global_view = next_slide == 0 || next_slide > @prez.slides.size
|
274
|
+
|
275
|
+
cam = global_view if use_global_view
|
276
|
+
cam = slide_view(next_slide) if next_slide > 0
|
277
|
+
|
278
|
+
# previous next is now old.
|
279
|
+
@prev_cam = @next_cam
|
280
|
+
@prev_cam.mat.set(@cam)
|
281
|
+
@next_cam = cam
|
282
|
+
|
283
|
+
@current_slide_no = next_slide
|
284
|
+
|
285
|
+
if use_global_view
|
286
|
+
@transition_duration = TRANSITION_DURATION.to_f
|
287
|
+
else
|
288
|
+
@transition_duration = @slides[@current_slide_no].transition_duration_ms
|
289
|
+
end
|
290
|
+
|
291
|
+
@transition_start_time = millis
|
292
|
+
@is_moving = true
|
293
|
+
@current_ratio = 0
|
294
|
+
|
295
|
+
## trigger the slide_change function (user defined)
|
296
|
+
slide_change
|
297
|
+
end
|
298
|
+
|
299
|
+
def slide_change
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
def update_cam
|
305
|
+
|
306
|
+
return unless @is_moving
|
307
|
+
return if @slides.size == 0
|
308
|
+
|
309
|
+
elapsed_time = millis - @transition_start_time
|
310
|
+
@current_ratio = elapsed_time.to_f / @transition_duration.to_f
|
311
|
+
|
312
|
+
@current_ratio = @slides[@current_slide_no].transition @current_ratio unless @current_slide_no == 0
|
313
|
+
|
314
|
+
if @current_ratio > 1 && @is_moving
|
315
|
+
@is_moving = false
|
316
|
+
@cam = @prev_cam.lerp(@next_cam, 1)
|
317
|
+
|
318
|
+
# save a copy
|
319
|
+
@prev_cam.mat = @cam.get
|
320
|
+
return
|
321
|
+
end
|
322
|
+
|
323
|
+
# puts @current_ratio
|
324
|
+
@cam = @prev_cam.lerp(@next_cam, @current_ratio)
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
def slide_view (slide_no)
|
329
|
+
|
330
|
+
if slide_no > @prez.slides.size
|
331
|
+
puts "No more slides"
|
332
|
+
return
|
333
|
+
end
|
334
|
+
|
335
|
+
return if @prez.slides[slide_no] == nil
|
336
|
+
|
337
|
+
puts "slide view..." << slide_no.to_s
|
338
|
+
|
339
|
+
# check slide number
|
340
|
+
w = @prez.slides[slide_no].width
|
341
|
+
h = @prez.slides[slide_no].height
|
342
|
+
x = @prez.slides[slide_no].x
|
343
|
+
y = @prez.slides[slide_no].y
|
344
|
+
|
345
|
+
sc1 = @width.to_f / w.to_f
|
346
|
+
sc2 = @height.to_f / h.to_f
|
347
|
+
|
348
|
+
# scale
|
349
|
+
sc = [sc1, sc2].min
|
350
|
+
|
351
|
+
|
352
|
+
# translate
|
353
|
+
dx = ((@width / sc) - w) * 0.5
|
354
|
+
dy = ((@height / sc) - h) * 0.5
|
355
|
+
|
356
|
+
cam = Cam.new
|
357
|
+
|
358
|
+
m = PMatrix3D.new
|
359
|
+
m.apply @prez.slides[slide_no].matrix
|
360
|
+
m.invert
|
361
|
+
|
362
|
+
# Scale
|
363
|
+
tr = PMatrix3D.new
|
364
|
+
tr.scale(sc)
|
365
|
+
m.preApply(tr)
|
366
|
+
m.translate(dx, dy)
|
367
|
+
|
368
|
+
# center
|
369
|
+
cam.mat = m
|
370
|
+
cam
|
371
|
+
|
372
|
+
end
|
373
|
+
|
374
|
+
|
375
|
+
def global_view
|
376
|
+
# ortho(left, right, bottom, top)
|
377
|
+
# frustum(left, right, bottom, top, near, far)
|
378
|
+
|
379
|
+
my_scale = find_scale
|
380
|
+
|
381
|
+
# centering
|
382
|
+
dx = ((@width / my_scale) - @prez.width) * 0.5
|
383
|
+
dy = ((@height / my_scale) - @prez.height) * 0.5
|
384
|
+
|
385
|
+
cam = Cam.new
|
386
|
+
cam.scale = my_scale
|
387
|
+
# Not necessary : display from the center...
|
388
|
+
cam.post_translation.set(dx, dy)
|
389
|
+
cam.compute_mat
|
390
|
+
cam
|
391
|
+
end
|
392
|
+
|
393
|
+
def find_scale
|
394
|
+
sc1 = @width / @prez.width
|
395
|
+
sc2 = @height / @prez.height
|
396
|
+
return [sc1, sc2].min
|
397
|
+
end
|
398
|
+
private :find_scale
|
399
|
+
|
400
|
+
|
401
|
+
end
|
402
|
+
|
403
|
+
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: soby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeremy Laviole
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.6.3
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.6.3
|
25
|
+
prerelease: false
|
26
|
+
type: :runtime
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-processing
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.6.7
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 2.6.7
|
39
|
+
prerelease: false
|
40
|
+
type: :runtime
|
41
|
+
description: Based on Sozi, it works plays presentation using Ruby-processing.
|
42
|
+
email: poqudrof@gmail.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- lib/soby.rb
|
48
|
+
- lib/soby/cam.rb
|
49
|
+
- lib/soby/slide.rb
|
50
|
+
- lib/soby/presentation.rb
|
51
|
+
- lib/soby/transforms.rb
|
52
|
+
homepage: https://github.com/poqudrof/Soby
|
53
|
+
licenses:
|
54
|
+
- LGPL
|
55
|
+
metadata: {}
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 2.1.9
|
73
|
+
signing_key:
|
74
|
+
specification_version: 4
|
75
|
+
summary: Sozi player !
|
76
|
+
test_files: []
|