Qual é a ideia por trás da definição de retângulos com dois pontos? [fechadas]


14

Não é que isso não faça sentido, mas funciona mal em 99% das vezes.

Geralmente, os retângulos gráficos 2D são inicializados, armazenados e manipulados como um par de pontos. Em nenhum idioma específico,

class Rect:
   p1, p2: point

Faz mais sentido definir um retângulo como dois valores x e dois valores y, assim:

class Rect
   xleft, xright: int
   ytop, ybottom: int

Com dois pontos, se em algum lugar do código-fonte você quiser usar o valor y do topo, você deve dizer rect.p1.y (hmmm, pare e pense, é p1 ou p2), mas com os quatro valores como membros simples dos dados, é claro e direto: rect.ytop (não é necessário pensar!) O uso de dois pontos significa que, ao lidar com a vertical, é necessário emaranhar a horizontal; há uma relação estranha entre elementos independentes.

Como surgiu essa ideia de dois pontos e por que persiste? Tem algum benefício sobre as coordenadas nuas x e y?

NOTA ADICIONADA: Esta pergunta está no contexto de retângulos alinhados com XY, como em gerenciadores de janelas e kits de ferramentas da GUI, não no contexto de formas arbitrárias no aplicativo de desenho e pintura.


8
Você escreveu uma quantidade substancial de código usando Rects?

6
Um retângulo é definido por dois pontos, portanto, representá-los como dois pontos faz muito sentido.
Adam Crossland

2
Um retângulo é mais naturalmente definido como um intervalo de valores x e um intervalo de valores y.
darenw

2
Ótima pergunta! Eu nunca pensei nisso, mas você faz um argumento interessante! FWIW, o Windows tem um RECT como você descreve bem (em cima, à esquerda, inferior, direita)
Dean Harding

3
Como você define um retângulo com dois pontos? Supõe-se que não tenha rotação?
Nick T

Respostas:


8

Você considerou menos propenso a erros?

Se você usar (Ponto1, Ponto2), ficará muito claro o que você está especificando. Se você fornecer 2 pontos, o único erro possível é que o usuário misturou seus x e y ao construir os pontos, pois a ordem dos pontos não importa.

Se você fornecer 4 números inteiros, se alguém não estiver prestando atenção, eles poderão fornecer (x1, x2, y1, y2) quando você desejar (x1, y1, x2, y2) ou vice-versa. Além disso, algumas APIs, como a estrutura Rect do WCF, definem um retângulo como (x, y, largura, altura), o que pode causar confusão sobre o que (1, 2, 3, 4) significa. Isso é (x, y, w, h) ou (x1, y1, x2, y2) ou (x1, x2, y1, y2)?

Em suma, (Ponto1, Ponto2) parece um pouco mais seguro para mim.


3
Que tal algo como Rect (xrange (x1, x2), yrange (y1, y2))? Isso parece o máximo em segurança e elegância no uso da API.
DarenW

9

Eu sempre gostei de definir um retângulo como um ponto + largura e altura, onde o ponto é o canto superior esquerdo do retângulo.

class Rect {
  float x, y;
  float width, height;
}

E adicione os métodos necessários para buscar as outras métricas. Como a versão Java


4
A parte superior y + altura, altura y ou apenas y?
Cameron MacFarland

2
@Cameron MacFarland: Isso depende do sistema de coordenadas do aplicativo, que não é motivo de retângulo baixo.
Jon Purdy

2
@ Martin Wickman: Qual é a vantagem em usar 2 pontos?
Kramii

@ Kramii: Uma vantagem é que você só precisa traduzir um ponto se estiver movendo todo o ret. Aliás, você sempre pode calcular o "ponto ausente" se precisar (troca de CPU / memória).
Martin Wickman

Isso também aparece na vida real. Acho complicado, pois uma das coisas mais comuns que faço com retângulos é testar se um ponto está contido. Desenho também é comum. Em ambos os casos, a adição precisa ser realizada, o que incomoda contadores de ciclo de relógio de alto desempenho como eu.
DarenW

7

Na verdade, um retângulo não é definido por 2 pontos. Um retângulo só pode ser definido por dois pontos se for paralelo aos eixos.

Existem várias maneiras de representar retângulos paralelos aos eixos:

  1. Dois pontos diagonalmente opostos
  2. Um ponto de canto, altura e largura
  3. Ponto central, meia altura e largura (incomum, mas às vezes útil).
  4. Como duas coordenadas X e duas coordenadas Y

Para (1), muitas bibliotecas usam uma convenção para determinar quais dois pontos são usados ​​- topLeft e bottomRight, por exemplo.

A escolha da representação pode ser orientada pelo objetivo original da definição do retângulo, mas imagino que ela seja frequentemente arbitrária . As representações são equivalentes nas informações que elas carregam. Porém, diferem na facilidade com que as propriedades do retângulo podem ser calculadas e na conveniência com que as operações podem ser executadas no retângulo.

Os benefícios da definição (1) sobre outros incluem:

  • Consistência da API com outros polígonos, linhas etc.
  • topLeft, bottomRight pode ser passado para qualquer método que aceite pontos
  • Os métodos da classe Point podem ser chamados em topLeft, bottomRight
  • A maioria das propriedades pode ser derivada facilmente, por exemplo. bottomLeft, topRight, largura, altura, centro, comprimento diagonal etc.

6

Bem, p1: Pointe p2: Pointcada uma delas terá duas intcoordenadas , de qualquer maneira, então sua classe não equivale à mesma coisa?

E se você armazenar esses dois pontos como Pointobjetos de primeira classe , não terá mais utilidade deles? Na maioria dos sistemas de coordenadas gráficas que eu conheço, os pontos são subclassificados dessa maneira para criar uma hierarquia de objetos: point -> circle -> ellipsee assim por diante.

Portanto, se você criar um objeto que não usa a Pointclasse, você o separou do restante da hierarquia de classes.


1
A vantagem que vejo da representação do OP é se você deseja conhecer o valor y inferior de um retângulo, você sabe que é "ybottom", onde, como em p1 / p2, você precisa descobrir qual é o menor. Isto é, a menos que você garanta que p1 terá os valores mais baixos.
Jason Viers

1
Embora seja verdade que as duas estruturas diferentes se resumem a quatro coordenadas, a versão de dois pontos introduz um nível estranho que reúne um xe um y, sem nenhuma justificativa específica para qual x combina com o que y. Não vejo esse nível extra como qualquer utilidade, depois de muitos anos de programação gráfica.
darenw

1
@ Jason: Bom ponto. Com a abordagem ytop/ ybottom, no entanto, também precisaria haver uma garantia em algum lugar que ybottomesteja realmente abaixo ytop.
Aprendiz do Dr. Wily

Ou chame-os de y1 e y2 e use min (y1, y2) e max (y1, y2) - é claro que seria mais desajeitado do que o acesso através de dois pontos p1, p2.
darenw

a nomeação de superior / inferior não comprará nada, pois nada impede o bottomx <topx, a menos que você o codifique especificamente. Eu acho que isso apenas acrescentaria confusão.
lkg

5

É por isso que eu gosto do Delphi TRect. É definido como um registro variante (estrutura de união em C-speak) que pode ser interpretado como um ponto TopLeft e BottomRight, ou inteiros Top, Left, Bottom e Right, o que for mais conveniente no momento.


1
Sim, característica muito útil isso.
Orbling em 30/11/10

4

Certamente, se você definir seu retângulo como:

class Rect
{
    Point bottomLeft;
    Point topRight;
}

então você sabe imediatamente qual é o ponto.

Melhor ainda seria adicionar propriedades extras que permitissem manipular o retângulo da maneira que fosse necessária para o seu aplicativo. Isso simplesmente atualizaria a estrutura de dados subjacente.

Ao adicionar uma transformação à forma, você pode orientar seu retângulo da maneira que desejar. Você ainda precisaria de uma caixa delimitadora alinhada ao eixo para verificações rápidas de aceitação / rejeição :)

No entanto, se seu modelo permitir retângulos em qualquer orientação sem aplicar uma transformação, "canto inferior esquerdo" e "canto superior direito" não terão significado, o que leva a "p1" e "p2" (ou algo equivalente).


Então, o que acontece quando você gira o retângulo em 90 graus? Agora você está acompanhando dois pontos diferentes do que estava inicialmente ou o seu "topRight" está agora à esquerda de "bottomLeft"?
Inaimathi

@Inaimathi - se você adicionar uma matriz de transformação, poderá manter o retângulo orientado com os eixos. No entanto, isso depende do seu aplicativo.
ChrisF

2

acho que faz mais sentido que um retângulo seja representado por uma extensão xey de um ponto; você pode até fazer a localização apontar para o centro do retângulo, para que seja independente da rotação

mas provavelmente foi mais fácil codificá-lo como dois pontos!


Como seria mais fácil codificar como dois pontos?
precisa saber é o seguinte

@DaremW: funções típicas de biblioteca draw-retângulo tomar superior esquerdo e inferior direito pontos-como argumentos
Steven A. Lowe

Mas por que essas APIs foram projetadas dessa maneira? Além de imitar as bibliotecas anteriores sem pensar, é isso.
darenw

@DarenW: meu palpite seria que as bibliotecas usar uma rotina de linha-desenho Bresenham, que leva dois pontos como entradas e é muito eficiente
Steven A. Lowe

2

Não gosto disso porque lançamos fora um grau de liberdade potencial, que essencialmente permite uma rotação arbitrária. Um retângulo 2D geral possui cinco incógnitas (graus de liberdade). Poderíamos especificá-los como as coordenadas de um ponto, os comprimentos dos dois lados que formam um vértice com esse ponto e o ângulo da horizontal da primeira linha (sendo o outro assumido um ângulo 90 graus maior). Um número infinito de outras possibilidades também pode ser usado, mas há cinco quantidades independentes que devem ser especificadas. Algumas escolhas levarão a álgebra mais fácil do que outras, dependendo do que for feito com elas.


5
É importante perceber que uma estrutura retangular padrão não é um retângulo matemático rigorosamente definido, mas uma versão simplificada sempre paralela aos eixos X e Y, porque foi criada para ser usada para uma coisa muito específica: definir regiões retangulares para um gerenciador de janelas, que é (quase) sempre paralelo aos eixos X e Y.
Mason Wheeler

+1, a estrutura esquerda / direita / superior / inferior é pré-classificada e, portanto, possui menos informações
Javier

Bom ponto, sobre retângulos alinhados xy. Eu tinha em mente que, e também se aplica como usado na modelagem 3D, e algumas outras coisas, mas todos xy (talvez também -z) alinhados.
darenw

1

Não é exatamente o mesmo que 2 pontos? Como isso é constrangedor ... a maioria das rotinas de desenho exige pontos, não componentes x / y separados.


1

Definir retângulos como pares de pontos permite reutilizar o ponto como um vértice para outra forma. Apenas um pensamento...


Mas parece jogar com meio baralho, para manter apenas dois pontos para definir uma forma de quatro cantos. Se você precisar de canto superior esquerdo, legal, mas se precisar de canto superior direito, precisará capturar dados sofisticados, relativamente falando.
DarenW

se houver um acessador definido como ponto AX, ponto BY e ponto BX, ponto AY, fica mais claro na memória / no disco, penso em armazenar metade do número de vértices e apenas apontá-los (dependendo do tamanho máximo da grade). Eu chego aonde você está indo com seu pensamento de alcance, mas você não está jogando com metade do baralho, apenas não está armazenando dados duplicados ... e os outros pontos a serem mencionados são quais são as restrições de memória? Quais são as implicações de reimplementação que reorganizam rects em todos os códigos interconectados.
RobotHumans

e desde que você não tem que fazer qualquer "trabalho" para obter os dois primeiros vértices sem memória, é provavelmente mais rápido
RobotHumans

1

Eu acredito que é principalmente estabelecer uniformidade entre todas as formas primitivas.

Claro que você pode definir o retângulo de várias maneiras diferentes, mas como você define um triângulo, uma estrela ou um círculo de uma maneira que possa usar estruturas de dados semelhantes?

Todos os polígonos podem ser definidos por seus pontos, com uma pequena quantidade de lógica para determinar o que fazer com os pontos.

As bibliotecas gráficas operam principalmente nesses polígonos em termos de vértices e arestas, portanto, pontos e linhas entre eles, todos os cálculos funcionam nesses dois recursos, bem isso e facetas, mas isso é apenas uma função das arestas.


1

Em duas dimensões, armazenar um retângulo como dois pontos é mais claro do que definir um canto específico e uma largura e altura - considere largura ou altura negativa ou os cálculos necessários para determinar cada opção da outra.

Executar rotações em um retângulo definido por pontos também é muito mais simples do que aquele definido com um ponto mais largura e altura.

Eu esperaria que o encapsulamento tornasse essa diferenciação sem importância como usuário da classe.

Um retângulo deve ser definido como três pontos a serem bem definidos em 3 dimensões. Não tenho muita certeza do requisito para definir um retângulo em 4 ou mais dimensões.


1

É completamente arbitrário. Você precisa de quatro informações para desenhar um retângulo. O (s) designer (s) da biblioteca decidiu representá-lo com dois pontos (cada um com uma coordenada xy), mas poderia facilmente fazê-lo com x / y / m / h ou em cima / baixo / esquerda / direita.

Suponho que a verdadeira questão do OP seja: por que essa escolha específica foi feita?


1

A escolha dos parâmetros é importante apenas para os projetistas / codificadores de baixo nível.

Usuários de alto nível só precisam pensar em:

  • IsPointInRect
  • Área
  • Interseção (ou recorte)
  • HasOverlap (igual a Intersection.Area> 0)
  • União (torna-se uma lista de retângulos)
  • Subtração (uma lista de retângulos que representam o mesmo conjunto de pontos que está no retângulo A, mas não no retângulo B)
  • Transformar
    • Mudanças em X e Y
    • Rotação (0, 90, 180, 270)
    • Escalonamento em X e Y (consulte a nota)
  • Sintaxe simples para as propriedades Xmin, Xmax, Ymin, Ymax, Largura, Altura, para que o usuário não precise saber a escolha exata dos parâmetros.

Nota: Para minimizar a perda de precisão durante a transformação de escala, às vezes é apropriado implementar uma segunda classe Rect que usa coordenadas de ponto flutuante, para que os resultados intermediários possam ser armazenados com precisão em uma sequência de transformações e arredondados apenas para número inteiro no último passo.


0

Como @Steven diz, acho que deveria ser em termos de um (x, y) ponto e um vetor de tamanho (w, h). Isso ocorre porque é fácil cair em uma ambiguidade. Suponha que você tenha o seguinte retângulo preenchido começando no ponto (0,0).

  012
0 XXX
1 XXX
2 XXX

Claramente é a largura, a altura é (3,3), mas qual é o segundo ponto? É (2,2) ou (3,3)?

Essa ambiguidade pode causar todos os tipos de problemas.

Eu aprendi a maneira duros anos atrás que é melhor pensar de coordenadas gráficas como as linhas entre os pixels, não como as linhas de pixels são on . Dessa forma, não há ambiguidade.


2
As rotinas originais do QuickDraw no Mac (as da década de 1980) usavam o modelo matemático de que os pontos eram infinitamente pequenos. Os pontos na tela estão entre os pixels. Portanto, uma linha traçada de (3,5) a (10,5) tinha um comprimento de 7 e ocupava 7 pixels. Nas coordenadas de hoje, essa linha teria um comprimento de 8.
Barry Brown

@ Barry: Isso faz sentido, já que ele usou muito o XOR, e se você estiver alinhando as linhas, deseja que eles se conectem sem um pixel ausente onde eles se encontram. Na verdade, publiquei um documento sobre o preenchimento de polígonos, considerando os dois sistemas de coordenadas.
Mike Dunlavey

0
Pa(x,y)*-----------------------------------*Pb(x,y)
       |                                   |
       |                                   |
       |                                   |
       |                                   |
       |                                   |
       |                                   |
Pc(x,y)*-----------------------------------*Pd(x,y)

Podemos definir ambos Pb & Pc assim:

Pb (Pd (x), Pa (y))

e

Pc (Pa (x), Pd (y))

Portanto, não há necessidade de definir todos os quatro pontos devido à simetria

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.