primocode 10.15.2 → 10.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES/edit-ia-pro.txt +32 -0
- package/LICENSES/fontes-OFL.txt +476 -0
- package/LICENSES/hyperframes-Apache-2.0.txt +205 -0
- package/LICENSES/permanent-marker-Apache-2.0.txt +204 -0
- package/LICENSES/selfie-multiclass-Apache-2.0.txt +227 -0
- package/LICENSES/three-MIT.txt +23 -0
- package/LICENSES/video-use-MIT.txt +24 -0
- package/LICENSES/woven-sfx-CC0.txt +16 -0
- package/LICENSES/yunet-MIT.txt +21 -0
- package/README.md +2 -2
- package/bin/primocode.js +74 -9
- package/editor/LICENSE.md +11 -0
- package/editor/README.md +321 -0
- package/editor/SKILL.md +307 -0
- package/editor/THIRD_PARTY_NOTICES.md +228 -0
- package/editor/assets/baixar.json +591 -0
- package/editor/assets/fonts/anton/OFL.txt +93 -0
- package/editor/assets/fonts/caveat/OFL.txt +93 -0
- package/editor/assets/fonts/figtree/OFL.txt +93 -0
- package/editor/assets/fonts/fonts.json +10 -0
- package/editor/assets/fonts/montserrat/OFL.txt +93 -0
- package/editor/assets/fonts/permanentmarker/LICENSE.txt +202 -0
- package/editor/assets/models/SELFIE_MULTICLASS_LICENSE.txt +227 -0
- package/editor/assets/models/YUNET_LICENSE.txt +21 -0
- package/editor/assets/sfx/licencas/bass_drop.txt +32 -0
- package/editor/assets/sfx/licencas/cash.txt +76 -0
- package/editor/assets/sfx/licencas/charge.txt +31 -0
- package/editor/assets/sfx/licencas/click.txt +46 -0
- package/editor/assets/sfx/licencas/coin.txt +50 -0
- package/editor/assets/sfx/licencas/count.txt +47 -0
- package/editor/assets/sfx/licencas/data.txt +31 -0
- package/editor/assets/sfx/licencas/error.txt +32 -0
- package/editor/assets/sfx/licencas/flash.txt +31 -0
- package/editor/assets/sfx/licencas/gears.txt +82 -0
- package/editor/assets/sfx/licencas/keyboard.txt +48 -0
- package/editor/assets/sfx/licencas/metal_hit.txt +62 -0
- package/editor/assets/sfx/licencas/metal_riser.txt +61 -0
- package/editor/assets/sfx/licencas/mouse_click.txt +50 -0
- package/editor/assets/sfx/licencas/notification.txt +54 -0
- package/editor/assets/sfx/licencas/pop.txt +38 -0
- package/editor/assets/sfx/licencas/rewind.txt +32 -0
- package/editor/assets/sfx/licencas/rise.txt +46 -0
- package/editor/assets/sfx/licencas/sinteticos.txt +21 -0
- package/editor/assets/sfx/licencas/snap.txt +50 -0
- package/editor/assets/sfx/licencas/success.txt +40 -0
- package/editor/assets/sfx/licencas/synth_hit.txt +32 -0
- package/editor/assets/sfx/licencas/tick.txt +14 -0
- package/editor/assets/sfx/licencas/whoosh.txt +47 -0
- package/editor/assets/sfx/licencas/wind.txt +31 -0
- package/editor/assets/sfx/sfx.json +832 -0
- package/editor/assets/sfx/sfx_sinteticos.json +317 -0
- package/editor/assets/sfx/sfx_woven.json +387 -0
- package/editor/assets/sfx/woven/LICENCA.md +16 -0
- package/editor/fichas/briefing-do-video.md +45 -0
- package/editor/fichas/conferencia-da-amostra.md +56 -0
- package/editor/fichas/matriz-de-ganchos.csv +3 -0
- package/editor/fichas/padrao.json +16 -0
- package/editor/fichas/planilha-de-cenas.csv +4 -0
- package/editor/fichas/planilha-de-versoes.csv +4 -0
- package/editor/fichas/plano-em-blocos.md +32 -0
- package/editor/fichas/registro-de-materiais.json +27 -0
- package/editor/helpers/assets_ocr/ocr_vision.swift +41 -0
- package/editor/helpers/bordas_precisas.py +272 -0
- package/editor/helpers/camera_viva.py +426 -0
- package/editor/helpers/captions.py +601 -0
- package/editor/helpers/common.py +308 -0
- package/editor/helpers/compose_final.py +3555 -0
- package/editor/helpers/conferir_corte.py +234 -0
- package/editor/helpers/conferir_dados.py +405 -0
- package/editor/helpers/contact_sheet.py +282 -0
- package/editor/helpers/corte_por_onda.py +433 -0
- package/editor/helpers/corte_seco.py +595 -0
- package/editor/helpers/denoise.py +971 -0
- package/editor/helpers/efeitos.py +408 -0
- package/editor/helpers/efeitos_gerar.py +159 -0
- package/editor/helpers/elevenlabs_api.py +442 -0
- package/editor/helpers/face_layout.py +570 -0
- package/editor/helpers/fetch_images.py +423 -0
- package/editor/helpers/ganho_por_trecho.py +214 -0
- package/editor/helpers/gen_image.py +312 -0
- package/editor/helpers/gen_music.py +316 -0
- package/editor/helpers/gen_sfx.py +148 -0
- package/editor/helpers/grade.py +307 -0
- package/editor/helpers/icloud.py +119 -0
- package/editor/helpers/make_sfx.py +940 -0
- package/editor/helpers/matte.py +1342 -0
- package/editor/helpers/medir_referencia.py +2232 -0
- package/editor/helpers/mixar_trilha.py +354 -0
- package/editor/helpers/montar_corte.py +295 -0
- package/editor/helpers/motion/__init__.py +13 -0
- package/editor/helpers/motion/engine.py +1915 -0
- package/editor/helpers/motion/registry.py +510 -0
- package/editor/helpers/motion/render.py +665 -0
- package/editor/helpers/motion/templates/barra_progresso.py +370 -0
- package/editor/helpers/motion/templates/carimbo.py +132 -0
- package/editor/helpers/motion/templates/checklist.py +271 -0
- package/editor/helpers/motion/templates/comentario_cta.py +380 -0
- package/editor/helpers/motion/templates/contador.py +222 -0
- package/editor/helpers/motion/templates/destaque.py +263 -0
- package/editor/helpers/motion/templates/etiqueta_preco.py +461 -0
- package/editor/helpers/motion/templates/grafico_barras.py +510 -0
- package/editor/helpers/motion/templates/grafico_linha.py +462 -0
- package/editor/helpers/motion/templates/notificacao.py +266 -0
- package/editor/helpers/motion/templates/palavra_impacto.py +410 -0
- package/editor/helpers/motion/templates/passos.py +321 -0
- package/editor/helpers/motion/templates/riscado.py +298 -0
- package/editor/helpers/motion/templates/selo_garantia.py +150 -0
- package/editor/helpers/motion_render.py +241 -0
- package/editor/helpers/motion_sugerir.py +961 -0
- package/editor/helpers/noise_check.py +134 -0
- package/editor/helpers/pack_transcripts.py +223 -0
- package/editor/helpers/palavras_na_onda.py +176 -0
- package/editor/helpers/plano_sfx.py +839 -0
- package/editor/helpers/portao.py +427 -0
- package/editor/helpers/preview_server.py +1177 -0
- package/editor/helpers/rastrear_rosto.py +298 -0
- package/editor/helpers/render_cut.py +1256 -0
- package/editor/helpers/respiros.py +89 -0
- package/editor/helpers/sincronizar_cameras.py +571 -0
- package/editor/helpers/speech_regions.py +423 -0
- package/editor/helpers/style_catalog.json +231 -0
- package/editor/helpers/style_samples.py +246 -0
- package/editor/helpers/tela_verde.py +2113 -0
- package/editor/helpers/text_render.py +2059 -0
- package/editor/helpers/texto_na_tela.py +366 -0
- package/editor/helpers/timeline_view.py +292 -0
- package/editor/helpers/tira_de_quadros.py +113 -0
- package/editor/helpers/transcribe.py +734 -0
- package/editor/helpers/transcribe_batch.py +129 -0
- package/editor/helpers/trilhas.py +1146 -0
- package/editor/helpers/trilhas_humores.json +66 -0
- package/editor/helpers/valores.py +187 -0
- package/editor/helpers/verify_cut.py +820 -0
- package/editor/helpers/voice_levels.py +233 -0
- package/editor/helpers/voz_check.py +386 -0
- package/editor/helpers/voz_limpa.py +81 -0
- package/editor/helpers/watch_preview.py +372 -0
- package/editor/modelo-projeto/LEIA.md +402 -0
- package/editor/modelo-projeto/assets/fontes/OFL-Caveat.txt +93 -0
- package/editor/modelo-projeto/assets/fontes/OFL-JetBrainsMono.txt +93 -0
- package/editor/modelo-projeto/assets/fontes/OFL-Montserrat.txt +93 -0
- package/editor/modelo-projeto/assets/fontes/larguras.json +1 -0
- package/editor/modelo-projeto/assets/three/BufferGeometryUtils.js +1438 -0
- package/editor/modelo-projeto/assets/three/GLTFLoader.js +4895 -0
- package/editor/modelo-projeto/assets/three/LICENSE +21 -0
- package/editor/modelo-projeto/assets/three/ORIGEM.txt +17 -0
- package/editor/modelo-projeto/assets/three/RoomEnvironment.js +185 -0
- package/editor/modelo-projeto/assets/three/three.core.min.js +6 -0
- package/editor/modelo-projeto/assets/three/three.module.min.js +6 -0
- package/editor/modelo-projeto/build.py +4828 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_antes_depois.html +199 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_arquivo.html +232 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_busca.html +315 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_comando.html +304 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_conversa.html +217 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_esquema.html +289 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_medidor.html +249 -0
- package/editor/modelo-projeto/compositions/cenas/2d/k2d_notas.html +233 -0
- package/editor/modelo-projeto/compositions/cenas/3d/k3d_caminho.html +197 -0
- package/editor/modelo-projeto/compositions/cenas/3d/k3d_explodida.html +152 -0
- package/editor/modelo-projeto/compositions/cenas/3d/k3d_tela.html +167 -0
- package/editor/modelo-projeto/compositions/cenas/3d/k3d_volume.html +124 -0
- package/editor/modelo-projeto/compositions/cenas/3d/motor-three.js +728 -0
- package/editor/modelo-projeto/compositions/cenas/LEIA.md +177 -0
- package/editor/modelo-projeto/ferramentas/medir_fontes.py +65 -0
- package/editor/modelo-projeto/ferramentas/pessoa_recortada.py +176 -0
- package/editor/modelo-projeto/ferramentas/rastrear_maos.py +189 -0
- package/editor/modelo-projeto/hyperframes.json +12 -0
- package/editor/modelo-projeto/package.json +11 -0
- package/editor/modelo-projeto/projeto.json +16 -0
- package/editor/preview/app.css +1009 -0
- package/editor/preview/app.js +2122 -0
- package/editor/preview/index.html +307 -0
- package/editor/pyproject.toml +23 -0
- package/editor/references/3d.md +236 -0
- package/editor/references/animacao-catalogo.md +672 -0
- package/editor/references/animacao.md +498 -0
- package/editor/references/comandos.md +92 -0
- package/editor/references/corte-seco-como-funciona.md +50 -0
- package/editor/references/corte-seco.md +88 -0
- package/editor/references/corte.md +290 -0
- package/editor/references/direcao-por-blocos.md +218 -0
- package/editor/references/enquadramento.md +654 -0
- package/editor/references/estilos-prontos.md +355 -0
- package/editor/references/estudio.md +100 -0
- package/editor/references/exemplo-eventos.json +67 -0
- package/editor/references/lettering.md +252 -0
- package/editor/references/modelos.md +494 -0
- package/editor/references/phase2.md +1007 -0
- package/editor/references/referencia-aplicar.md +203 -0
- package/editor/references/referencia-medir.md +375 -0
- package/editor/references/revisao.md +335 -0
- package/editor/references/som-e-trilha.md +146 -0
- package/editor/references/tabela-de-sons.md +130 -0
- package/editor/references/variacoes.md +234 -0
- package/editor/uv.lock +1141 -0
- package/lib/act.js +38 -8
- package/lib/app-chat.js +97 -0
- package/lib/catalogo.js +7 -1
- package/lib/chamada-torta.js +81 -0
- package/lib/editor-pro.js +786 -0
- package/lib/entrega.js +58 -0
- package/lib/estudio/conferencia-visual.js +76 -0
- package/lib/estudio/diretor.js +6 -1
- package/lib/estudio/exportar.js +3 -1
- package/lib/estudio/gosto/tela.html +1349 -0
- package/lib/estudio/gosto.js +305 -0
- package/lib/estudio/index.js +12 -1
- package/lib/estudio/kit-ae/base.jsx +119 -13
- package/lib/estudio/kit-ae/cenas.jsx +47 -15
- package/lib/estudio/kit-ae/guarda.jsx +274 -0
- package/lib/estudio/kit-ae/primitivas.jsx +180 -24
- package/lib/estudio/maquina-diretor.js +70 -12
- package/lib/estudio/maquina-foto.js +177 -0
- package/lib/estudio/maquina-guarda.js +207 -0
- package/lib/estudio/maquina-layout.js +22 -1
- package/lib/estudio/maquina.js +64 -11
- package/lib/estudio/referencias.js +1 -1
- package/lib/estudio/visao/visao.swift +97 -0
- package/lib/estudio/visao.js +60 -0
- package/lib/headless.js +15 -3
- package/lib/local/cerebro.json +285 -7
- package/lib/local/motor.js +5 -2
- package/lib/memoria-usuario.js +164 -0
- package/lib/nuvem.js +33 -2
- package/lib/passagem.js +50 -0
- package/lib/perigo.js +19 -11
- package/lib/protecao.js +134 -0
- package/lib/repeticao.js +1 -1
- package/lib/sandbox.js +57 -9
- package/lib/titulo-conversa.js +46 -0
- package/lib/tools.js +23 -4
- package/lib/ui.js +2 -2
- package/package.json +3 -2
- package/skills/editar-meu-video/SKILL.md +6 -2
- package/skills/editar-video/SKILL.md +113 -0
- package/studio/pessoa.py +52 -0
- package/voz/primo_voz/__main__.py +55 -40
- package/voz/primo_voz/app.py +500 -4
- package/voz/primo_voz/chat.html +1773 -0
- package/voz/primo_voz/conversa.py +0 -26
- package/voz/primo_voz/hud.html +8 -2
- package/voz/primo_voz/hud.py +129 -5
- package/voz/primo_voz/identidade.py +1 -1
- package/voz/primo_voz/painel_dados.py +134 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gosto.js — aprender o gosto da pessoa pro motion.
|
|
3
|
+
*
|
|
4
|
+
* No PRIMEIRO estudio_motion, antes de renderizar, abre uma tela local com 20 motions tocando em loop num carrossel:
|
|
5
|
+
* a pessoa escolhe os que curtiu e, numa pergunta só, marca "mais disso" e "menos disso". Fica em ~/.primocode/gosto-motion.json.
|
|
6
|
+
*
|
|
7
|
+
* Regra do dono: o gosto é CONTEXTO E PESO, não trilho. O diretor (maquina-diretor.js) continua sorteando com
|
|
8
|
+
* variedade e histórico; aqui só saem multiplicadores (pesos). Nunca vira "estilo fixo".
|
|
9
|
+
*
|
|
10
|
+
* ler() / salvar() / tem() o arquivo
|
|
11
|
+
* pesos(gosto) multiplicadores pro diretor (tudo 1 = neutro)
|
|
12
|
+
* resumo(gosto) 1 a 3 frases pro modelo ler
|
|
13
|
+
* abrirTela() servidor local 127.0.0.1 + token; resolve {ok, gosto} | {ok:false, motivo}
|
|
14
|
+
* antesDoMotion() no terminal abre o navegador; no app (-p com PRIMOCODE_APP=1) escreve
|
|
15
|
+
* `primocode:abrir-gosto <url>` no stderr e o app (desktop ou Primo.app) abre a tela
|
|
16
|
+
*/
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const http = require('http');
|
|
23
|
+
const crypto = require('crypto');
|
|
24
|
+
|
|
25
|
+
// Só nomes do vocabulário do diretor (maquina-diretor.js: CAMERAS, TRANSICOES, TIPOGRAFIAS).
|
|
26
|
+
const ESTILOS = [
|
|
27
|
+
{ id: 'brincalhao', nome: 'Brincalhão', frase: 'Quica, gira, pula. Leve e cheio de vida.', ritmo: 'rapido', cameras: ['pulso', 'orbita'], trocas: ['giro', 'encolhe', 'abre'], tipografias: ['arredondada', 'manuscrita'], overshoot: 0.18 },
|
|
28
|
+
{ id: 'premium', nome: 'Premium', frase: 'Lento, liso, caro. Desfoque saindo e nada de susto.', ritmo: 'lento', cameras: ['cinema', 'deriva'], trocas: ['desfoque', 'dissolve', 'cortina'], tipografias: ['serifa_fina', 'editorial'], overshoot: 0 },
|
|
29
|
+
{ id: 'corporativo', nome: 'Direto', frase: 'Entra empurrando, corte seco, tudo alinhado.', ritmo: 'medio', cameras: ['trilho', 'push'], trocas: ['empurra', 'persiana', 'corte'], tipografias: ['geometrica', 'tecnica'], overshoot: 0.04 },
|
|
30
|
+
{ id: 'energico', nome: 'Enérgico', frase: 'Chicote, punch de câmera, letra entrando rápido.', ritmo: 'rapido', cameras: ['chicote', 'pulso'], trocas: ['chicote', 'tremor', 'zoom', 'corte'], tipografias: ['pesada', 'poster'], overshoot: 0.25 },
|
|
31
|
+
{ id: 'cinema3d', nome: 'Cinema 3D', frase: 'Profundidade: tudo orbita e vem do fundo.', ritmo: 'medio', cameras: ['orbita', 'cinema', 'push'], trocas: ['giro', 'zoom', 'empurra'], tipografias: ['poster', 'editorial'], overshoot: 0.1 },
|
|
32
|
+
{ id: 'cinetico', nome: 'Letra que dança', frase: 'Cada letra entra sozinha, girando e trocando de peso.', ritmo: 'rapido', cameras: ['pulso', 'push'], trocas: ['corte', 'chicote', 'encolhe'], tipografias: ['poster', 'pesada', 'tecnica'], overshoot: 0.15 },
|
|
33
|
+
{ id: 'cubo', nome: 'Cubo', frase: 'Um cubo 3D gira de face em face e dá cambalhota.', ritmo: 'medio', cameras: ['orbita', 'push'], trocas: ['giro', 'zoom'], tipografias: ['pesada', 'poster'], overshoot: 0.2 },
|
|
34
|
+
{ id: 'glitch', nome: 'Glitch', frase: 'A palavra treme e se parte em fatias coloridas.', ritmo: 'rapido', cameras: ['chicote', 'pulso'], trocas: ['tremor', 'corte'], tipografias: ['tecnica', 'pesada'], overshoot: 0.05 },
|
|
35
|
+
{ id: 'espiral', nome: 'Espiral', frase: 'Pontos giram até o centro e explodem na palavra.', ritmo: 'rapido', cameras: ['orbita', 'pulso'], trocas: ['zoom', 'encolhe'], tipografias: ['pesada', 'arredondada'], overshoot: 0.2 },
|
|
36
|
+
{ id: 'slot', nome: 'Caça-níquel', frase: 'Cada letra roda como num rolo e trava uma por vez.', ritmo: 'rapido', cameras: ['pulso', 'trilho'], trocas: ['corte', 'persiana'], tipografias: ['pesada', 'poster'], overshoot: 0.1 },
|
|
37
|
+
{ id: 'tunel', nome: 'Túnel', frase: 'Quadros saem do centro girando, a palavra pulsa.', ritmo: 'rapido', cameras: ['push', 'pulso'], trocas: ['zoom', 'giro'], tipografias: ['geometrica', 'poster'], overshoot: 0.1 },
|
|
38
|
+
{ id: 'mosaico', nome: 'Mosaico', frase: 'Azulejos viram em onda e mostram a palavra.', ritmo: 'medio', cameras: ['push', 'deriva'], trocas: ['persiana', 'abre'], tipografias: ['geometrica', 'pesada'], overshoot: 0.15 },
|
|
39
|
+
{ id: 'estilhaco', nome: 'Estilhaço', frase: 'Um impacto quebra a palavra e ela se junta de volta.', ritmo: 'rapido', cameras: ['chicote', 'pulso'], trocas: ['zoom', 'tremor'], tipografias: ['pesada', 'poster'], overshoot: 0.22 },
|
|
40
|
+
{ id: 'contador', nome: 'Contador', frase: 'Os números rolam até 100% com a barra enchendo.', ritmo: 'medio', cameras: ['push', 'trilho'], trocas: ['empurra', 'corte'], tipografias: ['tecnica', 'geometrica'], overshoot: 0.06 },
|
|
41
|
+
{ id: 'orbita', nome: 'Órbita', frase: 'Anéis inclinados giram em volta da palavra.', ritmo: 'medio', cameras: ['orbita', 'cinema'], trocas: ['giro', 'dissolve'], tipografias: ['geometrica'], overshoot: 0.08 },
|
|
42
|
+
{ id: 'equalizador', nome: 'Equalizador', frase: 'Barras dançam e a palavra quica na batida.', ritmo: 'rapido', cameras: ['pulso', 'chicote'], trocas: ['corte', 'tremor'], tipografias: ['pesada', 'poster'], overshoot: 0.12 },
|
|
43
|
+
{ id: 'cartas', nome: 'Cartas', frase: 'Um baralho cai, abre em leque e junta de novo.', ritmo: 'medio', cameras: ['cinema', 'deriva'], trocas: ['abre', 'giro'], tipografias: ['editorial', 'arredondada'], overshoot: 0.18 },
|
|
44
|
+
{ id: 'gota', nome: 'Gota', frase: 'Uma gota cai, abre ondas e a palavra sobe delas.', ritmo: 'lento', cameras: ['deriva', 'cinema'], trocas: ['desfoque', 'dissolve'], tipografias: ['serifa_fina', 'editorial'], overshoot: 0.12 },
|
|
45
|
+
{ id: 'split', nome: 'Corte ao meio', frase: 'As metades da palavra entram por lados opostos.', ritmo: 'rapido', cameras: ['trilho', 'chicote'], trocas: ['persiana', 'empurra'], tipografias: ['pesada', 'tecnica'], overshoot: 0.04 },
|
|
46
|
+
{ id: 'traco', nome: 'Traço', frase: 'Uma linha se desenha e revela a palavra atrás.', ritmo: 'medio', cameras: ['trilho', 'deriva'], tipografias: ['manuscrita', 'editorial'], trocas: ['abre', 'cortina'], overshoot: 0.08 },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/** O que a pessoa pode ter gostado (passo 2). */
|
|
50
|
+
const GOSTEI = [
|
|
51
|
+
{ id: 'rapido', nome: 'a velocidade' },
|
|
52
|
+
{ id: 'quique', nome: 'o quique (passa do ponto e volta)' },
|
|
53
|
+
{ id: 'tres_d', nome: 'o 3D' },
|
|
54
|
+
{ id: 'camera', nome: 'a câmera mexendo' },
|
|
55
|
+
{ id: 'letra', nome: 'a letra' },
|
|
56
|
+
{ id: 'cortes', nome: 'os cortes' },
|
|
57
|
+
{ id: 'cores', nome: 'as cores' },
|
|
58
|
+
{ id: 'calmo', nome: 'ser calmo e liso' },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/** O que a pessoa NÃO quer (passo 3). */
|
|
62
|
+
const NAO_QUERO = [
|
|
63
|
+
{ id: 'lento', nome: 'devagar demais', curto: 'devagar demais' },
|
|
64
|
+
{ id: 'liso', nome: 'liso, sem graça', curto: 'liso' },
|
|
65
|
+
{ id: 'exagero', nome: 'exagerado, sujo', curto: 'exagero' },
|
|
66
|
+
{ id: 'flash', nome: 'flash na tela', curto: 'flash' },
|
|
67
|
+
{ id: 'tremido', nome: 'tremido demais', curto: 'tremido' },
|
|
68
|
+
{ id: 'letra_pequena', nome: 'letra pequena', curto: 'letra pequena' },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const MAX_TEXTO = 280;
|
|
72
|
+
const ID_ESTILO = new Set(ESTILOS.map((e) => e.id));
|
|
73
|
+
const ID_GOSTEI = new Set(GOSTEI.map((e) => e.id));
|
|
74
|
+
const ID_NAO = new Set(NAO_QUERO.map((e) => e.id));
|
|
75
|
+
|
|
76
|
+
function arquivo(home = os.homedir()) { return path.join(home, '.primocode', 'gosto-motion.json'); }
|
|
77
|
+
|
|
78
|
+
function texto(s) {
|
|
79
|
+
// tira caracteres de controle (menos nada: quebra de linha vira espaço) e corta
|
|
80
|
+
return String(s == null ? '' : s).replace(/[\u0000-\u001f\u007f]+/g, ' ').replace(/\s+/g, ' ').trim().slice(0, MAX_TEXTO);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function unicos(lista, valido) {
|
|
84
|
+
const out = [];
|
|
85
|
+
for (const x of Array.isArray(lista) ? lista : []) if (valido.has(x) && !out.includes(x)) out.push(x);
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Aceita só ids conhecidos, corta texto, tira controle. */
|
|
90
|
+
function normalizar(d) {
|
|
91
|
+
const o = d && typeof d === 'object' ? d : {};
|
|
92
|
+
const escolhidos = [];
|
|
93
|
+
for (const e of Array.isArray(o.escolhidos) ? o.escolhidos : []) {
|
|
94
|
+
if (!e || !ID_ESTILO.has(e.id) || escolhidos.some((x) => x.id === e.id)) continue;
|
|
95
|
+
escolhidos.push({ id: e.id, gostei: unicos(e.gostei, ID_GOSTEI), texto: texto(e.texto) });
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
versao: 1,
|
|
99
|
+
quando: typeof o.quando === 'string' && !Number.isNaN(Date.parse(o.quando)) ? o.quando : new Date().toISOString(),
|
|
100
|
+
escolhidos,
|
|
101
|
+
nao_quero: unicos(o.nao_quero, ID_NAO),
|
|
102
|
+
nao_texto: texto(o.nao_texto),
|
|
103
|
+
pulou: !!o.pulou,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function ler(home) {
|
|
108
|
+
try { return normalizar(JSON.parse(fs.readFileSync(arquivo(home), 'utf8'))); } catch { return null; }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function salvar(dados, home) {
|
|
112
|
+
const n = normalizar(dados);
|
|
113
|
+
const f = arquivo(home);
|
|
114
|
+
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
115
|
+
fs.writeFileSync(f, JSON.stringify(n, null, 2));
|
|
116
|
+
return n;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function tem(home) { return fs.existsSync(arquivo(home)); }
|
|
120
|
+
|
|
121
|
+
/** Multiplicadores pro diretor. Tudo em 1 = neutro. Nunca fixa nada: só inclina o sorteio. */
|
|
122
|
+
function pesos(gosto) {
|
|
123
|
+
const p = { ritmo: { rapido: 1, medio: 1, lento: 1 }, cameras: {}, trocas: {}, tipografias: {}, overshoot: null };
|
|
124
|
+
const mult = (grupo, nome, k, teto = 5) => { p[grupo][nome] = Math.min(teto, (p[grupo][nome] != null ? p[grupo][nome] : 1) * k); };
|
|
125
|
+
const baixa = (grupo, nome, k) => { p[grupo][nome] = (p[grupo][nome] != null ? p[grupo][nome] : 1) * k; };
|
|
126
|
+
if (!gosto || gosto.pulou || !Array.isArray(gosto.escolhidos) || !gosto.escolhidos.length) {
|
|
127
|
+
if (gosto && Array.isArray(gosto.nao_quero) && !gosto.pulou) aplicarNao(p, gosto.nao_quero, baixa);
|
|
128
|
+
return p;
|
|
129
|
+
}
|
|
130
|
+
const chips = new Set();
|
|
131
|
+
let somaOver = 0, nOver = 0;
|
|
132
|
+
for (const esc of gosto.escolhidos) {
|
|
133
|
+
const e = ESTILOS.find((x) => x.id === esc.id);
|
|
134
|
+
if (!e) continue;
|
|
135
|
+
p.ritmo[e.ritmo] *= 1.8;
|
|
136
|
+
e.cameras.forEach((n) => mult('cameras', n, 2.2));
|
|
137
|
+
e.trocas.forEach((n) => mult('trocas', n, 2.2));
|
|
138
|
+
e.tipografias.forEach((n) => mult('tipografias', n, 2.2));
|
|
139
|
+
somaOver += e.overshoot; nOver++;
|
|
140
|
+
(esc.gostei || []).forEach((g) => chips.add(g));
|
|
141
|
+
}
|
|
142
|
+
if (chips.has('rapido')) p.ritmo.rapido *= 1.5;
|
|
143
|
+
if (chips.has('calmo')) { p.ritmo.lento *= 1.5; baixa('trocas', 'tremor', 0.4); baixa('trocas', 'chicote', 0.4); }
|
|
144
|
+
if (chips.has('quique')) { mult('trocas', 'giro', 1.4); mult('trocas', 'encolhe', 1.4); }
|
|
145
|
+
if (chips.has('tres_d')) { mult('cameras', 'orbita', 1.5); mult('cameras', 'cinema', 1.5); mult('trocas', 'giro', 1.4); }
|
|
146
|
+
if (chips.has('camera')) ['pulso', 'chicote', 'orbita'].forEach((n) => mult('cameras', n, 1.3));
|
|
147
|
+
if (chips.has('cortes')) { mult('trocas', 'corte', 1.4); mult('trocas', 'chicote', 1.4); }
|
|
148
|
+
aplicarNao(p, gosto.nao_quero || [], baixa);
|
|
149
|
+
let over = nOver ? somaOver / nOver : null;
|
|
150
|
+
if (over != null) {
|
|
151
|
+
if (chips.has('quique')) over += 0.05;
|
|
152
|
+
if ((gosto.nao_quero || []).includes('exagero')) over -= 0.05;
|
|
153
|
+
over = Math.round(Math.max(0, Math.min(0.3, over)) * 1000) / 1000;
|
|
154
|
+
}
|
|
155
|
+
p.overshoot = over;
|
|
156
|
+
return p;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function aplicarNao(p, nao, baixa) {
|
|
160
|
+
if (nao.includes('lento')) p.ritmo.lento *= 0.3;
|
|
161
|
+
if (nao.includes('liso')) { baixa('trocas', 'dissolve', 0.4); baixa('trocas', 'desfoque', 0.4); }
|
|
162
|
+
if (nao.includes('exagero')) { baixa('trocas', 'tremor', 0.4); baixa('trocas', 'chicote', 0.4); }
|
|
163
|
+
if (nao.includes('tremido')) baixa('trocas', 'tremor', 0.2);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const lista = (a) => (a.length <= 1 ? a.join('') : a.slice(0, -1).join(', ') + ' e ' + a[a.length - 1]);
|
|
167
|
+
|
|
168
|
+
/** Frases em PT-BR pro modelo. Nulo → ''. */
|
|
169
|
+
function resumo(gosto) {
|
|
170
|
+
if (!gosto) return '';
|
|
171
|
+
if (gosto.pulou && !(gosto.nao_quero || []).length) return 'A pessoa pulou a tela de gosto: sem preferência salva.';
|
|
172
|
+
const partes = [];
|
|
173
|
+
const esc = (gosto.escolhidos || []).map((x) => {
|
|
174
|
+
const e = ESTILOS.find((s) => s.id === x.id);
|
|
175
|
+
if (!e) return null;
|
|
176
|
+
const g = (x.gostei || []).map((i) => (GOSTEI.find((c) => c.id === i) || {}).nome).filter(Boolean);
|
|
177
|
+
return g.length ? `${e.nome} (${g.join(', ')})` : e.nome;
|
|
178
|
+
}).filter(Boolean);
|
|
179
|
+
if (esc.length) partes.push(`Gosto salvo da pessoa: gostou de ${lista(esc)}.`);
|
|
180
|
+
const nao = (gosto.nao_quero || []).map((i) => (NAO_QUERO.find((c) => c.id === i) || {}).curto).filter(Boolean);
|
|
181
|
+
if (nao.length) partes.push(`Não quer: ${nao.join(', ')}.`);
|
|
182
|
+
const txt = [(gosto.escolhidos || []).map((x) => x.texto).filter(Boolean).join(' / '), gosto.nao_texto ? 'não quer: ' + gosto.nao_texto : ''].filter(Boolean).join(' · ');
|
|
183
|
+
if (txt) partes.push(`Escreveu: "${txt}".`);
|
|
184
|
+
if (!partes.length) return '';
|
|
185
|
+
partes.push('Use como direção, não como molde.');
|
|
186
|
+
return partes.join(' ');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Onde a tela de gosto abre no primeiro motion (sem gosto salvo):
|
|
191
|
+
* 'terminal' → no navegador, como sempre (tem terminal de verdade, fora do -p);
|
|
192
|
+
* 'app' → o motor roda em -p dentro do app desktop ou do Primo.app (PRIMOCODE_APP=1): ele NÃO abre navegador,
|
|
193
|
+
* escreve no stderr a linha de protocolo `primocode:abrir-gosto <url>` e o app abre a tela;
|
|
194
|
+
* null → não abre (já tem gosto, gosto:false, CI, script sem app).
|
|
195
|
+
*/
|
|
196
|
+
function modoTela(args = {}, env = process.env, home, tty = !!process.stdout.isTTY) {
|
|
197
|
+
if (tem(home) || (args && args.gosto === false) || env.CI) return null;
|
|
198
|
+
if (env.PRIMOCODE_APP === '1') return 'app';
|
|
199
|
+
if (env.PRIMOCODE_SEM_TELA || !tty) return null;
|
|
200
|
+
return 'terminal';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Só pergunta no primeiro pedido, com gente do outro lado (terminal ou app): nunca em CI ou script sem app. */
|
|
204
|
+
function deveAbrir(args = {}, env = process.env, home) {
|
|
205
|
+
return modoTela(args, env, home) !== null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** A linha que o app lê no stderr do motor. */
|
|
209
|
+
const PROTOCOLO = 'primocode:abrir-gosto';
|
|
210
|
+
const linhaProtocolo = (url) => `${PROTOCOLO} ${url}\n`;
|
|
211
|
+
/** A URL da linha de protocolo, só se for a tela local (127.0.0.1, porta, token hex). null no resto. */
|
|
212
|
+
function urlDaLinha(linha) {
|
|
213
|
+
const m = String(linha || '').trim().match(/^primocode:abrir-gosto (http:\/\/127\.0\.0\.1:(\d{2,5})\/\?t=([0-9a-f]{32}))$/);
|
|
214
|
+
return m ? m[1] : null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* O primeiro motion: abre a tela onde der (terminal → navegador; app → linha de protocolo pro app abrir) e espera.
|
|
219
|
+
* Resolve o que abrirTela resolve, ou {ok:false, motivo:'nao_abre'} sem fazer nada.
|
|
220
|
+
*/
|
|
221
|
+
async function antesDoMotion(args = {}, { env = process.env, home, tty, stderr = process.stderr, abrir, timeoutMs } = {}) {
|
|
222
|
+
const modo = modoTela(args, env, home, tty);
|
|
223
|
+
if (!modo) return { ok: false, motivo: 'nao_abre' };
|
|
224
|
+
if (modo === 'app') return abrirTela({ home, timeoutMs, abrir: (url) => stderr.write(linhaProtocolo(url)) });
|
|
225
|
+
try { stderr.write(require('../ui.js').c.warn(' ⎿ Antes do primeiro motion: abri uma tela pra aprender o seu gosto (até 10 min). Feche ou pule pra seguir.') + '\n'); } catch { /* sem terminal */ }
|
|
226
|
+
return abrirTela({ home, timeoutMs, abrir });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function lerCorpo(req, limite = 20 * 1024) {
|
|
230
|
+
return new Promise((resolve, reject) => {
|
|
231
|
+
let tam = 0; const partes = [];
|
|
232
|
+
req.on('data', (c) => {
|
|
233
|
+
tam += c.length;
|
|
234
|
+
if (tam > limite) { reject(new Error('grande')); req.destroy(); return; }
|
|
235
|
+
partes.push(c);
|
|
236
|
+
});
|
|
237
|
+
req.on('end', () => resolve(Buffer.concat(partes).toString('utf8')));
|
|
238
|
+
req.on('error', reject);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Abre a tela no navegador. Resolve {ok:true, gosto} (salvou), {ok:false, motivo:'pulou'|'tempo'|'sem_tela'}.
|
|
244
|
+
* `abrir(url)` e `home` são injetáveis (testes).
|
|
245
|
+
*/
|
|
246
|
+
function abrirTela({ home, abrir, timeoutMs = 10 * 60 * 1000, porta = 0 } = {}) {
|
|
247
|
+
return new Promise((resolve) => {
|
|
248
|
+
const token = crypto.randomBytes(16).toString('hex');
|
|
249
|
+
let html;
|
|
250
|
+
try { html = fs.readFileSync(path.join(__dirname, 'gosto', 'tela.html'), 'utf8'); } catch { return resolve({ ok: false, motivo: 'sem_tela' }); }
|
|
251
|
+
const dados = JSON.stringify({ ESTILOS, GOSTEI, NAO_QUERO }).replace(/</g, '\\u003c');
|
|
252
|
+
let fim = false, timer = null;
|
|
253
|
+
const srv = http.createServer(async (req, res) => {
|
|
254
|
+
const u = new URL(req.url, 'http://127.0.0.1');
|
|
255
|
+
const dizer = (cod, corpo, tipo = 'application/json; charset=utf-8') => { res.writeHead(cod, { 'content-type': tipo, 'cache-control': 'no-store' }); res.end(typeof corpo === 'string' ? corpo : JSON.stringify(corpo)); };
|
|
256
|
+
try {
|
|
257
|
+
if (req.method === 'GET' && u.pathname === '/') {
|
|
258
|
+
if (u.searchParams.get('t') !== token) return dizer(403, 'token inválido', 'text/plain; charset=utf-8');
|
|
259
|
+
return dizer(200, html.split('__TOKEN__').join(token).split('__DADOS__').join(dados), 'text/html; charset=utf-8');
|
|
260
|
+
}
|
|
261
|
+
if (req.method === 'POST' && (u.pathname === '/salvar' || u.pathname === '/pular' || u.pathname === '/fechar')) {
|
|
262
|
+
if (req.headers['x-gosto-token'] !== token) return dizer(403, { ok: false, error: 'token inválido' });
|
|
263
|
+
if (u.pathname === '/fechar') { // o app fechou a janela sem salvar: o motion segue, nada é gravado (o próximo pergunta de novo)
|
|
264
|
+
dizer(200, { ok: true });
|
|
265
|
+
return terminar({ ok: false, motivo: 'fechou' });
|
|
266
|
+
}
|
|
267
|
+
if (u.pathname === '/pular') {
|
|
268
|
+
salvar({ pulou: true }, home);
|
|
269
|
+
dizer(200, { ok: true });
|
|
270
|
+
return terminar({ ok: false, motivo: 'pulou' });
|
|
271
|
+
}
|
|
272
|
+
let corpo;
|
|
273
|
+
try { corpo = JSON.parse(await lerCorpo(req)); } catch { return dizer(400, { ok: false, error: 'corpo inválido (JSON até 20 KB)' }); }
|
|
274
|
+
const g = salvar(corpo, home);
|
|
275
|
+
dizer(200, { ok: true, resumo: resumo(g) });
|
|
276
|
+
return terminar({ ok: true, gosto: g });
|
|
277
|
+
}
|
|
278
|
+
dizer(404, 'não achei', 'text/plain; charset=utf-8');
|
|
279
|
+
} catch (e) { try { dizer(500, { ok: false, error: String(e.message || e) }); } catch { /* já respondeu */ } }
|
|
280
|
+
});
|
|
281
|
+
function terminar(r) {
|
|
282
|
+
if (fim) return;
|
|
283
|
+
fim = true;
|
|
284
|
+
clearTimeout(timer);
|
|
285
|
+
try { srv.close(); } catch { /* já fechou */ }
|
|
286
|
+
// deixa a resposta sair e depois derruba o que sobrar de conexão aberta (keep-alive do navegador)
|
|
287
|
+
const t = setTimeout(() => { try { if (srv.closeAllConnections) srv.closeAllConnections(); } catch { /* ok */ } }, 300);
|
|
288
|
+
if (t.unref) t.unref();
|
|
289
|
+
resolve(r);
|
|
290
|
+
}
|
|
291
|
+
srv.on('error', () => terminar({ ok: false, motivo: 'sem_tela' }));
|
|
292
|
+
srv.listen(porta, '127.0.0.1', () => {
|
|
293
|
+
timer = setTimeout(() => terminar({ ok: false, motivo: 'tempo' }), timeoutMs);
|
|
294
|
+
const url = `http://127.0.0.1:${srv.address().port}/?t=${token}`;
|
|
295
|
+
try {
|
|
296
|
+
const r = (abrir || ((x) => require('../tools.js').openInBrowser(x)))(url);
|
|
297
|
+
if (r && typeof r.catch === 'function') r.catch(() => {});
|
|
298
|
+
} catch { /* sem navegador: a pessoa espera o tempo ou pula */ }
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const _teste = { texto, MAX_TEXTO, aplicarNao };
|
|
304
|
+
|
|
305
|
+
module.exports = { ESTILOS, GOSTEI, NAO_QUERO, arquivo, ler, salvar, tem, normalizar, pesos, resumo, abrirTela, deveAbrir, modoTela, antesDoMotion, PROTOCOLO, linhaProtocolo, urlDaLinha, _teste };
|
package/lib/estudio/index.js
CHANGED
|
@@ -718,6 +718,9 @@ async function ferramentaMotion(args) {
|
|
|
718
718
|
if (r.bloqueado) return { ok: false, error: r.aviso, projeto: r.projeto };
|
|
719
719
|
receita = r.projeto;
|
|
720
720
|
}
|
|
721
|
+
// primeiro motion: aprende o gosto antes de renderizar (a pessoa pode fechar ou pular; o render segue de qualquer jeito)
|
|
722
|
+
// no terminal abre o navegador; no app (desktop ou Primo.app, -p com PRIMOCODE_APP=1) o app abre a tela pela linha de protocolo
|
|
723
|
+
try { await require('./gosto.js').antesDoMotion(args); } catch { /* sem tela, segue */ }
|
|
721
724
|
return require('./cota.js').comCota(() => renderMotion(args, p, receita));
|
|
722
725
|
}
|
|
723
726
|
|
|
@@ -731,10 +734,18 @@ async function renderMotion(args, p, receita) {
|
|
|
731
734
|
args.cenas = args.cenas.map((c, i) => (i === args.cenas.length - 1 && c && !c.logo ? { ...c, logo: kitM.logo } : c));
|
|
732
735
|
}
|
|
733
736
|
}
|
|
737
|
+
// As fotos do pedido: as que a IA passou em `fotos` e as que o terminal achou no texto do pedido (referencias.imagens()).
|
|
738
|
+
// Antes só o prepararRoteiro (Remotion) as lia: no motion elas viravam "referência" e nenhuma peça aceitava foto.
|
|
739
|
+
const doPedido = referencias.imagens();
|
|
740
|
+
const dadas = Array.isArray(args.fotos) ? args.fotos : (args.fotos ? [args.fotos] : []);
|
|
741
|
+
const todas = [...new Set([...dadas, ...doPedido])];
|
|
742
|
+
if (todas.length) args = { ...args, fotos: todas, ...(dadas.length ? {} : { fotosImplicitas: true }) };
|
|
734
743
|
const r = await require('./maquina.js').motion(args, { pasta: p.pasta, receita, deps: { narrar } });
|
|
735
744
|
if (r.ok) projeto.anotar(p.id, 'motion', `${r.duracao}s, ${r.cenas.length} cenas, ${r.lufs} LUFS`);
|
|
736
745
|
if (r.ok) entregar(args, p, r);
|
|
737
|
-
|
|
746
|
+
if (!r.ok) return r;
|
|
747
|
+
const gostoResumo = (() => { try { const G = require('./gosto.js'); return G.resumo(G.ler()); } catch { return ''; } })();
|
|
748
|
+
return { ...r, ...(gostoResumo ? { gosto: gostoResumo } : {}), nota: 'Abra os quadros (png em ae/) para conferir. Se algo ficou torto, mude o JSON e rode de novo.' };
|
|
738
749
|
}
|
|
739
750
|
|
|
740
751
|
// ── estudio_editar ───────────────────────────────────────────────────────
|
|
@@ -259,13 +259,27 @@ function kAtmosfera(comp, P) {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
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() {
|
|
262
|
+
function kTituloPorCima(comp) {
|
|
263
263
|
var i, T = KIT.titulosL || [];
|
|
264
|
+
if (comp && T.length && KIT.tituloB != null) kPlacaDoTitulo(comp);
|
|
264
265
|
for (i = T.length - 1; i >= 0; i--) { try { T[i].moveToBeginning(); } catch (e) { } }
|
|
265
266
|
KIT.titulosL = [];
|
|
266
267
|
}
|
|
268
|
+
/* A GUARDA do título sobre peça que sangra (mapa, parede, feed, pan, zoom: passam por trás do título de propósito): uma placa da cor do
|
|
269
|
+
fundo da cena atrás da faixa do título, com a borda de baixo em degradê. O título nunca fica em cima de rua, grade ou cartão. */
|
|
270
|
+
function kPlacaDoTitulo(comp) {
|
|
271
|
+
var i, sangra = false, c = KIT.cams[comp.name];
|
|
272
|
+
for (i = 1; i <= comp.numLayers && !sangra; i++) { var L = comp.layer(i); if (/^(mapa|parede|feed|pan|zoom)/.test(L.name) && L.parent && kEhCam(L.parent)) sangra = true; }
|
|
273
|
+
if (!sangra || !c || !c.camNo || !KIT.pal) return;
|
|
274
|
+
var W = comp.width, H = comp.height, u = Math.min(W, H) / 1080, B = KIT.tituloB, top = -H * 0.2;
|
|
275
|
+
var pl = forma(comp, 'retangulo', { x: W / 2, y: (top + B) / 2, w: W * 1.6, h: B - top, cor: KIT.pal.bg, parent: c.camNo, nome: 'fundo-titulo' });
|
|
276
|
+
var deg = forma(comp, 'retangulo', { x: W / 2, y: B + 45 * u, w: W * 1.6, h: 90 * u, cor: KIT.pal.bg, alfa: 55, parent: c.camNo, nome: 'fundo-titulo-degrade' });
|
|
277
|
+
try { var mk = deg.property('ADBE Mask Parade').addProperty('ADBE Mask Atom'); mk.property('ADBE Mask Shape').setValue(kRetShape(-W, -60 * u, W, 20 * u, 0)); mk.property('ADBE Mask Feather').setValue([0, 50 * u]); } catch (e) { }
|
|
278
|
+
KIT.guardaPlaca = (KIT.guardaPlaca || 0) + 1;
|
|
279
|
+
writeLn('GUARDA ' + JSON.stringify({ cena: KIT.cenaIdx, w: W, h: H, log: ['título sobre peça que sangra: placa da cor do fundo atrás do título'], caixas: [] }));
|
|
280
|
+
}
|
|
267
281
|
// 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); }
|
|
282
|
+
function fecha(comp) { var c = KIT.cams[comp.name]; kTituloPorCima(comp); kAjustaConteudo(comp, c); kGuarda(comp); if (KIT.ambienteFn) KIT.ambienteFn(comp, c); if (c) kAplicaCamera(c); kAtmosfera(comp, KIT.pal || { ac: '#FFFFFF' }); kConfereCena(comp); kAuditoria(comp); }
|
|
269
283
|
|
|
270
284
|
// ---------------------------------------------------------------- formas
|
|
271
285
|
function kMascaraRect(L, x0, y0, x1, y1, r) {
|
|
@@ -507,7 +521,7 @@ function texto(comp, str, o) {
|
|
|
507
521
|
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
522
|
var zs = kZonaTexto(comp), mxp = zs.x, myp = zs.top, myb = zs.bot;
|
|
509
523
|
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));
|
|
524
|
+
y = Math.max(myp + bloco / 2 + (o.titulo ? (KIT.tituloDesce || 0) * H : 0), Math.min(H - myb - bloco / 2, y)); // tituloDesce: a câmera levou o título pra cima da faixa do Reels
|
|
511
525
|
x = Math.round(x / 8) * 8; y = Math.round(y / 8) * 8; // grade de 8 px
|
|
512
526
|
}
|
|
513
527
|
wMax = 0;
|
|
@@ -558,6 +572,23 @@ function kQuebra(ws, sw, maxW) {
|
|
|
558
572
|
}
|
|
559
573
|
return linhas;
|
|
560
574
|
}
|
|
575
|
+
/* Quanto tempo (nominal) a entrada leva até a palavra estar INTEIRA na tela. 'voa' e 'letra' montam letra por letra (em ordem
|
|
576
|
+
aleatória no 'voa'): a palavra só se lê no fim. Quem tira a palavra da tela antes disso mostra "q … ho" no lugar de "quentinho". */
|
|
577
|
+
function kTempoMontagem(modo, txt) {
|
|
578
|
+
var n = Math.max(1, String(txt || '').replace(/\*/g, '').length);
|
|
579
|
+
if (modo == 'voa') return 0.3 + n * 0.06;
|
|
580
|
+
if (modo == 'letra') return 0.12 + n * 0.045;
|
|
581
|
+
if (modo == 'mascara') return 0.4;
|
|
582
|
+
if (modo == 'sobe') return 0.35;
|
|
583
|
+
return 0.2; // 'palavra': inteira desde o primeiro quadro, crescendo; aos 0,2 s já passa de 90%
|
|
584
|
+
}
|
|
585
|
+
/* A entrada que cabe na janela da palavra (entra em ti, sai em fim): a pedida, se a palavra fica inteira por `leitura` s; senão a
|
|
586
|
+
mais rápida que deixe ler; no último caso, 'palavra' (inteira desde o começo, só cresce). */
|
|
587
|
+
function kEntradaQueCabe(modo, txt, janela, leitura) {
|
|
588
|
+
var ord = [modo, 'mascara', 'sobe', 'palavra'], i;
|
|
589
|
+
for (i = 0; i < ord.length; i++) if (kTempoMontagem(ord[i], txt) + (leitura || 0) <= janela) return ord[i];
|
|
590
|
+
return 'palavra';
|
|
591
|
+
}
|
|
561
592
|
function kAnimaPalavra(comp, L, modo, ti, x, y, w, tam, linha, idx, txt) {
|
|
562
593
|
kMarcaEntrada(ti);
|
|
563
594
|
var drift = Math.max(2, tam / 55);
|
|
@@ -680,9 +711,13 @@ function celular(comp, o) {
|
|
|
680
711
|
|
|
681
712
|
/* botao(comp, {x,y,w,h,rotulo,cor,corTexto,parent,t0}) + botao.aperta(t): o botão que afunda e volta. */
|
|
682
713
|
function botao(comp, o) {
|
|
714
|
+
// a pílula cabe o rótulo: o texto() não desce de 3,6% da altura (fora de aparelho), então mede ANTES e alarga (nunca texto vazando do botão)
|
|
715
|
+
var tamB = (o.tam || 44) * KIT.ajuste * KIT.ajusteCena; if (kEhCam(o.parent || null) || !o.parent) tamB = Math.max(tamB, comp.height * 0.036);
|
|
716
|
+
var wr = kMedir(comp, String(o.rotulo || 'Toque aqui'), o.fonte || (KIT.marca && KIT.marca.titulo) || 'Poppins', tamB);
|
|
717
|
+
o.w = Math.min(comp.width * 0.84, Math.max(o.w || 360, wr / 0.86 + tamB * 0.4)); o.h = Math.max(o.h || 110, tamB * 1.6);
|
|
683
718
|
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
719
|
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) {
|
|
720
|
+
return { forma: b, texto: tx, w: o.w, h: o.h, aperta: function (t) {
|
|
686
721
|
// 100%: o valor lido de uma propriedade com chaves é o do tempo 0 (a mola de entrada começa em 0)
|
|
687
722
|
curva(kS(b), [[t, [100, 100], null, 25], [t + 0.07, [90, 90], 70, 40], [t + 0.25, [100, 100], 70, null]]);
|
|
688
723
|
som(t, 'clique', 0.9);
|
|
@@ -809,9 +844,11 @@ function elemento(comp, e, o) {
|
|
|
809
844
|
if (typeof e == 'number') e = kElem(e);
|
|
810
845
|
var w = o.w || 300, h = o.h || w, L = null;
|
|
811
846
|
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
|
-
|
|
847
|
+
var rectEl = null, fx = o.folgaX || 0, fy = o.folgaY || 0; // folga: o balanço do elemento (a guarda reserva o envelope inteiro)
|
|
848
|
+
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') { rectEl = { espaco: (o.parent && o.parent.name == 'CONTEUDO') ? 'c' : 't', cena: KIT.cenaIdx, x0: o.x - w / 2 - fx, x1: o.x + w / 2 + fx, y0: o.y - h / 2 - fy, y1: o.y + h / 2 + fy, t0: 0, t1: 1, texto: false, sobrepoe: true, nome: 'elemento ' + (e && e.nome || ''), camadas: [], infla: (w + 2 * fx) / w }; KIT.rects.push(rectEl); } }
|
|
813
849
|
if (!e) {
|
|
814
850
|
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' });
|
|
851
|
+
if (rectEl) rectEl.camadas.push(L);
|
|
815
852
|
} else {
|
|
816
853
|
KIT.cache = KIT.cache || {};
|
|
817
854
|
var it = KIT.cache[e.arquivo];
|
|
@@ -823,9 +860,10 @@ function elemento(comp, e, o) {
|
|
|
823
860
|
}
|
|
824
861
|
if (!it) return elemento(comp, null, o);
|
|
825
862
|
L = kPoeEl(comp, it, e, o, o.t0 && e.tipo == 'seq' ? o.t0 : 0);
|
|
863
|
+
if (rectEl) rectEl.camadas.push(L);
|
|
826
864
|
if (e.tipo == 'seq' && it.duration > 0.1) { // o GIF acaba em ~1 s: repete até o fim da cena
|
|
827
865
|
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);
|
|
866
|
+
for (n = 0; n < 12 && ini < comp.duration; n++, ini += dur) { var Lr = kPoeEl(comp, it, e, o, ini); if (rectEl) rectEl.camadas.push(Lr); }
|
|
829
867
|
}
|
|
830
868
|
KIT.nElem = (KIT.nElem || 0) + 1;
|
|
831
869
|
}
|
|
@@ -962,7 +1000,7 @@ function kConfereCena(comp) {
|
|
|
962
1000
|
}
|
|
963
1001
|
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
1002
|
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])); }
|
|
1003
|
+
for (i = 0; i < R.length; i++) if (R[i].cena == KIT.cenaIdx && !R[i].conferido && !R[i].fora) { R[i].conferido = true; mine.push(kRectLayout(R[i])); }
|
|
966
1004
|
for (i = 0; i < mine.length; i++) {
|
|
967
1005
|
a = mine[i];
|
|
968
1006
|
if (a.banda) continue;
|
|
@@ -987,6 +1025,57 @@ function kConfereCena(comp) {
|
|
|
987
1025
|
}
|
|
988
1026
|
}
|
|
989
1027
|
|
|
1028
|
+
// ---------------------------------------------------------------- a guarda de layout (kit-ae/guarda.jsx), com as caixas reais
|
|
1029
|
+
/* kGuarda(comp): no fecha() de cada cena, ANTES do render. Junta as caixas registradas (texto medido com sourceRectAtTime, elementos
|
|
1030
|
+
com o envelope do balanço, a foto) e roda gConsertar: ícone sobre texto/foto/outro ícone muda de lugar, encolhe ou sai; a foto que
|
|
1031
|
+
encosta no texto sobe ou desce; o que não tem saída vira "AVISO cena N: guarda: troca a peça". Move por expressão (value + delta),
|
|
1032
|
+
que soma às chaves e ao balanço sem mexer neles. Escreve "GUARDA {json}" com o log e as caixas finais (o Node confere depois). */
|
|
1033
|
+
function kZonaGuarda(comp) {
|
|
1034
|
+
var W = comp.width, H = comp.height, z = gZona(W, H, { legenda: KIT.enqDy > 0 }), a = kParaLayout(comp, z.x0, z.y0), b = kParaLayout(comp, z.x1, z.y1), tr = null;
|
|
1035
|
+
if (z.trilho) { var t = kParaLayout(comp, z.trilho[0], z.trilho[1]); tr = [t.x, t.y, W * 2, H * 2]; }
|
|
1036
|
+
return { x0: a.x, y0: a.y, x1: b.x, y1: b.y, trilho: tr };
|
|
1037
|
+
}
|
|
1038
|
+
function kExpr(prop) { var e = ''; try { e = prop.expression || ''; } catch (er) { e = ''; } return e ? '(' + e + ')' : 'value'; }
|
|
1039
|
+
function kDesloca(L, dx, dy, k) {
|
|
1040
|
+
try {
|
|
1041
|
+
if (Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5) { var p = kP(L); p.expression = 'var gv = ' + kExpr(p) + '; gv.length > 2 ? [gv[0] + ' + dx.toFixed(1) + ', gv[1] + ' + dy.toFixed(1) + ', gv[2]] : [gv[0] + ' + dx.toFixed(1) + ', gv[1] + ' + dy.toFixed(1) + ']'; }
|
|
1042
|
+
if (k && Math.abs(k - 1) > 0.005) { var sc = kS(L); sc.expression = 'var gs = ' + kExpr(sc) + '; gs.length > 2 ? [gs[0] * ' + k.toFixed(4) + ', gs[1] * ' + k.toFixed(4) + ', gs[2]] : [gs[0] * ' + k.toFixed(4) + ', gs[1] * ' + k.toFixed(4) + ']'; }
|
|
1043
|
+
} catch (e) { KIT.avisos.push('cena ' + KIT.cenaIdx + ': a guarda não conseguiu mover ' + L.name + ': ' + e); }
|
|
1044
|
+
}
|
|
1045
|
+
function kSome(L) { try { kO(L).expression = '0'; } catch (e) { } try { L.enabled = false; } catch (e2) { } }
|
|
1046
|
+
function kGuarda(comp) {
|
|
1047
|
+
if (KIT.guarda === false) return;
|
|
1048
|
+
var W = comp.width, H = comp.height, R = KIT.rects, itens = [], refs = [], i, j;
|
|
1049
|
+
for (i = 0; i < R.length; i++) {
|
|
1050
|
+
var r0 = R[i];
|
|
1051
|
+
if (r0.cena != KIT.cenaIdx || r0.banda || r0.fora) continue;
|
|
1052
|
+
if (r0.texto && r0.livre) continue; // texto dentro de aparelho (celular, janela): mora no desenho dele
|
|
1053
|
+
var r = kRectLayout(r0), tipo;
|
|
1054
|
+
if (r0.texto) tipo = 'texto';
|
|
1055
|
+
else if (r0.guarda == 'foto') tipo = 'foto';
|
|
1056
|
+
else if (!r0.camadas || !r0.camadas.length || KIT.cenaHeroi || (r.x1 - r.x0) / (r0.infla || 1) > W * 0.3) tipo = 'cartao';
|
|
1057
|
+
else tipo = 'icone';
|
|
1058
|
+
itens.push({ id: i, tipo: tipo, nome: tipo == 'texto' ? 'texto "' + String(r0.nome).slice(0, 24) + '"' : String(r0.nome || tipo).replace(/^elemento /, 'ícone '), c: [r.x0, r.y0, r.x1, r.y1], fixo: tipo == 'texto' || tipo == 'cartao', movel: tipo == 'foto', prio: tipo == 'icone' ? 1 : 5, infla: r0.infla || 1 });
|
|
1059
|
+
refs.push(r0);
|
|
1060
|
+
}
|
|
1061
|
+
if (!itens.length) return;
|
|
1062
|
+
var u = Math.min(W, H) / 1080;
|
|
1063
|
+
var res = gConsertar(itens, W, H, { zona: kZonaGuarda(comp), so: 'icones', folga: (KIT.guardaFolga || 10) * u, escalaMundo: KIT.enq * 1.03 });
|
|
1064
|
+
var caixas = [];
|
|
1065
|
+
for (j = 0; j < res.itens.length; j++) {
|
|
1066
|
+
var a = res.itens[j], q = refs[j], esc = (q.espaco == 'c' && KIT.remap) ? KIT.remap.s : 1, cl = q.camadas || [], m;
|
|
1067
|
+
caixas.push({ tipo: a.tipo, nome: a.nome, c: [Math.round(a.c[0]), Math.round(a.c[1]), Math.round(a.c[2]), Math.round(a.c[3])], fora: a.removido ? 1 : 0 });
|
|
1068
|
+
if (a.removido) { for (m = 0; m < cl.length; m++) kSome(cl[m]); q.fora = true; continue; }
|
|
1069
|
+
if (Math.abs(a.dx) > 0.5 || Math.abs(a.dy) > 0.5 || Math.abs(a.k - 1) > 0.005) {
|
|
1070
|
+
for (m = 0; m < cl.length; m++) kDesloca(cl[m], a.dx / esc, a.dy / esc, a.k);
|
|
1071
|
+
var cx = (q.x0 + q.x1) / 2 + a.dx / esc, cy = (q.y0 + q.y1) / 2 + a.dy / esc, hw = (q.x1 - q.x0) / 2 * a.k, hh = (q.y1 - q.y0) / 2 * a.k;
|
|
1072
|
+
q.x0 = cx - hw; q.x1 = cx + hw; q.y0 = cy - hh; q.y1 = cy + hh;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
if (res.trocar) KIT.avisos.push('cena ' + KIT.cenaIdx + ': guarda: troca a peça (' + res.log[res.log.length - 1] + ')');
|
|
1076
|
+
writeLn('GUARDA ' + JSON.stringify({ cena: KIT.cenaIdx, w: W, h: H, log: res.log, caixas: caixas, violacoes: res.violacoes }));
|
|
1077
|
+
}
|
|
1078
|
+
|
|
990
1079
|
// ---------------------------------------------------------------- quando as coisas entram (a regra dos 30–40%)
|
|
991
1080
|
/* 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
1081
|
kEntrada/kAnimaPalavra registraram. Cursor, ondas, partículas e atmosfera não contam: não são elementos novos. */
|
|
@@ -1039,6 +1128,13 @@ function kCaixaMundo(L, t, cache) {
|
|
|
1039
1128
|
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
1129
|
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
1130
|
}
|
|
1131
|
+
/* a caixa da setinha no mundo: o desenho dela (sourceRectAtTime da forma) com escala, pais e câmera */
|
|
1132
|
+
function kCaixaCursorMundo(L, t, cache) {
|
|
1133
|
+
var q = L.sourceRectAtTime(t, false), m = kMatMundo(L, t, cache), r = [q.left, q.top, q.left + q.width, q.top + q.height];
|
|
1134
|
+
var xs = [r[0], r[2], r[0], r[2]], ys = [r[1], r[1], r[3], r[3]], i, X = [], Y = [];
|
|
1135
|
+
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]); }
|
|
1136
|
+
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])];
|
|
1137
|
+
}
|
|
1042
1138
|
var KIT_SKIP_CADEIA = /^(feed|mapa|parede|pan|zoom|celular|janela|papel|carimbo|slot|carta)/;
|
|
1043
1139
|
function kAuditoria(comp) {
|
|
1044
1140
|
if (KIT.auditoria === false) return;
|
|
@@ -1046,25 +1142,26 @@ function kAuditoria(comp) {
|
|
|
1046
1142
|
var cand = [];
|
|
1047
1143
|
for (i = 1; i <= comp.numLayers; i++) {
|
|
1048
1144
|
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;
|
|
1145
|
+
var ehTexto = nm.indexOf('palavra ') == 0, ehEl = nm.indexOf('el ') == 0 || nm == 'elemento-reserva' || nm == 'ambiente', ehCur = nm == 'cursor';
|
|
1146
|
+
if (!ehTexto && !ehEl && !ehCur) continue;
|
|
1051
1147
|
var skip = false, P = L.parent;
|
|
1052
1148
|
while (P) { if (KIT_SKIP_CADEIA.test(P.name)) { skip = true; break; } P = P.parent; }
|
|
1053
1149
|
var nominal = (L.comment != null && L.comment !== '') ? parseFloat(L.comment) : 0;
|
|
1054
1150
|
if (isNaN(nominal)) nominal = 0;
|
|
1055
|
-
cand.push({ L: L, nome: nm, texto: ehTexto, skip: skip, entra: nominal, amb: nm == 'ambiente' });
|
|
1151
|
+
cand.push({ L: L, nome: nm, texto: ehTexto, cursor: ehCur, skip: skip && !ehCur, entra: ehCur ? 0 : nominal, amb: nm == 'ambiente' });
|
|
1056
1152
|
}
|
|
1057
1153
|
var passo = 0.1 * f, dur = comp.duration, fim = dur - 0.55 * f, t, amostras = 0, nFora = 0, nCol = 0, vistos = {}, exs = 0;
|
|
1058
1154
|
for (t = 0.3 * f; t < fim; t += passo) {
|
|
1059
1155
|
var cache = {}, vis = [];
|
|
1060
1156
|
for (i = 0; i < cand.length; i++) {
|
|
1061
1157
|
var c = cand[i];
|
|
1062
|
-
if (c.skip || t < c.entra + 0.5 * f) continue;
|
|
1158
|
+
if (c.skip || (!c.cursor && t < c.entra + 0.5 * f)) continue;
|
|
1063
1159
|
try {
|
|
1064
1160
|
var tr = c.L.property('ADBE Transform Group');
|
|
1065
1161
|
if (tr.property('ADBE Opacity').valueAtTime(t, false) < 40) continue;
|
|
1066
1162
|
if (t < c.L.inPoint || t >= c.L.outPoint) continue;
|
|
1067
|
-
var bx = kCaixaMundo(c.L, t, cache);
|
|
1163
|
+
var bx = c.cursor ? kCaixaCursorMundo(c.L, t, cache) : kCaixaMundo(c.L, t, cache);
|
|
1164
|
+
if (c.cursor) { var bx2 = kCaixaCursorMundo(c.L, t + 0.05 * f, {}); if (Math.abs(bx2[0] - bx[0]) + Math.abs(bx2[1] - bx[1]) > 4) continue; } // em trânsito: só conta parado (no clique)
|
|
1068
1165
|
if (bx[2] - bx[0] < 2 || bx[3] - bx[1] < 2) continue;
|
|
1069
1166
|
var h0 = bx[3] - bx[1];
|
|
1070
1167
|
vis.push({ c: c, b: bx, tinta: c.texto ? [bx[0], bx[1] + h0 * 0.14, bx[2], bx[3] - h0 * 0.14] : bx });
|
|
@@ -1075,13 +1172,22 @@ function kAuditoria(comp) {
|
|
|
1075
1172
|
for (i = 0; i < vis.length; i++) {
|
|
1076
1173
|
var a = vis[i], b = a.b;
|
|
1077
1174
|
var zz = a.c.texto ? zT : zE, limX0 = zz.x0, limX1 = zz.x1, limY0 = zz.y0, limY1 = zz.y1;
|
|
1078
|
-
if (b[0] < limX0 -
|
|
1175
|
+
if (!a.c.cursor && b[0] < limX0 - 12 || b[2] > limX1 + 12 || b[1] < limY0 - 12 || b[3] > limY1 + 12) { // 12 px (1%): a respiração da câmera passa disso e não encosta na borda
|
|
1079
1176
|
foraT = true;
|
|
1080
1177
|
var kf = 'f:' + a.c.nome;
|
|
1081
1178
|
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
1179
|
}
|
|
1083
1180
|
for (j = i + 1; j < vis.length; j++) {
|
|
1084
1181
|
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]);
|
|
1182
|
+
if (a.c.cursor || vis[j].c.cursor) { // o cursor é elemento: nunca em cima de letra (com elemento, pode)
|
|
1183
|
+
var tx2 = a.c.cursor ? vis[j].c : a.c;
|
|
1184
|
+
if (tx2.texto && ox > 2 && oy > 2) {
|
|
1185
|
+
colT = true;
|
|
1186
|
+
var kq = 'q:' + tx2.nome;
|
|
1187
|
+
if (!vistos[kq] && exs < 8) { vistos[kq] = 1; exs++; KIT.avisos.push('cena ' + KIT.cenaIdx + ': cursor sobre o texto (quadro ' + (t / f).toFixed(1) + ' s): ' + tx2.nome.replace('palavra ', '')); }
|
|
1188
|
+
}
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1085
1191
|
if (ox > 5 && oy > 5) {
|
|
1086
1192
|
var A1 = (bt[2] - bt[0]) * (bt[3] - bt[1]), A2 = (o2[2] - o2[0]) * (o2[3] - o2[1]);
|
|
1087
1193
|
if (ox * oy > 0.05 * Math.min(A1, A2)) {
|