primocode 10.12.0 → 10.14.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 (207) hide show
  1. package/README.md +74 -12
  2. package/bin/primocode.js +148 -8
  3. package/lib/act.js +48 -1
  4. package/lib/catalogo.js +12 -1
  5. package/lib/config.js +1 -0
  6. package/lib/estudio/assinaturas/gerativo.json +1 -1
  7. package/lib/estudio/camadas.js +4 -0
  8. package/lib/estudio/cota.js +150 -0
  9. package/lib/estudio/dados/projetos/clube-ad5.json +105 -0
  10. package/lib/estudio/dados/projetos/clube-ae-sem-memoria.json +107 -0
  11. package/lib/estudio/dados/projetos/clube-agent.json +120 -0
  12. package/lib/estudio/dados/projetos/clube-antes-e-agora.json +86 -0
  13. package/lib/estudio/dados/projetos/clube-apresentando-clube.json +110 -0
  14. package/lib/estudio/dados/projetos/clube-bluevisa.json +106 -0
  15. package/lib/estudio/dados/projetos/clube-caught-in-the-fire.json +127 -0
  16. package/lib/estudio/dados/projetos/clube-chat-cliente-satisfeito.json +98 -0
  17. package/lib/estudio/dados/projetos/clube-chatgpt-anuncios.json +120 -0
  18. package/lib/estudio/dados/projetos/clube-claude-dj.json +140 -0
  19. package/lib/estudio/dados/projetos/clube-claude-mcp.json +142 -0
  20. package/lib/estudio/dados/projetos/clube-cleanui.json +106 -0
  21. package/lib/estudio/dados/projetos/clube-clinica-take-unico.json +128 -0
  22. package/lib/estudio/dados/projetos/clube-clinica.json +104 -0
  23. package/lib/estudio/dados/projetos/clube-clube-opener.json +125 -0
  24. package/lib/estudio/dados/projetos/clube-clube.json +108 -0
  25. package/lib/estudio/dados/projetos/clube-codigo.json +101 -0
  26. package/lib/estudio/dados/projetos/clube-distilbook.json +104 -0
  27. package/lib/estudio/dados/projetos/clube-drive-upload.json +115 -0
  28. package/lib/estudio/dados/projetos/clube-dub.json +119 -0
  29. package/lib/estudio/dados/projetos/clube-enxovaly.json +105 -0
  30. package/lib/estudio/dados/projetos/clube-fecha7.json +118 -0
  31. package/lib/estudio/dados/projetos/clube-filatov.json +128 -0
  32. package/lib/estudio/dados/projetos/clube-frame.json +108 -0
  33. package/lib/estudio/dados/projetos/clube-glass.json +118 -0
  34. package/lib/estudio/dados/projetos/clube-letra-em-apps.json +84 -0
  35. package/lib/estudio/dados/projetos/clube-letra-globo-3d.json +111 -0
  36. package/lib/estudio/dados/projetos/clube-letra-nos-apps.json +146 -0
  37. package/lib/estudio/dados/projetos/clube-letra-seis-mundos.json +100 -0
  38. package/lib/estudio/dados/projetos/clube-lula-3.json +146 -0
  39. package/lib/estudio/dados/projetos/clube-morph.json +119 -0
  40. package/lib/estudio/dados/projetos/clube-motion-designer.json +115 -0
  41. package/lib/estudio/dados/projetos/clube-motion-pacote.json +112 -0
  42. package/lib/estudio/dados/projetos/clube-movimento.json +103 -0
  43. package/lib/estudio/dados/projetos/clube-not-like-us.json +117 -0
  44. package/lib/estudio/dados/projetos/clube-pinterest-inspiracao.json +86 -0
  45. package/lib/estudio/dados/projetos/clube-pixel-e-tinta.json +108 -0
  46. package/lib/estudio/dados/projetos/clube-ponte.json +118 -0
  47. package/lib/estudio/dados/projetos/clube-processo-edit-tonal.json +126 -0
  48. package/lib/estudio/dados/projetos/clube-processo-motion-100-ia.json +109 -0
  49. package/lib/estudio/dados/projetos/clube-promo.json +114 -0
  50. package/lib/estudio/dados/projetos/clube-proposito.json +115 -0
  51. package/lib/estudio/dados/projetos/clube-rankhog.json +128 -0
  52. package/lib/estudio/dados/projetos/clube-recursao.json +116 -0
  53. package/lib/estudio/dados/projetos/clube-revelar.json +118 -0
  54. package/lib/estudio/dados/projetos/clube-robo.json +125 -0
  55. package/lib/estudio/dados/projetos/clube-shelby-ep2.json +144 -0
  56. package/lib/estudio/dados/projetos/clube-shelby.json +97 -0
  57. package/lib/estudio/dados/projetos/clube-solar-eclipse.json +96 -0
  58. package/lib/estudio/dados/projetos/clube-soldier.json +128 -0
  59. package/lib/estudio/dados/projetos/clube-som-de-cena.json +108 -0
  60. package/lib/estudio/dados/projetos/clube-spotify-candidatura.json +112 -0
  61. package/lib/estudio/dados/projetos/clube-tda-app.json +118 -0
  62. package/lib/estudio/dados/projetos/clube-tda-ia.json +75 -0
  63. package/lib/estudio/dados/projetos/clube-telegram-chat.json +118 -0
  64. package/lib/estudio/dados/projetos/clube-telegram-ilha.json +106 -0
  65. package/lib/estudio/dados/projetos/clube-tennessee-nike.json +132 -0
  66. package/lib/estudio/dados/projetos/clube-tudo-se-move.json +112 -0
  67. package/lib/estudio/dados/projetos/clube-uiad.json +107 -0
  68. package/lib/estudio/dados/projetos/clube-viralgrow.json +108 -0
  69. package/lib/estudio/dados/projetos/clube-whose-side.json +140 -0
  70. package/lib/estudio/dados/projetos/clube-youtube-inscrever.json +108 -0
  71. package/lib/estudio/dados/projetos/elementos-do-site-em-movimento.json +49 -0
  72. package/lib/estudio/dados/projetos/explainer-narrado.json +51 -0
  73. package/lib/estudio/dados/projetos/lancamento-de-produto.json +51 -0
  74. package/lib/estudio/dados/projetos/motion-so-com-sons.json +50 -0
  75. package/lib/estudio/dados/projetos/titulo-em-after-effects.json +51 -0
  76. package/lib/estudio/dados/projetos/tour-de-saas.json +50 -0
  77. package/lib/estudio/dados/projetos-ae/antes-e-agora/ae/cenas.jsx +733 -0
  78. package/lib/estudio/dados/projetos-ae/antes-e-agora/assets/fazer_assets.py +88 -0
  79. package/lib/estudio/dados/projetos-ae/letra-em-apps/ae/cenas.jsx +402 -0
  80. package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/preparar_assets.py +17 -0
  81. package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/svg_para_ae.py +25 -0
  82. package/lib/estudio/dados/projetos-ae/letra-nos-apps/BRIEF-AGENTES.md +31 -0
  83. package/lib/estudio/dados/projetos-ae/letra-nos-apps/README.md +53 -0
  84. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ROTEIRO-CENAS.md +69 -0
  85. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/A-agenda.jsx +148 -0
  86. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/B-docs.jsx +174 -0
  87. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/C-alttab.jsx +113 -0
  88. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/D-spotify.jsx +118 -0
  89. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/E-notas.jsx +146 -0
  90. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/F-jornal1.jsx +144 -0
  91. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/G-google.jsx +153 -0
  92. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/H-mapa.jsx +176 -0
  93. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/I-imagens.jsx +132 -0
  94. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/J-jornal2.jsx +191 -0
  95. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/K-maps.jsx +184 -0
  96. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera-movimentos.jsx +48 -0
  97. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera.jsx +57 -0
  98. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/card-spotify.jsx +98 -0
  99. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/digitar.jsx +38 -0
  100. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/jornal-comum.jsx +196 -0
  101. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra.jsx +23 -0
  102. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra_automatica.py +83 -0
  103. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/montagem.jsx +74 -0
  104. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/render_html.py +13 -0
  105. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/util.jsx +162 -0
  106. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-midia-comum.jsx +114 -0
  107. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/wg-util.jsx +360 -0
  108. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/F-jornal1/render_html.py +13 -0
  109. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/render_w-midia.py +26 -0
  110. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/inclin.py +30 -0
  111. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_d.py +36 -0
  112. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_e.py +27 -0
  113. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ajustar_movimentos.py +61 -0
  114. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ancorar.py +74 -0
  115. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/assar.py +47 -0
  116. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/camera3d_jornal.py +172 -0
  117. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/comparar.py +32 -0
  118. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/dados_ae_jornal.py +46 -0
  119. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/folha_contato.py +28 -0
  120. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/homografia_jornal.py +74 -0
  121. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/integrar.py +44 -0
  122. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/medir_quadro_a_quadro.py +32 -0
  123. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/mesa_2d.py +64 -0
  124. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/plot_cenas.py +11 -0
  125. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/retificar_quadros.py +38 -0
  126. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/assar_ab.py +52 -0
  127. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/medir_ab.py +59 -0
  128. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ajustar_h.py +111 -0
  129. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_corrige_cam.py +17 -0
  130. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ecc_cadeia.py +80 -0
  131. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_finalizar_cam.py +65 -0
  132. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_mosaico.py +61 -0
  133. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/01-gramatica-e-roteiro.md +55 -0
  134. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/02-camera.md +66 -0
  135. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/03-montagem-no-ae.md +50 -0
  136. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/04-render-e-conferencia.md +43 -0
  137. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/05-abertura-celular-mergulho.md +43 -0
  138. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces.md +42 -0
  139. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/atraso_imagem.py +31 -0
  140. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/fundo.py +56 -0
  141. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/juntar.py +45 -0
  142. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/montar.py +177 -0
  143. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/rastreio.py +74 -0
  144. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/sincronia.py +18 -0
  145. package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/ae/cenas.jsx +530 -0
  146. package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/assets/fazer_assets.py +107 -0
  147. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/assets/pessoas/figurinha.py +46 -0
  148. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/BRIEF-A.md +83 -0
  149. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/audio.py +179 -0
  150. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/confere.py +20 -0
  151. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte1.js +1 -0
  152. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte2.js +1 -0
  153. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/gerar_emojis.py +11 -0
  154. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/img/filmes/_indice.js +122 -0
  155. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/base.js +380 -0
  156. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/main.js +86 -0
  157. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1a.js +169 -0
  158. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1b.js +455 -0
  159. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1c.js +389 -0
  160. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p2.js +379 -0
  161. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/video.py +62 -0
  162. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/README.md +65 -0
  163. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/oraculo_take.py +137 -0
  164. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/render_take.py +388 -0
  165. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/take.js +235 -0
  166. package/lib/estudio/diretor.js +2 -1
  167. package/lib/estudio/editor.js +285 -0
  168. package/lib/estudio/efeitos-fabrica/terminal-digitando.jsx +2 -1
  169. package/lib/estudio/effectcraft.js +152 -0
  170. package/lib/estudio/exportar.js +181 -0
  171. package/lib/estudio/gerativo.js +44 -15
  172. package/lib/estudio/index.js +126 -5
  173. package/lib/estudio/kit/gerativo.jsx +15 -0
  174. package/lib/estudio/kit-ae/base.jsx +1146 -0
  175. package/lib/estudio/kit-ae/cenas.jsx +395 -0
  176. package/lib/estudio/kit-ae/primitivas.jsx +911 -0
  177. package/lib/estudio/kokoro.js +3 -1
  178. package/lib/estudio/maquina-diretor.js +393 -0
  179. package/lib/estudio/maquina-elementos.js +134 -0
  180. package/lib/estudio/maquina-layout.js +198 -0
  181. package/lib/estudio/maquina-som.js +478 -0
  182. package/lib/estudio/maquina-trilha.js +160 -0
  183. package/lib/estudio/maquina-voz.js +429 -0
  184. package/lib/estudio/maquina.js +516 -0
  185. package/lib/estudio/projetos.js +60 -0
  186. package/lib/estudio/remotion.js +6 -4
  187. package/lib/estudio/revisor.js +61 -0
  188. package/lib/estudio/voicestudio.js +81 -0
  189. package/lib/headless.js +71 -0
  190. package/lib/hooks.js +72 -0
  191. package/lib/local/cerebro.json +398 -12
  192. package/lib/local/entrada.js +46 -2
  193. package/lib/marca-kit.js +309 -0
  194. package/lib/mcp.js +286 -6
  195. package/lib/perigo.js +31 -3
  196. package/lib/planos.js +47 -19
  197. package/lib/preparar.js +111 -0
  198. package/lib/retomar.js +55 -0
  199. package/lib/sandbox.js +197 -0
  200. package/lib/skills.js +3 -1
  201. package/lib/tools.js +13 -4
  202. package/lib/ui.js +1 -1
  203. package/lib/video/auto.js +18 -3
  204. package/package.json +4 -2
  205. package/skills/after-effects/SKILL.md +52 -0
  206. package/skills/maquina-de-motion/SKILL.md +84 -0
  207. package/skills/motion-de-primeira/SKILL.md +33 -51
@@ -0,0 +1,1146 @@
1
+ /* base.jsx — o kit de motion do PrimoCode para o EffectCraft (ES3: só var e function).
2
+
3
+ Entra ANTES do script do modelo (concatenado pelo maquina.js; um `#include` também serve).
4
+ O modelo só chama funções de alto nível; as curvas, as molas e a câmera ficam aqui.
5
+
6
+ O MÉTODO vem dos projetos do Clube do Motion (curva por segmento, mola com quique, câmera em
7
+ cadeia de nulls, botão que afunda, palavra-chave em itálico, entrada por palavra), reescrito com
8
+ código nosso.
9
+
10
+ LIMITES DO EFFECTCRAFT 0.11.1 (medidos rodando o bin; o kit já contorna cada um):
11
+ 1. `ADBE Text Selectors` nasce VAZIO e `.property('ADBE Text Range Advanced')` volta null: foi isso que
12
+ parou o antes-e-agora/cenas.jsx na linha 233 ("cannot convert null/undefined to object"). O
13
+ seletor existe só depois de `addProperty('ADBE Text Selector')`, e o grupo avançado se chama
14
+ 'Advanced' (filhos 'basedOn', 'smoothness', ...). O kit usa isso, e a forma "rampa" do seletor
15
+ não existe (a entrada por letra usa Start animado, sem rampa).
16
+ 2. Texto sem justificação explícita sai CENTRALIZADO na posição: o kit sempre define a justificação.
17
+ 3. O nome da fonte casa por família nos .ttf de EFFECTCRAFT_FONT_DIRS ("Poppins", "Playfair Display"
18
+ com o arquivo 700i): não há `app.fonts`, nem lista de fontes, nem aviso quando a fonte falta.
19
+ 4. 'ADBE Fast Blur' não existe; o desfoque é o 'ADBE Gaussian Blur 2'.
20
+ 5. `trackMatteType` precisa de uma camada logo acima; o kit usa máscara (Mask Atom), que funciona.
21
+ 6. A forma de máscara precisa de inTangents/outTangents explícitos (o kit os cria).
22
+ 7. `app.fonts` e `File.exists` volta SEMPRE false (medido na 0.11.1, até com o arquivo ali): o Node confere os arquivos e o kit só tenta `importFile` dentro de try/catch.
23
+ 8. O `writeLn` só chega ao Node na saída do script: o kit escreve uma linha "SOM {json}" por som
24
+ e "AVISO ..." por problema de layout (texto fora da tela).
25
+
26
+ Ordem de uso: novaComp -> fundo -> camera -> texto/forma/imagem/celular (parent = cam.no) -> som ->
27
+ fecha(comp). O maquina.js já faz isso por cena e junta as cenas com troca().
28
+ */
29
+
30
+ var KIT = { f: 1, entMax: 0, fps: 30, t0: 0, ajuste: 1, cams: {}, avisos: [], nSons: 0, marca: null, rects: [], voz: null, cenaIdx: 0, enq: 1, enqDy: 0, estilo: 'deriva', bpm: 0, elementos: [], ajusteCena: 1, MX: 0.08 };
31
+
32
+ // ---------------------------------------------------------------- básicos
33
+ function kCor(h) {
34
+ if (h instanceof Array) return h;
35
+ h = String(h).replace('#', '');
36
+ if (h.length == 3) h = h.charAt(0) + h.charAt(0) + h.charAt(1) + h.charAt(1) + h.charAt(2) + h.charAt(2);
37
+ return [parseInt(h.substr(0, 2), 16) / 255, parseInt(h.substr(2, 2), 16) / 255, parseInt(h.substr(4, 2), 16) / 255];
38
+ }
39
+ function kLum(h) { var c = kCor(h); return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]; }
40
+ function kMisturar(a, b, p) { var x = kCor(a), y = kCor(b), r = []; for (var i = 0; i < 3; i++) r.push(x[i] + (y[i] - x[i]) * p); return r; }
41
+ function kTr(L) { return L.property('ADBE Transform Group'); }
42
+ function kP(L) { return kTr(L).property('ADBE Position'); }
43
+ function kS(L) { return kTr(L).property('ADBE Scale'); }
44
+ function kR(L) { return kTr(L).property('ADBE Rotate Z'); }
45
+ function kO(L) { return kTr(L).property('ADBE Opacity'); }
46
+ function kA(L) { return kTr(L).property('ADBE Anchor Point'); }
47
+ function kFx(L, mn) { return L.property('ADBE Effect Parade').addProperty(mn); }
48
+ function kN(prop) {
49
+ var t = prop.propertyValueType;
50
+ if (t == PropertyValueType.TwoD || t == PropertyValueType.TwoD_SPATIAL) return 2;
51
+ if (t == PropertyValueType.ThreeD || t == PropertyValueType.ThreeD_SPATIAL) return 3;
52
+ return 1;
53
+ }
54
+ /* kPai(filho, pai): o EffectCraft COMPENSA escala e rotação do pai no parentesco (o filho de um pai a 78% ganha 128%).
55
+ Aqui o filho volta a 100% e 0°; a posição o chamador define depois. */
56
+ function kPai(L, p) { L.parent = p; kS(L).setValue([100, 100]); kR(L).setValue(0); }
57
+ function kFrame(t) { return Math.round(t * KIT.fps) / KIT.fps; }
58
+
59
+ function novaComp(nome, w, h, dur, fps) {
60
+ KIT.fps = fps || 30;
61
+ var c = app.project.items.addComp(nome, w, h, 1, dur, KIT.fps);
62
+ c.motionBlur = true;
63
+ // obturador curto (90°, sempre ≤ 180°) e amostras de sobra: o desfoque acompanha o movimento sem virar mancha nem código de barras
64
+ try { c.shutterAngle = 90; c.shutterPhase = -45; c.motionBlurSamplesPerFrame = 16; c.motionBlurAdaptiveSampleLimit = 128; } catch (e) { }
65
+ c.bgColor = [0, 0, 0];
66
+ return c;
67
+ }
68
+
69
+ // Fundo: cor lisa + gradiente que anda (Gradient Ramp com os pontos em órbita) + brilhos à deriva: o fundo nunca fica morto.
70
+ function fundo(comp, cor, o) {
71
+ o = o || {};
72
+ var s = comp.layers.addSolid(kCor(cor), 'fundo', comp.width, comp.height, 1);
73
+ var b = o.brilho, W = comp.width, H = comp.height, d = comp.duration, i;
74
+ if (b) {
75
+ try { // o gradiente se mexe: dois pontos giram em volta do centro e a cor de ponta é o brilho do tema
76
+ var rp = kFx(s, 'ADBE Ramp');
77
+ rp.property('ADBE Ramp-0002').setValue(kCor(cor));
78
+ rp.property('ADBE Ramp-0004').setValue(kMisturar(cor, b, 0.9));
79
+ rp.property('ADBE Ramp-0001').expression = '[' + (W / 2) + ' + Math.cos(time * 0.9) * ' + (W * 0.45) + ', ' + (H / 2) + ' + Math.sin(time * 0.7) * ' + (H * 0.45) + ']';
80
+ rp.property('ADBE Ramp-0003').expression = '[' + (W / 2) + ' - Math.cos(time * 0.9) * ' + (W * 0.55) + ', ' + (H / 2) + ' - Math.sin(time * 0.7) * ' + (H * 0.55) + ']';
81
+ rp.property('ADBE Ramp-0005').setValue(2);
82
+ } catch (e) { }
83
+ for (i = 0; i < 3; i++) {
84
+ var L = comp.layers.addShape(); L.name = 'brilho ' + i;
85
+ var g = L.property('ADBE Root Vectors Group').addProperty('ADBE Vector Group');
86
+ var el = g.property('ADBE Vectors Group').addProperty('ADBE Vector Shape - Ellipse');
87
+ var raio = Math.max(W, H) * (i == 1 ? 0.5 : i == 2 ? 0.4 : 0.7);
88
+ el.property('ADBE Vector Ellipse Size').setValue([raio, raio]);
89
+ var f = g.property('ADBE Vectors Group').addProperty('ADBE Vector Graphic - Fill');
90
+ f.property('ADBE Vector Fill Color').setValue(kCor(b));
91
+ kO(L).setValue(i == 0 ? 38 : i == 1 ? 30 : 24);
92
+ var bl = kFx(L, 'ADBE Gaussian Blur 2'); bl.property('ADBE Gaussian Blur 2-0001').setValue(raio * 0.35);
93
+ var a0 = [W * (i == 1 ? 0.85 : i == 2 ? 0.5 : 0.1), H * (i == 1 ? 0.75 : i == 2 ? 0.15 : 0.2)], a1 = [W * (i == 1 ? 0.2 : i == 2 ? 0.9 : 0.8), H * (i == 1 ? 0.3 : i == 2 ? 0.85 : 0.7)];
94
+ curva(kP(L), [[0, a0, 30, 30], [d / 2, a1, 30, 30], [d, a0, 30, 30]]);
95
+ L.motionBlur = false;
96
+ }
97
+ }
98
+ return s;
99
+ }
100
+
101
+ // ---------------------------------------------------------------- curvas e molas
102
+ /* curva(prop, chaves): chaves = [[t, valor, influenciaEntrada, influenciaSaida], ...].
103
+ TODA chave leva ease (padrão: sai rápida 28, chega macia 72 = "snap" de interface).
104
+ influência -1 = linear; 'a' = automática contínua (a velocidade nunca zera: câmera). */
105
+ function curva(prop, chaves) {
106
+ var i, k, e, n = kN(prop);
107
+ var cs = [];
108
+ for (i = 0; i < chaves.length; i++) {
109
+ var tk = kFrame(chaves[i][0]);
110
+ if (i > 0 && tk <= cs[i - 1][0] + 1e-6) tk = cs[i - 1][0] + 1 / KIT.fps; // duas chaves no mesmo quadro: a segunda vence e a primeira some (foi o bug do opacity 0 das trocas)
111
+ cs.push([tk].concat(chaves[i].slice(1)));
112
+ }
113
+ for (i = 0; i < cs.length; i++) prop.setValueAtTime(cs[i][0], cs[i][1]);
114
+ for (i = 0; i < cs.length; i++) {
115
+ k = prop.nearestKeyIndex(cs[i][0]);
116
+ var ei = cs[i].length > 2 && cs[i][2] != null ? cs[i][2] : 72;
117
+ var eo = cs[i].length > 3 && cs[i][3] != null ? cs[i][3] : 28;
118
+ if (ei === 'a' || eo === 'a') {
119
+ prop.setTemporalContinuousAtKey(k, true); prop.setTemporalAutoBezierAtKey(k, true);
120
+ continue;
121
+ }
122
+ var ie = [], oe = [];
123
+ for (e = 0; e < n; e++) { ie.push(new KeyframeEase(0, Math.max(0.1, Math.min(100, ei < 0 ? 33 : ei)))); oe.push(new KeyframeEase(0, Math.max(0.1, Math.min(100, eo < 0 ? 33 : eo)))); }
124
+ prop.setTemporalEaseAtKey(k, ie, oe);
125
+ if (ei < 0 || eo < 0) prop.setInterpolationTypeAtKey(k, ei < 0 ? KeyframeInterpolationType.LINEAR : KeyframeInterpolationType.BEZIER, eo < 0 ? KeyframeInterpolationType.LINEAR : KeyframeInterpolationType.BEZIER);
126
+ if (prop.isSpatial && prop.matchName == 'ADBE Position') {
127
+ var z = (n == 3) ? [0, 0, 0] : [0, 0];
128
+ prop.setSpatialAutoBezierAtKey(k, false); prop.setSpatialContinuousAtKey(k, false);
129
+ prop.setSpatialTangentsAtKey(k, z, z);
130
+ }
131
+ }
132
+ }
133
+
134
+ /* mola(prop, t, de, para, {quique, dur}): mola amortecida amostrada a cada quadro.
135
+ quique 0 = chega sem passar; 0.45 = passa uns 7% e assenta; 0.8 = elástico. */
136
+ function mola(prop, t, de, para, o) {
137
+ o = o || {};
138
+ var q = o.quique != null ? o.quique : 0.45, dur = o.dur || 0.7;
139
+ var z = Math.max(0.28, 0.95 - 0.7 * q), wn = 3.9 / (z * dur);
140
+ var wd = wn * Math.sqrt(Math.max(0.0001, 1 - z * z));
141
+ var arr = (de instanceof Array), n = arr ? de.length : 1, passos = Math.max(3, Math.round(dur * KIT.fps)), i, j;
142
+ for (i = 0; i <= passos; i++) {
143
+ var tau = dur * i / passos, p;
144
+ if (i == passos) p = 1;
145
+ else p = 1 - Math.exp(-z * wn * tau) * (Math.cos(wd * tau) + (z * wn / wd) * Math.sin(wd * tau));
146
+ var v;
147
+ if (arr) { v = []; for (j = 0; j < n; j++) v.push(de[j] + (para[j] - de[j]) * p); }
148
+ else v = de + (para - de) * p;
149
+ prop.setValueAtTime(kFrame(t) + tau, v);
150
+ }
151
+ return kFrame(t) + dur;
152
+ }
153
+
154
+ // ---------------------------------------------------------------- sons
155
+ /* som(t, tipo, ganho): registra um evento de som. O Node lê as linhas "SOM {json}" e põe o efeito no tempo. */
156
+ function som(t, tipo, ganho) {
157
+ KIT.nSons++;
158
+ writeLn('SOM ' + JSON.stringify({ t: Math.round((KIT.t0 + t / (KIT.f || 1)) * 1000) / 1000, tipo: tipo, ganho: ganho == null ? 1 : ganho }));
159
+ }
160
+
161
+ // ---------------------------------------------------------------- câmera
162
+ /* camera(comp): um null pai de tudo. O mundo tem as coordenadas do quadro; a câmera mira (âncora) e dá
163
+ zoom (escala). Nunca para: sempre há um zoom lento, e as chaves usam curva automática contínua.
164
+ cam.empurra(t, dx, dy, zoom): em t a câmera chega a (centro + dx, dy) com zoom (1 = quadro). */
165
+ function camera(comp) {
166
+ var no = comp.layers.addNull(comp.duration); no.name = 'CAM'; no.enabled = false;
167
+ var cx = comp.width / 2, cy = comp.height / 2;
168
+ kA(no).setValue([cx, cy]); kP(no).setValue([cx, cy - KIT.enqDy]);
169
+ var cam = { no: no, comp: comp, chaves: [], cx: cx, cy: cy, aplicado: false, estilo: KIT.estilo };
170
+ cam.empurra = function (t, dx, dy, zoom) { cam.chaves.push([t, dx || 0, dy || 0, zoom || 1]); return cam; };
171
+ KIT.cams[comp.name] = cam;
172
+ return cam;
173
+ }
174
+ function kAplicaCamera(cam) {
175
+ if (cam.aplicado) return; cam.aplicado = true;
176
+ var d = cam.comp.duration, ch = cam.chaves.slice(0);
177
+ ch.sort(function (a, b) { return a[0] - b[0]; });
178
+ var z0 = 1, z1 = ch.length ? Math.max(1.02, ch[ch.length - 1][3] + 0.04) : 1.06;
179
+ var lista = [[0, ch.length && ch[0][0] < 0.05 ? ch[0][1] : 0, ch.length && ch[0][0] < 0.05 ? ch[0][2] : 0, z0]];
180
+ var i;
181
+ for (i = 0; i < ch.length; i++) if (ch[i][0] >= 0.05) lista.push(ch[i]);
182
+ var u = lista[lista.length - 1];
183
+ if (u[0] < d - 0.05) lista.push([d, u[1] * 1.04, u[2] * 1.04 + 8, z1]);
184
+ var ka = [], ks = [];
185
+ for (i = 0; i < lista.length; i++) {
186
+ ka.push([lista[i][0], [cam.cx + lista[i][1] * 0.5, cam.cy + lista[i][2] * 0.5], 'a', 'a']);
187
+ var zc = Math.min(lista[i][3], 1.05); // o zoom das chaves é limitado: o conteúdo precisa continuar na área segura com a câmera perto
188
+ ks.push([lista[i][0], [zc * 100 * KIT.enq, zc * 100 * KIT.enq], 'a', 'a']);
189
+ }
190
+ curva(kA(cam.camNo || cam.no), ka); curva(kS(cam.camNo || cam.no), ks);
191
+ kEstiloCamera(cam, d);
192
+ }
193
+ /* Os estilos de câmera: cada um é uma expressão por cima das chaves (a câmera nunca para), sempre SUAVE e com ease: nada de tremor,
194
+ nada de corte seco de enquadramento (Joel, 18/10: "a tremedeira é horrível").
195
+ deriva (zoom lento) · pulso (respira na batida) · trilho (travelling lateral) · orbita (balanço de eixo suave)
196
+ · push (aproximação dramática) · chicote (chega varrendo, com ease) · cinema (eixo levemente torto + subida)
197
+ POR CIMA de qualquer estilo, a câmera RESPIRA: um push-in/out suave (+3,5% com ease cosseno) a cada ~1 s (ou a cada 1–2 batidas da
198
+ trilha) e, no começo da cena, um "speed ramp" (12% mais perto, fechando em 0,5 s com ease). */
199
+ function kPunchTimes(d) { // só o período (e a fase) da respiração; o instante é REAL, a cena roda em tempo nominal (×f)
200
+ var out = [], passo, tl, f = KIT.f || 1, dr = d / f;
201
+ if (KIT.bpm > 40) {
202
+ var bp = 60 / KIT.bpm; passo = bp < 0.45 ? bp * 2 : bp;
203
+ var off = (((KIT.t0 - (KIT.batidaFase || 0)) % passo) + passo) % passo; tl = passo - off; if (tl < 0.45) tl += passo;
204
+ } else { passo = 0.95; tl = 0.8; }
205
+ KIT.passoRespira = passo;
206
+ while (tl < dr - 0.35) { out.push(Math.round(tl * f * 1000) / 1000); tl += passo; }
207
+ return out;
208
+ }
209
+ function kPunchCodigo(P) { // mantido para as expressões da atmosfera: pe/pf agora são suaves (sem estouro): variam pouco, com ease
210
+ return 'var P = [' + P.join(',') + ']; var cur = -1; for (var i = 0; i < P.length; i++) { if (time >= P[i]) cur = i; } var tt = cur >= 0 ? time - P[cur] : 9; var pe = 0; var pf = 0; var sg = 1; ';
211
+ }
212
+ function kEstiloCamera(cam, d) {
213
+ var st = cam.estilo || 'deriva', u = Math.min(cam.comp.width, cam.comp.height) / 1080, W = cam.comp.width, f = KIT.f || 1;
214
+ var cn = cam.camNo || cam.no, A = kA(cn), S = kS(cn), R = kR(cn);
215
+ var P = kPunchTimes(d); KIT.punch = P;
216
+ var per = (KIT.passoRespira || 0.95), U = u.toFixed(3);
217
+ var fase = (KIT.bpm > 40 ? (KIT.t0 - (KIT.batidaFase || 0)) : KIT.t0);
218
+ var mult = '1', dx = '0', dy = '0', rot = '0';
219
+ if (st == 'pulso') { var bp = (KIT.bpm > 40 ? 60 / KIT.bpm : 0.5) * f; mult = '(1 + 0.02 * (0.5 - 0.5 * Math.cos(2 * Math.PI * (time % ' + bp.toFixed(4) + ') / ' + bp.toFixed(4) + ')))'; }
220
+ else if (st == 'trilho') { dx = '(time / ' + d.toFixed(3) + ' - 0.5) * ' + (W * 0.07).toFixed(1); mult = '1.05'; }
221
+ else if (st == 'orbita') { rot = 'Math.sin(time * 0.9 / ' + f + ') * 1.8'; mult = '1.05'; }
222
+ else if (st == 'push') { mult = '(1 + 0.06 * Math.pow(Math.min(1, time / ' + d.toFixed(3) + '), 1.6))'; rot = '-0.8 * Math.min(1, time / ' + d.toFixed(3) + ')'; }
223
+ else if (st == 'chicote') { dx = '(1 - Math.pow(Math.min(1, time / ' + (0.5 * f).toFixed(3) + '), 0.4)) * ' + (W * 0.08).toFixed(1); }
224
+ else if (st == 'cinema') { rot = '-1.4 + Math.sin(time * 0.6 / ' + f + ') * 0.4'; dy = '-Math.min(1, time / ' + d.toFixed(3) + ') * ' + (28 * u).toFixed(1); }
225
+ var ease = 'var e = Math.min(1, time / ' + (0.5 * f).toFixed(3) + '); e = e * e * (3 - 2 * e); ';
226
+ var ramp = '(1 + 0.12 * (1 - e))';
227
+ var resp = '(1 + 0.035 * (0.5 - 0.5 * Math.cos(2 * Math.PI * ((time / ' + f + ' + ' + fase.toFixed(3) + ') % ' + per.toFixed(3) + ') / ' + per.toFixed(3) + ')))';
228
+ S.expression = 'var s = value; ' + ease + 'var k = ' + mult + ' * ' + resp + ' * ' + ramp + '; [s[0] * k, s[1] * k]';
229
+ A.expression = 'value + [' + dx + ', ' + dy + ']';
230
+ R.expression = rot;
231
+ }
232
+
233
+ // ---------------------------------------------------------------- atmosfera: luz, flash, brilho, aberração, grão
234
+ /* kAtmosfera(comp, pal): NO MÁXIMO 3 efeitos visuais ao mesmo tempo (hoje 2), e NUNCA flash branco (Joel, 18/10):
235
+ 1) Glow (brilho do que é claro), 2) um vazamento de luz suave (a aberração cromática VR foi tirada: deixava uma faixa de 1–2% na borda direita e na base) (mancha de cor
236
+ borrada em ADD, varrendo o quadro). A energia vem do movimento e da câmera, não de entulho. */
237
+ function kAtmosfera(comp, P) {
238
+ if (KIT.fx === false) return;
239
+ var W = comp.width, H = comp.height, u = Math.min(W, H) / 1080, pun = KIT.punch || [], cod = kPunchCodigo(pun), d = comp.duration;
240
+ try { // 3) vazamento de luz: uma mancha, sem pulso
241
+ var lk = comp.layers.addShape(); lk.name = 'vazamento de luz';
242
+ var gg = lk.property('ADBE Root Vectors Group').addProperty('ADBE Vector Group');
243
+ var rr = Math.max(W, H) * 0.8;
244
+ gg.property('ADBE Vectors Group').addProperty('ADBE Vector Shape - Ellipse').property('ADBE Vector Ellipse Size').setValue([rr, rr * 0.7]);
245
+ gg.property('ADBE Vectors Group').addProperty('ADBE Vector Graphic - Fill').property('ADBE Vector Fill Color').setValue(kCor(P.ac));
246
+ kFx(lk, 'ADBE Gaussian Blur 2').property('ADBE Gaussian Blur 2-0001').setValue(rr * 0.3);
247
+ lk.blendingMode = BlendingMode.ADD;
248
+ lk.motionBlur = false;
249
+ kP(lk).expression = '[' + (-W * 0.2) + ' + (time / ' + d.toFixed(2) + ') * ' + (W * 1.4) + ', ' + (H * 0.3).toFixed(0) + ' + Math.sin(time * 1.1) * ' + (H * 0.18).toFixed(0) + ']';
250
+ kO(lk).setValue(20);
251
+ } catch (e) { KIT.avisos.push('vazamento de luz indisponível'); }
252
+ try { // 1) e 2): a camada de ajuste
253
+ var aj = comp.layers.addSolid([1, 1, 1], 'look', W, H, 1);
254
+ aj.adjustmentLayer = true;
255
+ var gl = kFx(aj, 'ADBE Glo2');
256
+ gl.property('ADBE Glo2-0002').setValue(165); gl.property('ADBE Glo2-0003').setValue(40 * u);
257
+ gl.property('ADBE Glo2-0004').setValue(0.8);
258
+ } catch (e) { KIT.avisos.push('camada de look indisponível: ' + e); }
259
+ }
260
+
261
+ /* O título é SEMPRE a camada de cima da cena (nada atravessa a caixa dele): as palavras vão para o topo da pilha. */
262
+ function kTituloPorCima() {
263
+ var i, T = KIT.titulosL || [];
264
+ for (i = T.length - 1; i >= 0; i--) { try { T[i].moveToBeginning(); } catch (e) { } }
265
+ KIT.titulosL = [];
266
+ }
267
+ // fecha(comp): aplica a câmera da comp, a atmosfera (luz, flash, look) e confere o layout. Chame uma vez, depois de montar a cena.
268
+ function fecha(comp) { var c = KIT.cams[comp.name]; kTituloPorCima(); kAjustaConteudo(comp, c); if (KIT.ambienteFn) KIT.ambienteFn(comp, c); if (c) kAplicaCamera(c); kAtmosfera(comp, KIT.pal || { ac: '#FFFFFF' }); kConfereCena(comp); kAuditoria(comp); }
269
+
270
+ // ---------------------------------------------------------------- formas
271
+ function kMascaraRect(L, x0, y0, x1, y1, r) {
272
+ var m = L.property('ADBE Mask Parade').addProperty('ADBE Mask Atom');
273
+ m.property('ADBE Mask Shape').setValue(kRetShape(x0, y0, x1, y1, r || 0));
274
+ return m.property('ADBE Mask Shape');
275
+ }
276
+ function kRetShape(x0, y0, x1, y1, r) {
277
+ var S = new Shape(), k = 0.5523;
278
+ r = Math.max(0, Math.min(r, (x1 - x0) / 2, (y1 - y0) / 2));
279
+ if (r < 0.5) {
280
+ S.vertices = [[x0, y0], [x1, y0], [x1, y1], [x0, y1]];
281
+ S.inTangents = [[0, 0], [0, 0], [0, 0], [0, 0]]; S.outTangents = [[0, 0], [0, 0], [0, 0], [0, 0]];
282
+ } else {
283
+ var c = r * (1 - k);
284
+ S.vertices = [[x0 + r, y0], [x1 - r, y0], [x1, y0 + r], [x1, y1 - r], [x1 - r, y1], [x0 + r, y1], [x0, y1 - r], [x0, y0 + r]];
285
+ S.inTangents = [[0, 0], [-r * k, 0], [0, 0], [0, -r * k], [0, 0], [r * k, 0], [0, 0], [0, r * k]];
286
+ S.outTangents = [[r * k, 0], [0, 0], [0, r * k], [0, 0], [-r * k, 0], [0, 0], [0, -r * k], [0, 0]];
287
+ }
288
+ S.closed = true;
289
+ return S;
290
+ }
291
+ function kCircShape(cx, cy, r) {
292
+ var S = new Shape(), k = 0.5523 * r;
293
+ S.vertices = [[cx, cy - r], [cx + r, cy], [cx, cy + r], [cx - r, cy]];
294
+ S.inTangents = [[-k, 0], [0, -k], [k, 0], [0, k]];
295
+ S.outTangents = [[k, 0], [0, k], [-k, 0], [0, -k]];
296
+ S.closed = true;
297
+ return S;
298
+ }
299
+
300
+ /* entrada: 'pop' (estala com quique forte e balança) · 'sobe' (sobe do chão com mola) · 'fade' · 'cresce' · 'flip' (vira em 3D) · null.
301
+ Toda entrada é EXAGERADA de propósito (motion é extravagante): a escala vai a ~125% e assenta; depois a camada respira. */
302
+ function kRespira(L, fase) {
303
+ try {
304
+ var ph = (fase == null ? (KIT.nResp = (KIT.nResp || 0) + 1) * 1.37 : fase).toFixed(2);
305
+ kS(L).expression = 'var s = value; var k = 1 + 0.016 * Math.sin(time * 2.4 + ' + ph + '); [s[0] * k, s[1] * k]';
306
+ } catch (e) { }
307
+ }
308
+ function kMarcaEntrada(t) { if (t > KIT.entMax) KIT.entMax = t; }
309
+ function kEntrada(L, t, tipo, quique) {
310
+ if (!tipo) return;
311
+ kMarcaEntrada(t);
312
+ var esc = kS(L).value, p = kP(L).value, rot0 = kR(L).value;
313
+ if (tipo == 'pop') {
314
+ mola(kS(L), t, [0, 0], [esc[0], esc[1]], { quique: quique != null ? Math.max(quique, 0.78) : 0.85, dur: 0.7 });
315
+ if (rot0 == 0) mola(kR(L), t, ((KIT.nResp = (KIT.nResp || 0) + 1) % 2 ? 1 : -1) * 11, 0, { quique: 0.7, dur: 0.7 });
316
+ curva(kO(L), [[t, 0, null, 30], [t + 0.06, 100, 60, null]]);
317
+ kRespira(L);
318
+ } else if (tipo == 'sobe') {
319
+ mola(kP(L), t, [p[0], p[1] + 130], [p[0], p[1]], { quique: 0.72, dur: 0.7 });
320
+ curva(kO(L), [[t, 0, null, 30], [t + 0.18, 100, 60, null]]);
321
+ } else if (tipo == 'cresce') {
322
+ mola(kS(L), t, [esc[0] * 0.45, esc[1] * 0.45], [esc[0], esc[1]], { quique: 0.78, dur: 0.7 });
323
+ curva(kO(L), [[t, 0, null, 30], [t + 0.12, 100, 60, null]]);
324
+ kRespira(L);
325
+ } else if (tipo == 'fade') {
326
+ curva(kO(L), [[t, 0, null, 30], [t + 0.35, 100, 60, null]]);
327
+ } else if (tipo == 'flip') { // cartão que vira em 3D, com quique
328
+ L.threeDLayer = true;
329
+ var rx = L.property('ADBE Transform Group').property('ADBE Rotate X');
330
+ mola(rx, t, -95, 0, { quique: 0.7, dur: 0.8 });
331
+ curva(kO(L), [[t, 0, null, 30], [t + 0.1, 100, 60, null]]);
332
+ }
333
+ }
334
+
335
+ /* forma(comp, 'circulo'|'retangulo'|'pilula', {x,y,w,h,r,cor,traco,tracoL,alfa,parent,t0,entrada,quique,nome}) */
336
+ function forma(comp, tipo, o) {
337
+ o = o || {};
338
+ var L = comp.layers.addShape(); L.name = o.nome || tipo;
339
+ if (o.parent) kPai(L, o.parent);
340
+ var g = L.property('ADBE Root Vectors Group').addProperty('ADBE Vector Group');
341
+ var v = g.property('ADBE Vectors Group');
342
+ var w = o.w || 200, h = o.h || (tipo == 'circulo' ? w : 100);
343
+ if (tipo == 'circulo') {
344
+ v.addProperty('ADBE Vector Shape - Ellipse').property('ADBE Vector Ellipse Size').setValue([w, w]);
345
+ } else {
346
+ var rc = v.addProperty('ADBE Vector Shape - Rect');
347
+ rc.property('ADBE Vector Rect Size').setValue([w, h]);
348
+ rc.property('ADBE Vector Rect Roundness').setValue(tipo == 'pilula' ? Math.min(w, h) / 2 : (o.r || 0));
349
+ }
350
+ if (o.cor) v.addProperty('ADBE Vector Graphic - Fill').property('ADBE Vector Fill Color').setValue(kCor(o.cor));
351
+ if (o.traco) {
352
+ var st = v.addProperty('ADBE Vector Graphic - Stroke');
353
+ st.property('ADBE Vector Stroke Color').setValue(kCor(o.traco));
354
+ st.property('ADBE Vector Stroke Width').setValue(o.tracoL || 4);
355
+ }
356
+ kP(L).setValue([o.x == null ? comp.width / 2 : o.x, o.y == null ? comp.height / 2 : o.y]);
357
+ if (o.alfa != null) kO(L).setValue(o.alfa);
358
+ if (o.esc) kS(L).setValue([o.esc, o.esc]);
359
+ if (o.entrada) kEntrada(L, o.t0 || 0, o.entrada, o.quique);
360
+ return L;
361
+ }
362
+
363
+ // o V de "feito": um traço aberto com ponta redonda, desenhado por trim
364
+ function check(comp, o) {
365
+ o = o || {};
366
+ var L = comp.layers.addShape(); L.name = o.nome || 'check';
367
+ if (o.parent) kPai(L, o.parent);
368
+ var g = L.property('ADBE Root Vectors Group').addProperty('ADBE Vector Group');
369
+ var v = g.property('ADBE Vectors Group');
370
+ var tam = o.tam || 40;
371
+ var pa = v.addProperty('ADBE Vector Shape - Group');
372
+ var S = new Shape(); S.vertices = [[-0.42 * tam, 0.02 * tam], [-0.1 * tam, 0.34 * tam], [0.44 * tam, -0.32 * tam]];
373
+ S.inTangents = [[0, 0], [0, 0], [0, 0]]; S.outTangents = [[0, 0], [0, 0], [0, 0]]; S.closed = false;
374
+ pa.property('ADBE Vector Shape').setValue(S);
375
+ var st = v.addProperty('ADBE Vector Graphic - Stroke');
376
+ st.property('ADBE Vector Stroke Color').setValue(kCor(o.cor || '#FFFFFF'));
377
+ st.property('ADBE Vector Stroke Width').setValue(o.espessura || tam * 0.2);
378
+ try { st.property('ADBE Vector Stroke Line Cap').setValue(2); st.property('ADBE Vector Stroke Line Join').setValue(2); } catch (e) { }
379
+ kP(L).setValue([o.x || 0, o.y || 0]);
380
+ if (o.t0 != null) {
381
+ var tm = v.addProperty('ADBE Vector Filter - Trim');
382
+ curva(tm.property('ADBE Vector Trim End'), [[o.t0, 0, null, 25], [o.t0 + 0.28, 100, 75, null]]);
383
+ }
384
+ return L;
385
+ }
386
+
387
+ // ---------------------------------------------------------------- texto
388
+ function kMedir(comp, s, fonte, tam, trk) {
389
+ var L = comp.layers.addText(s);
390
+ kDoc(L, fonte, tam, [1, 1, 1], { tracking: trk || 0 });
391
+ var w = L.sourceRectAtTime(0, false).width;
392
+ L.remove();
393
+ return w;
394
+ }
395
+ function kDoc(L, fonte, tam, cor, extra) {
396
+ var td = L.property('ADBE Text Properties').property('ADBE Text Document');
397
+ var d = td.value;
398
+ d.resetCharStyle();
399
+ d.font = fonte; d.fontSize = tam; d.applyFill = true; d.fillColor = kCor(cor); d.applyStroke = false;
400
+ d.justification = (extra && extra.just == 'left') ? ParagraphJustification.LEFT_JUSTIFY : ParagraphJustification.CENTER_JUSTIFY;
401
+ if (extra && extra.tracking != null) d.tracking = extra.tracking;
402
+ td.setValue(d);
403
+ return td;
404
+ }
405
+ function kPalavras(s) {
406
+ // *palavra* marca a palavra-chave; devolve [{t:'texto', kw:true|false}]
407
+ var p = String(s).replace(/\s+/g, ' ').split(' '), r = [], i;
408
+ for (i = 0; i < p.length; i++) {
409
+ if (!p[i]) continue;
410
+ var kw = false, t = p[i];
411
+ if (t.charAt(0) == '*' ) { kw = true; t = t.replace(/\*/g, ''); }
412
+ else if (t.indexOf('*') >= 0) { t = t.replace(/\*/g, ''); }
413
+ r.push({ t: t, kw: kw });
414
+ }
415
+ return r;
416
+ }
417
+
418
+ /* texto(comp, str, {fonte, fonteDestaque, tamanho, cor, corDestaque, x, y, largura, maxLinhas, peso, entrada, t0, parent, passo, livre, sobrepoe, fala})
419
+ entrada: 'palavra' | 'letra' | 'sobe' | 'mascara'. A palavra entre *asteriscos* é a palavra-chave; o REALCE vem do
420
+ sistema tipográfico do vídeo (KIT.tipo.realce): 'italico' (itálico + cor + sublinhado) · 'caixa' (marca-texto atrás) ·
421
+ 'cor' (só a cor) · 'sublinha' (sublinhado sem trocar a fonte). Cada palavra é uma camada: o kit mede com
422
+ sourceRectAtTime, quebra as linhas, encolhe o corpo até caber e ENCAIXA o bloco na área segura (parent = câmera).
423
+ Com narração (KIT.voz), cada palavra entra NO INSTANTE em que é dita ("nada aparece antes de ser dito").
424
+ Devolve {camadas, w, h, fim, tam, linhas}. */
425
+ function kNorm(s) {
426
+ s = String(s).toLowerCase();
427
+ var de = 'áàâãäéèêëíìîïóòôõöúùûüçñ', pa = 'aaaaaeeeeiiiiooooouuuucn', i;
428
+ for (i = 0; i < de.length; i++) s = s.split(de.charAt(i)).join(pa.charAt(i));
429
+ return s.replace(/[^a-z0-9]/g, '');
430
+ }
431
+ // o tempo (local da cena) em que a palavra é dita; null se não há voz ou a palavra não foi dita nesta cena
432
+ function kFalaBusca(w) {
433
+ if (!KIT.voz) return null;
434
+ var n = kNorm(w), P = KIT.voz.palavras, i;
435
+ if (n.length < 2) return null;
436
+ for (i = 0; i < P.length; i++) {
437
+ if (P[i].cena != KIT.cenaIdx) continue;
438
+ var pn = P[i].n || kNorm(P[i].w);
439
+ if (pn == n || (n.length >= 5 && pn.length >= 5 && pn.substr(0, 5) == n.substr(0, 5))) return Math.max(0.05, P[i].t - KIT.t0);
440
+ }
441
+ return null;
442
+ }
443
+ /* Q(trecho, padrao): o instante local em que o trecho é dito (a 1ª palavra que casa); sem voz, o padrão. */
444
+ function Q(trecho, padrao) {
445
+ if (!KIT.voz) return padrao;
446
+ var ps = String(trecho).replace(/\*/g, '').split(' '), i, melhor = null;
447
+ for (i = 0; i < ps.length; i++) { var t = kFalaBusca(ps[i]); if (t != null && (ps[i].length >= 3 || melhor == null)) { melhor = t; if (ps[i].length >= 3) break; } }
448
+ return melhor != null ? melhor : Math.max(padrao, 0.25);
449
+ }
450
+ /* A zona segura vem em coordenadas do MUNDO (o que o espectador vê) e é traduzida para as do LAYOUT (antes da câmera), que o kit usa para
451
+ posicionar: mundo = (cy − subida) + (y − cy) × enq × zoom, com zoom ≤ 1,08 (respiração × estilo). Assim o que está dentro da zona no layout
452
+ continua dentro dela com a câmera perto. 9:16: texto fora das faixas de UI do Reels (14% em cima, 20% embaixo; com legenda, 27%). */
453
+ function kParaLayout(comp, xw, yw) {
454
+ var f = KIT.enq * 1.12, cx = comp.width / 2, cy = comp.height / 2;
455
+ return { x: cx + (xw - cx) / f, y: cy + (yw - cy + KIT.enqDy) / f };
456
+ }
457
+ function kZonaMundo(comp, texto) {
458
+ var W = comp.width, H = comp.height, alto = H / W > 1.6;
459
+ if (!texto) return { x0: W * KIT.MX, x1: W * (1 - KIT.MX), y0: H * KIT.MX, y1: H * (1 - KIT.MX) };
460
+ return { x0: W * KIT.MX, x1: W * (1 - KIT.MX), y0: alto ? H * 0.14 : H * KIT.MX, y1: alto ? H * (KIT.enqDy > 0 ? 0.73 : 0.8) : H * (1 - KIT.MX) };
461
+ }
462
+ function kZonaTexto(comp, texto) { // em coordenadas de LAYOUT: {x (margem esquerda e direita), top, bot (margem de baixo)}
463
+ var W = comp.width, H = comp.height, z = kZonaMundo(comp, texto !== false), a = kParaLayout(comp, z.x0, z.y0), b = kParaLayout(comp, z.x1, z.y1);
464
+ return { x: a.x, top: a.y, bot: H - b.y };
465
+ }
466
+ function kEhCam(p) { return !!p && (p.name == 'CAM' || p.name == 'CONTEUDO'); }
467
+ function kAreaSegura(comp, o) { return !o.livre && (!o.parent || kEhCam(o.parent)); }
468
+ function texto(comp, str, o) {
469
+ o = o || {};
470
+ var T = KIT.tipo || {};
471
+ var fonte = o.fonte || (KIT.marca && KIT.marca.titulo) || 'Poppins';
472
+ var fonteD = o.fonteDestaque || (KIT.marca && KIT.marca.destaque) || fonte;
473
+ var realce = o.realce || T.realce || 'italico';
474
+ if (realce != 'italico') fonteD = fonte;
475
+ var cor = o.cor || '#FFFFFF', corD = o.corDestaque || cor;
476
+ var tam = (o.tamanho || 90) * KIT.ajuste * KIT.ajusteCena;
477
+ var W = comp.width, H = comp.height, seg = kAreaSegura(comp, o);
478
+ if (T.caixa == 'alta' && o.caixa !== false) str = String(str).toUpperCase();
479
+ var x = o.x == null ? W / 2 : o.x, y = o.y == null ? H / 2 : o.y;
480
+ if (o.opcional && KIT.semRotulos) return { camadas: [], tempos: [], w: 0, h: 0, fim: o.t0 || 0, tam: 0, linhas: 0 };
481
+ var tall = H / W > 1.6, princ = !!o.principal && tall;
482
+ if (kEhCam(o.parent || null) || !o.parent) tam = Math.max(tam, H * 0.036 * (o.principal ? 1 : 1)); // nenhum texto fora de aparelho abaixo de 3,2% da altura (no mundo)
483
+ if (princ) tam = Math.max(tam, 120); // texto principal ≥ 7% da altura do quadro no 9:16 (linha de ~134 px)
484
+ var maxW = o.largura || W * 0.84, maxLin = (o.maxLinhas || 4) + (princ ? 1 : 0);
485
+ if (seg) maxW = Math.min(maxW, W * (1 - 2 * KIT.MX));
486
+ var pal = kPalavras(str);
487
+ if (!pal.length) return { camadas: [], w: 0, h: 0, fim: o.t0 || 0, tam: tam, linhas: 0 };
488
+ var trk = T.tracking != null ? T.tracking : 0;
489
+ var linhas, ws, sw, i, tent = 0;
490
+ for (;;) {
491
+ ws = []; for (i = 0; i < pal.length; i++) ws.push(kMedir(comp, pal[i].t, pal[i].kw ? fonteD : fonte, tam, trk));
492
+ sw = Math.max(tam * 0.27, kMedir(comp, 'a a', fonte, tam, trk) - kMedir(comp, 'aa', fonte, tam, trk));
493
+ var mx = 0; for (i = 0; i < ws.length; i++) if (ws[i] > mx) mx = ws[i];
494
+ linhas = kQuebra(ws, sw, maxW);
495
+ if (linhas.length > 1) { // equilibra: linhas de tamanho parecido
496
+ var tot = 0; for (i = 0; i < ws.length; i++) tot += ws[i] + sw;
497
+ var alvo = Math.max(mx, tot / linhas.length * 1.12);
498
+ var l2 = kQuebra(ws, sw, Math.min(maxW, alvo));
499
+ if (l2.length <= linhas.length) linhas = l2;
500
+ }
501
+ if ((mx <= maxW && linhas.length <= maxLin) || tent > 14 || tam < (princ ? 112 : H * 0.036)) break;
502
+ tam *= 0.92; tent++;
503
+ }
504
+ var lh = tam * (o.entreLinhas || T.entreLinhas || 1.12), bloco = linhas.length * lh, camadas = [], tempos = [], wMax = 0, fim = o.t0 || 0, t0 = o.t0 || 0;
505
+ var k, l, idx = 0, lastT = t0;
506
+ for (l = 0; l < linhas.length; l++) { var lw0 = 0; for (k = 0; k < linhas[l].length; k++) lw0 += ws[linhas[l][k]] + (k ? sw : 0); if (lw0 > wMax) wMax = lw0; }
507
+ if (seg) { // encaixa na área segura: 8% nos lados; no 9:16 respeita as faixas de UI do Reels (14% em cima, 20% embaixo)
508
+ var zs = kZonaTexto(comp), mxp = zs.x, myp = zs.top, myb = zs.bot;
509
+ x = Math.max(mxp + wMax / 2, Math.min(W - mxp - wMax / 2, x));
510
+ y = Math.max(myp + bloco / 2, Math.min(H - myb - bloco / 2, y));
511
+ x = Math.round(x / 8) * 8; y = Math.round(y / 8) * 8; // grade de 8 px
512
+ }
513
+ wMax = 0;
514
+ for (l = 0; l < linhas.length; l++) {
515
+ var lw = 0; for (k = 0; k < linhas[l].length; k++) lw += ws[linhas[l][k]] + (k ? sw : 0);
516
+ if (lw > wMax) wMax = lw;
517
+ var xs = x - lw / 2, yb = y - bloco / 2 + lh * l + lh * 0.5 + tam * 0.33;
518
+ for (k = 0; k < linhas[l].length; k++) {
519
+ var j = linhas[l][k], w = ws[j], xc = xs + w / 2; xs += w + sw;
520
+ var ti = t0 + idx * (o.passo || T.passo || 0.1);
521
+ if (KIT.voz && o.fala !== false) { var tv = kFalaBusca(pal[j].t); ti = tv != null ? tv : Math.max(lastT + 0.08, 0.25); }
522
+ lastT = ti;
523
+ var corTxtCaixa = kLum(corD) > 0.5 ? '#14110F' : '#FFFFFF';
524
+ if (pal[j].kw && realce == 'caixa' && o.sublinhado !== false) { // o marca-texto vem ANTES: o texto entra por cima
525
+ var bw2 = w + tam * 0.3;
526
+ var cx2 = forma(comp, 'retangulo', { x: xc - bw2 / 2, y: yb - tam * 0.3, w: bw2, h: tam * 1.0, r: tam * 0.12, cor: corD, parent: o.parent, nome: 'marca-texto' });
527
+ kA(cx2).setValue([-bw2 / 2, 0]);
528
+ curva(kS(cx2), [[ti, [0, 100], null, 30], [ti + 0.3, [100, 100], 80, null]]);
529
+ }
530
+ var L = comp.layers.addText(pal[j].t); L.name = (o.semAuditoria ? 'fundo-palavra ' : 'palavra ') + pal[j].t;
531
+ if (o.parent) kPai(L, o.parent);
532
+ var kwCor = pal[j].kw ? ((realce == 'caixa') ? corTxtCaixa : corD) : cor;
533
+ kDoc(L, pal[j].kw ? fonteD : fonte, tam * (pal[j].kw ? (o.escalaDestaque || 1.0) : 1), kwCor, { tracking: trk });
534
+ kP(L).setValue([xc, yb]);
535
+ L.comment = ti.toFixed(3); // o instante (nominal) em que a palavra entra: a auditoria usa
536
+ if (o.titulo) { KIT.titulosL = KIT.titulosL || []; KIT.titulosL.push(L); }
537
+ kAnimaPalavra(comp, L, o.entrada || T.entrada || 'palavra', ti, xc, yb, w, tam, l, idx, pal[j].t);
538
+ if (pal[j].kw && realce != 'cor' && realce != 'caixa' && o.sublinhado !== false) {
539
+ var ub = forma(comp, 'pilula', { x: xc - w / 2, y: yb + tam * 0.16, w: w, h: Math.max(6, tam * 0.07), cor: corD, parent: o.parent, nome: 'sublinhado' });
540
+ kA(ub).setValue([-w / 2, 0]);
541
+ curva(kS(ub), [[ti + 0.2, [0, 100], null, 30], [ti + 0.62, [100, 100], 80, null]]);
542
+ }
543
+ camadas.push(L); tempos.push(ti); fim = Math.max(fim, ti + 0.5); idx++;
544
+ }
545
+ }
546
+ var tIni = tempos.length ? tempos[0] : t0;
547
+ if (!o.parent || kEhCam(o.parent)) KIT.rects.push({ espaco: (o.parent && o.parent.name == 'CONTEUDO') ? 'c' : 't', titulo: !!o.titulo, livre: !seg, cena: KIT.cenaIdx, x0: x - wMax / 2, x1: x + wMax / 2, y0: y - bloco / 2, y1: y + bloco / 2, t0: tIni, t1: comp.duration, tam: tam, texto: true, nome: String(str).slice(0, 30), sobrepoe: o.sobrepoe === true });
548
+ if (princ && tam * 1.2 < H * 0.07 - 2) KIT.avisos.push('cena ' + KIT.cenaIdx + ': texto principal pequeno (' + Math.round(tam * 1.2) + ' px < 7% da altura): ' + String(str).slice(0, 30));
549
+ if (wMax > W * (1 - 2 * KIT.MX) + 4 && seg) KIT.avisos.push('cena ' + KIT.cenaIdx + ': texto largo demais: ' + String(str).slice(0, 40));
550
+ return { camadas: camadas, tempos: tempos, w: wMax, h: bloco, fim: fim, tam: tam, linhas: linhas.length, x: x, y: y };
551
+ }
552
+ function kQuebra(ws, sw, maxW) {
553
+ var linhas = [[]], larg = 0, i;
554
+ for (i = 0; i < ws.length; i++) {
555
+ var add = ws[i] + (linhas[linhas.length - 1].length ? sw : 0);
556
+ if (larg + add > maxW && linhas[linhas.length - 1].length) { linhas.push([i]); larg = ws[i]; }
557
+ else { linhas[linhas.length - 1].push(i); larg += add; }
558
+ }
559
+ return linhas;
560
+ }
561
+ function kAnimaPalavra(comp, L, modo, ti, x, y, w, tam, linha, idx, txt) {
562
+ kMarcaEntrada(ti);
563
+ var drift = Math.max(2, tam / 55);
564
+ if (modo == 'letra' || modo == 'voa') {
565
+ // um animador por caractere. 'letra': opacidade 0 + deslocamento; 'voa': a letra chega de longe, grande e girando,
566
+ // em ordem aleatória (Randomize Order) e com suavização, como as letras voando dos reels de motion.
567
+ var an = L.property('ADBE Text Properties').property('ADBE Text Animators').addProperty('ADBE Text Animator');
568
+ var pr = an.property('ADBE Text Animator Properties');
569
+ pr.addProperty('ADBE Text Opacity').setValue(0);
570
+ pr.addProperty('ADBE Text Position 3D').setValue(modo == 'voa' ? [-tam * 0.5, -tam * 1.1, 0] : [0, tam * 0.35, 0]);
571
+ if (modo == 'voa') {
572
+ try { pr.addProperty('ADBE Text Scale 3D').setValue([320, 320, 320]); pr.addProperty('ADBE Text Rotation').setValue(80); } catch (e) { }
573
+ }
574
+ var sel = an.property('ADBE Text Selectors').addProperty('ADBE Text Selector');
575
+ try {
576
+ sel.property('Advanced').property('basedOn').setValue(2);
577
+ sel.property('Advanced').property('smoothness').setValue(modo == 'voa' ? 100 : 0);
578
+ if (modo == 'voa') sel.property('Advanced').property('randomize').setValue(1);
579
+ } catch (e) { }
580
+ var n = Math.max(1, txt.length), dur = (modo == 'voa' ? 0.3 : 0.12) + n * (modo == 'voa' ? 0.06 : 0.045);
581
+ curva(sel.property('ADBE Text Percent Start'), [[ti, 0, null, 30], [ti + dur, 100, 60, null]]);
582
+ kP(L).expression = 'value + [Math.sin(time*1.9+' + idx + ')*' + drift.toFixed(1) + ', Math.cos(time*1.5+' + idx + ')*' + drift.toFixed(1) + ']';
583
+ return;
584
+ }
585
+ if (modo == 'mascara') {
586
+ var m = kMascaraRect(L, -w / 2 - 20, -tam * 1.0, w / 2 + 20, tam * 0.4, 0);
587
+ var cheio = kRetShape(-w / 2 - 20, -tam * 1.0, w / 2 + 20, tam * 0.4, 0), vazio = kRetShape(-w / 2 - 20, tam * 0.4, w / 2 + 20, tam * 0.4, 0);
588
+ m.setValueAtTime(kFrame(ti), vazio); m.setValueAtTime(kFrame(ti) + 0.4, cheio);
589
+ mola(kP(L), ti, [x, y + tam * 0.6], [x, y], { quique: 0.7, dur: 0.6 });
590
+ return;
591
+ }
592
+ if (modo == 'sobe') {
593
+ mola(kP(L), ti, [x, y + tam * 1.0], [x, y], { quique: 0.75, dur: 0.65 });
594
+ curva(kO(L), [[ti, 0, null, 30], [ti + 0.16, 100, 60, null]]);
595
+ kS(L).expression = 'var s = value; var k = 1 + 0.014 * Math.sin(time*2.2+' + idx + '); [s[0]*k, s[1]*k]';
596
+ return;
597
+ }
598
+ // 'palavra': estala de ZERO a ~125% e assenta (sobe do chão, balança e respira)
599
+ mola(kP(L), ti, [x, y + tam * 0.7], [x, y], { quique: 0.75, dur: 0.65 });
600
+ mola(kS(L), ti, [0, 0], [100, 100], { quique: 0.84, dur: 0.62 });
601
+ mola(kR(L), ti, (idx % 2 ? 1 : -1) * 10, 0, { quique: 0.7, dur: 0.6 });
602
+ curva(kO(L), [[ti, 0, null, 30], [ti + 0.05, 100, 60, null]]);
603
+ kP(L).expression = 'value + [Math.sin(time*1.9+' + idx + ')*' + drift.toFixed(1) + ', Math.cos(time*1.5+' + idx + ')*' + drift.toFixed(1) + ']';
604
+ }
605
+
606
+ // ---------------------------------------------------------------- imagem
607
+ function imagem(comp, arquivo, o) {
608
+ o = o || {};
609
+ var f = new File(arquivo), L;
610
+ var w = o.w || 600, h = o.h || 600;
611
+ var it;
612
+ try { it = app.project.importFile(new ImportOptions(f)); } catch (er) { KIT.avisos.push('imagem não achada: ' + arquivo); L = forma(comp, 'retangulo', { x: o.x, y: o.y, w: w, h: h, cor: '#888888', parent: o.parent, r: o.r }); return L; }
613
+ L = comp.layers.add(it); L.name = o.nome || 'imagem';
614
+ if (o.parent) kPai(L, o.parent);
615
+ var k = Math.max(w / it.width, h / it.height) * 100; // cobre a caixa (cover)
616
+ kS(L).setValue([k, k]);
617
+ kP(L).setValue([o.x == null ? comp.width / 2 : o.x, o.y == null ? comp.height / 2 : o.y]);
618
+ // máscara na caixa (em coordenadas da camada: antes da escala, em px da imagem)
619
+ var mw = w / k * 100 / 2, mh = h / k * 100 / 2;
620
+ kMascaraRect(L, it.width / 2 - mw, it.height / 2 - mh, it.width / 2 + mw, it.height / 2 + mh, (o.r || 0) / k * 100);
621
+ kA(L).setValue([it.width / 2, it.height / 2]);
622
+ if (o.entrada) kEntrada(L, o.t0 || 0, o.entrada, o.quique);
623
+ return L;
624
+ }
625
+
626
+ // ---------------------------------------------------------------- celular
627
+ /* celular(comp, {tela, x, y, escala, parent, t0, entrada}): um iPhone de formas.
628
+ tela = '#cor' (ou caminho de imagem). Devolve {no, tela:{w,h}, toque(t,lx,ly), ...}.
629
+ As coordenadas locais têm a origem no centro da tela; 1 unidade = 1 px do celular a 100%. */
630
+ function celular(comp, o) {
631
+ o = o || {};
632
+ var no = comp.layers.addNull(comp.duration); no.name = 'celular'; no.enabled = false;
633
+ if (o.parent) kPai(no, o.parent);
634
+ var esc = (o.escala || 1) * 100;
635
+ kA(no).setValue([0, 0]);
636
+ kP(no).setValue([o.x == null ? comp.width / 2 : o.x, o.y == null ? comp.height / 2 : o.y]);
637
+ kS(no).setValue([esc, esc]);
638
+ var C = { no: no, comp: comp, tela: { w: 430, h: 920 }, filhos: [], dedo: null, fim: 0 };
639
+ var corpo = forma(comp, 'retangulo', { x: 0, y: 0, w: 470, h: 960, r: 84, cor: o.corpo || '#17171C', traco: '#4A4A55', tracoL: 5, parent: no, nome: 'corpo' });
640
+ var tc = o.tela || '#F6F6F8', telaL;
641
+ if (String(tc).charAt(0) == '#') telaL = forma(comp, 'retangulo', { x: 0, y: 0, w: 430, h: 920, r: 66, cor: tc, parent: no, nome: 'tela' });
642
+ else telaL = imagem(comp, tc, { x: 0, y: 0, w: 430, h: 920, r: 66, parent: no, nome: 'tela' });
643
+ var ilha = forma(comp, 'pilula', { x: 0, y: -418, w: 128, h: 36, cor: '#050507', parent: no, nome: 'ilha' });
644
+ var barra = forma(comp, 'pilula', { x: 0, y: 424, w: 140, h: 6, cor: '#101014', alfa: 55, parent: no, nome: 'barra' });
645
+ C.corpo = corpo; C.telaL = telaL; C.ilha = ilha;
646
+ // entrada do conjunto: sobe do chão, gira um pouco e assenta
647
+ var t0 = o.t0 || 0, x = kP(no).value[0], y = kP(no).value[1];
648
+ if (o.entrada !== false) {
649
+ mola(kP(no), t0, [x, y + comp.height * 0.18], [x, y], { quique: 0.4, dur: 0.85 });
650
+ mola(kR(no), t0, -7, 0, { quique: 0.5, dur: 0.9 });
651
+ mola(kS(no), t0, [esc * 0.82, esc * 0.82], [esc, esc], { quique: 0.4, dur: 0.8 });
652
+ som(t0 + 0.05, 'toque', 0.9);
653
+ }
654
+ /* toque(t, lx, ly, {nome}): o dedo (um círculo translúcido) vai até o ponto, afunda e solta uma onda. */
655
+ C.toque = function (t, lx, ly, op) {
656
+ op = op || {};
657
+ if (!C.dedo) {
658
+ C.dedo = forma(comp, 'circulo', { x: 0, y: 520, w: 70, cor: '#FFFFFF', alfa: 0, parent: no, nome: 'dedo' });
659
+ C.dedoPos = [60, 520];
660
+ kP(C.dedo).setValue([60, 520]);
661
+ C.dedo.property('ADBE Effect Parade').addProperty('ADBE Drop Shadow');
662
+ }
663
+ var d = C.dedo, de = C.dedoPos;
664
+ curva(kP(d), [[t - 0.5, de, 30, 20], [t - 0.04, [lx, ly], 78, 30]]);
665
+ curva(kO(d), [[t - 0.5, de[1] > 400 ? 0 : 85, null, 30], [t - 0.3, 85, 60, 30]]);
666
+ curva(kS(d), [[t - 0.04, [100, 100], null, 25], [t + 0.06, [72, 72], 70, 40], [t + 0.22, [100, 100], 70, null]]);
667
+ C.dedoPos = [lx + 40, ly + 70];
668
+ var r = forma(comp, 'circulo', { x: lx, y: ly, w: 90, traco: op.cor || '#FFFFFF', tracoL: 6, alfa: 0, parent: no, nome: 'onda' });
669
+ curva(kS(r), [[t, [30, 30], null, 20], [t + 0.45, [210, 210], 80, null]]);
670
+ curva(kO(r), [[t - 0.04, 0, null, 30], [t, 70, null, 30], [t + 0.45, 0, 70, null]]);
671
+ som(t, 'clique', 0.9);
672
+ C.fim = Math.max(C.fim, t + 0.45);
673
+ return r;
674
+ };
675
+ C.sai = function (t) { // o dedo some
676
+ if (C.dedo) curva(kO(C.dedo), [[t, 85, null, 30], [t + 0.25, 0, 60, null]]);
677
+ };
678
+ return C;
679
+ }
680
+
681
+ /* botao(comp, {x,y,w,h,rotulo,cor,corTexto,parent,t0}) + botao.aperta(t): o botão que afunda e volta. */
682
+ function botao(comp, o) {
683
+ var b = forma(comp, 'pilula', { x: o.x, y: o.y, w: o.w || 360, h: o.h || 110, cor: o.cor || '#FFFFFF', parent: o.parent, nome: 'botao', entrada: o.entrada === false ? null : 'pop', t0: o.t0 || 0, quique: 0.6 });
684
+ var tx = texto(comp, o.rotulo || 'Toque aqui', { x: o.x, y: o.y, tamanho: o.tam || 44, cor: o.corTexto || '#000000', parent: o.parent, entrada: 'sobe', t0: (o.t0 || 0) + 0.08, largura: (o.w || 360) * 0.86, maxLinhas: 1, fonte: o.fonte });
685
+ return { forma: b, texto: tx, aperta: function (t) {
686
+ // 100%: o valor lido de uma propriedade com chaves é o do tempo 0 (a mola de entrada começa em 0)
687
+ curva(kS(b), [[t, [100, 100], null, 25], [t + 0.07, [90, 90], 70, 40], [t + 0.25, [100, 100], 70, null]]);
688
+ som(t, 'clique', 0.9);
689
+ } };
690
+ }
691
+
692
+ // ---------------------------------------------------------------- trocas de cena
693
+ /* troca(cenaA, cenaB, t, tipo): cenaA e cenaB são camadas (comps de cena) na MAIN; a troca roda em [t, t+DUR]. B entra
694
+ por cima de A. TROCAS (15): encolhe · empurra · mascara · zoom · flash · persiana · chicote · desfoque · cortina ·
695
+ pontos · giro · tremor · abre · dissolve · corte. */
696
+ var TROCA_DUR = 0.45;
697
+ var TROCAS_TODAS = ['encolhe', 'empurra', 'mascara', 'zoom', 'persiana', 'chicote', 'desfoque', 'cortina', 'pontos', 'giro', 'tremor', 'abre', 'dissolve', 'corte'];
698
+ function kBlur(L, de, para, t, d) {
699
+ var b = kFx(L, 'ADBE Gaussian Blur 2');
700
+ curva(b.property('ADBE Gaussian Blur 2-0001'), [[t, de, null, 25], [t + d, para, 75, null]]);
701
+ }
702
+ function kMascaraAnim(L, de, para, t, d) {
703
+ var m = L.property('ADBE Mask Parade').addProperty('ADBE Mask Atom');
704
+ var p = m.property('ADBE Mask Shape');
705
+ p.setValue(de);
706
+ p.setValueAtTime(kFrame(t), de);
707
+ p.setValueAtTime(kFrame(t) + d, para);
708
+ return m;
709
+ }
710
+ function troca(cenaA, cenaB, t, tipo, cor) {
711
+ var comp = KIT.main || cenaA.containingComp, W = comp.width, H = comp.height, cx = W / 2, cy = H / 2, d = TROCA_DUR, i, u = Math.min(W, H) / 1080;
712
+ tipo = tipo || 'encolhe';
713
+ var vert = H > W, R = Math.sqrt(W * W + H * H) / 2 + 20;
714
+ cor = cor || '#FFFFFF';
715
+ if (tipo == 'empurra') {
716
+ var fora = vert ? [cx, cy - H] : [cx - W, cy], de = vert ? [cx, cy + H] : [cx + W, cy];
717
+ curva(kP(cenaA), [[t, [cx, cy], null, 25], [t + d, fora, 78, null]]);
718
+ curva(kP(cenaB), [[t, de, null, 25], [t + d, [cx, cy], 78, null]]);
719
+ } else if (tipo == 'mascara') {
720
+ kMascaraAnim(cenaB, kCircShape(cx, cy, 2), kCircShape(cx, cy, R), t, d + 0.1);
721
+ curva(kS(cenaA), [[t, [100, 100], null, 30], [t + d + 0.1, [108, 108], 70, null]]);
722
+ } else if (tipo == 'zoom') {
723
+ curva(kS(cenaA), [[t, [100, 100], null, 22], [t + d, [190, 190], 80, null]]);
724
+ curva(kO(cenaA), [[t + d * 0.3, 100, null, 30], [t + d, 0, 70, null]]);
725
+ curva(kS(cenaB), [[t, [58, 58], null, 22], [t + d, [100, 100], 80, null]]);
726
+ curva(kO(cenaB), [[t, 0, null, 30], [t + d * 0.6, 100, 70, null]]);
727
+ } else if (tipo == 'persiana') {
728
+ var n = 6;
729
+ for (i = 0; i < n; i++) {
730
+ var f0 = vert ? [0, i * H / n, W, i * H / n] : [i * W / n, 0, i * W / n, H], f1 = vert ? [0, i * H / n, W, (i + 1) * H / n + 2] : [i * W / n, 0, (i + 1) * W / n + 2, H];
731
+ kMascaraAnim(cenaB, kRetShape(f0[0], f0[1], f0[2], f0[3], 0), kRetShape(f1[0], f1[1], f1[2], f1[3], 0), t + i * 0.035, d * 0.7);
732
+ }
733
+ } else if (tipo == 'chicote') {
734
+ var dx = W * 0.9;
735
+ curva(kP(cenaA), [[t, [cx, cy], null, 15], [t + d * 0.8, [cx - dx, cy], 85, null]]);
736
+ kBlur(cenaA, 0, 26 * u, t, d * 0.8);
737
+ curva(kP(cenaB), [[t + d * 0.1, [cx + dx, cy], null, 15], [t + d, [cx, cy], 85, null]]);
738
+ kBlur(cenaB, 26 * u, 0, t + d * 0.1, d * 0.9);
739
+ } else if (tipo == 'desfoque') {
740
+ kBlur(cenaA, 0, 24 * u, t, d); curva(kO(cenaA), [[t, 100, null, 25], [t + d, 0, 75, null]]);
741
+ kBlur(cenaB, 24 * u, 0, t, d); curva(kO(cenaB), [[t, 0, null, 25], [t + d, 100, 75, null]]);
742
+ } else if (tipo == 'cortina') {
743
+ var pn = comp.layers.addSolid(kCor(cor), 'cortina', W, H, 1);
744
+ var d0 = vert ? [cx, cy + H] : [cx + W, cy], d1 = [cx, cy], d2 = vert ? [cx, cy - H] : [cx - W, cy];
745
+ curva(kP(pn), [[t, d0, null, 20], [t + d * 0.5, d1, 70, 20], [t + d, d2, 70, null]]);
746
+ curva(kO(cenaB), [[t, 0, null, null], [t + d * 0.5, 100, null, null]]);
747
+ curva(kO(cenaA), [[t + d * 0.5, 100, null, null], [t + d * 0.5 + 0.1, 0, null, null]]);
748
+ } else if (tipo == 'pontos') {
749
+ var cols = vert ? 3 : 5, rows = vert ? 5 : 3, rr = Math.max(W / cols, H / rows) * 0.78;
750
+ for (i = 0; i < cols * rows; i++) {
751
+ var c0 = i % cols, r0 = Math.floor(i / cols), px = (c0 + 0.5) * W / cols, py = (r0 + 0.5) * H / rows;
752
+ kMascaraAnim(cenaB, kCircShape(px, py, 1), kCircShape(px, py, rr), t + (c0 + r0) * 0.03, d * 0.7);
753
+ }
754
+ } else if (tipo == 'giro') {
755
+ curva(kR(cenaA), [[t, 0, null, 25], [t + d, -22, 78, null]]);
756
+ curva(kS(cenaA), [[t, [100, 100], null, 25], [t + d, [60, 60], 78, null]]);
757
+ curva(kO(cenaA), [[t, 100, null, 25], [t + d, 0, 78, null]]);
758
+ curva(kR(cenaB), [[t, 22, null, 25], [t + d, 0, 78, null]]);
759
+ curva(kS(cenaB), [[t, [140, 140], null, 25], [t + d, [100, 100], 78, null]]);
760
+ curva(kO(cenaB), [[t, 0, null, 25], [t + d * 0.6, 100, 78, null]]);
761
+ } else if (tipo == 'tremor') {
762
+ var tk = [], tk2 = [];
763
+ for (i = 0; i < 7; i++) { var jx = (i % 2 ? 1 : -1) * (46 - i * 5) * u, jy = (i % 3 - 1) * (24 - i * 3) * u; tk.push([t + i * 0.033, [cx + jx, cy + jy], -1, -1]); tk2.push([t + 0.1 + i * 0.033, [cx - jx * 0.6, cy - jy * 0.6], -1, -1]); }
764
+ tk.unshift([t - 0.1, [cx, cy], null, null]); // antes da troca a cena fica CENTRADA (a 1ª chave de jitter deslocava a cena e mostrava a borda da comp)
765
+ tk.push([t + 0.26, [cx, cy], -1, null]); curva(kP(cenaA), tk);
766
+ curva(kO(cenaA), [[t + 0.06, 100, null, null], [t + 0.14, 0, null, null]]);
767
+ curva(kO(cenaB), [[t + 0.04, 0, null, null], [t + 0.12, 100, null, null]]);
768
+ tk2.unshift([t, [cx, cy], null, null]);
769
+ tk2.push([t + 0.36, [cx, cy], -1, null]); curva(kP(cenaB), tk2);
770
+ } else if (tipo == 'abre') {
771
+ var rw = vert ? [0, cy - 4, W, cy + 4] : [cx - 4, 0, cx + 4, H], rf = [0, 0, W, H];
772
+ kMascaraAnim(cenaB, kRetShape(rw[0], rw[1], rw[2], rw[3], 0), kRetShape(rf[0], rf[1], rf[2], rf[3], 0), t, d);
773
+ } else if (tipo == 'dissolve') {
774
+ curva(kO(cenaA), [[t, 100, null, 30], [t + d, 0, 70, null]]);
775
+ curva(kO(cenaB), [[t, 0, null, 30], [t + d, 100, 70, null]]);
776
+ curva(kS(cenaB), [[t, [104, 104], null, 30], [t + d, [100, 100], 70, null]]);
777
+ } else if (tipo == 'corte') {
778
+ curva(kO(cenaB), [[t - 0.08, 0, null, null], [t, 100, null, null]]);
779
+ curva(kS(cenaB), [[t, [109, 109], null, 18], [t + 0.28, [100, 100], 80, null]]);
780
+ } else { // encolhe: A afunda e some, B cresce de fora para o quadro
781
+ curva(kS(cenaA), [[t, [100, 100], null, 25], [t + d, [72, 72], 78, null]]);
782
+ curva(kO(cenaA), [[t, 100, null, 25], [t + d, 0, 78, null]]);
783
+ curva(kS(cenaB), [[t, [128, 128], null, 25], [t + d, [100, 100], 78, null]]);
784
+ curva(kO(cenaB), [[t, 0, null, 25], [t + d * 0.7, 100, 78, null]]);
785
+ }
786
+ // o som da troca em CAMADAS: riser antes, whoosh na virada, impacto e sub-drop no pouso (o Node escolhe um arquivo diferente para cada)
787
+ som(Math.max(0.02, t - 0.62), 'sobe', 0.5);
788
+ som(t - 0.05, 'troca', 0.85);
789
+ som(t + 0.12, 'impacto', 0.7); som(t + 0.12, 'sub', 0.5);
790
+ if (tipo == 'tremor' || tipo == 'corte') som(t - 0.02, 'glitch', 0.4);
791
+ }
792
+
793
+ // ---------------------------------------------------------------- elementos reais, caminhos, partículas
794
+ /* elemento(comp, i|obj, {x,y,w,h,parent,t0,entrada,quique,flutua,nome}): um elemento REAL do assunto (PNG/SVG/sequência de
795
+ PNG de um GIF animado), encaixado na caixa w×h sem cortar. KIT.elementos vem do plano (Node baixou e converteu). */
796
+ function kElem(i) { var n = KIT.elementos.length; return n ? KIT.elementos[(((i + KIT.cenaIdx) % n) + n) % n] : null; } // cada cena começa por um elemento diferente
797
+ function kPoeEl(comp, it, e, o, t) {
798
+ var L = comp.layers.add(it); L.name = o.nome || ('el ' + e.nome);
799
+ if (o.parent) kPai(L, o.parent);
800
+ var w = o.w || 300, h = o.h || w, k = Math.min(w / it.width, h / it.height) * 100;
801
+ kS(L).setValue([k, k]);
802
+ kP(L).setValue([o.x == null ? comp.width / 2 : o.x, o.y == null ? comp.height / 2 : o.y]);
803
+ if (t) L.startTime = t;
804
+ if (o.rot) kR(L).setValue(o.rot);
805
+ return L;
806
+ }
807
+ function elemento(comp, e, o) {
808
+ o = o || {};
809
+ if (typeof e == 'number') e = kElem(e);
810
+ var w = o.w || 300, h = o.h || w, L = null;
811
+ if ((!o.parent || kEhCam(o.parent)) && w < comp.width * 0.12 && o.nome != 'ambiente') { var kk = comp.width * 0.12 / w; w *= kk; h *= kk; o.w = w; o.h = h; } // ícone de apoio ≥ 12% da largura
812
+ if ((!o.parent || kEhCam(o.parent)) && o.x != null) { var ze0 = kZonaTexto(comp, false); o.x = Math.max(ze0.x + w / 2, Math.min(comp.width - ze0.x - w / 2, Math.round(o.x / 8) * 8)); o.y = Math.max(ze0.top + h / 2, Math.min(comp.height - ze0.bot - h / 2, Math.round(o.y / 8) * 8)); if (o.nome != 'ambiente') KIT.rects.push({ espaco: (o.parent && o.parent.name == 'CONTEUDO') ? 'c' : 't', cena: KIT.cenaIdx, x0: o.x - w / 2, x1: o.x + w / 2, y0: o.y - h / 2, y1: o.y + h / 2, t0: 0, t1: 1, texto: false, sobrepoe: true, nome: 'elemento ' + (e && e.nome || '') }); }
813
+ if (!e) {
814
+ L = forma(comp, 'circulo', { x: o.x, y: o.y, w: Math.min(w, h) * 0.85, cor: o.corFallback || '#8A8A8A', parent: o.parent, nome: 'elemento-reserva' });
815
+ } else {
816
+ KIT.cache = KIT.cache || {};
817
+ var it = KIT.cache[e.arquivo];
818
+ if (!it) {
819
+ var op = new ImportOptions(new File(e.arquivo));
820
+ if (e.tipo == 'seq') op.sequence = true;
821
+ try { it = app.project.importFile(op); KIT.cache[e.arquivo] = it; }
822
+ catch (er) { KIT.avisos.push('cena ' + KIT.cenaIdx + ': elemento não importou: ' + e.arquivo); it = null; }
823
+ }
824
+ if (!it) return elemento(comp, null, o);
825
+ L = kPoeEl(comp, it, e, o, o.t0 && e.tipo == 'seq' ? o.t0 : 0);
826
+ if (e.tipo == 'seq' && it.duration > 0.1) { // o GIF acaba em ~1 s: repete até o fim da cena
827
+ var n, dur = it.duration, ini = (o.t0 || 0) + dur;
828
+ for (n = 0; n < 12 && ini < comp.duration; n++, ini += dur) kPoeEl(comp, it, e, o, ini);
829
+ }
830
+ KIT.nElem = (KIT.nElem || 0) + 1;
831
+ }
832
+ L.comment = String(o.t0 || 0);
833
+ if (o.entrada) kEntrada(L, o.t0 || 0, o.entrada, o.quique);
834
+ if (o.flutua) {
835
+ var a = o.flutua * Math.min(comp.width, comp.height) / 1080, ph = (o.fase || 0);
836
+ kP(L).expression = 'value + [Math.sin(time*1.7+' + ph + ')*' + a.toFixed(1) + ', Math.cos(time*1.3+' + ph + ')*' + (a * 1.1).toFixed(1) + ']';
837
+ }
838
+ return L;
839
+ }
840
+ // sombra de contato: uma elipse borrada no chão do objeto
841
+ function sombraChao(comp, x, y, w, o) {
842
+ o = o || {};
843
+ var L = forma(comp, 'circulo', { x: x, y: y, w: w, cor: o.cor || '#000000', alfa: o.alfa == null ? 28 : o.alfa, parent: o.parent, nome: 'sombra' });
844
+ kS(L).setValue([100, 26]);
845
+ try { var b = kFx(L, 'ADBE Gaussian Blur 2'); b.property('ADBE Gaussian Blur 2-0001').setValue(w * 0.09); } catch (e) { }
846
+ if (o.t0 != null) curva(kO(L), [[o.t0, 0, null, 30], [o.t0 + 0.4, o.alfa == null ? 28 : o.alfa, 60, null]]);
847
+ return L;
848
+ }
849
+ /* caminho(comp, pontos, {cor, esp, t0, dur, parent, fecha, preenche, suave}): polilinha (ou polígono) com o traço desenhado por trim. */
850
+ function caminho(comp, pts, o) {
851
+ o = o || {};
852
+ var L = comp.layers.addShape(); L.name = o.nome || 'caminho'; L.comment = 'caminho'; // o sourceRect de um caminho absoluto inclui a origem: fica fora da medida do herói
853
+ if (o.parent) kPai(L, o.parent);
854
+ var g = L.property('ADBE Root Vectors Group').addProperty('ADBE Vector Group');
855
+ var v = g.property('ADBE Vectors Group');
856
+ var pa = v.addProperty('ADBE Vector Shape - Group');
857
+ var S = new Shape(), vs = [], it = [], ot = [], i;
858
+ for (i = 0; i < pts.length; i++) { vs.push([pts[i][0], pts[i][1]]); it.push([0, 0]); ot.push([0, 0]); }
859
+ if (o.suave) {
860
+ for (i = 1; i < pts.length - 1; i++) { var dx = (pts[i + 1][0] - pts[i - 1][0]) / 6, dy = (pts[i + 1][1] - pts[i - 1][1]) / 6; it[i] = [-dx, -dy]; ot[i] = [dx, dy]; }
861
+ if (pts.length > 1) { ot[0] = [(pts[1][0] - pts[0][0]) / 3, (pts[1][1] - pts[0][1]) / 3]; it[pts.length - 1] = [-(pts[pts.length - 1][0] - pts[pts.length - 2][0]) / 3, -(pts[pts.length - 1][1] - pts[pts.length - 2][1]) / 3]; }
862
+ }
863
+ S.vertices = vs; S.inTangents = it; S.outTangents = ot; S.closed = !!o.fecha;
864
+ pa.property('ADBE Vector Shape').setValue(S);
865
+ if (o.preenche) v.addProperty('ADBE Vector Graphic - Fill').property('ADBE Vector Fill Color').setValue(kCor(o.preenche));
866
+ if (o.cor) {
867
+ var st = v.addProperty('ADBE Vector Graphic - Stroke');
868
+ st.property('ADBE Vector Stroke Color').setValue(kCor(o.cor));
869
+ st.property('ADBE Vector Stroke Width').setValue(o.esp || 8);
870
+ try { st.property('ADBE Vector Stroke Line Cap').setValue(2); st.property('ADBE Vector Stroke Line Join').setValue(2); } catch (e) { }
871
+ }
872
+ kP(L).setValue([0, 0]);
873
+ if (o.alfa != null) kO(L).setValue(o.alfa);
874
+ if (o.t0 != null && o.dur) {
875
+ var tm = v.addProperty('ADBE Vector Filter - Trim');
876
+ curva(tm.property('ADBE Vector Trim End'), [[o.t0, 0, null, 20], [o.t0 + o.dur, 100, 80, null]]);
877
+ }
878
+ return L;
879
+ }
880
+ /* grupo(comp, parent, x, y): um null para agrupar peças que se movem juntas (câmera em cadeia, cartões, painéis). */
881
+ function grupo(comp, o) {
882
+ o = o || {};
883
+ var no = comp.layers.addNull(comp.duration); no.name = o.nome || 'grupo'; no.enabled = false;
884
+ if (o.parent) kPai(no, o.parent);
885
+ kA(no).setValue([0, 0]);
886
+ kP(no).setValue([o.x == null ? comp.width / 2 : o.x, o.y == null ? comp.height / 2 : o.y]);
887
+ if (o.esc) kS(no).setValue([o.esc * 100, o.esc * 100]);
888
+ return no;
889
+ }
890
+ /* particulas(comp, {n, cor, parent, tipo:'sobe'|'cai'|'flutua', raio, area:[x0,y0,x1,y1], t0}) */
891
+ function particulas(comp, o) {
892
+ o = o || {};
893
+ var n = o.n || 24, i, W = comp.width, H = comp.height, u = Math.min(W, H) / 1080, ar = o.area || [0, 0, W, H], out = [];
894
+ for (i = 0; i < n; i++) {
895
+ var rnd = function (k) { var q = Math.sin((i + 1) * 12.9898 + k * 78.233) * 43758.5453; return q - Math.floor(q); };
896
+ var r = (o.raio || 14) * u * (0.5 + rnd(1)), x = ar[0] + rnd(2) * (ar[2] - ar[0]), y = ar[1] + rnd(3) * (ar[3] - ar[1]);
897
+ var L = forma(comp, o.forma || 'circulo', { x: x, y: y, w: r * 2, h: r * 2 * (o.forma == 'retangulo' ? 0.5 : 1), cor: (o.cores ? o.cores[i % o.cores.length] : o.cor || '#FFFFFF'), parent: o.parent, nome: 'particula', alfa: 0 });
898
+ var vel = 0.12 + rnd(4) * 0.2, off = rnd(5), amp = (20 + rnd(6) * 40) * u, f = 0.8 + rnd(7) * 1.6, dir = o.tipo == 'cai' ? 1 : -1, alc = (ar[3] - ar[1]) * (o.tipo == 'flutua' ? 0.12 : 0.8);
899
+ kP(L).expression = 'var p = value; var q = ((time * ' + vel.toFixed(3) + ' + ' + off.toFixed(3) + ') % 1); [p[0] + Math.sin(time * ' + f.toFixed(2) + ' + ' + (i * 1.7).toFixed(2) + ') * ' + amp.toFixed(1) + ', p[1] + ' + dir + ' * q * ' + alc.toFixed(1) + ']';
900
+ kO(L).expression = 'var q = ((time * ' + vel.toFixed(3) + ' + ' + off.toFixed(3) + ') % 1); Math.sin(Math.PI * q) * ' + (o.alfa || 70) + ' * (time > ' + (o.t0 || 0) + ' ? 1 : 0)';
901
+ if (o.gira) kR(L).expression = 'time * ' + (30 + rnd(8) * 90).toFixed(0);
902
+ out.push(L);
903
+ }
904
+ return out;
905
+ }
906
+
907
+ // ---------------------------------------------------------------- legenda por palavra (na MAIN)
908
+ /* legenda(main, chunks, o): faixa escura arredondada com o trecho falado; a palavra já dita acende, a palavra-chave
909
+ fica na cor de destaque. chunks = [{t, t2, palavras:[{w, t, t2, chave}]}] (maquina-voz.js legendaChunks). */
910
+ function legenda(main, chunks, o) {
911
+ o = o || {};
912
+ var W = main.width, H = main.height, u = Math.min(W, H) / 1080, tam0 = (o.tamanho || 54) * u, fonte = o.fonte || 'Poppins';
913
+ var yc = H * (o.y || 0.905), c, i, l;
914
+ for (c = 0; c < chunks.length; c++) {
915
+ var ch = chunks[c], P = ch.palavras, tam = tam0, sw = tam * 0.28;
916
+ // a FRASE inteira, em até 2 linhas equilibradas (nunca um fragmento)
917
+ var total = 0; for (i = 0; i < P.length; i++) total += P[i].w.length + 1;
918
+ var nl = total > 34 ? 2 : 1, linhas = [[]], acum = 0;
919
+ for (i = 0; i < P.length; i++) { if (nl == 2 && linhas.length == 1 && acum + P[i].w.length / 2 > total / 2 && linhas[0].length) linhas.push([]); linhas[linhas.length - 1].push(i); acum += P[i].w.length + 1; }
920
+ var ws = [], lw = [], maxLw = 0;
921
+ for (i = 0; i < P.length; i++) ws.push(kMedir(main, P[i].w, P[i].chave ? (o.fonteDestaque || fonte) : fonte, tam));
922
+ for (l = 0; l < linhas.length; l++) { var t0w = 0, q; for (q = 0; q < linhas[l].length; q++) t0w += ws[linhas[l][q]] + (q ? sw : 0); lw.push(t0w); maxLw = Math.max(maxLw, t0w); }
923
+ var esc = maxLw > W * 0.88 ? W * 0.88 / maxLw : 1;
924
+ if (esc < 1) { tam *= esc; sw *= esc; for (i = 0; i < ws.length; i++) ws[i] *= esc; for (l = 0; l < lw.length; l++) lw[l] *= esc; maxLw *= esc; }
925
+ var t = ch.t, t2 = Math.max(ch.t2, ch.t + 0.25), fim = c + 1 < chunks.length ? Math.min(t2 + 0.3, chunks[c + 1].t) : t2 + 0.3;
926
+ var lh = tam * 1.45, hp = linhas.length * lh + tam * 0.35, ytop = yc - (linhas.length - 1) * lh / 2;
927
+ var pill = forma(main, 'pilula', { x: W / 2, y: yc, w: maxLw + tam * 1.2, h: hp, cor: o.fundo || '#000000', alfa: o.alfaFundo == null ? 62 : o.alfaFundo, nome: 'legenda-fundo' });
928
+ pill.inPoint = t - 0.04; pill.outPoint = fim;
929
+ for (l = 0; l < linhas.length; l++) {
930
+ var xs = W / 2 - lw[l] / 2, yy = ytop + l * lh;
931
+ for (q = 0; q < linhas[l].length; q++) {
932
+ i = linhas[l][q];
933
+ var p = P[i], cx = xs + ws[i] / 2; xs += ws[i] + sw;
934
+ var L = main.layers.addText(p.w); L.name = 'legenda ' + p.w;
935
+ kDoc(L, p.chave ? (o.fonteDestaque || fonte) : fonte, tam, p.chave ? (o.corDestaque || '#FFD54A') : '#FFFFFF');
936
+ kP(L).setValue([cx, yy + tam * 0.34]);
937
+ L.inPoint = t - 0.04; L.outPoint = fim;
938
+ var op = kO(L), tp = Math.max(p.t, t);
939
+ op.setValueAtTime(kFrame(t - 0.04), 42);
940
+ if (kFrame(tp) > kFrame(t - 0.04) + 0.02) { op.setValueAtTime(kFrame(tp) - 1 / KIT.fps, 42); }
941
+ op.setValueAtTime(kFrame(tp), 100);
942
+ }
943
+ }
944
+ }
945
+ }
946
+
947
+ // ---------------------------------------------------------------- conferência de layout (por cena)
948
+ /* Fora da área segura e sobreposição de textos que existem juntos: escreve "AVISO cena N: ..." para o Node refazer. */
949
+ function kRectLayout(r) {
950
+ if (r.espaco != 'c' || !KIT.remap) return r;
951
+ var m = KIT.remap, q = {}, k;
952
+ for (k in r) q[k] = r[k];
953
+ q.x0 = m.px + (r.x0 - m.ax) * m.s; q.x1 = m.px + (r.x1 - m.ax) * m.s;
954
+ q.y0 = m.py + (r.y0 - m.ay) * m.s; q.y1 = m.py + (r.y1 - m.ay) * m.s;
955
+ return q;
956
+ }
957
+ function kConfereCena(comp) {
958
+ if (KIT.cenaHeroi) { // cena de herói: o elemento maior ocupa ≥ 45% da largura útil
959
+ var hmax = 0, hh;
960
+ for (hh = 0; hh < KIT.rects.length; hh++) { var rr0 = KIT.rects[hh]; if (rr0.cena == KIT.cenaIdx && !rr0.texto && String(rr0.nome).indexOf('elemento') == 0) hmax = Math.max(hmax, rr0.x1 - rr0.x0); }
961
+ if (hmax > 0 && hmax < comp.width * 0.84 * 0.45 - 2) KIT.avisos.push('cena ' + KIT.cenaIdx + ': herói pequeno (' + Math.round(hmax) + ' px < 45% da largura útil)');
962
+ }
963
+ var W = comp.width, H = comp.height, R = KIT.rects, i, j, a, b, zt = kZonaTexto(comp, true), ze = kZonaTexto(comp, false), mx = ze.x - 2, my = ze.top - 2;
964
+ var mine = [];
965
+ for (i = 0; i < R.length; i++) if (R[i].cena == KIT.cenaIdx && !R[i].conferido) { R[i].conferido = true; mine.push(kRectLayout(R[i])); }
966
+ for (i = 0; i < mine.length; i++) {
967
+ a = mine[i];
968
+ if (a.banda) continue;
969
+ for (j = 0; j < mine.length; j++) if (mine[j].banda && !a.titulo) { b = mine[j]; if (a.x0 < b.x1 && a.x1 > b.x0 && a.y0 < b.y1 - 2 && a.y1 > b.y0 + 2 && !a.livre) KIT.avisos.push('cena ' + KIT.cenaIdx + ': invade a faixa do título: ' + String(a.nome).slice(0, 30)); }
970
+ if (a.livre) continue;
971
+ if (a.espaco == 'c' && KIT.remap) continue; // com o conteúdo ajustado à zona, quem mede é a auditoria quadro a quadro (matrizes reais)
972
+ if (a.texto) { if (a.x0 < zt.x - 6 || a.x1 > W - zt.x + 6 || a.y0 < zt.top - 6 || a.y1 > H - zt.bot + 6) KIT.avisos.push('cena ' + KIT.cenaIdx + ': texto fora da área segura: ' + a.nome); }
973
+ else if (a.x0 < mx || a.x1 > W - mx || a.y0 < my || a.y1 > H - ze.bot + 2) KIT.avisos.push('cena ' + KIT.cenaIdx + ': elemento fora da área segura: ' + a.nome);
974
+ for (j = i + 1; j < mine.length; j++) {
975
+ b = mine[j];
976
+ if (a.sobrepoe || b.sobrepoe || a.livre || b.livre || !a.texto || !b.texto) continue;
977
+ if (Math.max(a.t0, b.t0) >= Math.min(a.t1, b.t1) - 0.2) continue;
978
+ var ox = Math.min(a.x1, b.x1) - Math.max(a.x0, b.x0), oy = Math.min(a.y1, b.y1) - Math.max(a.y0, b.y0);
979
+ if (ox > 4 && oy > 4) {
980
+ var ar = Math.min((a.x1 - a.x0) * (a.y1 - a.y0), (b.x1 - b.x0) * (b.y1 - b.y0));
981
+ if (ox * oy > ar * 0.1) KIT.avisos.push('cena ' + KIT.cenaIdx + ': textos se sobrepõem: ' + a.nome + ' / ' + b.nome);
982
+ } else if (ox > 4 && oy <= 4) { // um acima do outro: respiro mínimo = metade da altura do texto
983
+ var gap = -oy, min = 0.5 * Math.min(a.tam || 0, b.tam || 0);
984
+ if (gap < min - 1) KIT.avisos.push('cena ' + KIT.cenaIdx + ': pouco espaço entre textos (' + Math.round(gap) + ' < ' + Math.round(min) + ' px): ' + a.nome + ' / ' + b.nome);
985
+ }
986
+ }
987
+ }
988
+ }
989
+
990
+ // ---------------------------------------------------------------- quando as coisas entram (a regra dos 30–40%)
991
+ /* kEntradaMax(comp): o instante (nominal) em que a ÚLTIMA camada de conteúdo fica visível, lido das chaves de opacidade, e o que
992
+ kEntrada/kAnimaPalavra registraram. Cursor, ondas, partículas e atmosfera não contam: não são elementos novos. */
993
+ var KIT_NAO_ENTRADA = /^(fundo|brilho|vazamento|look|flash|ambiente|CAM|particula|sombra|onda|estouro|halo|cursor|dedo|seta|grade|rua|terra|rio|tampa|guia|linha-skel|barra-rolagem|digitando|badge)/;
994
+ function kEntradaMax(comp) {
995
+ var mx = KIT.entMax, i;
996
+ for (i = 1; i <= comp.numLayers; i++) {
997
+ var L = comp.layer(i);
998
+ if (KIT_NAO_ENTRADA.test(L.name) || L.nullLayer) continue;
999
+ try {
1000
+ var tr = L.property('ADBE Transform Group'), vis = L.inPoint, k, t0;
1001
+ var op = tr.property('ADBE Opacity');
1002
+ if (op.numKeys > 0 && !op.expressionEnabled) { t0 = null; for (k = 1; k <= op.numKeys; k++) { if (op.keyValue(k) >= 40) { t0 = op.keyTime(k); break; } } if (t0 != null && t0 > vis) vis = t0; }
1003
+ var sc = tr.property('ADBE Scale');
1004
+ if (sc.numKeys > 0 && !sc.expressionEnabled) { t0 = null; for (k = 1; k <= sc.numKeys; k++) { if (sc.keyValue(k)[0] >= 40) { t0 = sc.keyTime(k); break; } } if (t0 != null && t0 > vis) vis = t0; }
1005
+ var cm = (L.comment != null && L.comment !== '') ? parseFloat(L.comment) : NaN;
1006
+ if (!isNaN(cm) && cm > vis) vis = cm;
1007
+ if (vis > mx) mx = vis;
1008
+ } catch (e) { }
1009
+ }
1010
+ return mx;
1011
+ }
1012
+
1013
+ // ---------------------------------------------------------------- auditoria quadro a quadro
1014
+ /* kAuditoria(comp): a cada 0,1 s (tempo real) mede a caixa de cada texto e de cada elemento JÁ com escala, posição, pais e câmera
1015
+ (matriz do layer × matriz dos pais, sourceRectAtTime para texto) e reprova: (1) fora da área segura, (2) colisão entre caixas visíveis
1016
+ no mesmo instante (texto sobre texto/elemento; só fundo e atmosfera ficam atrás). Só conta o que já assentou (entrada + 0,5 s) e
1017
+ termina antes da troca. Escreve "AVISO cena N: ..." (a máquina corrige e refaz) e "AUDIT {json}" com as contagens. */
1018
+ function kMulMat(m, n) { return [m[0] * n[0] + m[2] * n[1], m[1] * n[0] + m[3] * n[1], m[0] * n[2] + m[2] * n[3], m[1] * n[2] + m[3] * n[3], m[0] * n[4] + m[2] * n[5] + m[4], m[1] * n[4] + m[3] * n[5] + m[5]]; }
1019
+ function kMatLocal(L, t) {
1020
+ var tr = L.property('ADBE Transform Group');
1021
+ var p = tr.property('ADBE Position').valueAtTime(t, false), s = tr.property('ADBE Scale').valueAtTime(t, false), r = tr.property('ADBE Rotate Z').valueAtTime(t, false), an = tr.property('ADBE Anchor Point').valueAtTime(t, false);
1022
+ var rad = r * Math.PI / 180, cs = Math.cos(rad), sn = Math.sin(rad), sx = s[0] / 100, sy = s[1] / 100;
1023
+ var a = cs * sx, b = sn * sx, c = -sn * sy, d = cs * sy;
1024
+ return [a, b, c, d, p[0] - a * an[0] - c * an[1], p[1] - b * an[0] - d * an[1]];
1025
+ }
1026
+ function kMatMundo(L, t, cache) {
1027
+ var k = L.index;
1028
+ if (cache[k]) return cache[k];
1029
+ var m = kMatLocal(L, t);
1030
+ if (L.parent) m = kMulMat(kMatMundo(L.parent, t, cache), m);
1031
+ cache[k] = m;
1032
+ return m;
1033
+ }
1034
+ function kCaixaMundo(L, t, cache) {
1035
+ var r, m = kMatMundo(L, t, cache);
1036
+ if (L.name.indexOf('palavra ') == 0) { var q = L.sourceRectAtTime(t, false); r = [q.left, q.top, q.left + q.width, q.top + q.height]; }
1037
+ else { var sw = L.source ? L.source.width : 0, sh = L.source ? L.source.height : 0; r = [0, 0, sw, sh]; }
1038
+ var xs = [r[0], r[2], r[0], r[2]], ys = [r[1], r[1], r[3], r[3]], i, X = [], Y = [];
1039
+ for (i = 0; i < 4; i++) { X.push(m[0] * xs[i] + m[2] * ys[i] + m[4]); Y.push(m[1] * xs[i] + m[3] * ys[i] + m[5]); }
1040
+ return [Math.min(X[0], X[1], X[2], X[3]), Math.min(Y[0], Y[1], Y[2], Y[3]), Math.max(X[0], X[1], X[2], X[3]), Math.max(Y[0], Y[1], Y[2], Y[3])];
1041
+ }
1042
+ var KIT_SKIP_CADEIA = /^(feed|mapa|parede|pan|zoom|celular|janela|papel|carimbo|slot|carta)/;
1043
+ function kAuditoria(comp) {
1044
+ if (KIT.auditoria === false) return;
1045
+ var W = comp.width, H = comp.height, f = KIT.f || 1, zT = kZonaMundo(comp, true), zE = kZonaMundo(comp, false), i, j, k;
1046
+ var cand = [];
1047
+ for (i = 1; i <= comp.numLayers; i++) {
1048
+ var L = comp.layer(i), nm = L.name;
1049
+ var ehTexto = nm.indexOf('palavra ') == 0, ehEl = nm.indexOf('el ') == 0 || nm == 'elemento-reserva' || nm == 'ambiente';
1050
+ if (!ehTexto && !ehEl) continue;
1051
+ var skip = false, P = L.parent;
1052
+ while (P) { if (KIT_SKIP_CADEIA.test(P.name)) { skip = true; break; } P = P.parent; }
1053
+ var nominal = (L.comment != null && L.comment !== '') ? parseFloat(L.comment) : 0;
1054
+ if (isNaN(nominal)) nominal = 0;
1055
+ cand.push({ L: L, nome: nm, texto: ehTexto, skip: skip, entra: nominal, amb: nm == 'ambiente' });
1056
+ }
1057
+ var passo = 0.1 * f, dur = comp.duration, fim = dur - 0.55 * f, t, amostras = 0, nFora = 0, nCol = 0, vistos = {}, exs = 0;
1058
+ for (t = 0.3 * f; t < fim; t += passo) {
1059
+ var cache = {}, vis = [];
1060
+ for (i = 0; i < cand.length; i++) {
1061
+ var c = cand[i];
1062
+ if (c.skip || t < c.entra + 0.5 * f) continue;
1063
+ try {
1064
+ var tr = c.L.property('ADBE Transform Group');
1065
+ if (tr.property('ADBE Opacity').valueAtTime(t, false) < 40) continue;
1066
+ if (t < c.L.inPoint || t >= c.L.outPoint) continue;
1067
+ var bx = kCaixaMundo(c.L, t, cache);
1068
+ if (bx[2] - bx[0] < 2 || bx[3] - bx[1] < 2) continue;
1069
+ var h0 = bx[3] - bx[1];
1070
+ vis.push({ c: c, b: bx, tinta: c.texto ? [bx[0], bx[1] + h0 * 0.14, bx[2], bx[3] - h0 * 0.14] : bx });
1071
+ } catch (e) { }
1072
+ }
1073
+ amostras++;
1074
+ var foraT = false, colT = false;
1075
+ for (i = 0; i < vis.length; i++) {
1076
+ var a = vis[i], b = a.b;
1077
+ var zz = a.c.texto ? zT : zE, limX0 = zz.x0, limX1 = zz.x1, limY0 = zz.y0, limY1 = zz.y1;
1078
+ if (b[0] < limX0 - 8 || b[2] > limX1 + 8 || b[1] < limY0 - 8 || b[3] > limY1 + 8) {
1079
+ foraT = true;
1080
+ var kf = 'f:' + a.c.nome;
1081
+ if (!vistos[kf] && exs < 8) { vistos[kf] = 1; exs++; KIT.avisos.push('cena ' + KIT.cenaIdx + ': ' + (a.c.texto ? 'texto' : 'elemento') + ' fora da área segura (quadro ' + (t / f).toFixed(1) + ' s): ' + a.c.nome.replace('palavra ', '') + ' [' + Math.round(b[0]) + ',' + Math.round(b[1]) + ',' + Math.round(b[2]) + ',' + Math.round(b[3]) + ']'); }
1082
+ }
1083
+ for (j = i + 1; j < vis.length; j++) {
1084
+ var bt = a.tinta, o2 = vis[j].tinta, ox = Math.min(bt[2], o2[2]) - Math.max(bt[0], o2[0]), oy = Math.min(bt[3], o2[3]) - Math.max(bt[1], o2[1]);
1085
+ if (ox > 5 && oy > 5) {
1086
+ var A1 = (bt[2] - bt[0]) * (bt[3] - bt[1]), A2 = (o2[2] - o2[0]) * (o2[3] - o2[1]);
1087
+ if (ox * oy > 0.05 * Math.min(A1, A2)) {
1088
+ colT = true;
1089
+ var kc = 'c:' + a.c.nome + '|' + vis[j].c.nome;
1090
+ if (!vistos[kc] && exs < 8) { vistos[kc] = 1; exs++; KIT.avisos.push('cena ' + KIT.cenaIdx + ': colisão (quadro ' + (t / f).toFixed(1) + ' s): ' + a.c.nome.replace('palavra ', '') + ' x ' + vis[j].c.nome.replace('palavra ', '')); }
1091
+ }
1092
+ }
1093
+ }
1094
+ }
1095
+ if (foraT) nFora++;
1096
+ if (colT) nCol++;
1097
+ }
1098
+ writeLn('AUDIT ' + JSON.stringify({ cena: KIT.cenaIdx, amostras: amostras, foraDaArea: nFora, colisoes: nCol }));
1099
+ }
1100
+
1101
+ // ---------------------------------------------------------------- o herói ocupa a zona dele inteira
1102
+ function kMatRel(L, t, cache, stop) {
1103
+ var k = L.index;
1104
+ if (cache[k]) return cache[k];
1105
+ var m = kMatLocal(L, t);
1106
+ if (L.parent && L.parent.index != stop) m = kMulMat(kMatRel(L.parent, t, cache, stop), m);
1107
+ cache[k] = m;
1108
+ return m;
1109
+ }
1110
+ var KIT_NAO_CONTA = /^(fundo|antes-fundo|depois-fundo|brilho|vazamento|look|ambiente|particula|sombra|estouro|onda|halo|cursor|dedo|tampa|guia|CAM|CONTEUDO)/;
1111
+ /* kAjustaConteudo(comp, cam): mede a caixa do CONTEÚDO (tudo que é filho do CONTEUDO, já assentado) e põe essa caixa na zona dele —
1112
+ entre a faixa reservada do título (+24 px) e o fim da zona segura —, escalando de 70% a 140% e centrando. Cena sem título (ou com
1113
+ mapa, feed, parede… que sangram de propósito) fica como foi desenhada. */
1114
+ function kAjustaConteudo(comp, cam) {
1115
+ if (!cam || !cam.camNo || KIT.tituloB == null || KIT.fit === false) return;
1116
+ var W = comp.width, H = comp.height, cont = cam.no, dur = comp.duration, i, k, ts = [0.45 * dur, 0.6 * dur];
1117
+ var x0 = 1e9, y0 = 1e9, x1 = -1e9, y1 = -1e9, n = 0;
1118
+ for (i = 1; i <= comp.numLayers; i++) {
1119
+ var L = comp.layer(i);
1120
+ if (KIT_NAO_CONTA.test(L.name) || L.nullLayer || L.comment == 'caminho') continue;
1121
+ var P = L.parent, dentro = false;
1122
+ while (P) { if (P.index == cont.index) { dentro = true; break; } if (KIT_SKIP_CADEIA.test(P.name) && P.name != 'celular' && P.name != 'janela' && P.name != 'papel' && P.name != 'carimbo') return; P = P.parent; }
1123
+ if (!dentro) continue;
1124
+ for (k = 0; k < ts.length; k++) {
1125
+ var t = ts[k];
1126
+ try {
1127
+ if (t < L.inPoint || t >= L.outPoint) continue;
1128
+ if (L.property('ADBE Transform Group').property('ADBE Opacity').valueAtTime(t, false) < 40) continue;
1129
+ var cache = {}, m = kMatRel(L, t, cache, cont.index), r;
1130
+ if (L.name.indexOf('palavra ') == 0) { var q = L.sourceRectAtTime(t, false); r = [q.left, q.top, q.left + q.width, q.top + q.height]; }
1131
+ else if (L.name.indexOf('el ') == 0 && L.source && L.source.width) r = [0, 0, L.source.width, L.source.height];
1132
+ else { var q2 = L.sourceRectAtTime(t, false); r = [q2.left, q2.top, q2.left + q2.width, q2.top + q2.height]; }
1133
+ var xs = [r[0], r[2], r[0], r[2]], ys = [r[1], r[1], r[3], r[3]], j;
1134
+ for (j = 0; j < 4; j++) { var X = m[0] * xs[j] + m[2] * ys[j] + m[4], Y = m[1] * xs[j] + m[3] * ys[j] + m[5]; if (KIT.debugFit && (X < 20 || Y < 20)) writeLn('FITDBG ' + L.name + ' ' + Math.round(X) + ',' + Math.round(Y)); x0 = Math.min(x0, X); y0 = Math.min(y0, Y); x1 = Math.max(x1, X); y1 = Math.max(y1, Y); }
1135
+ n++;
1136
+ } catch (e) { }
1137
+ }
1138
+ }
1139
+ if (!n || x1 - x0 < 50 || y1 - y0 < 50) return;
1140
+ var z = kZonaTexto(comp, true), top = KIT.tituloB, bot = H - z.bot, aw = W - 2 * z.x;
1141
+ var s = Math.max(0.7, Math.min(1.4, aw / (x1 - x0), (bot - top) / (y1 - y0)));
1142
+ var bx = (x0 + x1) / 2, by = (y0 + y1) / 2, py = (top + bot) / 2;
1143
+ kA(cont).setValue([bx, by]); kP(cont).setValue([W / 2, py]); kS(cont).setValue([s * 100, s * 100]);
1144
+ KIT.remap = { s: s, ax: bx, ay: by, px: W / 2, py: py };
1145
+ writeLn('FIT ' + JSON.stringify({ cena: KIT.cenaIdx, escala: Math.round(s * 100) / 100, caixa: [Math.round(x0), Math.round(y0), Math.round(x1), Math.round(y1)] }));
1146
+ }