primocode 10.16.0 → 10.17.0

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.
Files changed (234) hide show
  1. package/LICENSES/edit-ia-pro.txt +32 -0
  2. package/LICENSES/fontes-OFL.txt +476 -0
  3. package/LICENSES/hyperframes-Apache-2.0.txt +205 -0
  4. package/LICENSES/permanent-marker-Apache-2.0.txt +204 -0
  5. package/LICENSES/selfie-multiclass-Apache-2.0.txt +227 -0
  6. package/LICENSES/three-MIT.txt +23 -0
  7. package/LICENSES/video-use-MIT.txt +24 -0
  8. package/LICENSES/woven-sfx-CC0.txt +16 -0
  9. package/LICENSES/yunet-MIT.txt +21 -0
  10. package/README.md +2 -2
  11. package/bin/primocode.js +11 -2
  12. package/editor/LICENSE.md +11 -0
  13. package/editor/README.md +321 -0
  14. package/editor/SKILL.md +307 -0
  15. package/editor/THIRD_PARTY_NOTICES.md +228 -0
  16. package/editor/assets/baixar.json +591 -0
  17. package/editor/assets/fonts/anton/OFL.txt +93 -0
  18. package/editor/assets/fonts/caveat/OFL.txt +93 -0
  19. package/editor/assets/fonts/figtree/OFL.txt +93 -0
  20. package/editor/assets/fonts/fonts.json +10 -0
  21. package/editor/assets/fonts/montserrat/OFL.txt +93 -0
  22. package/editor/assets/fonts/permanentmarker/LICENSE.txt +202 -0
  23. package/editor/assets/models/SELFIE_MULTICLASS_LICENSE.txt +227 -0
  24. package/editor/assets/models/YUNET_LICENSE.txt +21 -0
  25. package/editor/assets/sfx/licencas/bass_drop.txt +32 -0
  26. package/editor/assets/sfx/licencas/cash.txt +76 -0
  27. package/editor/assets/sfx/licencas/charge.txt +31 -0
  28. package/editor/assets/sfx/licencas/click.txt +46 -0
  29. package/editor/assets/sfx/licencas/coin.txt +50 -0
  30. package/editor/assets/sfx/licencas/count.txt +47 -0
  31. package/editor/assets/sfx/licencas/data.txt +31 -0
  32. package/editor/assets/sfx/licencas/error.txt +32 -0
  33. package/editor/assets/sfx/licencas/flash.txt +31 -0
  34. package/editor/assets/sfx/licencas/gears.txt +82 -0
  35. package/editor/assets/sfx/licencas/keyboard.txt +48 -0
  36. package/editor/assets/sfx/licencas/metal_hit.txt +62 -0
  37. package/editor/assets/sfx/licencas/metal_riser.txt +61 -0
  38. package/editor/assets/sfx/licencas/mouse_click.txt +50 -0
  39. package/editor/assets/sfx/licencas/notification.txt +54 -0
  40. package/editor/assets/sfx/licencas/pop.txt +38 -0
  41. package/editor/assets/sfx/licencas/rewind.txt +32 -0
  42. package/editor/assets/sfx/licencas/rise.txt +46 -0
  43. package/editor/assets/sfx/licencas/sinteticos.txt +21 -0
  44. package/editor/assets/sfx/licencas/snap.txt +50 -0
  45. package/editor/assets/sfx/licencas/success.txt +40 -0
  46. package/editor/assets/sfx/licencas/synth_hit.txt +32 -0
  47. package/editor/assets/sfx/licencas/tick.txt +14 -0
  48. package/editor/assets/sfx/licencas/whoosh.txt +47 -0
  49. package/editor/assets/sfx/licencas/wind.txt +31 -0
  50. package/editor/assets/sfx/sfx.json +832 -0
  51. package/editor/assets/sfx/sfx_sinteticos.json +317 -0
  52. package/editor/assets/sfx/sfx_woven.json +387 -0
  53. package/editor/assets/sfx/woven/LICENCA.md +16 -0
  54. package/editor/fichas/briefing-do-video.md +45 -0
  55. package/editor/fichas/conferencia-da-amostra.md +56 -0
  56. package/editor/fichas/matriz-de-ganchos.csv +3 -0
  57. package/editor/fichas/padrao.json +16 -0
  58. package/editor/fichas/planilha-de-cenas.csv +4 -0
  59. package/editor/fichas/planilha-de-versoes.csv +4 -0
  60. package/editor/fichas/plano-em-blocos.md +32 -0
  61. package/editor/fichas/registro-de-materiais.json +27 -0
  62. package/editor/helpers/assets_ocr/ocr_vision.swift +41 -0
  63. package/editor/helpers/bordas_precisas.py +272 -0
  64. package/editor/helpers/camera_viva.py +426 -0
  65. package/editor/helpers/captions.py +601 -0
  66. package/editor/helpers/common.py +308 -0
  67. package/editor/helpers/compose_final.py +3555 -0
  68. package/editor/helpers/conferir_corte.py +234 -0
  69. package/editor/helpers/conferir_dados.py +405 -0
  70. package/editor/helpers/contact_sheet.py +282 -0
  71. package/editor/helpers/corte_por_onda.py +433 -0
  72. package/editor/helpers/corte_seco.py +595 -0
  73. package/editor/helpers/denoise.py +971 -0
  74. package/editor/helpers/efeitos.py +408 -0
  75. package/editor/helpers/efeitos_gerar.py +159 -0
  76. package/editor/helpers/elevenlabs_api.py +442 -0
  77. package/editor/helpers/face_layout.py +570 -0
  78. package/editor/helpers/fetch_images.py +423 -0
  79. package/editor/helpers/ganho_por_trecho.py +214 -0
  80. package/editor/helpers/gen_image.py +312 -0
  81. package/editor/helpers/gen_music.py +316 -0
  82. package/editor/helpers/gen_sfx.py +148 -0
  83. package/editor/helpers/grade.py +307 -0
  84. package/editor/helpers/icloud.py +119 -0
  85. package/editor/helpers/make_sfx.py +940 -0
  86. package/editor/helpers/matte.py +1342 -0
  87. package/editor/helpers/medir_referencia.py +2232 -0
  88. package/editor/helpers/mixar_trilha.py +354 -0
  89. package/editor/helpers/montar_corte.py +295 -0
  90. package/editor/helpers/motion/__init__.py +13 -0
  91. package/editor/helpers/motion/engine.py +1915 -0
  92. package/editor/helpers/motion/registry.py +510 -0
  93. package/editor/helpers/motion/render.py +665 -0
  94. package/editor/helpers/motion/templates/barra_progresso.py +370 -0
  95. package/editor/helpers/motion/templates/carimbo.py +132 -0
  96. package/editor/helpers/motion/templates/checklist.py +271 -0
  97. package/editor/helpers/motion/templates/comentario_cta.py +380 -0
  98. package/editor/helpers/motion/templates/contador.py +222 -0
  99. package/editor/helpers/motion/templates/destaque.py +263 -0
  100. package/editor/helpers/motion/templates/etiqueta_preco.py +461 -0
  101. package/editor/helpers/motion/templates/grafico_barras.py +510 -0
  102. package/editor/helpers/motion/templates/grafico_linha.py +462 -0
  103. package/editor/helpers/motion/templates/notificacao.py +266 -0
  104. package/editor/helpers/motion/templates/palavra_impacto.py +410 -0
  105. package/editor/helpers/motion/templates/passos.py +321 -0
  106. package/editor/helpers/motion/templates/riscado.py +298 -0
  107. package/editor/helpers/motion/templates/selo_garantia.py +150 -0
  108. package/editor/helpers/motion_render.py +241 -0
  109. package/editor/helpers/motion_sugerir.py +961 -0
  110. package/editor/helpers/noise_check.py +134 -0
  111. package/editor/helpers/pack_transcripts.py +223 -0
  112. package/editor/helpers/palavras_na_onda.py +176 -0
  113. package/editor/helpers/plano_sfx.py +839 -0
  114. package/editor/helpers/portao.py +427 -0
  115. package/editor/helpers/preview_server.py +1177 -0
  116. package/editor/helpers/rastrear_rosto.py +298 -0
  117. package/editor/helpers/render_cut.py +1256 -0
  118. package/editor/helpers/respiros.py +89 -0
  119. package/editor/helpers/sincronizar_cameras.py +571 -0
  120. package/editor/helpers/speech_regions.py +423 -0
  121. package/editor/helpers/style_catalog.json +231 -0
  122. package/editor/helpers/style_samples.py +246 -0
  123. package/editor/helpers/tela_verde.py +2113 -0
  124. package/editor/helpers/text_render.py +2059 -0
  125. package/editor/helpers/texto_na_tela.py +366 -0
  126. package/editor/helpers/timeline_view.py +292 -0
  127. package/editor/helpers/tira_de_quadros.py +113 -0
  128. package/editor/helpers/transcribe.py +734 -0
  129. package/editor/helpers/transcribe_batch.py +129 -0
  130. package/editor/helpers/trilhas.py +1146 -0
  131. package/editor/helpers/trilhas_humores.json +66 -0
  132. package/editor/helpers/valores.py +187 -0
  133. package/editor/helpers/verify_cut.py +820 -0
  134. package/editor/helpers/voice_levels.py +233 -0
  135. package/editor/helpers/voz_check.py +386 -0
  136. package/editor/helpers/voz_limpa.py +81 -0
  137. package/editor/helpers/watch_preview.py +372 -0
  138. package/editor/modelo-projeto/LEIA.md +402 -0
  139. package/editor/modelo-projeto/assets/fontes/OFL-Caveat.txt +93 -0
  140. package/editor/modelo-projeto/assets/fontes/OFL-JetBrainsMono.txt +93 -0
  141. package/editor/modelo-projeto/assets/fontes/OFL-Montserrat.txt +93 -0
  142. package/editor/modelo-projeto/assets/fontes/larguras.json +1 -0
  143. package/editor/modelo-projeto/assets/three/BufferGeometryUtils.js +1438 -0
  144. package/editor/modelo-projeto/assets/three/GLTFLoader.js +4895 -0
  145. package/editor/modelo-projeto/assets/three/LICENSE +21 -0
  146. package/editor/modelo-projeto/assets/three/ORIGEM.txt +17 -0
  147. package/editor/modelo-projeto/assets/three/RoomEnvironment.js +185 -0
  148. package/editor/modelo-projeto/assets/three/three.core.min.js +6 -0
  149. package/editor/modelo-projeto/assets/three/three.module.min.js +6 -0
  150. package/editor/modelo-projeto/build.py +4828 -0
  151. package/editor/modelo-projeto/compositions/cenas/2d/k2d_antes_depois.html +199 -0
  152. package/editor/modelo-projeto/compositions/cenas/2d/k2d_arquivo.html +232 -0
  153. package/editor/modelo-projeto/compositions/cenas/2d/k2d_busca.html +315 -0
  154. package/editor/modelo-projeto/compositions/cenas/2d/k2d_comando.html +304 -0
  155. package/editor/modelo-projeto/compositions/cenas/2d/k2d_conversa.html +217 -0
  156. package/editor/modelo-projeto/compositions/cenas/2d/k2d_esquema.html +289 -0
  157. package/editor/modelo-projeto/compositions/cenas/2d/k2d_medidor.html +249 -0
  158. package/editor/modelo-projeto/compositions/cenas/2d/k2d_notas.html +233 -0
  159. package/editor/modelo-projeto/compositions/cenas/3d/k3d_caminho.html +197 -0
  160. package/editor/modelo-projeto/compositions/cenas/3d/k3d_explodida.html +152 -0
  161. package/editor/modelo-projeto/compositions/cenas/3d/k3d_tela.html +167 -0
  162. package/editor/modelo-projeto/compositions/cenas/3d/k3d_volume.html +124 -0
  163. package/editor/modelo-projeto/compositions/cenas/3d/motor-three.js +728 -0
  164. package/editor/modelo-projeto/compositions/cenas/LEIA.md +177 -0
  165. package/editor/modelo-projeto/ferramentas/medir_fontes.py +65 -0
  166. package/editor/modelo-projeto/ferramentas/pessoa_recortada.py +176 -0
  167. package/editor/modelo-projeto/ferramentas/rastrear_maos.py +189 -0
  168. package/editor/modelo-projeto/hyperframes.json +12 -0
  169. package/editor/modelo-projeto/package.json +11 -0
  170. package/editor/modelo-projeto/projeto.json +16 -0
  171. package/editor/preview/app.css +1009 -0
  172. package/editor/preview/app.js +2122 -0
  173. package/editor/preview/index.html +307 -0
  174. package/editor/pyproject.toml +23 -0
  175. package/editor/references/3d.md +236 -0
  176. package/editor/references/animacao-catalogo.md +672 -0
  177. package/editor/references/animacao.md +498 -0
  178. package/editor/references/comandos.md +92 -0
  179. package/editor/references/corte-seco-como-funciona.md +50 -0
  180. package/editor/references/corte-seco.md +88 -0
  181. package/editor/references/corte.md +290 -0
  182. package/editor/references/direcao-por-blocos.md +218 -0
  183. package/editor/references/enquadramento.md +654 -0
  184. package/editor/references/estilos-prontos.md +355 -0
  185. package/editor/references/estudio.md +100 -0
  186. package/editor/references/exemplo-eventos.json +67 -0
  187. package/editor/references/lettering.md +252 -0
  188. package/editor/references/modelos.md +494 -0
  189. package/editor/references/phase2.md +1007 -0
  190. package/editor/references/referencia-aplicar.md +203 -0
  191. package/editor/references/referencia-medir.md +375 -0
  192. package/editor/references/revisao.md +335 -0
  193. package/editor/references/som-e-trilha.md +146 -0
  194. package/editor/references/tabela-de-sons.md +130 -0
  195. package/editor/references/variacoes.md +234 -0
  196. package/editor/uv.lock +1141 -0
  197. package/lib/act.js +36 -8
  198. package/lib/catalogo.js +4 -0
  199. package/lib/chamada-torta.js +81 -0
  200. package/lib/editor-pro.js +786 -0
  201. package/lib/entrega.js +58 -0
  202. package/lib/estudio/conferencia-visual.js +76 -0
  203. package/lib/estudio/diretor.js +6 -1
  204. package/lib/estudio/exportar.js +3 -1
  205. package/lib/estudio/gosto.js +45 -4
  206. package/lib/estudio/index.js +8 -5
  207. package/lib/estudio/kit-ae/base.jsx +119 -13
  208. package/lib/estudio/kit-ae/cenas.jsx +47 -15
  209. package/lib/estudio/kit-ae/guarda.jsx +274 -0
  210. package/lib/estudio/kit-ae/primitivas.jsx +180 -24
  211. package/lib/estudio/maquina-diretor.js +61 -7
  212. package/lib/estudio/maquina-foto.js +177 -0
  213. package/lib/estudio/maquina-guarda.js +207 -0
  214. package/lib/estudio/maquina-layout.js +22 -1
  215. package/lib/estudio/maquina.js +60 -11
  216. package/lib/estudio/referencias.js +1 -1
  217. package/lib/estudio/visao/visao.swift +97 -0
  218. package/lib/estudio/visao.js +60 -0
  219. package/lib/local/cerebro.json +192 -6
  220. package/lib/local/motor.js +5 -2
  221. package/lib/nuvem.js +11 -1
  222. package/lib/passagem.js +50 -0
  223. package/lib/perigo.js +19 -11
  224. package/lib/protecao.js +134 -0
  225. package/lib/titulo-conversa.js +46 -0
  226. package/lib/tools.js +3 -0
  227. package/lib/ui.js +1 -1
  228. package/package.json +3 -2
  229. package/skills/editar-meu-video/SKILL.md +6 -2
  230. package/skills/editar-video/SKILL.md +113 -0
  231. package/studio/pessoa.py +52 -0
  232. package/voz/primo_voz/app.py +43 -4
  233. package/voz/primo_voz/chat.html +17 -6
  234. package/voz/primo_voz/painel_dados.py +134 -0
@@ -13,6 +13,9 @@
13
13
 
14
14
  function kPre(comp, P) { var g = { W: comp.width, H: comp.height, u: kU(comp), v: kVert(comp), cam: kBase(comp, P) }; g.cx = g.W / 2; g.cy = g.H / 2; return g; }
15
15
  function kSobre(P) { return kLum(P.ac) > 0.5 ? '#14110F' : '#FFFFFF'; }
16
+ /* O cartão em destaque vem da paleta da cena (maquina.js, cartaoDestaque): o acento quando é cor; senão o brilho. Nunca preto solto. */
17
+ function kCardAc(P) { return P.cardAc || P.ac; }
18
+ function kSobreCard(P) { return P.sobreCardAc || kSobre(P); }
16
19
  function kT(g, comp, P, str, y, o) {
17
20
  o = o || {};
18
21
  var t0 = o.t0 != null ? o.t0 : 0.14, W = g.W, H = g.H;
@@ -31,7 +34,13 @@ function kT(g, comp, P, str, y, o) {
31
34
  }
32
35
  return r;
33
36
  }
34
- function kItens(d, padrao, n) { var it = (d.itens && d.itens.length ? d.itens : padrao).slice(0, n || 5); return it; }
37
+ /* Os itens da lista, sem repetir (o mesmo texto, ignorando caixa, acento e espaço, entra uma vez só): faltou item, a lista fica menor. */
38
+ function kChaveItem(s) { return String(s || '').toLowerCase().replace(/[\*\s.,;:!?]+/g, '').replace(/[áàâã]/g, 'a').replace(/[éê]/g, 'e').replace(/[í]/g, 'i').replace(/[óôõ]/g, 'o').replace(/[ú]/g, 'u').replace(/ç/g, 'c'); }
39
+ function kItens(d, padrao, n) {
40
+ var fonte = d.itens && d.itens.length ? d.itens : padrao, it = [], visto = {}, i;
41
+ for (i = 0; i < fonte.length && it.length < (n || 5); i++) { var k = kChaveItem(fonte[i]); if (!k || visto[k]) continue; visto[k] = 1; it.push(fonte[i]); }
42
+ return it;
43
+ }
35
44
  function kEstrelaPts(r, ri) { var p = [], i; for (i = 0; i < 10; i++) { var a = -Math.PI / 2 + i * Math.PI / 5, rr = i % 2 ? ri : r; p.push([Math.cos(a) * rr, Math.sin(a) * rr]); } return p; }
36
45
  function kContr(a, b) { var x = kLum(a) + 0.05, y = kLum(b) + 0.05; return x > y ? x / y : y / x; }
37
46
  function kEsc(s) { return String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\r?\n/g, ' '); }
@@ -59,8 +68,10 @@ function cenaCinetico(comp, P, d) {
59
68
  var disco = forma(comp, 'circulo', { x: g.cx, y: g.cy, w: Math.min(W, H) * 0.9, cor: P.card, parent: cam.no, nome: 'disco' });
60
69
  for (i = 0; i < n; i++) {
61
70
  var ti = Q(pal[i].t, 0.14 + i * passo), fim = i < n - 1 ? Math.max(ti + 0.22, Q(pal[i + 1].t, 0.14 + (i + 1) * passo) - 0.02) : comp.duration;
62
- var cor = pal[i].kw ? P.ac : (i % 2 ? P.fg : P.mudo == P.fg ? P.fg : P.fg);
63
- var r = texto(comp, (pal[i].kw ? '*' : '') + pal[i].t, { tamanho: (g.v ? 230 : 200) * u, cor: i % 3 == 2 ? P.ac : P.fg, corDestaque: P.ac, x: g.cx, y: g.cy, parent: cam.no, entrada: modos[i % modos.length], t0: ti, largura: W * 0.84, maxLinhas: 1, sublinhado: false, fala: false, sobrepoe: true });
71
+ // a palavra tem que ficar INTEIRA e legível antes de sair (0,15 s de verdade: a cena nominal toca f× mais rápido); a letra
72
+ // que voa só entra se dá tempo de montar e ler
73
+ var modo = kEntradaQueCabe(modos[i % modos.length], pal[i].t, (i < n - 1 ? fim : comp.duration) - ti, 0.15 * (KIT.f || 1));
74
+ var r = texto(comp, (pal[i].kw ? '*' : '') + pal[i].t, { tamanho: (g.v ? 230 : 200) * u, cor: i % 3 == 2 ? P.ac : P.fg, corDestaque: P.ac, x: g.cx, y: g.cy, parent: cam.no, entrada: modo, t0: ti, largura: W * 0.84, maxLinhas: 1, sublinhado: false, fala: false, sobrepoe: true });
64
75
  var j; for (j = 0; j < r.camadas.length; j++) if (i < n - 1) curva(kO(r.camadas[j]), [[fim, 100, null, 30], [fim + 0.06, 0, 60, null]]);
65
76
  mola(kS(disco), ti, [88, 88], [100, 100], { quique: 0.5, dur: 0.45 });
66
77
  som(ti, i % 2 ? 'tique' : 'pop', 0.55);
@@ -97,8 +108,8 @@ function cenaInterface(comp, P, d) {
97
108
  var cx0 = sx + sw / 2 + 24 * u, cwid = ww / 2 - cx0 - 22 * u, k, vals = [d.valor || '+128', '4,9', '32'];
98
109
  for (k = 0; k < 3; k++) {
99
110
  var cw = (cwid - 2 * 18 * u) / 3, kx = cx0 + cw / 2 + k * (cw + 18 * u), ky = -wh / 2 + 160 * u, tk = Q(vals[k], T0 + 1.0 + k * 0.2);
100
- var cd = forma(comp, 'retangulo', { x: kx, y: ky, w: cw, h: 130 * u, r: 24 * u, cor: k == 0 ? P.ac : P.bg, alfa: k == 0 ? 100 : 70, parent: frente, entrada: 'pop', t0: tk, quique: 0.6, nome: 'card' });
101
- texto(comp, String(vals[k]), { fonte: P.fonteTitulo, tamanho: 46 * u, cor: k == 0 ? kSobre(P) : P.fg, x: kx, y: ky - 6 * u, parent: frente, entrada: 'palavra', t0: tk + 0.08, largura: cw * 0.86, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
111
+ var cd = forma(comp, 'retangulo', { x: kx, y: ky, w: cw, h: 130 * u, r: 24 * u, cor: k == 0 ? kCardAc(P) : P.bg, alfa: k == 0 ? 100 : 70, parent: frente, entrada: 'pop', t0: tk, quique: 0.6, nome: 'card' });
112
+ texto(comp, String(vals[k]), { fonte: P.fonteTitulo, tamanho: 46 * u, cor: k == 0 ? kSobreCard(P) : P.fg, x: kx, y: ky - 6 * u, parent: frente, entrada: 'palavra', t0: tk + 0.08, largura: cw * 0.86, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
102
113
  som(tk, 'pop', 0.5);
103
114
  }
104
115
  // gráfico de barras dentro da janela
@@ -152,8 +163,8 @@ function cenaCameraNulls(comp, P, d) {
152
163
  for (i = 0; i < n; i++) {
153
164
  var ox = v ? 0 : i * gap, oy = v ? i * gap : 0, ti = Q(it[i], 0.3 + i * passo);
154
165
  ts.push(ti);
155
- var c = forma(comp, 'retangulo', { x: ox, y: oy, w: pw, h: ph, r: 56 * u, cor: i % 2 ? P.card : P.ac, parent: zoom, nome: 'painel' });
156
- var tc = i % 2 ? P.fg : kSobre(P), ta = i % 2 ? P.ac : kSobre(P);
166
+ var c = forma(comp, 'retangulo', { x: ox, y: oy, w: pw, h: ph, r: 56 * u, cor: i % 2 ? P.card : kCardAc(P), parent: zoom, nome: 'painel' });
167
+ var tc = i % 2 ? P.fg : kSobreCard(P), ta = i % 2 ? P.ac : kSobreCard(P);
157
168
  texto(comp, String(i + 1), { fonte: P.fonteTitulo, tamanho: 220 * u, cor: ta, corDestaque: ta, x: ox - pw * 0.28, y: oy - ph * 0.25, parent: zoom, entrada: 'palavra', t0: 0.3, largura: pw * 0.4, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
158
169
  elemento(comp, i, { x: ox + pw * 0.25, y: oy - ph * 0.22, w: pw * 0.38, parent: zoom, flutua: 8, fase: i });
159
170
  texto(comp, it[i], { tamanho: 70 * u, cor: tc, corDestaque: ta, x: ox, y: oy + ph * 0.22, parent: zoom, entrada: 'mascara', t0: 0.4, largura: pw * 0.82, maxLinhas: 3, sublinhado: false, livre: true, fala: false });
@@ -185,7 +196,7 @@ function cenaBarras(comp, P, d) {
185
196
  var tops = [];
186
197
  for (i = 0; i < n; i++) {
187
198
  var altura = ah * (0.2 + 0.8 * Math.pow((i + 1) / n, 1.5)), bx = area[0] + gap * (i + 0.5), ti = Q(it[i], T0 + 0.3 + i * 0.4), ult = i == n - 1;
188
- var b = forma(comp, 'retangulo', { x: bx, y: area[3], w: bw, h: altura, r: 18 * u, cor: ult ? P.ac : P.card, traco: ult ? null : P.mudo, tracoL: 3 * u, parent: cam.no, nome: 'barra' });
199
+ var b = forma(comp, 'retangulo', { x: bx, y: area[3], w: bw, h: altura, r: 18 * u, cor: ult ? kCardAc(P) : P.card, traco: ult ? null : P.mudo, tracoL: 3 * u, parent: cam.no, nome: 'barra' });
189
200
  kA(b).setValue([0, altura / 2]);
190
201
  mola(kS(b), ti, [100, 0], [100, 100], { quique: ult ? 0.7 : 0.4, dur: 0.7 });
191
202
  som(ti, ult ? 'impacto' : 'pop', ult ? 0.8 : 0.55);
@@ -274,14 +285,14 @@ function cenaCardsPulam(comp, P, d) {
274
285
  var cw = v ? W * 0.8 : W * 0.44, ch = Math.min(v ? H * 0.15 : H * 0.2, (H * 0.62) / n - 16 * u), top = v ? H * 0.31 : H * 0.5 - (n * (ch + 18 * u)) / 2 + ch / 2, cx0 = v ? g.cx : W * 0.72;
275
286
  for (i = 0; i < n; i++) {
276
287
  var y = top + i * (ch + 18 * u) + (v ? ch / 2 : 0), ti = Q(it[i], T0 + 0.3 + i * 0.42), ult = i == n - 1, rot = (i % 2 ? 1 : -1) * 5;
277
- var c = forma(comp, 'retangulo', { x: cx0, y: y, w: cw, h: ch, r: 36 * u, cor: ult ? P.ac : P.card, traco: ult ? null : P.mudo, tracoL: 3 * u, parent: cam.no, nome: 'card' });
288
+ var c = forma(comp, 'retangulo', { x: cx0, y: y, w: cw, h: ch, r: 36 * u, cor: ult ? kCardAc(P) : P.card, traco: ult ? null : P.mudo, tracoL: 3 * u, parent: cam.no, nome: 'card' });
278
289
  mola(kP(c), ti, [cx0 + (i % 2 ? 1 : -1) * 40 * u, y + H * 0.45], [cx0, y], { quique: 0.75, dur: 0.85 });
279
290
  mola(kR(c), ti, rot * 2, 0, { quique: 0.6, dur: 0.9 });
280
291
  curva(kO(c), [[ti - 0.04, 0, null, 30], [ti, 100, null, 30]]);
281
292
  som(ti + 0.22, 'pop', 0.6);
282
293
  var ex = cx0 - cw / 2 + ch * 0.62;
283
294
  var ic = elemento(comp, i, { x: ex, y: y, w: ch * 0.78, parent: cam.no, entrada: 'pop', t0: ti + 0.3, quique: 0.7 });
284
- texto(comp, it[i], { tamanho: Math.min(58 * u, ch * 0.42), cor: ult ? kSobre(P) : P.fg, corDestaque: ult ? kSobre(P) : P.ac, x: cx0 + ch * 0.4, y: y, parent: cam.no, entrada: 'sobe', t0: ti + 0.3, largura: cw - ch * 1.6, maxLinhas: 2, sublinhado: false, fala: false });
295
+ texto(comp, it[i], { tamanho: Math.min(58 * u, ch * 0.42), cor: ult ? kSobreCard(P) : P.fg, corDestaque: ult ? kSobreCard(P) : P.ac, x: cx0 + ch * 0.4, y: y, parent: cam.no, entrada: 'sobe', t0: ti + 0.3, largura: cw - ch * 1.6, maxLinhas: 2, sublinhado: false, fala: false });
285
296
  }
286
297
  cam.empurra(0.8, 0, -10 * u, 1.03).empurra(comp.duration - 0.2, 0, 20 * u, 1.08);
287
298
  fecha(comp);
@@ -314,20 +325,24 @@ function cenaCursor(comp, P, d) {
314
325
  var bt = botao(comp, { x: px, y: by, w: bw, h: bh, rotulo: d.botao || 'Enviar', cor: P.ac, corTexto: kSobre(P), t0: T0 + 0.4, tam: 46 * u, parent: cam.no });
315
326
  var cur = kCursor(comp, cam.no, u, P.fg, P.bg);
316
327
  var tc = T0 + 0.9, txt = d.texto || d.sub || d.rotulo || 'o que você quer';
328
+ // o cursor é elemento: clica no fim do campo (o texto digitado nasce à esquerda) e na borda do botão, nunca em cima de letra
329
+ var xt0 = px - campoW / 2 + 36 * u, wt = Math.min(kMedir(comp, txt, P.fonteTexto, 42 * u), campoW * 0.86);
330
+ var pc = kCursorNoBotao(comp, [px - campoW / 2, cy1 - campoH / 2, px + campoW / 2, cy1 + campoH / 2], { x: xt0 + wt / 2, y: cy1, w: wt + 12 * u, h: 60 * u }, 0, 0, 1, [px + campoW * 0.34, cy1 + 6 * u], u);
331
+ var pb = kCursorNoBotao(comp, [px - bt.w / 2, by - bt.h / 2, px + bt.w / 2, by + bt.h / 2], bt.texto, 0, 0, 1, [px + bt.w / 2 - bt.h * 0.45, by + bt.h * 0.12], u);
317
332
  kP(cur).setValue([px + pw * 0.6, py + ph * 0.9]);
318
- curva(kP(cur), [[tc - 0.7, [px + pw * 0.6, py + ph * 0.9], null, 20], [tc, [px - campoW * 0.3, cy1 + 10 * u], 75, 30]]);
333
+ curva(kP(cur), [[tc - 0.7, [px + pw * 0.6, py + ph * 0.9], null, 20], [tc, pc, 75, 30]]);
319
334
  curva(kO(cur), [[tc - 0.7, 0, null, 30], [tc - 0.5, 100, 60, null]]);
320
335
  curva(kS(cur), [[tc, [100, 100], null, 25], [tc + 0.07, [82, 82], 70, 40], [tc + 0.2, [100, 100], 70, null]]);
321
336
  curva(kO(foco), [[tc, 0, null, 30], [tc + 0.12, 100, 60, null]]);
322
337
  som(tc, 'clique', 0.8);
323
338
  var dg = digita(comp, txt, { x: px - campoW / 2 + 36 * u, y: cy1, tamanho: 42 * u, fonte: P.fonteTexto, cor: P.fg, largura: campoW * 0.86, parent: cam.no, t0: tc + 0.25, cps: 26 });
324
339
  var t2 = Math.max(dg.fim + 0.15, tc + 0.9);
325
- curva(kP(cur), [[tc + 0.1, [px - campoW * 0.3, cy1 + 10 * u], null, 25], [t2, [px + bw * 0.1, by + 8 * u], 75, 30]]);
340
+ curva(kP(cur), [[tc + 0.1, pc, null, 25], [t2, pb, 75, 30]]);
326
341
  curva(kS(cur), [[t2, [100, 100], null, 25], [t2 + 0.07, [82, 82], 70, 40], [t2 + 0.2, [100, 100], 70, null]]);
327
342
  bt.aperta(t2);
328
- var res = forma(comp, 'pilula', { x: px, y: py + ph * 0.02, w: campoW * 0.9, h: 96 * u, cor: P.ac, parent: cam.no, entrada: 'pop', t0: t2 + 0.25, quique: 0.7, nome: 'resultado' });
329
- check(comp, { x: px - campoW * 0.36, y: py + ph * 0.02, tam: 44 * u, cor: kSobre(P), parent: cam.no, t0: t2 + 0.4 });
330
- texto(comp, d.rotulo || 'Pronto!', { fonte: P.fonteTitulo, tamanho: 44 * u, cor: kSobre(P), corDestaque: kSobre(P), x: px + 20 * u, y: py + ph * 0.02, parent: cam.no, entrada: 'sobe', t0: t2 + 0.3, largura: campoW * 0.6, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
343
+ var res = forma(comp, 'pilula', { x: px, y: py + ph * 0.02, w: campoW * 0.9, h: 96 * u, cor: kCardAc(P), parent: cam.no, entrada: 'pop', t0: t2 + 0.25, quique: 0.7, nome: 'resultado' });
344
+ check(comp, { x: px - campoW * 0.36, y: py + ph * 0.02, tam: 44 * u, cor: kSobreCard(P), parent: cam.no, t0: t2 + 0.4 });
345
+ texto(comp, d.rotulo || 'Pronto!', { fonte: P.fonteTitulo, tamanho: 44 * u, cor: kSobreCard(P), corDestaque: kSobreCard(P), x: px + 20 * u, y: py + ph * 0.02, parent: cam.no, entrada: 'sobe', t0: t2 + 0.3, largura: campoW * 0.6, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
331
346
  som(t2 + 0.28, 'brilho', 0.7);
332
347
  curva(kO(cur), [[t2 + 0.7, 100, null, 30], [t2 + 0.95, 0, 60, null]]);
333
348
  cam.empurra(0.8, 0, 0, 1.02).empurra(tc, 0, -10 * u, 1.06).empurra(t2 + 0.2, 0, 30 * u, 1.1).empurra(comp.duration - 0.2, 0, 40 * u, 1.12);
@@ -687,8 +702,29 @@ function cenaParedeLogos(comp, P, d) {
687
702
  }
688
703
  }
689
704
  kR(par).setValue(-8);
690
- var plaquinha = forma(comp, 'retangulo', { x: g.cx, y: g.cy, w: W * (v ? 0.88 : 0.62), h: H * (v ? 0.2 : 0.26), r: 48 * u, cor: P.bg, traco: P.ac, tracoL: 8 * u, parent: cam.no, entrada: 'pop', t0: T0 + 0.7, quique: 0.5, nome: 'faixa' });
691
- kT(g, comp, P, d.titulo || '', g.cy, { maxLinhas: 2, tam: v ? 84 : 72, t0: T0 + 0.8, largura: W * (v ? 0.8 : 0.56), sobrepoe: true });
705
+ var pw0 = W * (v ? 0.88 : 0.62), ph0 = H * (v ? 0.2 : 0.26);
706
+ var plaquinha = forma(comp, 'retangulo', { x: g.cx, y: g.cy, w: pw0, h: ph0, r: 48 * u, cor: P.bg, traco: P.ac, tracoL: 8 * u, parent: cam.no, nome: 'faixa' });
707
+ var n0 = comp.numLayers;
708
+ var tt = kT(g, comp, P, d.titulo || '', g.cy, { maxLinhas: 2, tam: v ? 84 : 72, t0: T0 + 0.8, largura: W * (v ? 0.8 : 0.56), sobrepoe: true });
709
+ // texto dentro de cartão, pela guarda: cabe com folga; senão o corpo encolhe até o mínimo; senão a faixa cresce (o texto
710
+ // principal ganha linha e "PrimoCode" e "de bolso" vazavam da faixa fixa)
711
+ if (tt && tt.h) {
712
+ var tc = [tt.x - tt.w / 2, tt.y - tt.h / 2, tt.x + tt.w / 2, tt.y + tt.h / 2], pc = [g.cx - pw0 / 2, g.cy - ph0 / 2, g.cx + pw0 / 2, g.cy + ph0 / 2];
713
+ var tMin = (H / W > 1.6 ? 112 : H * 0.036) * KIT.ajuste * KIT.ajusteCena; // o piso do texto() (principal no 9:16, 3,6% fora)
714
+ var gc = gTextoNoCartao(tc, pc, { folga: 55 * u, kMin: Math.min(1, tMin / Math.max(1, tt.tam)), max: [W * 0.03, H * 0.1, W * 0.97, H * 0.9] });
715
+ if (gc.k < 0.999) { // o corpo encolhe em volta do centro do bloco: cada camada criada pelo título (palavras, sublinhado)
716
+ var j0, cxT = tt.x, cyT = tt.y;
717
+ for (j0 = 1; j0 <= comp.numLayers - n0; j0++) { var Lt = comp.layer(j0), pv = kP(Lt).valueAtTime(comp.duration, false); kDesloca(Lt, (pv[0] - cxT) * (gc.k - 1), (pv[1] - cyT) * (gc.k - 1), gc.k); }
718
+ }
719
+ if (gc.cresceu) {
720
+ var pw = gW(gc.cartao), ph = gH(gc.cartao);
721
+ try { plaquinha.property('ADBE Root Vectors Group').property(1).property('ADBE Vectors Group').property('ADBE Vector Shape - Rect').property('ADBE Vector Rect Size').setValue([pw, ph]); }
722
+ catch (e) { kS(plaquinha).setValue([100 * pw / pw0, 100 * ph / ph0]); }
723
+ kP(plaquinha).setValue([gCx(gc.cartao), gCy(gc.cartao)]);
724
+ }
725
+ if (gc.log.length) writeLn('GUARDA ' + JSON.stringify({ cena: KIT.cenaIdx, w: W, h: H, log: gc.log, caixas: [{ tipo: 'cartao', nome: 'faixa do título', c: [Math.round(gc.cartao[0]), Math.round(gc.cartao[1]), Math.round(gc.cartao[2]), Math.round(gc.cartao[3])], fora: 0 }], violacoes: gc.ok ? [] : ['título maior que a faixa máxima'] }));
726
+ }
727
+ kEntrada(plaquinha, T0 + 0.7, 'pop', 0.5);
692
728
  som(T0, 'sobe', 0.5); som(T0 + 0.7, 'impacto', 0.7);
693
729
  cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.08);
694
730
  fecha(comp);
@@ -719,10 +755,10 @@ function cenaPrompt(comp, P, d) {
719
755
  for (i = 0; i < n; i++) {
720
756
  var ry = by + 70 * u + rh / 2 + i * (rh + 14 * u), ti = Q(it[i], tr + i * 0.2);
721
757
  ti = Math.max(ti, tr);
722
- forma(comp, 'retangulo', { x: px, y: ry, w: iw, h: rh, r: 26 * u, cor: i == 0 ? P.ac : P.bg, parent: cam.no, entrada: 'pop', t0: ti, quique: 0.7, nome: 'resultado' });
758
+ forma(comp, 'retangulo', { x: px, y: ry, w: iw, h: rh, r: 26 * u, cor: i == 0 ? kCardAc(P) : P.bg, parent: cam.no, entrada: 'pop', t0: ti, quique: 0.7, nome: 'resultado' });
723
759
  if (v) elemento(comp, i, { x: px - iw / 2 + rh * 0.7, y: ry, w: rh * 0.7, parent: cam.no, entrada: 'pop', t0: ti + 0.06 }); // em 16:9 a linha é baixa: só o texto e o check (ícone de 12% da largura não cabe)
724
- texto(comp, it[i], { tamanho: Math.min(40 * u, rh * 0.45), cor: i == 0 ? kSobre(P) : P.fg, corDestaque: P.ac, x: px + rh * 0.3, y: ry, parent: cam.no, entrada: 'sobe', t0: ti + 0.1, largura: iw - rh * 2, maxLinhas: 1, sublinhado: false, fala: false });
725
- check(comp, { x: px + iw / 2 - rh * 0.6, y: ry, tam: rh * 0.34, cor: i == 0 ? kSobre(P) : P.ac, parent: cam.no, t0: ti + 0.25 });
760
+ texto(comp, it[i], { tamanho: Math.min(40 * u, rh * 0.45), cor: i == 0 ? kSobreCard(P) : P.fg, corDestaque: P.ac, x: px + rh * 0.3, y: ry, parent: cam.no, entrada: 'sobe', t0: ti + 0.1, largura: iw - rh * 2, maxLinhas: 1, sublinhado: false, fala: false });
761
+ check(comp, { x: px + iw / 2 - rh * 0.6, y: ry, tam: rh * 0.34, cor: i == 0 ? kSobreCard(P) : P.ac, parent: cam.no, t0: ti + 0.25 });
726
762
  som(ti, 'notif', 0.6);
727
763
  }
728
764
  cam.empurra(0.8, 0, 0, 1.02).empurra(tr, 0, 20 * u, 1.06).empurra(comp.duration - 0.2, 0, 30 * u, 1.1);
@@ -829,9 +865,9 @@ function cenaLeque(comp, P, d) {
829
865
  var ang = (i - (n - 1) / 2) * spread, ti = Q(it[i], T0 + 0.35 + i * 0.28), ult = i == n - 1;
830
866
  var carta = grupo(comp, { x: px, y: py, parent: cam.no, nome: 'carta' });
831
867
  kA(carta).setValue([0, 0]);
832
- var c = forma(comp, 'retangulo', { x: 0, y: -ch * 0.78, w: cw, h: ch, r: 34 * u, cor: ult ? P.ac : P.card, traco: ult ? null : P.mudo, tracoL: 3 * u, parent: carta, nome: 'carta-corpo' });
868
+ var c = forma(comp, 'retangulo', { x: 0, y: -ch * 0.78, w: cw, h: ch, r: 34 * u, cor: ult ? kCardAc(P) : P.card, traco: ult ? null : P.mudo, tracoL: 3 * u, parent: carta, nome: 'carta-corpo' });
833
869
  elemento(comp, i, { x: 0, y: -ch * 0.95, w: cw * 0.6, parent: carta });
834
- texto(comp, it[i], { fonte: P.fonteTexto, tamanho: 36 * u, cor: ult ? kSobre(P) : P.fg, corDestaque: P.ac, x: 0, y: -ch * 0.52, parent: carta, entrada: 'fade', t0: ti, largura: cw * 0.84, maxLinhas: 2, sublinhado: false, fala: false, livre: true });
870
+ texto(comp, it[i], { fonte: P.fonteTexto, tamanho: 36 * u, cor: ult ? kSobreCard(P) : P.fg, corDestaque: P.ac, x: 0, y: -ch * 0.52, parent: carta, entrada: 'fade', t0: ti, largura: cw * 0.84, maxLinhas: 2, sublinhado: false, fala: false, livre: true });
835
871
  mola(kR(carta), ti - 0.1, 0, ang, { quique: 0.55, dur: 0.8 });
836
872
  curva(kO(carta), [[ti - 0.14, 0, null, 30], [ti - 0.1, 100, null, 30]]);
837
873
  som(ti, 'papel', 0.6);
@@ -865,8 +901,10 @@ function cenaRolagem(comp, P, d) {
865
901
  var cw = v ? W * 0.8 : W * 0.38, ch = v ? H * 0.2 : H * 0.28, gap = 28 * u, x = v ? g.cx : W * 0.7;
866
902
  var feed = grupo(comp, { x: x, y: 0, parent: cam.no, nome: 'feed' });
867
903
  var total = n * (ch + gap), y0 = v ? H * 0.3 : H * 0.14, tl = T0 + 0.2, dd = 1.3;
868
- for (i = 0; i < n + 1; i++) {
869
- var y = y0 + i * (ch + gap) + ch / 2, idx = i % n;
904
+ // no 9:16 com título, o feed sobe UM cartão: começa um cartão abaixo do topo do conteúdo (Ct do kT) pra nunca passar por baixo do título
905
+ if (v && d.titulo) y0 = H * 0.2 + gap + (ch + gap);
906
+ for (i = 0; i < n; i++) { // um post por item: nada de repetir o primeiro no fim pra encher o feed
907
+ var y = y0 + i * (ch + gap) + ch / 2, idx = i;
870
908
  forma(comp, 'retangulo', { x: 0, y: y, w: cw, h: ch, r: 36 * u, cor: P.card, traco: P.mudo, tracoL: 3 * u, parent: feed, nome: 'post' });
871
909
  elemento(comp, i, { x: -cw / 2 + ch * 0.55, y: y, w: ch * 0.7, parent: feed });
872
910
  texto(comp, it[idx], { tamanho: Math.min(44 * u, ch * 0.28), cor: P.fg, corDestaque: P.ac, x: ch * 0.28, y: y - ch * 0.12, parent: feed, entrada: 'fade', t0: T0, largura: cw - ch * 1.5, maxLinhas: 2, sublinhado: false, fala: false, livre: true });
@@ -909,3 +947,121 @@ function cenaEstrelas(comp, P, d) {
909
947
  cam.empurra(0.8, 0, 0, 1.02).empurra(comp.duration - 0.2, 0, 0, 1.08);
910
948
  fecha(comp);
911
949
  }
950
+
951
+ // ---------------------------------------------------------------- 31. foto: a foto do pedido em 3 planos, em 3D, com passeio de câmera
952
+ /* d.foto = {arquivo, fundo, recorte, w, h, moldura} (o Node já copiou pro projeto, desfocou o fundo e tentou o recorte);
953
+ d.movimento (aproxima · passeio · revela · orbita · afasta) é o que o diretor sorteou a partir da câmera da cena, e
954
+ d.sentido (±1) alterna entre fotos seguidas. Planos: 1) o fundo desfocado e ampliado, 2) a foto (recorte com alfa, ou a foto
955
+ inteira numa moldura arredondada que é uma comp própria: o passeio acontece DENTRO da moldura), em 3D com rotação,
956
+ 3) elementos reais na frente, que andam mais que a foto (parallax). Tudo com ease: nada linear. */
957
+ function kFotoImp(arq) {
958
+ if (!arq) return null;
959
+ KIT.cache = KIT.cache || {};
960
+ if (KIT.cache[arq]) return KIT.cache[arq];
961
+ try { var it = app.project.importFile(new ImportOptions(new File(arq))); KIT.cache[arq] = it; return it; }
962
+ catch (er) { KIT.avisos.push('cena ' + KIT.cenaIdx + ': a foto não abriu: ' + arq); return null; }
963
+ }
964
+ function kFotoPonto(cw, ch, pan, z, fx, fy) { var lim = (z - 1) / 2 * pan; return [cw / 2 - fx * lim * cw, ch / 2 - fy * lim * ch]; }
965
+ function kRot(L, eixo) { return L.property('ADBE Transform Group').property('ADBE Rotate ' + eixo); }
966
+ function cenaFoto(comp, P, d) {
967
+ var F = d.foto, itF = F ? kFotoImp(F.recorte || F.arquivo) : null;
968
+ if (!itF) { KIT.avisos.push('cena ' + KIT.cenaIdx + ': sem foto, a cena virou herói'); return cenaHeroi(comp, P, d); }
969
+ var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.1, i, dur = comp.duration;
970
+ var mov = d.movimento || 'aproxima', sg = d.sentido || 1, recorte = !!F.recorte;
971
+ var ease = [60, 60];
972
+ // 1. o plano de trás: a própria foto desfocada, ampliada, com um véu da cor da cena; anda ao contrário da foto
973
+ var itB = F.fundo ? kFotoImp(F.fundo) : null;
974
+ if (itB) {
975
+ var bg = comp.layers.add(itB); bg.name = 'foto-fundo'; // sem pai: a câmera encolhe o quadro (88%) e deixaria borda; o fundo tem o movimento dele
976
+ var kb = Math.max(W / itB.width, H / itB.height) * 100;
977
+ kP(bg).setValue([g.cx, g.cy]);
978
+ curva(kS(bg), [[0, [kb * 1.32, kb * 1.32], ease[0], ease[1]], [dur, [kb * 1.5, kb * 1.5], ease[0], ease[1]]]);
979
+ curva(kP(bg), [[0, [g.cx + sg * W * 0.035, g.cy], ease[0], ease[1]], [dur, [g.cx - sg * W * 0.035, g.cy - H * 0.01], ease[0], ease[1]]]);
980
+ var veu = comp.layers.addSolid(kCor(P.bg), 'foto-veu', W, H, 1); kO(veu).setValue(40);
981
+ }
982
+ // 2. a foto: o tamanho da caixa respeita a proporção dela (nada cortado) e a zona do texto
983
+ var fw = recorte ? (F.rw || F.w) : F.w, fh = recorte ? (F.rh || F.h) : F.h; // o recorte já vem aparado: só a pessoa
984
+ var LY = d.layout || null; // o plano da GUARDA (maquina-guarda.js): foto grande, título sem encostar, ícones só no espaço livre
985
+ var bx = v ? W * (recorte ? 0.9 : 0.8) : W * 0.4, by = v ? H * (recorte ? 0.5 : 0.46) : H * (recorte ? 0.84 : 0.74);
986
+ var cwf = Math.min(bx, by * fw / fh), chf = cwf * fh / fw;
987
+ var px = v ? g.cx : W * 0.3, py = recorte ? (v ? H * 0.64 - chf / 2 : H * 0.9 - chf / 2) : (v ? H * 0.38 : g.cy);
988
+ var corte = 1; // a parte da altura que aparece (foto alta cortada em 4:5 pela guarda)
989
+ if (LY && LY.foto) { cwf = LY.foto.w; chf = LY.foto.h; px = LY.foto.x; py = LY.foto.y; corte = LY.foto.corte || 1; }
990
+ var halo;
991
+ if (LY && LY.halo && LY.halo.d) halo = forma(comp, 'circulo', { x: LY.halo.x, y: LY.halo.y, w: LY.halo.d, cor: P.ac, alfa: 70, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'halo' });
992
+ else if (LY && LY.halo) halo = forma(comp, 'retangulo', { x: LY.halo.x, y: LY.halo.y, w: LY.halo.w, h: LY.halo.h, r: 46 * u, cor: P.ac, alfa: 34, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'halo' });
993
+ else halo = forma(comp, 'circulo', { x: px, y: py, w: Math.max(cwf, chf) * (recorte ? 0.95 : 1.05), cor: P.ac, alfa: recorte ? 70 : 26, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'halo' });
994
+ var card, kBase0;
995
+ if (recorte) {
996
+ card = comp.layers.add(itF); card.name = 'foto-recorte'; kPai(card, cam.no);
997
+ kBase0 = cwf / itF.width * 100;
998
+ kA(card).setValue([itF.width / 2, itF.height * corte / 2]);
999
+ // o corte da foto (ombros e braços na beira, ou o corpo abaixo do 4:5) some num degradê: a pessoa não termina numa linha reta
1000
+ try { var mk = card.property('ADBE Mask Parade').addProperty('ADBE Mask Atom'); mk.property('ADBE Mask Shape').setValue(kRetShape(itF.width * 0.02, -itF.height, itF.width * 0.98, itF.height * corte * 0.96, 0)); mk.property('ADBE Mask Feather').setValue([itF.width * 0.05, itF.height * corte * 0.12]); } catch (eM) { }
1001
+ } else {
1002
+ var inner = novaComp('foto-int ' + KIT.cenaIdx, Math.round(cwf), Math.round(chf), dur, KIT.fps);
1003
+ var im = inner.layers.add(itF); im.name = 'foto-img';
1004
+ var ki = Math.max(cwf / itF.width, chf / itF.height) * 100;
1005
+ kP(im).setValue([cwf / 2, chf / 2]);
1006
+ // o passeio DENTRO da moldura (ease, nunca linear): escala e deslocamento por movimento
1007
+ var z0 = 1, z1 = 1.2, x0 = 0, x1 = 0, y0 = 0, y1 = 0, pan = 0.8;
1008
+ if (mov == 'aproxima') { z0 = 1.0; z1 = 1.3; y0 = 0; y1 = -0.06; }
1009
+ else if (mov == 'passeio') { z0 = 1.26; z1 = 1.26; x0 = -sg * 0.5; x1 = sg * 0.5; y0 = 0.25; y1 = -0.25; }
1010
+ else if (mov == 'revela') { z0 = 1.45; z1 = 1.05; x0 = sg * 0.2; x1 = 0; y0 = 0.3; y1 = 0; }
1011
+ else if (mov == 'orbita') { z0 = 1.08; z1 = 1.22; x0 = -sg * 0.25; x1 = sg * 0.25; }
1012
+ else if (mov == 'afasta') { z0 = 1.34; z1 = 1.0; y0 = -0.12; y1 = 0.0; }
1013
+ curva(kS(im), [[0, [ki * z0, ki * z0], ease[0], ease[1]], [T0 + 0.9 * dur / 3, [ki * (z0 + (z1 - z0) * 0.5), ki * (z0 + (z1 - z0) * 0.5)], ease[0], ease[1]], [dur, [ki * z1, ki * z1], ease[0], ease[1]]]);
1014
+ curva(kP(im), [[0, kFotoPonto(cwf, chf, pan, z0, x0, y0), ease[0], ease[1]], [dur, kFotoPonto(cwf, chf, pan, z1, x1, y1), ease[0], ease[1]]]);
1015
+ card = comp.layers.add(inner); card.name = 'foto-moldura'; kPai(card, cam.no);
1016
+ kMascaraRect(card, 0, 0, cwf, chf, 38 * u);
1017
+ kBase0 = 100;
1018
+ }
1019
+ kP(card).setValue([px, py]);
1020
+ kS(card).setValue([kBase0, kBase0]);
1021
+ if (recorte) { // o recorte anda e cresce no próprio plano (a foto inteira não tem moldura onde passear)
1022
+ // com a guarda (LY) o passeio é mais curto na escala e no deslocamento: a foto fica GRANDE e o 3D (rotação) leva o movimento
1023
+ var rzA = LY ? 0.97 : 0.94, rzB = LY ? 1.06 : 1.12, rzC = LY ? 1.06 : 1.1, dxF = LY ? 0.025 : 0.04, dyF = LY ? 0.015 : 0.03;
1024
+ var rz1 = mov == 'afasta' || mov == 'revela' ? rzA : rzC, rz0 = mov == 'afasta' || mov == 'revela' ? rzB : 1.0;
1025
+ curva(kS(card), [[0, [kBase0 * rz0, kBase0 * rz0], ease[0], ease[1]], [dur, [kBase0 * rz1, kBase0 * rz1], ease[0], ease[1]]]);
1026
+ if (mov == 'passeio' || mov == 'orbita') curva(kP(card), [[0, [px - sg * W * dxF, py], ease[0], ease[1]], [dur, [px + sg * W * dxF, py], ease[0], ease[1]]]);
1027
+ else curva(kP(card), [[0, [px, py + H * dyF], ease[0], ease[1]], [dur, [px, py - H * dyF / 3], ease[0], ease[1]]]);
1028
+ }
1029
+ // sombra no chão e entrada: a foto chega girando e assenta (a rotação é o 3D; a mola é a entrada)
1030
+ var sombra = LY ? sombraChao(comp, px, py + chf * 0.5 - 30 * u, cwf * 0.5, { parent: cam.no, t0: T0 + 0.25, alfa: 22 }) // com a guarda: a sombra fica no envelope da foto
1031
+ : sombraChao(comp, px, py + chf * 0.5 + 18 * u, cwf * 0.7, { parent: cam.no, t0: T0 + 0.25, alfa: 28 });
1032
+ curva(kO(card), [[T0, 0, null, 30], [T0 + 0.3, 100, 60, null]]);
1033
+ kMarcaEntrada(T0 + 0.3);
1034
+ try {
1035
+ card.threeDLayer = true;
1036
+ var A = (mov == 'aproxima') ? [[-22, -7, 5], [8, 3, -2]] : (mov == 'passeio') ? [[-12, 0, 0], [12, 4, 0]] : (mov == 'revela') ? [[60, 0, 16], [0, 0, 0]] : (mov == 'orbita') ? [[-34, 0, 4], [34, 0, -2]] : [[14, 0, -12], [0, 0, 0]];
1037
+ var tm = Math.min(dur * 0.55, T0 + 0.95);
1038
+ curva(kRot(card, 'Y'), [[0, A[0][0] * sg, ease[0], ease[1]], [mov == 'passeio' || mov == 'orbita' ? dur : tm, (mov == 'passeio' || mov == 'orbita' ? A[1][0] : (A[0][0] + A[1][0]) / 2 * 0.4) * sg, ease[0], ease[1]], [dur, A[1][0] * sg, ease[0], ease[1]]]);
1039
+ curva(kRot(card, 'X'), [[0, A[0][2], ease[0], ease[1]], [dur, A[1][2], ease[0], ease[1]]]);
1040
+ } catch (e) { KIT.avisos.push('cena ' + KIT.cenaIdx + ': 3D da foto indisponível: ' + e); }
1041
+ som(T0, 'impacto', 0.6); som(T0 + 0.5, 'sobe', 0.45);
1042
+ // 3. na frente: elementos reais do assunto, que andam mais que a foto (parallax)
1043
+ var nEl = Math.min(KIT.elementos.length, v ? 3 : 2), S = Math.min(W, H) * (v ? 0.2 : 0.24);
1044
+ var posV = [[W * 0.2, H * 0.17], [W * 0.84, H * 0.5], [W * 0.18, H * 0.58]], posH = [[W * 0.54, H * 0.2], [W * 0.5, H * 0.8]];
1045
+ var ics = [];
1046
+ if (LY && LY.icones) { for (i = 0; i < LY.icones.length; i++) if (!LY.icones[i].fora) ics.push({ i: LY.icones[i].i, x: LY.icones[i].x, y: LY.icones[i].y, w: LY.icones[i].w }); }
1047
+ else for (i = 0; i < nEl; i++) ics.push({ i: i, x: (v ? posV[i] : posH[i])[0], y: (v ? posV[i] : posH[i])[1], w: S });
1048
+ for (i = 0; i < ics.length; i++) {
1049
+ var ph = ics[i].i * 1.9, a = 12 * u, kx = (ics[i].i % 2 ? -1 : 1) * W * 0.05;
1050
+ // o envelope do balanço vai pra caixa (folga): a guarda do kit reserva o caminho inteiro do ícone
1051
+ var el = elemento(comp, ics[i].i, { x: ics[i].x, y: ics[i].y, w: ics[i].w, parent: cam.no, entrada: 'pop', t0: T0 + 0.55 + i * 0.18, quique: 0.7, folgaX: a + Math.abs(kx), folgaY: a * 1.1 });
1052
+ kP(el).expression = 'value + [Math.sin(time*1.7+' + ph.toFixed(1) + ')*' + a.toFixed(1) + ' + ' + (sg * kx).toFixed(1) + ' * (time / ' + dur.toFixed(2) + ' - 0.5) * 2, Math.cos(time*1.3+' + ph.toFixed(1) + ')*' + (a * 1.1).toFixed(1) + ']';
1053
+ }
1054
+ if (d.titulo) {
1055
+ var TY = LY && LY.titulo;
1056
+ kT(g, comp, P, d.titulo, TY ? TY.y : (v ? H * 0.79 : g.cy), { x: TY ? TY.x : (v ? g.cx : W * 0.73), largura: TY ? TY.largura : (v ? W * 0.86 : W * 0.42), maxLinhas: 3, tam: TY ? TY.tam : (v ? 86 : 74), t0: T0 + 0.7 });
1057
+ }
1058
+ // a foto entra na guarda do kit (fecha → kGuarda) com a caixa do envelope: se o texto real encostar, ela sobe ou encolhe
1059
+ if (LY && LY.particulas) {
1060
+ var ec = LY.particulas;
1061
+ KIT.rects.push({ espaco: 'c', cena: KIT.cenaIdx, x0: ec[0], y0: ec[1], x1: ec[2], y1: ec[3], t0: 0, t1: 1, texto: false, livre: true, sobrepoe: true, guarda: 'foto', nome: 'a foto', camadas: [card, halo, sombra], infla: (ec[2] - ec[0]) / Math.max(1, cwf) });
1062
+ }
1063
+ var pa = LY && LY.particulas ? [LY.particulas[0], LY.particulas[1], LY.particulas[2], LY.particulas[3]] : [px - cwf * 0.6, py - chf * 0.6, px + cwf * 0.6, py + chf * 0.5];
1064
+ particulas(comp, { n: 10, cor: P.ac, parent: cam.no, tipo: 'flutua', raio: 8, area: pa, alfa: 70, t0: T0 + 0.4 });
1065
+ cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.06);
1066
+ fecha(comp);
1067
+ }
@@ -58,12 +58,18 @@ const PRIMITIVAS = {
58
58
  split: { fam: 'comparacao', entra: 2.6, papeis: ['problema', 'virada'], dur: 3.2, elemento: true },
59
59
  ondas: { fam: 'som', entra: 1.0, papeis: ['gancho', 'processo', 'virada'], dur: 2.8, elemento: true },
60
60
  estrelas: { fam: 'social', entra: 2.7, papeis: ['prova'], dur: 3.2, elemento: true },
61
+ // a foto do pedido (maquina-foto.js): só entra quando há foto; o sorteio comum nunca a escolhe
62
+ foto: { fam: 'foto', entra: 1.6, papeis: ['gancho', 'virada', 'prova', 'processo'], dur: 3.4, foto: true },
61
63
  };
62
64
  const TIPOS = Object.keys(PRIMITIVAS);
63
65
 
64
66
  const TRANSICOES = ['encolhe', 'empurra', 'zoom', 'persiana', 'chicote', 'desfoque', 'cortina', 'giro', 'tremor', 'abre', 'dissolve', 'corte']; // sem íris/pontos: o círculo claro crescendo parecia flash
65
67
  const CAMERAS = ['deriva', 'pulso', 'trilho', 'orbita', 'push', 'chicote', 'cinema']; // sem câmera na mão: tremedeira fora
66
68
 
69
+ /** Os passeios pela foto (Ken Burns com ease, nunca linear) e qual deles cada câmera do diretor pede. */
70
+ const MOVIMENTOS_FOTO = ['aproxima', 'passeio', 'revela', 'orbita', 'afasta'];
71
+ const CAMERA_FOTO = { push: 'aproxima', pulso: 'aproxima', deriva: 'passeio', trilho: 'passeio', orbita: 'orbita', chicote: 'revela', cinema: 'afasta' };
72
+
67
73
  /** Sistemas tipográficos: fontes + como a palavra entra + como a palavra-chave é realçada. */
68
74
  const TIPOGRAFIAS = {
69
75
  editorial: { fontes: { titulo: 'Poppins', texto: 'Plus Jakarta Sans', destaque: 'Playfair Display' }, entrada: 'palavra', realce: 'italico', passo: 0.1 },
@@ -229,7 +235,11 @@ function marcarChave(titulo) {
229
235
  return ps.join(' ');
230
236
  }
231
237
  const primeirasPalavras = (s, n) => String(s || '').replace(/\*/g, '').split(/\s+/).filter(Boolean).slice(0, n).join(' ');
232
- const itensDe = (v, n, m) => (Array.isArray(v) ? v : []).map((x) => limpa(typeof x === 'object' && x ? (x.titulo || x.texto || x.nome || '') : x, m)).filter(Boolean).slice(0, n);
238
+ /** A chave de um item pra achar repetido: sem caixa, acento, espaço nem pontuação. */
239
+ const chaveItem = (s) => String(s || '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-z0-9]/g, '');
240
+ /** Os itens de uma lista: limpos, sem vazio e SEM REPETIR (faltou item, a lista fica menor). */
241
+ const itensDe = (v, n, m) => (Array.isArray(v) ? v : []).map((x) => limpa(typeof x === 'object' && x ? (x.titulo || x.texto || x.nome || '') : x, m))
242
+ .filter((x, i, a) => x && a.findIndex((y) => chaveItem(y) === chaveItem(x)) === i).slice(0, n);
233
243
 
234
244
  /** Põe em `c` o que a primitiva `tipo` precisa e o modelo não mandou. `ctx`: {assunto, meta, proxima}. */
235
245
  function completar(c, tipo, ctx) {
@@ -239,7 +249,7 @@ function completar(c, tipo, ctx) {
239
249
  out.titulo = marcarChave(base);
240
250
  out.sub = limpa(c.sub, 70) || undefined;
241
251
  let it = itensDe(c.itens, 6, 28);
242
- if (spec.itens && it.length < spec.itens) it = (it.length ? it.concat(ctx.meta.itens) : ctx.meta.itens).slice(0, Math.max(spec.itens, it.length));
252
+ if (spec.itens && it.length < spec.itens) it = itensDe(it.length ? it.concat(ctx.meta.itens) : ctx.meta.itens, Math.max(spec.itens, it.length), 28); // completa sem repetir
243
253
  out.itens = it;
244
254
  out.valor = limpa(c.valor, 12) || (tipo === 'relogio' ? '7:00' : tipo === 'medidor' ? '82%' : tipo === 'estrelas' ? '4,9' : '100');
245
255
  out.rotulo = limpa(c.rotulo, 60) || undefined;
@@ -251,7 +261,7 @@ function completar(c, tipo, ctx) {
251
261
  out.toques = itensDe(c.toques, 4, 24);
252
262
  if (ctx.proxima) { out.prox = limpa(ctx.proxima.titulo || ctx.proxima.texto, 40).replace(/\*/g, ''); }
253
263
  out.fala = c.fala ? String(c.fala) : undefined;
254
- out.dur = Math.max(1.4, Math.min(tipo === 'celular' || tipo === 'prompt' ? 5 : 4.2, Number(c.dur || c.duracao) || spec.dur));
264
+ out.dur = Math.max(1.4, Math.min(tipo === 'celular' || tipo === 'prompt' ? 5 : tipo === 'foto' ? 4.8 : 4.2, Number(c.dur || c.duracao) || spec.dur));
255
265
  return out;
256
266
  }
257
267
 
@@ -269,8 +279,8 @@ function escolherPrimitiva(papel, ctx, rnd) {
269
279
  const recentes = new Map();
270
280
  ctx.historico.slice(-5).forEach((h, i, arr) => (h.primitivas || []).forEach((p) => recentes.set(p, (recentes.get(p) || 0) + 1 + i / arr.length)));
271
281
  const nao = new Set(ctx.meta.nao || []);
272
- const cand = TIPOS.filter((t) => PRIMITIVAS[t].papeis.includes(papel) && !usados.has(t) && !nao.has(t) && (!PRIMITIVAS[t].elemento || ctx.temElemento));
273
- const lista = (cand.length ? cand : TIPOS.filter((t) => !usados.has(t) && PRIMITIVAS[t].papeis.includes(papel))).map((t) => {
282
+ const cand = TIPOS.filter((t) => !PRIMITIVAS[t].foto && PRIMITIVAS[t].papeis.includes(papel) && !usados.has(t) && !nao.has(t) && (!PRIMITIVAS[t].elemento || ctx.temElemento));
283
+ const lista = (cand.length ? cand : TIPOS.filter((t) => !PRIMITIVAS[t].foto && !usados.has(t) && PRIMITIVAS[t].papeis.includes(papel))).map((t) => {
274
284
  let peso = 1;
275
285
  peso *= ctx.meta.prefere[t] != null ? ctx.meta.prefere[t] : 1;
276
286
  peso *= Math.pow(0.55, recentes.get(t) || 0);
@@ -326,24 +336,28 @@ function montarDirecao({ args, assunto, meta, duracao, hist, rnd, temElemento })
326
336
  while (papeis.length > n + 1 && papeis.includes('processo')) papeis.splice(papeis.lastIndexOf('processo'), 1);
327
337
  pedidas = papeis.map((papel) => ({ papel }));
328
338
  }
339
+ const nFotosPedido = Array.isArray(args.fotos) ? args.fotos.length : (Number(args.nFotos) || 0);
329
340
  const papeisUsados = [];
330
341
  const cenas = [];
331
342
  pedidas.forEach((c, i) => {
332
343
  let tipo = normTipo(c.tipo);
333
344
  const papel = c.papel || (i === 0 ? 'gancho' : i === pedidas.length - 1 ? 'cta' : ['problema', 'processo', 'prova', 'virada'][i % 4]);
334
345
  papeisUsados.push(papel);
346
+ if (tipo === 'foto' && !nFotosPedido) tipo = ''; // "foto" sem foto: o diretor escolhe outra peça
335
347
  if (!PRIMITIVAS[tipo]) tipo = papel === 'cta' ? 'cta' : escolherPrimitiva(papel, ctx, rnd); // todo vídeo termina num CTA de verdade (botão grande)
336
348
  ctx.usados.add(tipo);
337
349
  ctx.ultimaFam = PRIMITIVAS[tipo].fam;
338
350
  cenas.push({ c, tipo, papel });
339
351
  });
352
+ // a FOTO do pedido: pelo menos uma cena "foto" (a de abertura e/ou a de destaque), nunca duas coladas
353
+ if (nFotosPedido > 0) colocarFotos(cenas, nFotosPedido);
340
354
  // o assunto aparece em OBJETOS: pelo menos 2 cenas com primitiva que usa o elemento real (as que o modelo fixou ficam como estão)
341
355
  if (temElemento) {
342
356
  const nao = new Set(meta.nao || []);
343
357
  const conta = () => cenas.filter((x) => PRIMITIVAS[x.tipo].elemento).length;
344
358
  for (let i = 0; i < cenas.length && conta() < 2; i++) {
345
359
  const x = cenas[(i * 2 + 1) % cenas.length];
346
- if (PRIMITIVAS[x.tipo].elemento || (x.c && PRIMITIVAS[normTipo(x.c.tipo)])) continue;
360
+ if (x.tipo === 'foto' || PRIMITIVAS[x.tipo].elemento || (x.c && PRIMITIVAS[normTipo(x.c.tipo)])) continue;
347
361
  const cand = TIPOS.filter((t) => PRIMITIVAS[t].elemento && PRIMITIVAS[t].papeis.includes(x.papel) && !nao.has(t) && !cenas.some((y) => y.tipo === t));
348
362
  if (cand.length) { ctx.usados.delete(x.tipo); x.tipo = sortear(rnd, cand.map((t) => [t, meta.prefere[t] != null ? meta.prefere[t] : 1])); ctx.usados.add(x.tipo); }
349
363
  }
@@ -373,6 +387,7 @@ function montarDirecao({ args, assunto, meta, duracao, hist, rnd, temElemento })
373
387
  const camera = CAMERAS.includes(args.camera) ? args.camera : sortear(rnd, CAMERAS.map((x) => [x, (meta.cameras.includes(x) ? 3 : 1) * Math.pow(0.45, recC.get(x) || 0) * gm('cameras', x)]));
374
388
  const camera2 = sortear(rnd, CAMERAS.filter((x) => x !== camera).map((x) => [x, (meta.cameras.includes(x) ? 2 : 1) * gm('cameras', x)]));
375
389
  out.forEach((c, i) => { c.camera = c.camera && CAMERAS.includes(c.camera) ? c.camera : (i % 3 === 2 ? camera2 : camera); });
390
+ movimentosDasFotos(out);
376
391
 
377
392
  // tipografia
378
393
  const recTi = new Map(); hist.slice(-3).forEach((h) => recTi.set(h.tipografia, (recTi.get(h.tipografia) || 0) + 1));
@@ -391,7 +406,46 @@ function montarDirecao({ args, assunto, meta, duracao, hist, rnd, temElemento })
391
406
  return { metafora: meta, cenas: out, trocas, camera, tipografia, ritmo, paleta, ordemPal, assinatura };
392
407
  }
393
408
 
409
+ /**
410
+ * Põe a primitiva "foto" nas cenas: a de abertura primeiro, depois as de destaque (prova, virada, processo), nunca coladas,
411
+ * nunca no CTA, no máximo 3 cenas. Cena que o modelo já fixou como "foto" conta; cena fixada com outro tipo só cede se não
412
+ * sobrar nenhuma livre. Cada cena "foto" recebe `foto` (o índice da foto em args.fotos).
413
+ */
414
+ function colocarFotos(cenas, nFotos) {
415
+ const max = Math.max(1, Math.min(3, nFotos, Math.ceil(cenas.length / 2)));
416
+ const ehFoto = (x) => x.tipo === 'foto';
417
+ const fixa = (x) => !!(x.c && x.c.tipo && PRIMITIVAS[normTipo(x.c.tipo)] && normTipo(x.c.tipo) !== 'foto');
418
+ const vizinhaDeFoto = (i) => (cenas[i - 1] && ehFoto(cenas[i - 1])) || (cenas[i + 1] && ehFoto(cenas[i + 1]));
419
+ const podeSer = (i, livre) => i < cenas.length && !ehFoto(cenas[i]) && cenas[i].papel !== 'cta' && !(i === cenas.length - 1 && cenas.length > 1) && !vizinhaDeFoto(i) && (!livre || !fixa(cenas[i]));
420
+ const poe = (i) => { cenas[i].tipo = 'foto'; cenas[i].c = { ...cenas[i].c, tipo: 'foto' }; };
421
+ let n = cenas.filter(ehFoto).length;
422
+ const ordem = [0, ...cenas.map((x, i) => i).filter((i) => i > 0 && ['prova', 'virada', 'processo', 'problema'].includes(cenas[i].papel)), ...cenas.map((x, i) => i)];
423
+ for (const livre of [true, false]) {
424
+ for (const i of ordem) { if (n >= max) break; if (podeSer(i, livre)) { poe(i); n++; } }
425
+ if (n > 0) break; // a foto entrou: o resto das fixadas pelo modelo fica como está
426
+ }
427
+ if (!n && cenas.length) { poe(0); n = 1; } // uma cena só: a foto vira a cena
428
+ let k = 0;
429
+ cenas.forEach((x) => { if (ehFoto(x)) { x.c = { ...x.c, foto: x.c.foto != null ? x.c.foto : k % nFotos }; k++; } });
430
+ }
431
+
432
+ /** O passeio de cada foto: o que a câmera da cena pede, sem repetir o de nenhuma outra foto do vídeo (e o sentido alterna). */
433
+ function movimentosDasFotos(out) {
434
+ const usados = [];
435
+ let sentido = 1;
436
+ out.forEach((c) => {
437
+ if (c.tipo !== 'foto') return;
438
+ let mov = MOVIMENTOS_FOTO.includes(c.movimento) && !usados.includes(c.movimento) ? c.movimento : CAMERA_FOTO[c.camera] || 'aproxima';
439
+ if (usados.includes(mov)) mov = MOVIMENTOS_FOTO[(MOVIMENTOS_FOTO.indexOf(mov) + 1 + usados.length) % MOVIMENTOS_FOTO.length];
440
+ for (let g = 0; usados.includes(mov) && g < MOVIMENTOS_FOTO.length; g++) mov = MOVIMENTOS_FOTO[(MOVIMENTOS_FOTO.indexOf(mov) + 1) % MOVIMENTOS_FOTO.length];
441
+ usados.push(mov);
442
+ c.movimento = mov;
443
+ c.sentido = sentido;
444
+ sentido = -sentido;
445
+ });
446
+ }
447
+
394
448
  /** Quantos itens do vocabulário (para o teste e para o relatório). */
395
449
  const VOCABULARIO = () => ({ primitivas: TIPOS.length, transicoes: TRANSICOES.length, cameras: CAMERAS.length, tipografias: Object.keys(TIPOGRAFIAS).length });
396
450
 
397
- module.exports = { PRIMITIVAS, TIPOS, TRANSICOES, CAMERAS, TIPOGRAFIAS, METAFORAS, PALETAS, achar, dirigir, similaridade, lerHistorico, registrar, completar, marcarChave, normTipo, escolherPrimitiva, VOCABULARIO, LIMITE_PARECIDO, hashTexto, mulberry, arquivoHistorico };
451
+ module.exports = { itensDe, PRIMITIVAS, TIPOS, TRANSICOES, CAMERAS, MOVIMENTOS_FOTO, CAMERA_FOTO, colocarFotos, movimentosDasFotos, TIPOGRAFIAS, METAFORAS, PALETAS, achar, dirigir, similaridade, lerHistorico, registrar, completar, marcarChave, normTipo, escolherPrimitiva, VOCABULARIO, LIMITE_PARECIDO, hashTexto, mulberry, arquivoHistorico };