um círculo de Bresenham em Scala (35)
O algoritmo Bresenham - possui 2 pontos principais:
- trabalha sem pecado / cosina.
- você calcula apenas ¼ * ½ círculo, os outros pontos são encontrados pelo espelhamento.
Como fazer isso:
2 1
DCBABCD
GFE EFG
IJ y | ---- JI
GJ / JG
F / | F
DE r / | ED
C / | C
B 4 | / | B 3
A + ------- A
B 4 'x B 3'
CC
ATO
FF
GJ JG
IJ JI
GFE EFG
DCBABCD
2'1 '
- Apenas calculamos os números de A no zenit para I.
- O ponto I está em 45 °, definido por x == y.
- O ponto zero é onde está o +.
- OA no zenit é o ponto (x = 0, y = r), r = raio.
- Para desenhar um círculo fechado, movemos no sentido horário (++ x), que é para a direita (x + = 1) ou para baixo até o próximo ponto (y- = 1).
- todos os pontos (x, y) do círculo estão afastados do centro. Pitágoras diz, r² = x² + y².
- Isso cheira a raiz quadrada e equações com 2 soluções, mas cuidado!
- começamos em A e queremos saber se pintaremos o próximo ponto abaixo ou o ponto abaixo à direita.
- calculamos para ambos os pontos (x² + y²) e construímos para ambos a diferença para r² (que permanece, é claro, constante).
- Uma vez que a diferença pode ser negativa, tiramos os abdominais dela.
- então olhamos qual ponto está mais próximo do resultado (r²) e o ipso menor.
- dependendo disso, desenhamos o vizinho direito ou inferior.
- o ponto tão encontrado
- 1 x, y é espelhado
- 2 -x, y para a esquerda
- 3 y, x na diagonal
- 4 -y, x daí para a esquerda
- todos esses pontos são espelhados novamente para o sul
- 1 'x, -y
- 2 '-x, -y
- 3 'y, -x
- 4 '-y, -x pronto.
Isso não é código de golfe, mas todos esses números no topo das soluções existentes me fizeram pensar que era, então eu passei um tempo inútil jogando minha solução no golfe. Por isso, adicionei também um número inútil no topo. É 11 vezes o Pi arredondado.
object BresenhamCircle extends App {
var count = 0
val r = args(0).toInt
// ratio > 1 means expansion in horizontal direction
val ratio = args(1).toInt
val field = ((0 to 2 * r).map (i=> (0 to 2 * r * ratio).map (j=> ' ').toArray)).toArray
def square (x: Int, y: Int): Int = x * x + y * y
def setPoint (x: Int, y: Int) {
field (x)(y*ratio) = "Bresenham"(count)
field (y)(x*ratio) = "Bresenham"(count)
}
def points (x: Int, y: Int)
{
setPoint (r + x, r + y)
setPoint (r - x, r + y)
setPoint (r + x, r - y)
setPoint (r - x, r - y)
}
def bresenwalk () {
var x = 0;
var y = r;
val rxr = r * r
points (x, y);
do
{
val (dx, dy) = { if (math.abs (rxr - square ((x+1), y)) < math.abs (rxr - square (x, (y-1))))
(1, 0)
else
(0, -1)
}
count = (count + 1) % "Bresenham".length
x += dx
y += dy
points (x, y)
}while ((x <= y))
}
bresenwalk ()
println (field.map (_.mkString ("")).mkString ("\n"))
}
A questão da fonte é decidida pelo servidor de sites e pelas configurações do seu navegador. Agora, que eu estou olhando, é
'Droid Sans Mono',Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif
O tamanho da fonte é 12 px. Informações bastante inúteis, se você me perguntar, mas quem faz?
Bônus: elipses e saída de amostra:
A invocação é
scala BresenhamCircle SIZE RATIO
por exemplo
scala BresenhamCircle 10 2
s e r B r e s
h n e e n h
e m a a m e
e r r e
m m
h a a h
n n
s e e s
e e
r r
B B
r r
e e
s e e s
n n
h a a h
m m
e r r e
e m a a m e
h n e e n h
s e r B r e s
A ratio of 2 will print a circular shape for most fonts which happen to be about twice as tall than wide. To compensate for that, we widen by 2.
# As smaller value than 2 only 1 is available:
scala BresenhamCircle 6 1
erBre
aes sea
ah ha
e e
es se
r r
B B
r r
es se
e e
ah ha
aes sea
erBre
# widening it has more freedom:
scala BresenhamCircle 12 5
s e r B r e s
a h n e e n h a
B m m B
e r r e
e s s e
B r r B
a m m a
h h
n n
s e e s
e e
r r
B B
r r
e e
s e e s
n n
h h
a m m a
B r r B
e s s e
e r r e
B m m B
a h n e e n h a
s e r B r e s
Eu restringi o parâmetro ratio para Int para simplificá-lo, mas ele pode ser facilmente ampliado para permitir flutuações.