flowchart LR
A["programa de autoria<br/>trabalha em centimetro<br/>vertical em Z"]
B["arquivo em formato<br/>de troca aberto"]
C["cena em execucao<br/>metro, vertical em Y"]
A -->|"geometria"| B
A -->|"materiais"| B
A -->|"animacoes"| B
B --> C
D["a unidade escolhida"]
E["qual eixo era o vertical"]
F["quanto a peca mede<br/>fora do computador"]
A -.->|"nao tem campo"| D
A -.->|"nao tem campo"| E
A -.->|"ninguem sabe la"| F
D -.-> G["fica para tras"]
E -.-> G
F -.-> G
1 Ativos, formatos de troca e orçamento de cena — Resumo
Versão de revisão. As oito definições deste capítulo estão aqui inteiras, e as ressalvas vêm coladas nelas. São as ressalvas que somem quando se revisa com pressa, e são elas que decidem se o próximo ativo custa três minutos ou uma tarde. O que encolheu foi o número de exemplos. O tratamento longo está na versão completa deste capítulo e no capítulo correspondente do livro.
A peça mais bem acabada desta oficina chegou pronta de fora. A primeira coisa que ela fez foi errar o próprio tamanho por um fator de cem.
Peça o arquivo à cena e ele entra. O carregador devolve quatro malhas e dois materiais, chamados FerroFundido e AcoUsinado, mais uma animação de nome AbrirFechar com três segundos de duração. Nada falha. O console fica limpo, e o quadro sai no ritmo de antes.
A morsa aparece deitada. Aparece com dezoito metros no maior lado, quando a peça que ela representa tem dezoito centímetros. E aparece pairando trinta centímetros acima do tampo, pendurada num gancho que ninguém modelou. Guarde essa imagem, porque ela volta no fim valendo por um instrumento inteiro.
1.1 O arquivo está correto, e a peça está errada
Nenhum dos três desvios produz uma linha de erro.
O veredito imediato é que o arquivo veio quebrado. Ele não veio.
Passe-o por qualquer validador da especificação e ele sai aprovado, com a mesma cara de arquivo bem escrito que teria se estivesse perfeito. A especificação fixa o metro como unidade e o eixo Y como vertical; quem exportou escreveu centímetro e Z. Nenhum campo do cabeçalho tem onde registrar essa troca. O terceiro desvio é a origem. Dentro do arquivo ela está trinta centímetros abaixo da base da peça. O número sai redondo porque veio de um plano de apoio que depois sumiu (ninguém modela sobre o nada). Some os três e você tem exatamente o que a tela mostra. Uma morsa deitada, gigante, flutuando, e nenhum aviso amarelo em lugar nenhum.
Um formato de troca aberto é um arquivo com especificação pública que transporta, de um programa de autoria até um ambiente em execução, a geometria, os materiais e as animações de um objeto.
A especificação pode fixar convenções — a unidade em que os números são lidos e qual eixo é o vertical — e é isso que ela faz. O que ela não pode fazer é obrigar quem exporta a obedecê-las.
Daí decorre a única coisa que o formato não transporta: o tamanho da peça no mundo. Um arquivo escrito noutra unidade continua formalmente válido, nenhum campo do cabeçalho denuncia a troca, e o número de metros que a peça mede fora do computador é conhecido por uma pessoa, não pelo arquivo.
Perceba o tamanho do estrago no menor exemplo possível. Um cubo de um metro de aresta, exportado em centímetro, guarda cem em cada direção. Quem lê aplica a especificação e monta um cubo de cem metros. Qual dos dois lados você mandaria corrigir? Os dois fizeram a coisa certa, e o resultado tem o tamanho de um prédio de trinta andares.
Repare no que a definição não exige. Ela não exige arquivo mal formado, não exige engano de quem exportou, e não exige que exista uma versão correta em algum lugar. Quem exporta em centímetro raramente está errando: o programa de modelagem dele trabalha assim porque a peça cabe numa bancada. A escolha faz todo sentido lá. Ela só não sobrevive à viagem.
Isso muda o vocabulário do problema. E a mudança tem consequência prática. Um arquivo com defeito se devolve para a origem. Um arquivo com convenção diferente se converte na chegada. A conversão é trabalho de quem monta a cena, faz-se uma vez por ativo, e sobrevive a qualquer troca de fornecedor.
A convenção da cena é o conjunto de escolhas que todo objeto da cena obedece, escrito uma vez e em um lugar só: a unidade de medida, qual eixo é o vertical, onde fica a origem de cada peça e qual sentido é a frente.
Ela se declara antes, em vez de se descobrir no que chega. Uma cena sem convenção escrita não tem contra o que acusar divergência: cada objeto importado passa a trazer a sua, cada ajuste é feito no olho, e não sobra ninguém capaz de dizer qual dos dois lados está errado.
O que a convenção converte é o trabalho de reconciliar. Com ela, a diferença entre o que chegou e o que a cena espera é uma conta; sem ela, é uma negociação, e ela se repete em cada objeto novo.
Quatro campos, nenhuma lógica, uma constante que o programa quase não lê. O valor dela está em existir escrita quando alguém precisa decidir se a peça que chegou está certa. Sem ela, chega o primeiro ativo e alguém divide por cem. Chega o segundo, e alguém multiplica por dez. Chega o terceiro, e a discussão passa a ser sobre qual dos dois anteriores serve de referência.
O campo da origem é o que as pessoas menos escrevem, e ele merece uma frase. Aqui a origem fica no ponto em que a peça toca a superfície de apoio. Assentar um objeto sobre o tampo passa a ser uma altura só. Fosse a origem no centro geométrico, cada peça exigiria meia altura de compensação — que é justamente o número que ninguém sabe de cor.
1.2 Metade se mede, metade alguém escreve
Metade dos parâmetros sai de uma divisão. A outra metade só existe se alguém a escrever.
O ajuste de importação é a transformação aplicada ao que chegou para trazê-lo à convenção da cena, e ele tem duas metades de naturezas diferentes.
A metade medida sai da caixa envolvente do objeto que chegou, por divisão e por subtração: o fator de escala e o assentamento da base. Ela não tem como estar errada por distração, e trocar o ativo por outro a recalcula sozinha.
A metade declarada é o que o arquivo não carrega e não tem onde carregar: qual eixo o autor usou como vertical e quanto a peça mede no mundo. Ela é escrita por quem conhece a peça.
A fronteira entre as duas é o que separa importar de arrastar. Um fator de escala digitado é a metade medida transformada em declaração, e funciona até chegar um ativo em outra unidade — quando falha, falha em silêncio, e o conserto é feito num terceiro lugar.
A caixa envolvente é um instrumento honesto e limitado. Ela percorre os vértices. Guarda o menor e o maior valor em cada direção, e devolve três larguras. Na morsa, devolve 18,00, 12,00 e 17,00. Pergunte a ela se dezoito são centímetros e ela não responde. Pergunte se a peça está deitada e ela também não responde, porque uma peça deitada tem caixa tão legítima quanto uma peça em pé.
O que ela permite é uma divisão. Declare que o maior lado real da peça mede 0,18 metro. Ponha ao lado os 18,00 que a caixa mediu, com a peça já em pé. O fator sai da razão entre os dois: 0,0100. Esse número não foi escolhido por ninguém. Ele foi produzido, e se refaz sozinho quando o ativo muda. Com o assentamento acontece o mesmo, e na morsa ele dá 0,30 metro.
o que quem importa precisa declarar, em src/bancada/content/ativos.ts
/**
* O que quem importa precisa declarar, porque o arquivo não responde.
*
* `maiorLadoRealM` é a medida do objeto no mundo, em metros, obtida por quem
* conhece a peça. Dela sai o fator de escala, por divisão contra o que foi
* medido no arquivo — e é por isso que o fator não é digitado: digitar 0.01
* funciona até o dia em que chega um ativo em milímetro.
*/
export interface AjusteDeImportacao {
readonly maiorLadoRealM: number;
O campo se chama maiorLadoRealM, e a terminação marca a unidade. Ele pede uma medida do mundo, em metros, e não um fator de conversão. A diferença parece de estilo e é de arquitetura. Suponha que o campo pedisse o fator: quem importa a morsa escreveria 0.01, o resultado de hoje seria idêntico, e ninguém notaria diferença por meses.
O defeito só nasce no dia em que chegar um ativo escrito em milímetro. Nesse dia, o 0.01 continua sendo um número válido — ele apenas descreve o arquivo anterior. A peça nova entra dez vezes maior. Alguém a vê enorme, e a correção mais provável é dividir a escala daquele objeto por dez, noutro lugar do código. A partir daí há dois fatores espalhados, sem parentesco visível, e o próximo ativo herda a confusão.
O número declarado tem ainda uma propriedade que o fator não tem. Ele é o mesmo que alguém consultaria num catálogo de fornecedor, e por isso é conferível por quem não escreveu o código. Um fator descreve o arquivo em vez da peça, e conferi-lo exige abrir o arquivo com uma ferramenta que saiba lê-lo.
flowchart TD
A["o que chegou<br/>18,00 x 12,00 x 17,00<br/>unidades do arquivo"]
B["girar<br/>-90 graus em X"]
C["medir a caixa envolvente<br/>com a peca ja em pe"]
D["escalar<br/>fator medido: 0,0100"]
E["assentar<br/>residuo de 0,30 m na base"]
F["18,0 x 17,0 x 12,0 cm<br/>na convencao da cena"]
A --> B --> C --> D --> E --> F
E -. "trocar esta ordem<br/>assenta pela face<br/>que vira lateral" .-> B
Os três ajustes acontecem numa ordem fixa, e a ordem é consequência da geometria da caixa. O giro vem primeiro, porque é ele que decide o que a caixa vai medir depois. A peça chega com o vertical em Z e precisa terminar com ele em Y, o que se faz com noventa graus negativos em torno de X. A escala vem em seguida, porque o fator depende de qual dimensão é o maior lado. Numa peça deitada, esse lado é medido noutra direção.
Metade das tentativas começa pelo sinal oposto do giro. Com o sinal negativo, o que apontava para o Z positivo passa a apontar para o Y positivo; com o positivo, a peça termina de cabeça para baixo. E aí mora um sintoma disfarçado. Uma peça aproximadamente simétrica em torno do vertical fica parecida consigo mesma invertida. A base larga vira topo largo. O mordente aponta para o tampo, e a silhueta continua reconhecível como uma morsa. Trocar o sinal e olhar é a conferência inteira.
Antes de seguir, responda de cabeça. Se o carregamento foi bem-sucedido nos três desvios, o que exatamente vai acusar o problema? Guarde a sua resposta: ela decide se o próximo ativo custa três minutos ou uma tarde.
Inverter os dois primeiros passos produz um resultado específico e fácil de reproduzir sem perceber. Assentar antes de girar significa medir a face inferior da peça deitada, que é uma das laterais da peça em pé. O deslocamento calculado a partir dela move o objeto pela dimensão errada, e o giro posterior o carrega junto. O objeto fica flutuando ou enterrado por alguns centímetros. É pouco para chamar atenção, e suficiente para atrapalhar quem for apanhar a peça.
Escalar antes ou depois de girar dá no mesmo, porque a escala é uniforme nas três direções. Quem manda é a ordem entre assentamento e giro, a única das três que não admite troca. E o pior dessa inversão é o que ela faz com o registro da importação: todos os números saem corretos, porque cada um descreve fielmente uma operação que de fato aconteceu.
1.3 Sete campos que outra pessoa consegue ler
Toda importação devolve um registro do que aconteceu, e é aí que está a peça que separa importar de arrastar. Ele nasce dentro da mesma rotina que aplica a transformação, e os valores registrados são os que a rotina de fato aplicou. Não os que alguém pretendia aplicar. A distinção parece pequena e é o que dá valor à folha inteira.
o laudo da importação, em src/bancada/content/ativos.ts
Os dois primeiros campos sustentam a conferência. O campo comoChegou guarda as dimensões no sistema do arquivo, e na morsa registra 18,00 por 12,00 por 17,00 unidades. O campo comoFicou guarda as dimensões na convenção da cena, e registra 18,0 por 17,0 por 12,0 centímetros. Ler as duas linhas em sequência é ver a viagem inteira num relance.
Os três campos seguintes registram o que foi aplicado: o fator de 0,0100, o giro de noventa graus negativos em X e o deslocamento corrigido. O sétimo é a lista de divergências. É ele que transforma o registro em instrumento. Os cinco primeiros descrevem, e valem tanto para um ativo perfeito quanto para um ativo torto. A lista acusa, e só tem conteúdo quando alguma coisa saiu do que a convenção esperava.
flowchart TD
L["o laudo da importacao"]
L --> D["cinco campos que DESCREVEM"]
L --> A["dois campos que permitem JULGAR"]
D --> D1["arquivo"]
D --> D2["como chegou<br/>18,00 x 12,00 x 17,00 unidades"]
D --> D3["como ficou<br/>18,0 x 17,0 x 12,0 cm"]
D --> D4["fator aplicado: 0,0100"]
D --> D5["giro aplicado: -90 graus em X"]
A --> A1["deslocamento corrigido: 0,30 m"]
A --> A2["lista de divergencias<br/>tres linhas nesta peca"]
A2 --> N["quando nada diverge,<br/>a lista diz isso<br/>em vez de sair vazia"]
Na morsa a lista sai com três linhas. A primeira diz que o arquivo mede 18.00 para uma peça de 0.18 m, o que dá 100 unidades por metro. A segunda diz que a peça foi girada −90 graus em X. A terceira diz que ela vinha deslocada 0.30 m da própria base. Repare na frase sobre as cem unidades por metro: ela não afirma que o arquivo está em centímetro, afirma o que foi medido. Quem lê conclui o centímetro sozinho, com a evidência na mão.
Os limiares são largos de propósito. Um fator entre 0,5 e 2,0 passa sem comentário, e um deslocamento residual acima de um centímetro é acusado. Pouco rigor? É o contrário disso. Exportadores produzem ruído numérico, e uma peça que deveria medir 0,180 sai medindo 0,17964, com fator em 0,998. Uma lista que reclamasse disso teria uma linha em toda importação, todas irrelevantes, e a linha que importa apareceria no meio delas.
O que a lista existe para pegar erra por ordens de grandeza. Centímetro contra metro é cem. Milímetro contra metro é mil. Nenhum desses desvios chega perto da faixa entre 0,5 e 2, e nenhum ruído de exportação chega a sair dela. Quando nada diverge acima da tolerância, a lista diz isso com todas as letras, em vez de sair vazia. Uma lista vazia é ambígua: pode significar que nada divergiu, ou que ninguém examinou.
A morsa traz também a animação AbrirFechar, que move o mordente móvel e é conteúdo autorado fora do projeto tanto quanto a geometria. Quem a faz andar? Há dois candidatos plausíveis. Um relógio próprio, que a biblioteca oferece pronto. Ou o intervalo que o laço já calcula para mover o resto da cena. A morsa usa o segundo, e a razão aparece quando dois computadores são comparados. Com relógio próprio, a peça andaria à velocidade dela e a cena à do laço. Numa máquina que desenha mais rápido, a diferença entre as duas mudaria, e a morsa passaria a viver num tempo ligeiramente diferente do da oficina. Esse defeito tem perfil desconfortável. Ele não aparece no desenvolvimento, que acontece numa máquina só, e aparece quando o ambiente é aberto em dois aparelhos lado a lado. Ele também não deixa rastro no laudo, que examina geometria.
Quando o ativo simplesmente não chega
Um ativo externo depende de rede, de caminho e de servidor, e qualquer um dos três pode falhar. A oficina trata isso como falha visível: a cena continua de pé, sem a morsa, e o painel escreve que o ativo não pôde ser trazido. O silêncio seria pior justamente porque a cena continua funcionando — ninguém nota a ausência de uma peça entre outras vinte.
1.4 Duas técnicas, duas colunas
Instanciar e simplificar aparecem juntos em toda conversa sobre desempenho, e atacam problemas que não se tocam.
Uma chamada de desenho é uma conversa entre o programa e a placa de vídeo para desenhar um objeto: a placa recebe qual forma usar, com qual material e em que posição, e só então desenha.
O custo dessa conversa é fixo por objeto e independe do tamanho dele. Um objeto minúsculo custa a mesma preparação que um objeto grande, e é por isso que uma cena de mil peças pequenas pode engasgar sem que a contagem de triângulos assuste ninguém.
Chamada de desenho e triângulo são unidades de contas diferentes. As duas se pagam no mesmo envelope, e nenhuma das duas se lê na outra.
O menor exemplo concreto cabe numa comparação de duas cenas. A primeira tem um objeto com dez mil triângulos. A segunda tem mil objetos de dez triângulos cada, o que dá dez mil também. Na coluna dos triângulos elas são idênticas. Na coluna das chamadas, um fator de mil as separa, e é a segunda que engasga. Cada chamada é barata. A repetição delas é que pesa.
flowchart TD
Q["o quadro estourou"]
Q --> CH["coluna das<br/>chamadas de desenho"]
Q --> TR["coluna dos<br/>triangulos"]
CH --> I["instanciar"]
TR --> N["nivel de detalhe"]
I --> I1["206 chamadas passam a 1"]
I --> I2["os 4.944 triangulos<br/>continuam 4.944"]
N --> N1["224 triangulos passam a 82"]
N --> N2["a chamada por lote<br/>continua uma"]
Olha só o caso mais comum: alguém aplica as duas técnicas com esforço genuíno e no alvo errado. Duas horas de trabalho, e o quadro continua onde estava.
Essa pessoa olha para a borda de parafusos, vê muitos objetos e conclui que a cena tem geometria demais. Simplifica o parafuso, que já tinha vinte e quatro triângulos, e o transforma num de dezoito. Depois olha para a morsa importada, vê uma peça complexa, e a instancia: uma peça, um lote de uma cópia.
Os dois trabalhos foram feitos com competência. O primeiro cortou seis triângulos por cabeça em duzentas e seis cabeças, o que some no ruído. O segundo trocou uma chamada de desenho por uma chamada de desenho. E as duzentas e seis conversas com a placa continuam exatamente onde estavam. Complexidade aparente e custo não andam juntos: uma engrenagem de catorze dentes parece cara e é uma chamada só.
De onde saem as duzentas e seis cabeças? O tampo da bancada mede 1,4 por 0,8 metro, e os parafusos ficam a cada dois centímetros ao longo do perímetro. O número não aparece em lugar nenhum do código; o que aparece é a rotina que dispõe posições ao longo da borda de um retângulo. Com o número digitado, trocar o tampo por um maior deixaria os parafusos agrupados num pedaço da borda. Com a conta, o tampo maior traz mais parafusos sozinho.
Cada cabeça é uma peça sextavada de vinte e quatro triângulos, e é a cabeça e nada mais. O corpo do parafuso está dentro da madeira, e modelar o que ninguém vê é orçamento gasto sem contrapartida. Vinte e quatro vezes duzentas e seis dá 4.944 triângulos na borda inteira. Fixe esse número antes de a técnica entrar, porque ele é o que não vai mudar.
Instanciar é desenhar muitas cópias de uma mesma forma numa chamada de desenho só: a placa recebe a forma uma vez e, em seguida, a lista das posições de todas as cópias.
O aviso de que aquelas posições não mudam mais é o que autoriza a placa a guardar o lote e não voltar a tocá-lo. A matriz de cada cópia é escrita uma vez, na montagem, e não a cada quadro.
O que a instanciação move é a coluna das chamadas. A contagem de triângulos não muda: eles continuam sendo os mesmos, um por um, e a placa os desenha igual. Instanciar não é sobre triângulo, e esperar que o número de triângulos caia é o modo mais comum de concluir que a técnica não funcionou.
a montagem do lote, em src/bancada/content/repeticao.ts
/**
* Monta um lote instanciado a partir de uma malha e de uma lista de posições.
*
* A matriz de cada cópia é escrita uma vez, na montagem, e não a cada quadro. O
* aviso de que a matriz não muda mais é o que autoriza a biblioteca a mandar o
* lote inteiro para a placa e não voltar a tocá-lo — e é ele que transforma
* duzentas e vinte chamadas em uma.
*/
export function montarLoteInstanciado(
A rotina recebe uma malha, um material e uma lista de posições, e devolve um objeto só. Cada posição vira uma matriz escrita na tabela do lote, uma vez, na montagem da cena. O ganho está no aviso que acompanha a tabela. Ao dizer que aquelas matrizes não vão mudar, o programa autoriza a biblioteca a enviar o lote uma vez e não voltar a tocá-lo.
Isso põe um limite claro no alcance da técnica. Um lote de objetos que se movem sozinhos a cada quadro perde a maior parte da vantagem. A tabela volta a ser escrita e enviada, quadro após quadro. Os parafusos são candidatos ideais justamente por serem entediantes: nascem parafusados e ficam onde nasceram. Com o lote montado, as duzentas e seis chamadas passam a uma e a contagem de triângulos continua em 4.944.
1.5 A mesma malha, duas decisões opostas
Um nível de detalhe é uma segunda versão da mesma malha, com menos triângulos, exibida no lugar da original quando o objeto está longe o bastante para que a diferença não chegue ao olho.
O critério é a distância, e não o objeto. A mesma peça pode receber nível de detalhe num lugar da cena e não receber em outro, e simplificar o que fica sempre perto do olho é trabalho jogado fora.
A simplificação tem um pré-requisito e uma conferência, e os dois são frequentemente omitidos. O pré-requisito é unir os vértices coincidentes: uma malha que duplica vértice em cada face, para ter aresta viva, é lida pelo algoritmo como um monte de triângulos soltos, sem aresta alguma para colapsar. A conferência é do resultado: a ferramenta pode devolver malha vazia ou com coordenada inválida, e uma peça que some da cena é defeito pior do que um nível distante mais pesado que o ideal. Quando o resultado não passa, a malha original fica no lugar e a observação vai para o relatório.
Depois de simplificar, as normais são recalculadas: os triângulos que sobraram têm outra inclinação, e manter a normal antiga produz iluminação que não corresponde à forma — um defeito que aparece como mancha, e não como buraco.
A prateleira ao fundo guarda vinte e quatro peças de reposição, e elas são a mesma malha da engrenagem que está sobre a bancada. Essa identidade é o argumento inteiro. A engrenagem da bancada fica a meio metro do olho e não recebe nível de detalhe nenhum; as vinte e quatro da prateleira recebem. Nada mudou no objeto entre uma frase e a outra. Mudou a distância, e com ela a quantidade de pixels que cada triângulo ocupa.
A ferramenta corta a malha de 224 para 82 triângulos, e de 338 para 169 vértices, o que dá 63% de redução. Nas vinte e quatro peças da prateleira, isso são 5.376 triângulos que viram 1.968. A troca entre os dois lotes acontece a 2,2 metros da câmera ao centro da prateleira. Ela vale para o lote inteiro, e não para cada peça.
O pré-requisito da definição foi decidido lá atrás, por outro motivo. A malha do projeto duplica vértice em toda face, para que os cantos das peças fiquem vivos em vez de macios. Duas faces que se encontram numa aresta viva chamam a função duas vezes para o mesmo ponto do espaço, com normais diferentes. Do ponto de vista da geometria da peça é um ponto; do ponto de vista da lista, são dois.
Um algoritmo de simplificação trabalha colapsando arestas, e para descobrir quais arestas existem ele olha quais triângulos compartilham vértices. Numa malha com vértices duplicados por face, nenhum triângulo compartilha vértice com nenhum outro. O que ele enxerga é um monte de triângulos soltos flutuando lado a lado. Ele devolve a malha quase intacta, sem erro e sem mensagem, e quem confere pelo contador conclui que a peça já estava simplificada ao máximo.
O conserto é unir os vértices coincidentes antes de simplificar. Ele custa a aresta viva: o ponto unificado passa a ter uma normal só para as duas faces. Veja bem o que se compra com isso. No nível distante, a perda é exatamente o que se quer pagar, porque a três metros canto duro e canto macio são o mesmo punhado de pixels.
Duas conferências que quase ninguém faz. Depois de simplificar, a normal antiga descreve uma superfície que deixou de existir, e a iluminação passa a errar por pouco em cada ponto. O sintoma é mancha, e mancha manda quem procura direto para o material e para a luz, onde não há nada errado. E a ferramenta pode devolver malha vazia ou com coordenada não finita: aí a peça some da cena, junto com a linha do relatório que a mencionaria.
Existe ainda um modo de anular as duas técnicas ao mesmo tempo, e ele vem de uma escolha razoável. As bibliotecas oferecem um mecanismo pronto de nível de detalhe que alterna a malha objeto por objeto, conforme cada um se afasta. Aplicado a um lote instanciado, ele desmonta o lote: cada peça passa a decidir sozinha qual malha usar, e cada decisão custa uma chamada. O ganho de uma técnica paga a perda da outra, e quem lê o relatório conclui que nenhuma das duas serviu. Por isso, aqui, cada nível é um lote inteiro, e a troca liga um e desliga o outro.
1.6 Duas contas com o mesmo nome
Dois números de triângulos na mesma folha. Por que eles quase nunca batem?
O censo é estático: percorre a árvore da cena e soma o que existe, esteja ou não à vista. Responde quanto esta cena pesa.
A leitura é dinâmica: conta o que a placa desenhou no quadro que acabou de sair. Responde quanto custou o que estava no campo de visão.
A diferença entre os dois mede o descarte do que ficou fora do enquadramento, e nunca um erro de contagem. Um censo alto com leitura baixa descreve uma cena grande vista por um buraco de fechadura, e vira problema no instante em que alguém gira a cabeça — o que, dentro de um visor, acontece o tempo todo.
O censo conta o lote instanciado como uma chamada e como o produto de triângulos por instância. Somar tudo num número só esconderia exatamente a decisão que se está tomando.
Acontece que o censo percorre a árvore a partir da raiz e soma. Ele não sabe onde está a câmera, e não perguntaria mesmo que soubesse.
A resposta dele é a mesma com a câmera na bancada, na prateleira ou apontada para a parede vazia atrás. A leitura vem do laço, logo depois de a placa terminar o quadro, e descreve um instante que o instante seguinte pode contradizer.
a forma do censo, em src/bancada/content/medicao.ts
export interface Censo {
readonly malhas: number;
readonly lotesInstanciados: number;
readonly instancias: number;
readonly triangulos: number;
readonly materiaisDistintos: number;
/** Piso de chamadas de desenho: uma por malha, uma por lote — não uma por instância. */
readonly chamadasPrevistas: number;
}
O censo devolve seis números. A escolha deles carrega a decisão do módulo. Malhas soltas e lotes instanciados são contados em campos separados, e o número de instâncias tem campo próprio. As chamadas previstas somam uma por malha e uma por lote, nunca uma por instância. Um total que contasse cada parafuso como uma chamada mostraria a cena antes da instanciação. Um que ignorasse os parafusos mostraria uma cena que não existe.
flowchart TD
C["censo da cena<br/>estatico, percorre a arvore"]
L["leitura do quadro<br/>dinamica, le o quadro que saiu"]
C --> P1["quanto esta cena pesa"]
L --> P2["quanto custou o que estava<br/>no campo de visao"]
P1 --> DIF["a diferenca entre os dois"]
P2 --> DIF
DIF --> S["descarte por enquadramento<br/>nunca erro de contagem"]
S --> J["camera parada na janela:<br/>numero fixo e confortavel"]
S --> V["cabeca girando no visor:<br/>o que estava fora entra"]
Na janela do computador, com a câmera apontada para a bancada, a prateleira fica fora do enquadramento a maior parte do tempo. O conteúdo dela é pago em memória e não é pago por quadro. Alguém que meça só nessa posição colhe números confortáveis e conclui que a cena tem folga generosa — e a conclusão vale exatamente para aquele ângulo. Note a diferença dentro de um visor. A cabeça gira sem que ninguém decida girá-la, e cada giro traz conteúdo novo.
A defesa é medir em mais de uma posição de câmera e registrar as duas leituras. Mede-se junto à bancada, mede-se junto à prateleira, e comparam-se as duas folhas contra o mesmo censo. Duas leituras idênticas sobre uma cena espalhada indicam que o descarte por enquadramento não está acontecendo. O lugar de procurar é a hierarquia: um objeto pendurado no galho errado arrasta os filhos dele para dentro do campo de visão.
Cada quadro é um envelope de onze milissegundos, e o que não couber nele não fica para depois: some. Quando essa frase foi escrita, ela descrevia uma restrição sem adversário. A oficina tinha um tampo, um suporte, cinco peças e um painel, com quatrocentos e quarenta triângulos ao todo. Agora ela tem três adversários concretos. Uma peça importada com quatro malhas e animação própria. Uma borda com 4.944 triângulos numa chamada. E uma prateleira com 5.376 triângulos, que caem para 1.968 quando a câmera se afasta.
A ordem em que essas duas coisas aconteceram é o que faz diferença. O teto veio primeiro, e a despesa veio depois. Um teto declarado depois da cena montada tem outro nome: é um laudo do que já foi gasto. Ele informa que a cena está pesada e não oferece critério algum para decidir o que cortar. A essa altura, cortar significa remodelar peças prontas. O código declara dois números. Onze vírgula um milissegundos para o visor, dezesseis vírgula sete para o computador de mesa, e o teto adotado é o do visor. Eu prefiro pagar em aparência, e os dois lados dessa escolha ficam escritos. Orçar pelo visor deixa a versão em janela menos bonita do que a máquina aguentaria. Orçar pelo computador de mesa faz a versão no visor estourar. E no visor o estouro produz desconforto físico, não uma animação menos lisa.
O instrumento tem um limite de alcance, e reconhecê-lo evita procurar dentro dele uma resposta que ele não guarda. Ele mede tempo: diz quantos milissegundos o quadro levou e qual foi o pior da janela observada. Ele não diz onde esses milissegundos foram gastos. Um quadro de vinte milissegundos pode ter gasto quinze desenhando, quinze percorrendo a árvore ou quinze esperando o navegador terminar outra coisa.
Localizar o gasto é trabalho das ferramentas de perfilamento do navegador, que registram a pilha de chamadas e mostram quanto tempo cada função consumiu. Elas respondem onde, e respondem bem, mas rodam na janela do computador, fora da sessão imersiva. O painel dentro da cena responde se o quadro está estourando e quando. Ele responde de dentro do visor, com a cabeça em movimento e a cena no estado real de uso.
1.7 Nenhum número viaja sozinho
Duas pessoas competentes, olhando medições verdadeiras do mesmo ambiente, chegam a decisões opostas quando os aparelhos diferem. Esse é o preço de um número que circula sozinho.
A máquina de referência é o aparelho contra o qual a cena é orçada, e ela é a mais modesta do parque previsto — não a de quem escreveu o ambiente.
Um número de desempenho sem a máquina ao lado não permite a ninguém repetir a conta. “Roda bem” é uma frase sem sujeito; a mesma cena, no mesmo estado, produz números diferentes em aparelhos diferentes, e a decisão que se toma a partir de cada um é oposta.
Medir na máquina mais forte disponível produz um teto que as outras não alcançam, e o resultado prático é o ambiente estourar justamente nos computadores em que ele vai ser apresentado.
Quando o aparelho não informa o que se pediu — o nome da placa, por exemplo, que o navegador frequentemente recusa dizer —, o relatório declara que não sabe. Preencher o campo com um nome plausível é pior do que deixá-lo vazio: quem lê passa a acreditar que a medição é reproduzível.
a máquina declarada, em src/bancada/content/medicao.ts
Três campos, e o terceiro é uma frase em português que nenhum programa lê. Ela existe para quem for acrescentar conteúdo à cena depois. O parque previsto tem vídeo integrado e não tem placa dedicada (limite duro do lugar, e não preferência de quem escreveu). Nenhum número deste ambiente pode depender de uma. Repare onde a constante mora: ao lado do código que mede. A proximidade é o ponto. Números e anotação guardados em lugares diferentes se separam na primeira vez que alguém copia os números para outro lugar.
flowchart TD
A["1. a maquina de referencia<br/>a mais modesta do parque"]
B["2. o navegador previsto"]
C["3. o que o aparelho disse da placa<br/>ou que ela nao foi identificada"]
D["4. o teto declarado por quadro"]
E["5. o censo estatico"]
F["6. a leitura do quadro<br/>ou o aviso de que ela entra depois"]
A --> B --> C --> D --> E --> F
A -.-> G["o contexto chega antes do numero"]
F -.-> G
Você mediria no computador mais rápido da sala? Pense no que isso significa. Os números saem confortáveis, a folga contra o teto parece generosa, e a decisão razoável a partir deles é acrescentar conteúdo. A decisão razoável a partir dos números da máquina fraca é o contrário. Nenhuma das duas pessoas tem como perceber isso, se o número circular sozinho.
O instrumento guarda uma janela de cento e vinte quadros. O espaço é fixo, e cada leitura nova se escreve por cima da mais antiga (pouco mais de um segundo, num aparelho confortável). Essa janela é curta, e a brevidade dela é a decisão. Acumular a sessão inteira produziria uma média que melhora sozinha com o tempo, sem que nada na cena tivesse melhorado. Um engasgo de trinta milissegundos, diluído em dez mil quadros confortáveis, desaparece na terceira casa decimal. Numa janela de cento e vinte, o mesmo engasgo ainda pesa.
A leitura devolve o intervalo médio, o pior intervalo da janela e a contagem de quadros que passaram do teto. Esta última é calculada e fica disponível a quem escreve código, embora não seja impressa na folha. O pior intervalo é o número que descreve a experiência de quem usa o aparelho. O corpo registra o engasgo, e não registra a média. Dentro de um visor, um quadro perdido é uma imagem repetida enquanto a cabeça continuou girando.
O nome da placa fecha o assunto com o caso mais desconfortável. Ele é pedido ao navegador por uma extensão opcional, e navegadores modernos frequentemente a bloqueiam por privacidade. Quando o pedido não é atendido, a oficina escreve que a placa não foi identificada, com o motivo entre parênteses. Um campo preenchido com um nome plausível transformaria a folha inteira numa medição aparentemente reproduzível que ninguém consegue repetir.
1.8 A linha de base que julga o resto
O eixo de modelagem fecha aqui, e o que sobrevive dele é a medição.
A oficina tem agora conteúdo de três procedências, e as três se conferem de jeitos diferentes. Geometria autorada dentro do projeto, com cada vértice decidido em código. Uma peça trazida de fora, corrigida na chegada e registrada num laudo. E repetição tratada como repetição, com uma coluna do relatório movida e a outra deixada onde estava.
flowchart LR
A["geometria autorada<br/>no proprio projeto"] --> A1["confere-se abrindo<br/>a rotina que a escreveu"]
B["peca trazida de fora"] --> B1["confere-se pelo laudo:<br/>como chegou, como ficou,<br/>o que foi corrigido"]
C["repeticao tratada<br/>como repeticao"] --> C1["confere-se pelas duas colunas:<br/>uma se move, a outra fica"]
A1 --> D["folha com censo, leitura<br/>e maquina lado a lado"]
B1 --> D
C1 --> D
- Existe uma convenção escrita — unidade, vertical, origem e frente —, e todo objeto da cena a obedece.
- Cada ativo trazido de fora tem laudo, com o que chegou, o que ficou e o que foi corrigido, e os números do laudo saíram de medição em vez de digitação.
- Existe uma folha com censo, leitura e máquina lado a lado, e a máquina é a mais modesta do parque previsto.
Volte à morsa deitada e gigante do começo. Quantas das três afirmações você conseguiria mostrar numa captura de tela? Nenhuma.
A cena de quem fez as três e a cena de quem arrastou tudo com o mouse produzem a mesma imagem. A diferença aparece no primeiro ativo novo, no primeiro aparelho diferente e no primeiro recurso acrescentado. Daqui em diante, cada assunto acrescenta trabalho por quadro, e o mais caro deles desenha a cena inteira duas vezes.
Sem um número medido agora, a primeira queda de desempenho será atribuída ao último recurso acrescentado. Essa atribuição é quase sempre errada, e é convincente: o recurso novo é a coisa que mudou, e a coincidência temporal parece prova. Com a linha de base registrada, a comparação é entre duas folhas, e a folha antiga não tem opinião sobre quem é o culpado. O trabalho é apertar um botão duas vezes, com a mesma posição de câmera nos dois momentos.
A oficina, até aqui, foi vista de um ângulo só, e essa imobilidade escondeu duas coisas. A diferença entre censo e leitura, com a câmera parada, é um número fixo. Ela vira uma grandeza que muda a cada segundo assim que alguém puder olhar em volta. E a troca de nível de detalhe nunca dispara sozinha, porque a distância até a prateleira é constante. Ela foi testada por um botão que aproxima a câmera à força (demonstração honesta e artificial).
O passo seguinte não acrescenta conteúdo à cena. Ele acrescenta um jeito de olhar para ela: a câmera deixa de ser fixa e passa a orbitar sob o cursor. A oficina vira algo que outra pessoa consegue abrir e percorrer sem instrução verbal. É a primeira vez que a cena sai da tela de quem a construiu.