pfa 1.0.1

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.
@@ -0,0 +1,551 @@
1
+ #
2
+ # Module inclus dans la classe RelativePFA::Node
3
+ #
4
+ # @note
5
+ #
6
+ # On le met en module pour avoir toutes les méthodes séparées
7
+ # du module principal.
8
+ #
9
+ module MagickPFA
10
+
11
+
12
+ #
13
+ # Largeur totale du graphique
14
+ # (noter que les dimensions sont les mêmes pour l'image JPG et
15
+ # l'image HTML)
16
+ #
17
+ # @note
18
+ # Les tailles sont calculées pour un livre de
19
+ # 5.25 po (1334 mm) x 8 po (2032 mm)
20
+ # avec des marges de 20 mm
21
+ # Donc une surface utilisable de :
22
+ # 1334 - 2 * 20 = 1334 - 40 = 1294
23
+ # x 2032 - 2 * 20 = 2032 - 40 = 1992
24
+ #
25
+ # Comme l'image sera mise "de travers" dans la page, sa largeur
26
+ # sera de 1992 et sa hauteur 1294. On triple pour obtenir une
27
+ # bonne taille pour l'imprimerie.
28
+ # Donc :
29
+ # 3 x 1992 = 5976 px en largeur (PFA_WIDTH)
30
+ # 3 x 1294 = 3882 px en hauteur (PFA_HEIGHT)
31
+ #
32
+ # Soit un rapport de 5976 / 3882 = 1,5394
33
+ #
34
+ # Pour les tests, on utilise les valeurs
35
+ # 4000 px x 2598 px (4000 / 1,5394)
36
+ #
37
+ # @note
38
+ #
39
+ # En fait, :pfa_height ne définit pas vraiment la hauteur des
40
+ # PFA (et donc de l'image) mais permet de calculer la hauteur
41
+ # de ligne LINE_HEIGHT. L'image, elle, fera 12 * LINE_HEIGHT de
42
+ # hauteur.
43
+ #
44
+ DIMS_CONSTANTS_PER_THING = {
45
+ real_book: {
46
+ pfa_width: 5976, pfa_height: 3882, pfa_left_margin: 0, pfa_right_margin:0,
47
+ font_size: 15
48
+ },
49
+ test: {
50
+ pfa_width: 4000, pfa_height: 2598, pfa_left_margin: 20, pfa_right_margin:20,
51
+ font_size: 15
52
+ },
53
+ default: {
54
+ pfa_width: 4000, pfa_height: 2598, pfa_left_margin: 20, pfa_right_margin:20,
55
+ font_size: 15
56
+ },
57
+ }
58
+
59
+ FONTHEIGHT_MAIN_TITLE = 25
60
+
61
+ def self.rem_const_if_exists(const_name)
62
+ if MagickPFA.const_defined?(const_name)
63
+ # dbg "Je dois détruire la constante #{key.to_s.upcase}".orange
64
+ MagickPFA.send(:remove_const, const_name)
65
+ end
66
+ if MagickPFA.const_defined?(const_name)
67
+ MagickPFA.send(:remove_const, const_name)
68
+ end
69
+ end
70
+ # Définition des dimensions
71
+ #
72
+ # @param params [Symbol|Hash]
73
+ # Soit la clé dans la table DIMS_CONSTANTS_PER_THING
74
+ # Soit une table définissant :pfa_width, :pfa_height,
75
+ # :pfa_left_margin, pfa_right_margin
76
+ #
77
+ def self.define_dims_constants(key_thing)
78
+
79
+ params = nil
80
+ case key_thing
81
+ when Symbol then params = DIMS_CONSTANTS_PER_THING[key_thing]
82
+ when Hash
83
+ #
84
+ # Table fournie
85
+ # (dans ce cas, il faut vérifier les valeurs et mettre les
86
+ # valeurs par défaut des valeurs manquantes)
87
+ #
88
+ params = key_thing
89
+ DIMS_CONSTANTS_PER_THING[:default].each do |key, value|
90
+ params.key?(key) || params.merge!(key => value)
91
+ end
92
+ else raise PFAFatalError.new(101)
93
+ end
94
+
95
+ params.each do |key, value|
96
+ const_name = key.to_s.upcase
97
+ rem_const_if_exists(const_name)
98
+ # Object.const_set(const_name, value)
99
+ # self.class.const_set(const_name, value)
100
+ MagickPFA.const_set(const_name, value)
101
+ end
102
+
103
+ #
104
+ # Définition de la hauteur de ligne
105
+ #
106
+ rem_const_if_exists('LINE_HEIGHT') # tests
107
+ MagickPFA.const_set('LINE_HEIGHT', (MagickPFA::PFA_HEIGHT.to_f / 15).to_i)
108
+ # MagickPFA.const_set('LINE_HEIGHT', (MagickPFA::PFA_HEIGHT.to_f / 10).to_i)
109
+
110
+ rem_const_if_exists('QUART_LINE_HEIGHT') # tests
111
+ MagickPFA.const_set('QUART_LINE_HEIGHT', LINE_HEIGHT / 4)
112
+
113
+ #
114
+ # Différence en hauteur du paradigme réel par rapport au paradigme
115
+ # idéal
116
+ #
117
+ rem_const_if_exists('VOFFSET_REL_PFA') # tests
118
+ MagickPFA.const_set('VOFFSET_REL_PFA', 6 * LINE_HEIGHT)
119
+
120
+ rem_const_if_exists('TOP_MARGIN')
121
+ MagickPFA.const_set('TOP_MARGIN', 4 * FONTHEIGHT_MAIN_TITLE)
122
+
123
+ #
124
+ # Position verticale des éléments en fonction de leur nature
125
+ #
126
+ rem_const_if_exists('ABS_TOPS') # tests
127
+ MagickPFA.const_set('ABS_TOPS', {
128
+ part: TOP_MARGIN,
129
+ sequence: TOP_MARGIN + 3 * LINE_HEIGHT,
130
+ noeud: TOP_MARGIN + 3 * LINE_HEIGHT,
131
+ })
132
+ rem_const_if_exists('ABS_BOTTOMS') # tests
133
+ MagickPFA.const_set('ABS_BOTTOMS', {
134
+ part: ABS_TOPS[:part] + VOFFSET_REL_PFA - LINE_HEIGHT - 100,
135
+ sequence: ABS_TOPS[:sequence] + VOFFSET_REL_PFA,
136
+ noeud: ABS_TOPS[:noeud] + VOFFSET_REL_PFA,
137
+ })
138
+ rem_const_if_exists('TOPS') # tests
139
+ MagickPFA.const_set('TOPS', {
140
+ part: ABS_BOTTOMS[:part] + LINE_HEIGHT,
141
+ sequence: ABS_TOPS[:sequence] + VOFFSET_REL_PFA,
142
+ noeud: ABS_TOPS[:noeud] + VOFFSET_REL_PFA,
143
+ })
144
+ rem_const_if_exists('BOTTOMS') # tests
145
+ MagickPFA.const_set('BOTTOMS', {
146
+ part: ABS_BOTTOMS[:part] + VOFFSET_REL_PFA,
147
+ sequence: ABS_BOTTOMS[:sequence] + VOFFSET_REL_PFA,
148
+ noeud: ABS_BOTTOMS[:noeud] + VOFFSET_REL_PFA,
149
+ })
150
+ #
151
+ # Hauteur en fonction du type des éléments
152
+ #
153
+ rem_const_if_exists('HEIGHTS') # tests
154
+ MagickPFA.const_set('HEIGHTS', {
155
+ part: PFA_HEIGHT / 1.4,
156
+ sequence: 50, # avant : PFA::LINE_HEIGHT (dans fichier relatif)
157
+ noeud: 50 # idem
158
+ })
159
+
160
+ #
161
+ # Taille de fonte de base. Elle correspond à la grosseur
162
+ # des séquences
163
+ #
164
+ # @todo
165
+ # Plus tard, on pourra modifier aussi les proportions de
166
+ # chaque taille de partie
167
+ #
168
+ rem_const_if_exists('BASE_FONTSIZE') # tests
169
+ MagickPFA.const_set('BASE_FONTSIZE', params[:font_size])
170
+
171
+ rem_const_if_exists('FONTSIZES') # tests
172
+ MagickPFA.const_set('FONTSIZES', {
173
+ part: 1.6 * BASE_FONTSIZE,
174
+ sequence: 1.0 * BASE_FONTSIZE,
175
+ noeud: 1.0 * BASE_FONTSIZE,
176
+ })
177
+
178
+ rem_const_if_exists('Label_Width') # tests
179
+ MagickPFA.const_set('Label_Width', {
180
+ part: 400,
181
+ sequence: 400,
182
+ noeud: 400,
183
+ })
184
+ rem_const_if_exists('Label_Height') # tests
185
+ MagickPFA.const_set('Label_Height', {
186
+ part: 200,
187
+ sequence: 200,
188
+ noeud: 200,
189
+ })
190
+
191
+ # Taille de fonte des horloges par partie
192
+ rem_const_if_exists('FontSize_Horloges') # tests
193
+ MagickPFA.const_set('FontSize_Horloges', {
194
+ part: 1.2 * BASE_FONTSIZE,
195
+ sequence: 1.2 * BASE_FONTSIZE,
196
+ noeud: 1.2 * BASE_FONTSIZE,
197
+ })
198
+ rem_const_if_exists('Horloge_Width') # tests
199
+ MagickPFA.const_set('Horloge_Width', {
200
+ part: 300,
201
+ sequence: 300,
202
+ noeud: 300
203
+ })
204
+ # Hauteur de l'horloge (pour glisser l'offset en dessous)
205
+ rem_const_if_exists('Horloge_Height') # tests
206
+ MagickPFA.const_set('Horloge_Height', {
207
+ part: 112,
208
+ sequence: 112,
209
+ noeud: 112
210
+ })
211
+
212
+ # Décalage vertical pour que les horloges des actes et de fin
213
+ # soient bien collées au bord haut
214
+ rem_const_if_exists('VOFFSET_MAIN_HORLOGES') # tests
215
+ MagickPFA.const_set('VOFFSET_MAIN_HORLOGES', Horloge_Height[:part] - 60)
216
+
217
+ end # define_dims_constants
218
+
219
+
220
+ # -- Commande ImageMagick --
221
+
222
+ CONVERT_CMD = 'convert' # /usr/local/bin/convert si pas d'alias
223
+
224
+ # Le début du code de la commande convert
225
+ def self.code_for_intro
226
+ <<~CMD.strip
227
+ #{CONVERT_CMD} -size #{PFA_WIDTH}x#{LINE_HEIGHT * 12} xc:white
228
+ -units PixelsPerInch
229
+ -density 300
230
+ -background transparent
231
+ -set colorspace sRGB
232
+ CMD
233
+ end
234
+
235
+ #
236
+ # Pour écrire le titre du paradigme
237
+ CODE_TITRE_PARADIGME = <<~CMD.strip.freeze
238
+ -pointsize %{lh}
239
+ -font Arial
240
+ -draw "text %{lf},%{tp} '%{titre}'"
241
+ CMD
242
+
243
+ CODE_BOITE_ACTE = <<~CMD.strip.freeze
244
+ -stroke black
245
+ -strokewidth 3
246
+ -background transparent
247
+ -fill transparent
248
+ -draw "rectangle %{lf},%{tp} %{rg},%{bt}"
249
+ CMD
250
+
251
+ CODE_PART_NAME = <<~CMD.strip.freeze
252
+ \\( -extent %{w}x%{h} -stroke black -strokewidth 1 -background transparent
253
+ -pointsize %{fs} -font Arial -gravity Center label:"%{n}" \\)
254
+ -geometry +%{gh}+%{gv} -gravity NorthWest -composite
255
+ CMD
256
+ # {
257
+ # w: width, h: font_height + 20, n: mark, fs: font_size,
258
+ # gh: left, # pour la géométrie, décalage par rapport au Nord-Ouest
259
+ # gv: top + LINE_HEIGHT, # pour la géométrie, décalage vertical
260
+ # }
261
+
262
+ # --- HORLOGES ---
263
+
264
+ # Code ImageMagick de l'horloge pour les parties
265
+ #
266
+ CODE_HORLOGE = <<~CMD.strip.freeze
267
+ \\( -extent %{w}x%{h} -background %{bg} -stroke %{fg} -strokewidth 1 -fill %{fg}
268
+ -pointsize %{fs} -font Georgia -gravity Center label:"%{mk}" \\)
269
+ -geometry +%{l}+%{t} -gravity %{g} -composite
270
+ CMD
271
+ # -gravity NorthWest
272
+
273
+ # Code ImageMagick pour indiquer le décalage entre le temps réel
274
+ # et le temps absolu.
275
+ CODE_OFFSET = <<~CMD.strip.freeze
276
+ \\( -extent %{wo}x%{ho} -pointsize %{fso} -font Georgia -background %{bgo}
277
+ -fill %{fgo} -stroke %{fgo} -strokewidth 1 -gravity Center
278
+ label:"%{mko}" \\) -geometry +%{lo}+%{to} -gravity NorthWest -composite
279
+ CMD
280
+
281
+ # @return [BashString] Le code Image Magick de l'horloge pour tous
282
+ # les éléments
283
+ #
284
+ def horloge_code(real_pfa, current_left)
285
+ start_ntime = real_pfa ? start_at : abs_start
286
+ #
287
+ # Si c'est le noeud réel, il faut calculer son décalage avec le
288
+ # noeud absolu
289
+ #
290
+ start_at.calc_offset(abs_start) if real_pfa
291
+ #
292
+ # Le left dépend de la nature de l'élément
293
+ # @TODO Il serait possible de mettre ça en propriété de l'éménet
294
+ #
295
+ theleft = if part?
296
+ (real_pfa ? left : abs_left) + 2
297
+ else
298
+ (real_pfa ? left : abs_left) - Horloge_Width[type] / 2
299
+ end
300
+
301
+ # Problème d'overlay
302
+ if current_left && theleft < current_left
303
+ theleft = current_left + 10
304
+ end
305
+
306
+ #
307
+ # Le top dépend de la nature de l'élément
308
+ #
309
+ thetop = if part?
310
+ (real_pfa ? top : abs_top) + VOFFSET_MAIN_HORLOGES
311
+ else
312
+ (real_pfa ? top : abs_top) + 50
313
+ end
314
+
315
+ background = part? ? 'black' : 'gray90'
316
+ foreground = part? ? 'gray90' : 'black'
317
+
318
+ #
319
+ # Les données template en fonction du pfa absolu ou réel
320
+ #
321
+ data_template = {
322
+ w: Horloge_Width[type], # largeur (sauf pour actes)
323
+ h: Horloge_Height[type], # hauteur de l'horloge (surtout pour décalage)
324
+ l: theleft, # left de l'horloge
325
+ t: thetop, # top de l'horloge (et du décalage)
326
+ fs: FontSize_Horloges[type], # Font size
327
+ bg: background,
328
+ fg: foreground,
329
+ mk: start_ntime.to_horloge, # Horloge
330
+ g: 'NorthWest',
331
+ }
332
+ #
333
+ # On ajoute les valeurs pour le pfa réel
334
+ #
335
+ data_template.merge!({
336
+ wo: Horloge_Width[type],
337
+ ho: Horloge_Height[type],
338
+ lo: theleft, # left de la marque de décalage
339
+ to: thetop + Horloge_Height[type], # top de l'offset (sous l'horloge)
340
+ fso: FontSize_Horloges[type] - 4, # Font size pour le décalage
341
+ mko: start_ntime.offset_as_horloge(abs_start), # marque du décalage
342
+ # @note
343
+ # Il faut impérativement appeler offset_as_horloge avant
344
+ # le reste ci-dessous,
345
+ bgo: start_ntime.background_per_offset, # background du décalage
346
+ fgo: start_ntime.foreground_per_offset, # couleur du décalage
347
+ }) if real_pfa
348
+
349
+ code = CODE_HORLOGE
350
+ code += ' ' + CODE_OFFSET if real_pfa
351
+ code % data_template
352
+ end
353
+
354
+ def horloge_fin(real_pfa)
355
+ CODE_HORLOGE % {
356
+ w: Horloge_Width[type],
357
+ h: Horloge_Height[type],
358
+ l: 0,
359
+ t: (real_pfa ? top : abs_top) + VOFFSET_MAIN_HORLOGES,
360
+ fs: FontSize_Horloges[type], # Font size
361
+ mk: pfa.duration.as_horloge,
362
+ bg: 'black',
363
+ fg: 'white',
364
+ g: 'NorthEast'
365
+ }
366
+ end
367
+
368
+ # [BashCode] Code ImageMagick pour le titre des paradigmes
369
+ def titre_pfa(for_real)
370
+ CODE_TITRE_PARADIGME % {
371
+ lf: 20,
372
+ lh: 25, # line-height (aka font-size)
373
+ tp: (for_real ? top : abs_top),
374
+ titre: for_real ? "PARADIGME RÉEL DU FILM" : "PARADIGME IDÉAL DU FILM"
375
+ }
376
+ end
377
+
378
+ # [BashString] Code pour dessiner le noeud, quand c'est un acte,
379
+ # dans l'image.
380
+ def act_box_code(real_pfa)
381
+ CODE_BOITE_ACTE % {
382
+ lf: real_pfa ? left : abs_left,
383
+ tp: (real_pfa ? top : abs_top) + 50, # +50 pour ne pas coller au main title
384
+ rg: real_pfa ? right : abs_right,
385
+ bt: real_pfa ? bottom : abs_bottom
386
+ }
387
+ end
388
+
389
+ # [BashString] Code pour dessiner les noms des parties (actes)
390
+ def act_name_code(for_pfa)
391
+ CODE_PART_NAME % {
392
+ w: for_pfa ? width : abs_width, h: FONTSIZES[:part] + 100, n: mark, fs: FONTSIZES[:part],
393
+ gh: for_pfa ? left : abs_left, # pour la géométrie, décalage par rapport au Nord-Ouest
394
+ gv: (for_pfa ? top : abs_top) + LINE_HEIGHT, # pour la géométrie, décalage vertical
395
+ }
396
+ end
397
+
398
+
399
+ # @return [BashString] Le code pour dessiner l'élément
400
+ #
401
+ # @param real_pfa [Bool]
402
+ # Pour savoir si on doit retourner le code pour le pfa
403
+ # réel ou pour le pfa absolu.
404
+ #
405
+ # @param current_left [Integer|Float]
406
+ # Le left sur lequel on se trouve, pour savoir si ça déborde
407
+ #
408
+ def image_magick_code(real_pfa, current_left)
409
+ code = if type == :sequence
410
+ CODE_IMAGEMAGICK_SEQUENCE
411
+ else
412
+ CODE_IMAGEMAGICK_NOEUD
413
+ end
414
+ # Données template (en fonction du paradigem)
415
+ # dbg "Node : #{id}".orange
416
+ # dbg "Node type : #{type.inspect}".orange
417
+ data_temp = {
418
+ l: real_pfa ? left : abs_left, # alignement gauche
419
+ t: real_pfa ? top : abs_top, # top
420
+ w: Label_Width[type], # largeur pour la marque (pour le moment, arbitraire)
421
+ h: Label_Height[type], # hauteur pour la marque (sur deux lignes)
422
+ fs: FONTSIZES[type],
423
+ a: 'Center', # modifié par l'overlay
424
+ m: mark.gsub(/ /,"\\n"),
425
+ }
426
+
427
+ data_temp.merge!({
428
+ # Noter que ci-dessous on n'utilise pas le @width de l'élément
429
+ # pour avoir des ronds uniformes
430
+ r: data_temp[:l] + 20, # right (pour la boite du nom)
431
+ b: data_temp[:t] + 20,
432
+ lm: data_temp[:l] - data_temp[:w] / 2, # le left de la marque du nom
433
+ tm: data_temp[:t] - 250, # le top de la marque
434
+ lh: data_temp[:l] - data_temp[:w] / 2, # le left de l'horloge
435
+ th: data_temp[:t] + 150, # le top de l'horloge
436
+ })
437
+
438
+ if current_left && data_temp[:lm] < current_left
439
+ # dbg "PROBLÈME D'OVERLAY avec #{id} (:l)".rouge
440
+ data_temp[:lm] = current_left
441
+ data_temp[:a] = 'West'
442
+ end
443
+
444
+ code % data_temp
445
+ end
446
+
447
+ # @return [BashString] Code Image Magick pour un noeud du Paradigme
448
+ # de Field Augmenté
449
+ #
450
+ CODE_IMAGEMAGICK_NOEUD = <<~CMD.strip.freeze
451
+ -stroke black
452
+ -fill gray40
453
+ -draw "circle %{l},%{t} %{r},%{b}"
454
+ \\( -extent %{w}x%{h} -pointsize %{fs} -stroke black -fill black -background transparent -gravity %{a}
455
+ label:"%{m}" \\) -geometry +%{lm}+%{tm} -gravity NorthWest -composite
456
+ CMD
457
+
458
+ # TODO : À FAIRE
459
+ CODE_IMAGEMAGICK_SEQUENCE = <<~CMD.strip.freeze
460
+ -stroke black
461
+ -fill black
462
+ -draw "circle %{l},%{t} %{r},%{b}"
463
+ CMD
464
+
465
+
466
+
467
+ # # La marque pour une séquence (un crochet allongé)
468
+ # #
469
+ # def img_lines_for_real_sequence
470
+ # <<~CMD
471
+ # -strokewidth #{AnyBuilder::BORDERS[:seq]}
472
+ # -stroke #{AnyBuilder::COLORS[:seq]}
473
+ # -fill white
474
+ # -draw "polyline #{left+4},#{top+demiheight} #{left+4},#{bottom} #{right-4},#{bottom} #{right-4},#{top+demiheight}"
475
+ # #{mark_horloge}
476
+ # CMD
477
+ # end
478
+
479
+ # # La marque pour un nœud (un rond/point)
480
+ # def img_lines_for_real_noeud
481
+ # <<~CMD
482
+ # -strokewidth #{AnyBuilder::BORDERS[:noeud]}
483
+ # -stroke #{AnyBuilder::COLORS[:noeud]}
484
+ # -fill white
485
+ # -draw "roundrectangle #{left},#{top} #{right},#{bottom} 10,10"
486
+ # #{mark_horloge}
487
+ # CMD
488
+ # end
489
+
490
+ RECTIFS = {
491
+ part: 50,
492
+ sequence: 0,
493
+ noeud: 0
494
+ }
495
+
496
+ #
497
+ # Graisse de la police en fonction du type de l'élément
498
+ #
499
+ ABS_FONTWEIGHTS = {
500
+ part: 3,
501
+ sequence: 2,
502
+ noeud: 1
503
+ }
504
+ FONTWEIGHTS = {
505
+ part: 1,
506
+ sequence: 1,
507
+ noeud: 1
508
+ }
509
+
510
+ #
511
+ # Couleur en fonction du type de l'élément
512
+ #
513
+ COLORS = {
514
+ part: 'gray75',
515
+ sequence: 'gray55',
516
+ noeud: 'gray55'
517
+ }
518
+
519
+ #
520
+ # Couleur plus sombre en fonction de l'élément
521
+ #
522
+ DARKERS = {
523
+ part: 'gray50',
524
+ sequence: 'gray45',
525
+ noeud: 'gray45'
526
+ }
527
+
528
+ #
529
+ # Gravité en fonction du type de l'élément
530
+ #
531
+ GRAVITIES = {
532
+ part: 'Center',
533
+ sequence: 'Center',
534
+ noeud: 'Center'
535
+ }
536
+
537
+ #
538
+ # Largeur des bords en fonction du type de l'élément
539
+ #
540
+ ABS_BORDERS = {
541
+ part: 3,
542
+ sequence: 2,
543
+ noeud: 1
544
+ }
545
+ BORDERS = {
546
+ part: 1,
547
+ sequence: 1,
548
+ noeud: 1
549
+ }
550
+
551
+ end