Eu gostaria de uma função para animar um objeto que se move do ponto A para o ponto B ao longo do tempo, de modo que ele atinja B em algum tempo fixo, mas sua posição a qualquer momento seja aleatoriamente perturbada de maneira contínua, mas nunca retrocede. Os objetos se movem ao longo de linhas retas, então eu só preciso de uma dimensão.
Matematicamente, isso significa que estou procurando por alguns f (x), x continuous [0,1] contínuos, tais que:
- f (0) = 0
- f (1) = 1
- x <y → f (x) ≤ f (y)
- Na "maioria" dos pontos, f (x + d) - f (x) não tem relação óbvia com d. (A função não está aumentando uniformemente ou é previsível; acho que também é equivalente a dizer que nenhum grau de derivada é uma constante.)
Idealmente, eu realmente gostaria de alguma maneira de ter uma família dessas funções, fornecendo algum estado inicial. Eu precisaria de pelo menos 4 bits de semente (16 funções possíveis), para o meu uso atual, mas como isso não é muito, sinta-se à vontade para fornecer ainda mais.
Para evitar vários problemas com erros de acumulação, prefiro que a função não exija nenhum tipo de estado interno. Ou seja, quero que seja uma função real, não uma "função" de programação.
f'(x)>0
, de modo que a integração normalizada do valor absoluto de qualquer função de ruído cumpra todos os seus requisitos. Infelizmente, não conheço nenhuma maneira fácil de calcular isso, mas talvez alguém o faça. :)Respostas:
Para este post, y = f (t) onde t é o parâmetro que você varia (tempo / progresso) e y é a distância do alvo. Então, falarei em termos de pontos em gráficos 2D em que o eixo horizontal é tempo / progresso e a vertical é distância.
Eu acho que você pode fazer uma curva cúbica de Bezier com o primeiro ponto em (0, 1) e o quarto (último) ponto em (1, 0). Os dois pontos médios podem ser colocados aleatoriamente (x = rand, y = rand) dentro desse retângulo 1 por 1. Não consigo verificar isso analiticamente, mas apenas brincando com um applet (sim, vá em frente e ria) parece que a curva de Bezier nunca diminuirá com essa restrição.
Essa será sua função elementar b (p1, p2), que fornece um caminho não decrescente do ponto p1 ao ponto p2.
Agora você pode gerar ab (p (1) = (0, 1), p (n) = (1, 0)) e selecionar um número de p (i) ao longo dessa curva, de modo que 1
Essencialmente, você está gerando um caminho "geral" e, em seguida, dividindo-o em segmentos e regenerando cada segmento.
Como você deseja uma função matemática: Suponha que o procedimento acima seja empacotado em uma função y = f (t, s), que fornece a distância em t para a função de semente s. Você precisará de:
Portanto, cada semente deve fornecer um dos seguintes:
Eu imagino que você pode realizar qualquer um desses simplesmente fornecendo uma matriz de números como a semente s. Como alternativa, você pode fornecer algo como um número s como semente e, em seguida, chamar o gerador de números aleatórios interno com rand (s), rand (s + 1), rand (s + 2) e assim por diante (ou inicializar com se continuar chamando rand.NextNumber).
Observe que, embora toda a função f (t, s) seja composta de muitos segmentos, você está avaliando apenas um segmento para cada t. Você vai precisar para calcular repetidamente os limites de segmentos com este método, porque você vai ter que classificá-los para se certificar que não há dois segmentos se sobrepõem. Você provavelmente pode otimizar e se livrar desse trabalho extra e encontrar apenas os pontos finais de um segmento para cada chamada, mas isso não é óbvio para mim no momento.
Além disso, as curvas de Bezier não são necessárias; qualquer spline com comportamento adequado servirá.
Criei uma implementação de amostra do Matlab.
A função de Bezier (vetorizada):
A função composta de Bezier descrita acima (deliberadamente deixada sem vetor para esclarecer quanta avaliação é necessária para cada chamada):
O script que plota a função para uma semente aleatória (observe que este é o único local em que uma função aleatória é chamada, as variáveis aleatórias para todos os outros códigos são propagadas a partir dessa matriz aleatória):
Aqui está um exemplo de saída:
Parece atender a maioria dos seus critérios. Contudo:
fonte
Eu acho que, em vez de misturar um monte de cossenos transformados (como os produtos de ponto em ruído perlin dão a você), você pode misturar várias funções monotônicas que começam em f (0) = 0, como f (x) = x ou 2x, ou x ^ 2, etc. De fato, como seu domínio é limitado a 0 => 1, você também pode combinar funções trigonométricas que se ajustam à conta nesse domínio, como cos (90 * x + 270). Para normalizar seus métodos para terminar em 1, você pode simplesmente dividir a soma ponderada desses métodos monotônicos começando em f (0) = 0 por f (1). Algo como isso também deve ser bastante fácil de inverter (o que, a meu ver, você quer um pouco sobre funções reais sem estado versus funções de programação).
Espero que isto ajude.
fonte
Pode-se analisar essa imagem grosseira Você pode acabar com uma função que executa sua animação em tempo real, usando uma função de rand uniforme. Eu sei que essa não é a fórmula matemática exata, mas na verdade não existe uma fórmula matemática para uma função aleatória, e mesmo que houvesse uma, você estaria codificando muito para conseguir isso. Considerando que você não especificou nenhuma condição de suavidade, o perfil de velocidade é $ C ^ 0 $ contínuo (mas como você não está lidando com robôs, não precisa se preocupar com perfis de aceleração descontínuos).
fonte
A maneira usual de gerar uma seqüência crescente de N números aleatórios a partir de [0,1] é gerar N números aleatórios em qualquer intervalo, depois dividi-los pela soma total e depois somar um de cada vez para obter a seqüência.
Isso pode ser estendido para 2D, gerando esses valores para X e Y. Você pode aumentar N para obter a granularidade desejada.
Na resposta semelhante da @ teodron, você citou preocupações de eficiência com grandes escalas de tempo. Sem saber o problema real que você está enfrentando, não sei dizer se essa preocupação é válida; mas outra opção seria gerar para N pequeno e simplesmente suavizar o resultado. Dependendo da aplicação, isso pode realmente dar melhores resultados.
N = 100, sem suavização
N = 15, com suavização
fonte
Eu sugiro esta implementação inspirada no somatório de oitavas encontradas no ruído fractal, com um pouco de baralhamento barato aqui e ali. Acredito que seja razoavelmente rápido e possa ser ajustado solicitando menos oitavas do que armazenadas nos parâmetros com uma perda de precisão de aproximadamente
1/2^octave
.Você poderia vê-lo como uma implementação por partes que requer apenas tempo O (log (partes)) . A matriz de parâmetros é usada tanto para a posição do pivô de dividir e conquistar quanto para a distância percorrida ao atingir o pivô.
Isso poderia ser feito mais rápido pré-computando as divisões de ponto flutuante, com o custo de armazenar três vezes mais informações.
Este é um exemplo rápido:
O exemplo foi obtido com o seguinte código:
fonte
Pensar em voz alta e admitir cálculo não é o meu ponto forte ... isso talvez não seja possível? Para evitar qualquer padrão óbvio, a média da função de ruído sobre qualquer alteração em x deve ser próxima de zero e para garantir a monotonicidade, a amplitude de ruído sobre essa alteração em x deve ser menor que a alteração em x, como qualquer amplitude maior poderia resulta em um valor menor em x 'em relação a x. Mas isso significaria que, à medida que você reduz dx para 0, essa função também deve reduzir dA (onde A é amplitude) para zero, o que significa que você não recebe contribuição de nenhuma função de ruído compatível.
Eu posso imaginar que seja possível formular uma função que diminua gradualmente a contribuição do ruído quando x se aproxima de 1, mas isso lhe dará uma função curva que desacelera quando x se aproxima de 1, que não é o que eu acho que você deseja.
fonte
g(x) = 1 - f(1 - x)
, que, em vez acelera quando x 0. afasta