soby 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|