Estou procurando um algoritmo para detectar se dois retângulos se cruzam (um em um ângulo arbitrário, o outro apenas com linhas verticais / horizontais).
Testar se um canto de um está no outro QUASE funciona. Ele falha se os retângulos formarem uma forma de cruz.
Parece uma boa idéia evitar o uso de inclinações das linhas, o que exigiria casos especiais para linhas verticais.
Respostas:
O método padrão seria fazer o teste do eixo de separação (faça uma pesquisa no google sobre isso).
Em resumo:
O mais divertido é que basta verificar todas as arestas dos dois retângulos. Se os retângulos não se sobrepuserem, uma das arestas será o eixo de separação.
Em 2D, você pode fazer isso sem usar inclinações. Uma aresta é simplesmente definida como a diferença entre dois vértices, por exemplo
Você pode obter uma perpendicular a isso girando em 90 °. Em 2D, é fácil como:
Portanto, não há trigonometria ou declives envolvidos. A normalização do vetor para o comprimento da unidade também não é necessária.
Se você quiser testar se um ponto está em um ou outro lado da linha, basta usar o produto escalar. o sinal indicará de que lado você está:
Agora teste todos os pontos do retângulo A contra as bordas do retângulo B e vice-versa. Se você encontrar uma aresta de separação, os objetos não se cruzam (desde que todos os outros pontos em B estejam do outro lado da aresta sendo testada - veja o desenho abaixo). Se você não encontrar uma aresta de separação, os retângulos estão se cruzando ou um retângulo está contido no outro.
O teste funciona com qualquer polígono convexo entre.
Alteração: para identificar uma aresta de separação, não basta testar todos os pontos de um retângulo em relação a cada aresta da outra. A aresta candidata E (abaixo) seria, como tal, identificada como aresta de separação, pois todos os pontos em A estão no mesmo semiplano de E. No entanto, não é uma aresta de separação porque os vértices Vb1 e Vb2 de B também estão nesse semi-plano. Seria apenas uma aresta de separação se esse não fosse o caso http://www.iassess.com/collision.png
fonte
Basicamente, observe a seguinte imagem:
Se as duas caixas colidirem, as linhas A e B se sobrepõem.
Observe que isso terá que ser feito nos eixos X e Y, e ambos precisam se sobrepor para que os retângulos colidam.
Há um bom artigo no gamasutra.com que responde à pergunta (a imagem é do artigo). Eu fiz um algoritmo semelhante há 5 anos e tenho que encontrar meu snippet de código para publicá-lo aqui mais tarde
Alteração : O Teorema do Eixo Separador afirma que duas formas convexas não se sobrepõem se existir um eixo de separação (ou seja, aquele em que as projeções mostradas não se sobrepõem). Então "Existe um eixo de separação" => "Sem sobreposição". Isso não é uma implicação dupla, portanto você não pode concluir o inverso.
fonte
A resposta do m_pGladiator está certa e eu prefiro. O teste do eixo de separação é o método mais simples e padrão para detectar a sobreposição de retângulos. Uma linha para a qual os intervalos de projeção não se sobrepõem, chamamos de eixo de separação . A solução de Nils Pipenbrinck é muito geral. Ele usa produto de ponto para verificar se uma forma está totalmente de um lado da borda da outra. Esta solução é realmente capaz de induzir polígonos convexos n-edge. No entanto, não é otimizado para dois retângulos.
o ponto crítico da resposta de m_pGladiator é que devemos verificar a projeção de dois retângulos nos dois eixos (x e y). Se duas projeções forem sobrepostas, poderíamos dizer que esses dois retângulos estão sobrepostos. Portanto, os comentários acima para a resposta de m_pGladiator estão errados.
para a situação simples, se dois retângulos não são rotacionados, apresentamos um retângulo com estrutura:
nomeamos o retângulo A, B com rectA, rectB.
se qualquer um dos dois retângulos for girado, pode ser necessário algum esforço para determinar a projeção deles nos eixos xey. Defina struct RotatedRect da seguinte maneira:
a diferença é como a largura 'agora é um pouco diferente: widthA' para rectA:
Math.sqrt(rectA.width*rectA.width + rectA.height*rectA.height) * Math.cos(rectA.angle)
widthB 'para rectB:Math.sqrt(rectB.width*rectB.width + rectB.height*rectB.height) * Math.cos(rectB.angle)
Pode se referir a um PPT da GDC (Game Development Conference 2007) www.realtimecollisiondetection.net/pubs/GDC07_Ericson_Physics_Tutorial_SAT.ppt
fonte
No Cocoa, você pode detectar facilmente se o retângulo de Área selecionada cruza o retângulo de quadro do NSView girado. Você nem precisa calcular polígonos, normais como esse. Basta adicionar esses métodos à sua subclasse NSView. Por exemplo, o usuário seleciona uma área na superview do NSView e chama o método DoesThisRectSelectMe passando o rectArea selectedArea. A API convertRect: fará esse trabalho. O mesmo truque funciona quando você clica no NSView para selecioná-lo. Nesse caso, simplesmente substitua o método hitTest como abaixo. A API convertPoint: fará esse trabalho ;-)
fonte
Verifique se alguma das linhas de um retângulo cruza alguma das linhas do outro. A interseção ingênua do segmento de linha é fácil de codificar.
Se você precisar de mais velocidade, existem algoritmos avançados para interseção de segmento de linha (linha de varredura). Veja http://en.wikipedia.org/wiki/Line_segment_intersection
fonte
Uma solução é usar algo chamado polígono sem ajuste. Esse polígono é calculado a partir dos dois polígonos (conceitualmente deslizando um ao redor do outro) e define a área pela qual os polígonos se sobrepõem, devido ao seu deslocamento relativo. Depois de ter esse NFP, basta fazer um teste de inclusão com um ponto dado pelo deslocamento relativo dos dois polígonos. Esse teste de inclusão é rápido e fácil, mas você precisa criar o NFP primeiro.
Pesquise o polígono No Fit na Web e veja se consegue encontrar um algoritmo para polígonos convexos (fica muito mais complexo se você tiver polígonos côncavos). Se você não encontrar nada, envie-me um e-mail para howard dot J dot may gmail dot com
fonte
Aqui está o que eu acho que vai cuidar de todos os casos possíveis. Faça os seguintes testes.
Se os 2 testes acima retornarem falsos, esses 2 retângulos não se sobrepõem.
fonte
Se você estiver usando Java, todas as implementações da interface Shape têm um método de interseção que usa um retângulo.
fonte
Bem, o método da força bruta é percorrer as bordas do retângulo horizontal e verificar cada ponto ao longo da borda para ver se ele cai sobre ou no outro retângulo.
A resposta matemática é formar equações descrevendo cada aresta dos dois retângulos. Agora você pode simplesmente descobrir se alguma das quatro linhas do retângulo A cruza alguma das linhas do retângulo B, que deve ser um solucionador de equações linear simples (rápido).
-Adão
fonte
Você pode encontrar a interseção de cada lado do retângulo angular com cada lado do retângulo alinhado ao eixo. Faça isso encontrando a equação da linha infinita na qual cada lado se encontra (por exemplo, v1 + t (v2-v1) e v'1 + t '(v'2-v'1) basicamente), encontrando o ponto em que o as linhas se encontram resolvendo t quando essas duas equações são iguais (se forem paralelas, você pode testar isso) e testando se esse ponto está no segmento de linha entre os dois vértices, ou seja, é verdade que 0 <= t <= 1 e 0 <= t '<= 1.
No entanto, isso não cobre o caso quando um retângulo cobre completamente o outro. Isso você pode cobrir testando se todos os quatro pontos de um retângulo estão dentro do outro retângulo.
fonte
Isto é o que eu faria, para a versão 3D deste problema:
Modele os 2 retângulos como planos descritos pelas equações P1 e P2, depois escreva P1 = P2 e derive a linha de equação de interseção, que não existirá se os planos forem paralelos (sem interseção) ou estiverem no mesmo plano, nesse caso, você obtém 0 = 0. Nesse caso, você precisará empregar um algoritmo de interseção de retângulo 2D.
Então eu veria se essa linha, que está no plano dos dois retângulos, passa por ambos os retângulos. Se isso acontecer, você terá uma interseção de 2 retângulos, caso contrário, você não tem (ou não deveria, talvez eu tenha perdido uma caixa de canto na minha cabeça).
Para descobrir se uma linha passa por um retângulo no mesmo plano, eu encontraria os 2 pontos de interseção da linha e os lados do retângulo (modelando-os usando equações de linha) e, em seguida, verifique se os pontos de interseção estão em alcance.
Essas são as descrições matemáticas, infelizmente não tenho código para fazer o acima.
fonte
Outra maneira de fazer o teste um pouco mais rápido do que o teste do eixo de separação é usar o algoritmo dos números de enrolamento (apenas nos quadrantes - não a soma dos ângulos, que é terrivelmente lenta) em cada vértice de qualquer retângulo (escolhido arbitrariamente). Se algum dos vértices tiver um número de corda diferente de zero, os dois retângulos se sobrepõem.
Esse algoritmo é um pouco mais demorado do que o teste do eixo de separação, mas é mais rápido porque exige apenas um teste de meio plano se as arestas estiverem cruzando dois quadrantes (em oposição a até 32 testes usando o método do eixo de separação)
O algoritmo tem a vantagem adicional de poder ser usado para testar a sobreposição de qualquer polígono (convexo ou côncavo). Até onde eu sei, o algoritmo funciona apenas no espaço 2D.
fonte
Ou estou perdendo outra coisa, por que tornar isso tão complicado?
se (x1, y1) e (X1, Y1) são cantos dos retângulos, para encontrar a interseção, faça:
fonte
Eu implementei assim:
A matriz mB é qualquer matriz de transformação afim que converte pontos no espaço B em pontos no espaço A. Isso inclui rotação e translação simples, rotação mais escala e distorções afins completas, mas não distorções de perspectiva.
Pode não ser o melhor possível. A velocidade não era uma grande preocupação. No entanto, parece funcionar bem para mim.
fonte
Aqui está uma implementação matlab da resposta aceita:
fonte
Este é o método convencional, vá linha por linha e verifique se as linhas estão se cruzando. Este é o código no MATLAB.
a função InterX pode ser baixada em: https://in.mathworks.com/matlabcentral/fileexchange/22441-curve-intersections?focused=5165138&tab=function
fonte
Eu tenho um método mais simples, se tivermos 2 retângulos:
R1 = (min_x1, max_x1, min_y1, max_y1)
R2 = (min_x2, max_x2, min_y2, max_y2)
Eles se sobrepõem se e somente se:
Sobreposição = (max_x1> min_x2) e (max_x2> min_x1) e (max_y1> min_y2) e (max_y2> min_y1)
Você também pode fazer isso em caixas 3D, na verdade funciona para qualquer número de dimensões.
fonte
Já foi dito o suficiente em outras respostas, então adicionarei apenas uma linha de pseudocódigo:
fonte