primocode 10.16.0 → 10.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +11 -2
- 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 +36 -8
- package/lib/catalogo.js +4 -0
- 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.js +45 -4
- package/lib/estudio/index.js +8 -5
- 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 +61 -7
- 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 +60 -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/local/cerebro.json +192 -6
- package/lib/local/motor.js +5 -2
- package/lib/nuvem.js +11 -1
- package/lib/passagem.js +50 -0
- package/lib/perigo.js +19 -11
- package/lib/protecao.js +134 -0
- package/lib/titulo-conversa.js +46 -0
- package/lib/tools.js +3 -0
- package/lib/ui.js +1 -1
- 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/app.py +43 -4
- package/voz/primo_voz/chat.html +17 -6
- package/voz/primo_voz/painel_dados.py +134 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"""barra_progresso: a progress bar (or ring) filling up to a percentage, with the label and the percentage
|
|
2
|
+
counting, moving stripes and a bright tip while it fills, and a short glow when it lands (SPEC Contract 12).
|
|
3
|
+
Optional segments turn it into a step bar ("etapa 3 de 5").
|
|
4
|
+
|
|
5
|
+
`_stripes` draws moving diagonal stripes clipped to a mask. The card, label and track are drawn once
|
|
6
|
+
(`E.static_sprite`) and pasted after the entrance.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import math
|
|
13
|
+
from functools import lru_cache
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
from motion import engine as E
|
|
18
|
+
|
|
19
|
+
TEMPLATE = {
|
|
20
|
+
"id": "barra_progresso",
|
|
21
|
+
"label_pt": "Barra de progresso",
|
|
22
|
+
"description_pt": "Uma barra que enche até a porcentagem, com o nome em cima e a porcentagem contando. "
|
|
23
|
+
"Pode virar um anel ou uma barra em etapas.",
|
|
24
|
+
"use_when_pt": "Quando a fala cita quanto de algo já foi feito ou falta: 80% do curso, 3 de 5 etapas, "
|
|
25
|
+
"vagas quase acabando, parte da meta batida.",
|
|
26
|
+
"category": "números",
|
|
27
|
+
"version": 1,
|
|
28
|
+
"params": {
|
|
29
|
+
"value": {"type": "number", "default": 75, "min": 0, "max": 100, "label_pt": "Porcentagem final, de 0 a 100"},
|
|
30
|
+
"start_value": {"type": "number", "default": 0, "min": 0, "max": 100,
|
|
31
|
+
"label_pt": "Porcentagem onde começa"},
|
|
32
|
+
"label": {"type": "text", "default": "Concluído", "max_len": 30, "label_pt": "Nome da barra"},
|
|
33
|
+
"note": {"type": "text", "default": "", "max_len": 36,
|
|
34
|
+
"label_pt": "Texto pequeno embaixo, ex.: 3 de 4 módulos (opcional)"},
|
|
35
|
+
"decimals": {"type": "integer", "default": None, "nullable": True, "min": 0, "max": 1,
|
|
36
|
+
"label_pt": "Casas decimais (vazio: automático)"},
|
|
37
|
+
"mode": {"type": "choice", "choices": ["barra", "anel"], "default": "barra",
|
|
38
|
+
"label_pt": "barra (deitada) ou anel (círculo com o número no meio)"},
|
|
39
|
+
"segments": {"type": "integer", "default": None, "nullable": True, "min": 2, "max": 10,
|
|
40
|
+
"label_pt": "Dividir em etapas (vazio: barra contínua)"},
|
|
41
|
+
"show_value": {"type": "bool", "default": True, "label_pt": "Mostrar a porcentagem"},
|
|
42
|
+
"duration": {"type": "number", "default": 1.4, "min": 0.3, "max": 10,
|
|
43
|
+
"label_pt": "Segundos para encher"},
|
|
44
|
+
"delay": {"type": "number", "default": 0.15, "min": 0, "max": 5,
|
|
45
|
+
"label_pt": "Segundos antes de começar a encher"},
|
|
46
|
+
"ease": {"type": "choice", "choices": ["suave", "linear"], "default": "suave",
|
|
47
|
+
"label_pt": "Ritmo: suave (rápido e freia no fim) ou linear (constante)"},
|
|
48
|
+
"card": {"type": "bool", "default": True, "label_pt": "Cartão atrás da barra"},
|
|
49
|
+
},
|
|
50
|
+
"default_duration": 3.0,
|
|
51
|
+
"default_region": "baixo",
|
|
52
|
+
"sfx": [{"at": 0.0, "name": "pop"}, {"at": 0.15, "name": "count", "volume": 1.3}],
|
|
53
|
+
"example": {"value": 87, "label": "Vagas preenchidas", "note": "restam poucas vagas nesta turma"},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
LAND = 0.4
|
|
57
|
+
FIT = 0.97 # text fitted to a full width keeps 3% spare: the half-size preview rounds glyphs wider
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _decimals(p: dict) -> int:
|
|
61
|
+
if p["decimals"] is not None:
|
|
62
|
+
return int(p["decimals"])
|
|
63
|
+
return max(E.auto_decimals(p["value"], 1), E.auto_decimals(p["start_value"], 1))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _stripes(cv: E.Canvas, m, t: float, period: float, opacity: float) -> None:
|
|
67
|
+
"""Diagonal light stripes moving to the right, clipped to the mask."""
|
|
68
|
+
if m is None or opacity <= 0.003:
|
|
69
|
+
return
|
|
70
|
+
h, w = m.a.shape
|
|
71
|
+
per = cv.len(period)
|
|
72
|
+
xs = np.arange(w, dtype=np.float32)[None, :] + m.x + 0.5
|
|
73
|
+
ys = np.arange(h, dtype=np.float32)[:, None] + m.y + 0.5
|
|
74
|
+
u = (xs + ys - cv.len(t * period * 1.6)) / per
|
|
75
|
+
tri = np.abs((u % 1.0) - 0.5) * 2.0 # 0..1 triangle wave
|
|
76
|
+
edge = 1.0 / max(1.0, per) # about one pixel of soft edge
|
|
77
|
+
band = np.clip((tri - 0.5) / (2 * edge) + 0.5, 0.0, 1.0)
|
|
78
|
+
cv.fill_mask(E.Mask(m.a * band, m.x, m.y), "white", opacity)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ------------------------------------------------------------------ layout (once per item)
|
|
82
|
+
|
|
83
|
+
@lru_cache(maxsize=64)
|
|
84
|
+
def _layout(params_json: str, box: E.Box) -> dict:
|
|
85
|
+
p = json.loads(params_json)
|
|
86
|
+
theme = box.theme
|
|
87
|
+
card = p["card"]
|
|
88
|
+
dec = _decimals(p)
|
|
89
|
+
label = p["label"].strip()
|
|
90
|
+
note = p["note"].strip()
|
|
91
|
+
show = p["show_value"]
|
|
92
|
+
widest = E.format_number(100 if p["value"] >= 99.5 or p["start_value"] >= 99.5 else 88.8, dec)
|
|
93
|
+
accent_ok = not card or E.contrast(theme.accent, theme.card) >= 2.4
|
|
94
|
+
out = {"dec": dec, "label": label, "note": note, "card": card, "show": show,
|
|
95
|
+
"value_color": "accent" if accent_ok else "text", "mode": p["mode"], "segments": p["segments"]}
|
|
96
|
+
note_size = 32.0
|
|
97
|
+
if p["mode"] == "barra":
|
|
98
|
+
pad = 44.0 if card else 8.0
|
|
99
|
+
card_w = box.w
|
|
100
|
+
inner = card_w - 2 * pad
|
|
101
|
+
v_size = 66.0
|
|
102
|
+
pct = 0.56
|
|
103
|
+
num_w = E.measure(widest, "sans_black", v_size, tabular=True).width
|
|
104
|
+
val_w = (num_w + v_size * 0.03 + E.measure("%", "sans_black", v_size * pct).width) if show else 0.0
|
|
105
|
+
l_size = E.fit_text(label, "text_bold", inner - val_w - 30.0, 44.0, 28.0) if label else 0.0
|
|
106
|
+
l_cap = E.measure("H", "text_bold", l_size).cap if label else 0.0
|
|
107
|
+
v_cap = E.measure("0", "sans_black", v_size).cap if show else 0.0
|
|
108
|
+
head = max(l_cap, v_cap)
|
|
109
|
+
bar_h = 38.0
|
|
110
|
+
gap = 28.0 if head else 0.0
|
|
111
|
+
if note:
|
|
112
|
+
note_size = E.fit_text(note, "text_semibold", inner * FIT, 32.0, 24.0)
|
|
113
|
+
n_cap = E.measure("H", "text_semibold", note_size).cap if note else 0.0
|
|
114
|
+
n_gap = 26.0 if note else 0.0
|
|
115
|
+
card_h = 2 * pad + head + gap + bar_h + n_gap + n_cap + (4.0 if note else 0.0)
|
|
116
|
+
top = (box.h - card_h) / 2.0
|
|
117
|
+
x0 = (box.w - card_w) / 2.0
|
|
118
|
+
head_base = top + pad + head
|
|
119
|
+
out.update(card_box=(x0, top, card_w, card_h), radius=min(40.0, card_h * 0.2), l_size=l_size,
|
|
120
|
+
v_size=v_size, pct=pct, num_w=num_w, head_base=head_base, left=x0 + pad, right=x0 + pad + inner,
|
|
121
|
+
bar=(x0 + pad, head_base + gap, inner, bar_h), note_size=note_size,
|
|
122
|
+
note_base=head_base + gap + bar_h + n_gap + n_cap)
|
|
123
|
+
else:
|
|
124
|
+
pad = 44.0 if card else 8.0
|
|
125
|
+
R = 150.0
|
|
126
|
+
ring_w = 30.0
|
|
127
|
+
v_size = 92.0
|
|
128
|
+
pct = 0.5
|
|
129
|
+
num_w = E.measure(widest, "sans_black", v_size, tabular=True).width
|
|
130
|
+
full_w = num_w + v_size * 0.03 + E.measure("%", "sans_black", v_size * pct).width
|
|
131
|
+
inner_room = (R - ring_w / 2.0) * 2 * 0.8
|
|
132
|
+
if full_w > inner_room:
|
|
133
|
+
v_size *= inner_room / full_w
|
|
134
|
+
num_w = E.measure(widest, "sans_black", v_size, tabular=True).width
|
|
135
|
+
l_size = E.fit_text(label, "text_bold", (box.w - 2 * pad) * FIT, 42.0, 28.0) if label else 0.0
|
|
136
|
+
l_cap = E.measure("H", "text_bold", l_size).cap if label else 0.0
|
|
137
|
+
if note:
|
|
138
|
+
note_size = E.fit_text(note, "text_semibold", (box.w - 2 * pad) * FIT, 32.0, 24.0)
|
|
139
|
+
n_cap = E.measure("H", "text_semibold", note_size).cap if note else 0.0
|
|
140
|
+
l_gap = 34.0 if label else 0.0
|
|
141
|
+
n_gap = 18.0 if note else 0.0
|
|
142
|
+
content_w = max(2 * R + ring_w, E.measure(label, "text_bold", l_size).width if label else 0.0,
|
|
143
|
+
E.measure(note, "text_semibold", note_size).width if note else 0.0)
|
|
144
|
+
card_w = min(box.w, content_w + 2 * pad + 20.0)
|
|
145
|
+
card_h = 2 * pad + 2 * R + ring_w + l_gap + l_cap + n_gap + n_cap + (4.0 if note else 0.0)
|
|
146
|
+
if card_h > box.h: # a short region: shrink the ring
|
|
147
|
+
k = max(0.5, 1.0 - (card_h - box.h) / (2 * R + ring_w))
|
|
148
|
+
R, ring_w, v_size = R * k, ring_w * k, v_size * k
|
|
149
|
+
num_w = E.measure(widest, "sans_black", v_size, tabular=True).width
|
|
150
|
+
card_h = 2 * pad + 2 * R + ring_w + l_gap + l_cap + n_gap + n_cap + (4.0 if note else 0.0)
|
|
151
|
+
top = (box.h - card_h) / 2.0
|
|
152
|
+
x0 = (box.w - card_w) / 2.0
|
|
153
|
+
cy = top + pad + ring_w / 2.0 + R
|
|
154
|
+
v_cap = E.measure("0", "sans_black", v_size).cap
|
|
155
|
+
label_base = cy + R + ring_w / 2.0 + l_gap + l_cap
|
|
156
|
+
out.update(card_box=(x0, top, card_w, card_h), radius=min(44.0, card_w * 0.1), l_size=l_size,
|
|
157
|
+
v_size=v_size, pct=pct, num_w=num_w, ring=(box.w / 2.0, cy, R, ring_w),
|
|
158
|
+
value_base=cy + v_cap / 2.0, label_base=label_base, note_size=note_size,
|
|
159
|
+
note_base=label_base + n_gap + n_cap, cx=box.w / 2.0)
|
|
160
|
+
return out
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# ------------------------------------------------------------------ drawing
|
|
164
|
+
|
|
165
|
+
def _fill(params: dict, t: float) -> tuple[float, float]:
|
|
166
|
+
"""(percentage shown now, eased progress 0..1)."""
|
|
167
|
+
fn = "linear" if params["ease"] == "linear" else "out_quart"
|
|
168
|
+
k = E.eased(t, params["delay"], params["duration"], fn)
|
|
169
|
+
return params["start_value"] + (params["value"] - params["start_value"]) * k, k
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _t_end(params: dict) -> float:
|
|
173
|
+
return params["delay"] + params["duration"]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _value(cv: E.Canvas, L: dict, x: float, base: float, v: float, anchor: str, scale: float, outline: dict) -> None:
|
|
177
|
+
size = L["v_size"]
|
|
178
|
+
num = E.format_number(v, L["dec"])
|
|
179
|
+
pct_s = size * L["pct"]
|
|
180
|
+
num_w = E.measure(num, "sans_black", size, tabular=True).width
|
|
181
|
+
pct_w = E.measure("%", "sans_black", pct_s).width
|
|
182
|
+
total = num_w + size * 0.03 + pct_w
|
|
183
|
+
x0 = x - total if anchor == "r" else x - total / 2.0
|
|
184
|
+
with cv.group(scale=scale, pivot=(x0 + total / 2.0, base - size * 0.36)):
|
|
185
|
+
cv.text(x0, base, num, "sans_black", size, color=L["value_color"], tabular=True, **outline)
|
|
186
|
+
cv.text(x0 + num_w + size * 0.03, base, "%", "sans_black", pct_s, color="text", opacity=0.9, **outline)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
STATIC_AFTER = 0.5 # seconds: from here on the card, label, note and tracks never change
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _segments(L: dict) -> list[tuple[float, float]]:
|
|
193
|
+
"""(x, width) of each piece of the bar."""
|
|
194
|
+
bx, by, bw, bh = L["bar"]
|
|
195
|
+
n = L["segments"] or 1
|
|
196
|
+
gap = 10.0 if L["segments"] else 0.0
|
|
197
|
+
sw = (bw - gap * (n - 1)) / n
|
|
198
|
+
return [(bx + i * (sw + gap), sw) for i in range(n)]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _arcs(L: dict) -> list[tuple[float, float]]:
|
|
202
|
+
"""(start degrees, sweep degrees) of each piece of the ring."""
|
|
203
|
+
rcx, rcy, R, rw = L["ring"]
|
|
204
|
+
segs = L["segments"]
|
|
205
|
+
n = segs or 1
|
|
206
|
+
# round caps stick out rw/2 at each end: leave room for them so the gaps stay open
|
|
207
|
+
cap_deg = math.degrees(rw / 2.0 / R) if segs else 0.0
|
|
208
|
+
gap_deg = 8.0 if segs else 0.0
|
|
209
|
+
span = 360.0 / n
|
|
210
|
+
return [(-90.0 + i * span + gap_deg / 2.0 + cap_deg, span - gap_deg - 2 * cap_deg) for i in range(n)]
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _draw_static(cv: E.Canvas, L: dict, note_in: float) -> None:
|
|
214
|
+
"""Everything that does not move once the card is in: card, label, note and the empty track."""
|
|
215
|
+
card = L["card"]
|
|
216
|
+
outline = {} if card else {"stroke": 5.0, "stroke_color": "black", "shadow": E.TEXT_SHADOW}
|
|
217
|
+
soft = {} if card else {"stroke": 4.0, "stroke_color": "black"}
|
|
218
|
+
track = "track" if card else (0, 0, 0, 120)
|
|
219
|
+
if card:
|
|
220
|
+
cx0, cy0, cw, ch = L["card_box"]
|
|
221
|
+
cv.card(cx0, cy0, cw, ch, radius=L["radius"])
|
|
222
|
+
if L["mode"] == "barra":
|
|
223
|
+
if L["label"]:
|
|
224
|
+
cv.text(L["left"], L["head_base"], L["label"], "text_bold", L["l_size"], color="text", **outline)
|
|
225
|
+
bx, by, bw, bh = L["bar"]
|
|
226
|
+
for sx, sw in _segments(L):
|
|
227
|
+
cv.pill(sx, by, sw, bh, fill=track)
|
|
228
|
+
if L["note"]:
|
|
229
|
+
cv.text(L["left"], L["note_base"], L["note"], "text_semibold", L["note_size"],
|
|
230
|
+
color="muted" if card else "text", opacity=note_in, **soft)
|
|
231
|
+
return
|
|
232
|
+
rcx, rcy, R, rw = L["ring"]
|
|
233
|
+
if L["segments"]:
|
|
234
|
+
for a0, sweep in _arcs(L):
|
|
235
|
+
cv.polyline(E.ellipse_points(rcx, rcy, R, R, a0, sweep), rw, color=track)
|
|
236
|
+
else:
|
|
237
|
+
cv.ring(rcx, rcy, R, rw, color=track)
|
|
238
|
+
if L["label"]:
|
|
239
|
+
cv.text(L["cx"], L["label_base"], L["label"], "text_bold", L["l_size"], color="text", anchor="ms", **outline)
|
|
240
|
+
if L["note"]:
|
|
241
|
+
cv.text(L["cx"], L["note_base"], L["note"], "text_semibold", L["note_size"],
|
|
242
|
+
color="muted" if card else "text", opacity=note_in, anchor="ms", **soft)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _static_sprite(params_json: str, box: E.Box):
|
|
246
|
+
"""The static part drawn once (card, label, note and the empty track)."""
|
|
247
|
+
return E.static_sprite(("barra_progresso", params_json), box,
|
|
248
|
+
lambda c: _draw_static(c, _layout(params_json, box), 1.0))
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def render(frame_index: int, fps: int, params: dict, box: E.Box) -> E.Canvas:
|
|
252
|
+
cv = E.Canvas(box)
|
|
253
|
+
t = frame_index / fps
|
|
254
|
+
key = json.dumps(params, sort_keys=True)
|
|
255
|
+
L = _layout(key, box)
|
|
256
|
+
cx0, cy0, cw, ch = L["card_box"]
|
|
257
|
+
card = L["card"]
|
|
258
|
+
t_in = t + 1.0 / fps
|
|
259
|
+
alpha = E.eased(t_in, 0.0, 0.18, "out_quad")
|
|
260
|
+
scale = 0.92 + 0.08 * E.out_back(E.progress(t_in, 0.0, 0.5), 1.4)
|
|
261
|
+
rise = 30.0 * (1.0 - E.eased(t_in, 0.0, 0.45))
|
|
262
|
+
outline = {} if card else {"stroke": 5.0, "stroke_color": "black", "shadow": E.TEXT_SHADOW}
|
|
263
|
+
v, k = _fill(params, t)
|
|
264
|
+
frac = v / 100.0
|
|
265
|
+
t_end = _t_end(params)
|
|
266
|
+
moving = params["value"] != params["start_value"] and 0.0 < k < 1.0
|
|
267
|
+
land = E.pulse(t, t_end - 0.02, LAND) if params["value"] != params["start_value"] else 0.0
|
|
268
|
+
stripe_op = 0.16 * (1.0 - E.eased(t, t_end - 0.25, 0.3))
|
|
269
|
+
segs = L["segments"]
|
|
270
|
+
|
|
271
|
+
with cv.group(opacity=alpha, scale=scale, dy=rise, pivot=(cx0 + cw / 2.0, cy0 + ch / 2.0)):
|
|
272
|
+
if t_in >= STATIC_AFTER: # the entrance is over: the group is the identity
|
|
273
|
+
cv.paste_sprite(_static_sprite(key, box))
|
|
274
|
+
else:
|
|
275
|
+
_draw_static(cv, L, E.eased(t, 0.1, 0.35))
|
|
276
|
+
|
|
277
|
+
if L["mode"] == "barra":
|
|
278
|
+
if L["show"]:
|
|
279
|
+
_value(cv, L, L["right"], L["head_base"], v, "r", 1.0 + 0.07 * land, outline)
|
|
280
|
+
bx, by, bw, bh = L["bar"]
|
|
281
|
+
fills = []
|
|
282
|
+
pieces = _segments(L)
|
|
283
|
+
for i, (sx, sw) in enumerate(pieces):
|
|
284
|
+
f = E.clamp01(frac * len(pieces) - i)
|
|
285
|
+
if f <= 0:
|
|
286
|
+
break
|
|
287
|
+
fw = sw * f
|
|
288
|
+
m = cv.rect_mask(sx, by, max(fw, bh), bh, radius=bh / 2.0)
|
|
289
|
+
if fw < bh:
|
|
290
|
+
m = E.clip_mask(m, x1=cv.px(sx + fw, 0.0)[0])
|
|
291
|
+
fills.append(m)
|
|
292
|
+
tip_x = bx + bw * frac if not segs else None
|
|
293
|
+
|
|
294
|
+
def draw_fill() -> None:
|
|
295
|
+
grad = E.Gradient(box.theme.accent, E.mix_color(box.theme.accent, (255, 255, 255), 0.28),
|
|
296
|
+
start=(bx, 0.0), end=(bx + bw, 0.0))
|
|
297
|
+
for m in fills:
|
|
298
|
+
if m is None:
|
|
299
|
+
continue
|
|
300
|
+
cv.fill_mask(m, grad)
|
|
301
|
+
cv.fill_mask(_gloss(m), "white", 0.16)
|
|
302
|
+
if moving:
|
|
303
|
+
_stripes(cv, m, t, 26.0, stripe_op)
|
|
304
|
+
|
|
305
|
+
if fills:
|
|
306
|
+
if land > 0.02:
|
|
307
|
+
cv.glow(draw_fill, radius=20.0, opacity=0.65 * land)
|
|
308
|
+
else:
|
|
309
|
+
draw_fill()
|
|
310
|
+
if moving and tip_x is not None and frac > 0:
|
|
311
|
+
tx = max(bx + bh / 2.0, tip_x - bh / 2.0)
|
|
312
|
+
cv.glow(lambda: cv.circle(tx, by + bh / 2.0, bh * 0.2, fill="white"), radius=14.0, opacity=0.9,
|
|
313
|
+
color="white")
|
|
314
|
+
else:
|
|
315
|
+
rcx, rcy, R, rw = L["ring"]
|
|
316
|
+
arcs = []
|
|
317
|
+
n = len(_arcs(L))
|
|
318
|
+
for i, (a0, sweep) in enumerate(_arcs(L)):
|
|
319
|
+
f = E.clamp01(frac * n - i)
|
|
320
|
+
if f > 0:
|
|
321
|
+
arcs.append((a0, sweep * f))
|
|
322
|
+
|
|
323
|
+
def draw_arcs() -> None:
|
|
324
|
+
for a0, sw in arcs:
|
|
325
|
+
if sw >= 359.99:
|
|
326
|
+
cv.ring(rcx, rcy, R, rw, color="accent")
|
|
327
|
+
else:
|
|
328
|
+
cv.polyline(E.ellipse_points(rcx, rcy, R, R, a0, sw), rw, color="accent")
|
|
329
|
+
|
|
330
|
+
if arcs:
|
|
331
|
+
if land > 0.02:
|
|
332
|
+
cv.glow(draw_arcs, radius=22.0, opacity=0.65 * land)
|
|
333
|
+
else:
|
|
334
|
+
draw_arcs()
|
|
335
|
+
if moving and not segs and frac > 0:
|
|
336
|
+
ang = math.radians(-90.0 + 360.0 * frac)
|
|
337
|
+
tx, ty = rcx + R * math.cos(ang), rcy + R * math.sin(ang)
|
|
338
|
+
cv.glow(lambda: cv.circle(tx, ty, rw * 0.2, fill="white"), radius=14.0, opacity=0.9, color="white")
|
|
339
|
+
if L["show"]:
|
|
340
|
+
_value(cv, L, rcx, L["value_base"], v, "m", 1.0 + 0.07 * land, outline)
|
|
341
|
+
return cv
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _gloss(m):
|
|
345
|
+
"""The upper 45% of a bar mask (a soft highlight on top of the fill)."""
|
|
346
|
+
h = m.a.shape[0]
|
|
347
|
+
rows = np.arange(h, dtype=np.float32)
|
|
348
|
+
k = np.clip((h * 0.45 - rows) / max(1.0, h * 0.25), 0.0, 1.0)
|
|
349
|
+
return E.Mask(m.a * k[:, None], m.x, m.y)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def sfx(params: dict, fps: int, frames: int) -> list[dict]:
|
|
353
|
+
cues = [{"at": 0.0, "name": "pop", "why": "entrada"}]
|
|
354
|
+
start, dur = params["delay"], params["duration"]
|
|
355
|
+
if params["value"] != params["start_value"]:
|
|
356
|
+
steps = 1 if params["ease"] != "linear" else max(1, int(dur / 0.6 + 0.5))
|
|
357
|
+
for k in range(steps):
|
|
358
|
+
cues.append({"at": start + k * 0.6, "name": "count", "volume": 1.3, "why": "barra enchendo"})
|
|
359
|
+
if params["value"] >= 100:
|
|
360
|
+
cues.append({"at": start + dur, "name": "success", "why": "100%"})
|
|
361
|
+
return [{"frame": E.frame_of(c.pop("at"), fps), **c} for c in cues]
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def suggested_duration(params: dict) -> float:
|
|
365
|
+
"""Fill, landing glow and about 1.5 s to read."""
|
|
366
|
+
return round(max(TEMPLATE["default_duration"], _t_end(params) + LAND + 1.5), 2)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def settle(params: dict, fps: int) -> float:
|
|
370
|
+
return _t_end(params) + LAND + 0.05
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""carimbo: uma palavra que cai carimbada na tela, torta, com a moldura batendo junto.
|
|
2
|
+
|
|
3
|
+
O carimbo vem de cima grande e desfocado, bate (a tela treme de leve), e fica. Serve para fechar uma
|
|
4
|
+
frase com uma palavra só: APROVADO, ESGOTADO, TEM UM CUSTO. Pode ter uma linha menor embaixo.
|
|
5
|
+
|
|
6
|
+
Tempo: `delay` até a batida; a partir daí fica parado até o fim do item.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from functools import lru_cache
|
|
13
|
+
|
|
14
|
+
from motion import engine as E
|
|
15
|
+
|
|
16
|
+
TEMPLATE = {
|
|
17
|
+
"id": "carimbo",
|
|
18
|
+
"label_pt": "Carimbo",
|
|
19
|
+
"description_pt": "Uma palavra cai carimbada na tela, torta e com moldura, e a tela treme junto. "
|
|
20
|
+
"Pode ter uma linha menor embaixo.",
|
|
21
|
+
"use_when_pt": "Para fechar uma frase com uma palavra só, no instante em que ela é falada: "
|
|
22
|
+
"APROVADO, ESGOTADO, TEM UM CUSTO, VAGAS ENCERRADAS.",
|
|
23
|
+
"notes_pt": "Palavra curta lê melhor (até 22 letras). Não use como legenda: é um golpe só, na palavra "
|
|
24
|
+
"que importa.",
|
|
25
|
+
"category": "enfase",
|
|
26
|
+
"version": 1,
|
|
27
|
+
"params": {
|
|
28
|
+
"texto": {"type": "text", "default": "APROVADO", "max_len": 22, "required_text": True,
|
|
29
|
+
"label_pt": "A palavra do carimbo (até 22 letras)"},
|
|
30
|
+
"subtexto": {"type": "text", "default": "", "max_len": 28,
|
|
31
|
+
"label_pt": "Linha menor embaixo, dentro da moldura (opcional)"},
|
|
32
|
+
"delay": {"type": "number", "default": 0.12, "min": 0, "max": 30,
|
|
33
|
+
"label_pt": "Segundos até a batida do carimbo"},
|
|
34
|
+
"angulo": {"type": "number", "default": -7.0, "min": -20, "max": 20,
|
|
35
|
+
"label_pt": "Inclinação do carimbo em graus (negativo inclina para a esquerda)"},
|
|
36
|
+
"estilo": {"type": "choice", "choices": ["moldura", "cheio"], "default": "moldura",
|
|
37
|
+
"label_pt": "Moldura vazada (padrão) ou retângulo pintado na cor de destaque"},
|
|
38
|
+
"tremer": {"type": "bool", "default": True,
|
|
39
|
+
"label_pt": "A moldura treme quando o carimbo bate"},
|
|
40
|
+
},
|
|
41
|
+
"default_duration": 2.0,
|
|
42
|
+
"default_region": "centro",
|
|
43
|
+
"sfx": [{"at": 0.12, "name": "bass_drop", "volume": 0.9}],
|
|
44
|
+
"example": {"texto": "TEM UM CUSTO", "subtexto": "servidor, IA, suporte", "angulo": -5},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
FONT = "sans_black"
|
|
48
|
+
SUB_FONT = "text_bold"
|
|
49
|
+
MAX_SIZE = 150.0
|
|
50
|
+
MIN_SIZE = 44.0
|
|
51
|
+
QUEDA = 0.20 # segundos da queda até bater
|
|
52
|
+
TREMOR = 0.22 # segundos de tremida depois da batida
|
|
53
|
+
ESCALA_INICIAL = 2.6
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@lru_cache(maxsize=64)
|
|
57
|
+
def _layout(params_json: str, box: E.Box) -> dict:
|
|
58
|
+
p = json.loads(params_json)
|
|
59
|
+
texto = p["texto"].strip()
|
|
60
|
+
sub = p["subtexto"].strip()
|
|
61
|
+
pad_x, pad_y = 46.0, 30.0
|
|
62
|
+
disp = max(220.0, box.w * 0.86) - 2 * pad_x
|
|
63
|
+
size = min(MAX_SIZE, E.fit_text(texto.upper(), FONT, disp, MAX_SIZE, MIN_SIZE))
|
|
64
|
+
cap = E.measure("H", FONT, size).cap
|
|
65
|
+
largura = E.measure(texto.upper(), FONT, size).width
|
|
66
|
+
sub_size = sub_cap = 0.0
|
|
67
|
+
sub_w = 0.0
|
|
68
|
+
if sub:
|
|
69
|
+
sub_size = min(size * 0.34, E.fit_text(sub, SUB_FONT, disp, size * 0.34, 20.0))
|
|
70
|
+
sub_cap = E.measure("H", SUB_FONT, sub_size).cap
|
|
71
|
+
sub_w = E.measure(sub, SUB_FONT, sub_size).width
|
|
72
|
+
conteudo_h = cap + (sub_cap + size * 0.28 if sub else 0.0)
|
|
73
|
+
w = min(box.w - 8.0, max(largura, sub_w) + 2 * pad_x)
|
|
74
|
+
h = conteudo_h + 2 * pad_y
|
|
75
|
+
return {"size": size, "cap": cap, "w": w, "h": h, "pad_y": pad_y,
|
|
76
|
+
"sub_size": sub_size, "sub_cap": sub_cap, "texto": texto.upper(), "sub": sub,
|
|
77
|
+
"borda": max(7.0, size * 0.085), "raio": max(14.0, size * 0.16)}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def render(frame_index: int, fps: int, params: dict, box: E.Box) -> E.Canvas:
|
|
81
|
+
cv = E.Canvas(box)
|
|
82
|
+
t = frame_index / fps + 1.0 / fps
|
|
83
|
+
L = _layout(json.dumps(params, sort_keys=True), box)
|
|
84
|
+
bate = float(params["delay"])
|
|
85
|
+
if t < bate - QUEDA:
|
|
86
|
+
return cv
|
|
87
|
+
queda = E.clamp01((t - (bate - QUEDA)) / QUEDA) # 0 no alto, 1 na batida
|
|
88
|
+
escala = ESCALA_INICIAL + (1.0 - ESCALA_INICIAL) * (queda ** 2.2) # desaba no fim (in_quart)
|
|
89
|
+
opac = min(1.0, queda * 1.8)
|
|
90
|
+
tremida = 0.0
|
|
91
|
+
if params["tremer"] and 0 <= t - bate < TREMOR:
|
|
92
|
+
f = E.clamp01((t - bate) / TREMOR)
|
|
93
|
+
tremida = (1.0 - f) * 5.0 * (1 if int((t - bate) * fps) % 2 == 0 else -1)
|
|
94
|
+
x = box.cx - L["w"] / 2.0
|
|
95
|
+
y = box.cy - L["h"] / 2.0
|
|
96
|
+
cheio = params["estilo"] == "cheio"
|
|
97
|
+
with cv.group(opacity=opac, scale=escala, pivot=(box.cx, box.cy),
|
|
98
|
+
rotate=float(params["angulo"]) + tremida * 0.25, dx=tremida):
|
|
99
|
+
if cheio:
|
|
100
|
+
cv.rect(x, y, L["w"], L["h"], radius=L["raio"], fill="accent",
|
|
101
|
+
shadow=E.Shadow(blur=18.0, dy=8.0, opacity=0.45))
|
|
102
|
+
else:
|
|
103
|
+
if box.theme.style != "vidro": # papel, pop, neon: the stamp lands on a card, not on the video
|
|
104
|
+
cv.card(x, y, L["w"], L["h"], radius=L["raio"], border=False)
|
|
105
|
+
cv.rect(x, y, L["w"], L["h"], radius=L["raio"], fill=None, stroke="accent",
|
|
106
|
+
stroke_width=L["borda"], shadow=E.Shadow(blur=14.0, dy=6.0, opacity=0.4))
|
|
107
|
+
cor = "on_accent" if cheio else "accent"
|
|
108
|
+
base = y + L["pad_y"] + L["cap"]
|
|
109
|
+
cv.text(box.cx, base, L["texto"], FONT, L["size"], color=cor, anchor="ms",
|
|
110
|
+
stroke=0.0, shadow=E.Shadow(blur=10.0, dy=4.0, opacity=0.35))
|
|
111
|
+
if L["sub"]:
|
|
112
|
+
cv.text(box.cx, base + L["size"] * 0.28 + L["sub_cap"], L["sub"], SUB_FONT, L["sub_size"],
|
|
113
|
+
color=cor, opacity=0.9, anchor="ms")
|
|
114
|
+
return cv
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def sfx(params: dict, fps: int, frames: int) -> list[dict]:
|
|
118
|
+
return [{"frame": E.frame_of(float(params["delay"]), fps), "name": "bass_drop", "volume": 0.9,
|
|
119
|
+
"why": "batida do carimbo"}]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def settle(params: dict, fps: int) -> float:
|
|
123
|
+
return float(params["delay"]) + TREMOR + 0.05
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def suggested_duration(params: dict) -> float:
|
|
127
|
+
return round(max(TEMPLATE["default_duration"], float(params["delay"]) + 1.4), 2)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def ink(params: dict, box: E.Box) -> tuple[float, float, float, float]:
|
|
131
|
+
L = _layout(json.dumps(params, sort_keys=True), box)
|
|
132
|
+
return (box.cx - L["w"] / 2.0, box.cy - L["h"] / 2.0, box.cx + L["w"] / 2.0, box.cy + L["h"] / 2.0)
|