Hoje, enquanto estudava o livro "Fundamentos de Animação HTML5+Javascript", na terceira seção do Capítulo 8, falei sobre como usar três molas para conectar três pontos para realizar movimentos de alongamento.
Depois de terminar o exemplo, pensei e se fossem quatro pontos ou cinco pontos.
Reescrevi o código e tornei variável o número de pontos. O efeito final é conseguir o movimento final de alongamento de cada ponto para equilibrar, mas as conexões entre os pontos não são muito bonitas e algumas são cruzadas.
Então eu estava pensando se essa área poderia ser otimizada.
Girar a linhaOs pontos no exemplo anterior estão todos em posições aleatórias, portanto as conexões são incontroláveis. Então, quero começar com isso primeiro.
Primeiro, use um determinado ponto como ponto de referência para obter os ângulos de outros pontos em relação a este ponto.
Em seguida, conecte esses pontos de acordo com o ângulo de pequeno a grande, para que você possa desenhar um polígono normal.
O código de implementação aproximado é o seguinte:
deixe bolas = []; deixe bolaNum = 6; ) * largura; ball.y = Math.random() * altura; balls.push(ball) if (!firstBall) { firstBall = ball ball.angle = 0 } else { const dx = ball.x - firstBall.x, dy = ball.y - firstBall.y; ball.angle = Math.atan2(dy, dx); // Tente fazer da linha que conecta as bolas um polígono regular. = bolas .sort((bolaA, bolaB) => { return bolaA.ângulo - bolaB.ângulo})
Desta forma, quando a conexão é finalmente estabelecida, o ângulo pode ser traçado de pequeno a grande atravessando a matriz.
O efeito é o seguinte:
Isso pode reduzir bastante o número de linhas cruzadas, mas ainda não pode ser completamente evitado.
A seguir, quero tentar otimizar esta solução. Por exemplo, use Math.abs para corrigir o ângulo ou encontre o ponto com o menor ângulo para conectar cada ponto. Mas o resultado não é bom, o cruzamento dos limites não pode ser evitado.
Girar com base no ponto centralEntão outra ideia veio à mente. Se o ponto central do polígono puder ser determinado, os ângulos de todos os pontos em relação ao ponto central poderão ser calculados separadamente e esses pontos poderão ser conectados no sentido horário ou anti-horário.
Porém, depois de muito tempo pesquisando na Internet, todos os algoritmos de pontos exigem uma série de pontos organizados em uma determinada ordem no sentido horário.
Mas se eu tiver esses pontos, já consigo desenhar o polígono. Tive que desistir
Segmentação bipolar do eixo XEm desespero, tive que pesquisar no Google e encontrei uma resposta muito boa em Zhihu: Como conectar um grupo de pontos desordenados no plano em um polígono simples?
Para a descrição específica do algoritmo, basta olhar a resposta e não entrarei em detalhes.
Porém, ao conectar a corrente superior e a corrente inferior, na verdade, você só precisa garantir que a corrente superior esteja conectada em ordem decrescente no eixo X, e a corrente inferior esteja conectada em ordem ascendente no eixo X (desenhado em sentido anti-horário). Já para os pontos com o mesmo eixo X, não importa se o eixo Y é maior ou menor.
Quando implementado, é implementado estritamente de acordo com o algoritmo da resposta.
Ao julgar se um ponto pertence à cadeia superior ou à cadeia inferior, a idéia inicial é determinar a equação da função da reta com base em dois pontos e, em seguida, introduzir as coordenadas dos pontos para cálculo. Mas pensei depois que todos os pontos usam o pólo mais à esquerda para calcular o ângulo oblíquo e depois o dividem de acordo com o tamanho do ângulo, que é mais fácil de entender visualmente.
O código aproximado é o seguinte:
deixe bolas = []; deixe tempBalls = []; deixe ballNum = 6; x = Math.random() * largura ball.y = Math.random() * altura; Deixe os pontos serem classificados em ordem crescente em tempBalls.length -1];let smallXBalls = tempBalls.filter(ball => ball.x === firstBall.x), bigXBalls = tempBalls.filter(ball => ball.x === lastBall.x)// Lidar com a situação em que existem vários pólos esquerdo e direito if (smallXBalls.length > 1) { smallXBalls.sort((ballA, ballB) => { return bolaB.y - bolaA.y })}if (bigXBalls.length > 1) { bigXBalls.sort((bolaA, bolaB) => { return ballB.y - ballA.y })}firstBall = smallXBalls[0]lastBall = bigXBalls[0] // Obtenha o ângulo da linha do poste let splitLineAngle = Math.atan2(lastBall.y - firstBall.y, lastBall.x - firstBall.x);let upperBalls = [], lowerBalls = [];//Todos os outros pontos calculam ângulos com firstBall// Qualquer coisa maior que splitLineAngle está na cadeia descendente // Outros estão na cadeia ascendente tempBalls.forEach(ball => { if (ball === firstBall || ball === lastBall) { return false } let angle = Math.atan2(ball.y - firstBall.y, ball.x - firstBall.x); if (ângulo > splitLineAngle) { lowerBalls.push(ball) } else { UpperBalls.push(ball) }})// Lida com a classificação da mesma situação no eixo X lowerBalls = lowerBalls.sort((ballA, ballB) => { if (ballA.x !== ballB.x) { return ballA.x - ballB.x } return ballB.y - ballA.y})upperBalls = upperBalls.sort((ballA, ballB) => { if (ballA.x !== ballB.x) { return ballB.x - ballA.x } return ballB.y - ballB.x})// Conecte todos os pontos no sentido anti-horário balls = [firstBall].concat(lowerBalls, [lastBall], upperBalls)balls = balls. mapa((bola, i) => {bola.texto = i + 1; retornar bola})
As bolas finalmente retornadas são os pontos do polígono ordenados no sentido anti-horário.
O efeito é o seguinte:
O estado interno de cada bola é o seguinte:
O texto acima é todo o conteúdo deste artigo. Espero que seja útil para o estudo de todos. Também espero que todos apoiem a Rede VeVb Wulin.