8 de jan. de 2017

Super Formas com JavaFX


Mais uma postagem sobre efeitos visuais com JavaFX e hoje vamos falar sobre a aplicação da Superformula, derivada da fórmula Superellipse para gerar supershapes! Pode parecer confuso, mas vamos aplicar uma fórmula no JavaFX e gerar formas geométricas bacanas somente trocando os parâmetros da fórmula!
Claro a inspiração novamente veio do Shiffman! Veja abaixo o vídeo dele:


O código principal foi tirado do artigo do Paulo Bourke aplicando o algoritmo em um ângulo 0 até 2*PI temos diversas formas geradas.

O código é bem simples! Novamente usamos aquela classe utilitária para desenhar em JavaFX e a cada ciclo nós vamos aplicar a fórmula com novos parâmetros e então transformamos os resultados em pontos X e Y e desenhamos na tela pontinhos. O número de pontos define a resolução e para gerar todos os pontos fazemos um loop e pegamos um ângulo baseado no ponto atual do loop, aplicamos a fórmula e pegamos os pontos.

Em outras palavras, se quisermos 10 pontos, aplicamos a fórmula usando (0 * 10 / PI *2), (1 * 10 / PI *2), , (2 * 10 / PI *2), (1 * 10 / PI *2) e assim vai...

Mas o ponto principal são as constantes. Ao executar a fórmula, podemos mudar algumas constantes e como resultado temos formas geométricas completamente diferentes! As constantes são m, n1, n2 e n3!

A nossa aplicação muda os valores das  "constantes" e aplica a fórmula a cada frame executado. O resultado é o seguinte:


Os tipos de azul utilizados foram escolhidos pela minha amada Luana e o código da aplicação ficou bem compacto, pode ver abaixo:



Projeto completo no github

import org.fxapps.drawingfx.DrawingApp;
import static java.lang.Math.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
public class SuperShapesApp extends DrawingApp {
int numberOfPoints = 1300;
private double maxPhi = Math.PI * 2;
private double m = 1, n1 = 0.3, n2 = 0.3, n3 = 0.3;
public static void main(String[] args) {
launch();
}
public void setup() {
title = "Super Shapes";
width = 800;
height = 600;
frames = 1;
ctx.setFont(Font.font(30));
ctx.setStroke(Color.NAVY);
}
public void draw() {
ctx.setFill(Color.LIGHTBLUE);
ctx.fillRect(0, 0, width, height);
double[][] points = new double[numberOfPoints][2];
for (int i = 0; i < numberOfPoints; i++) {
double phi = toDegrees(i * (maxPhi) / numberOfPoints);
double r = superShape(m, n1, n2, n3, phi);
double x = 250 * r * cos(phi) + width / 2;
double y = 250 * r * sin(phi) + height / 2;
points[i][0] = x;
points[i][1] = y;
}
for (int i = 0; i < numberOfPoints; i++) {
ctx.strokeOval(points[i][0], points[i][1], 2, 2);
}
ctx.setFill(Color.WHITE);
String str = String.format("M=%.1f, N1=%.1f, N2=%.1f, N3=%.1f, PHI= %s",
m, n1, n2, n3, maxPhi == PI * 2 ? "2PI" : "12PI");
ctx.fillText(str, 50, height - 20);
m += 0.20;
if (m > 10) {
if (n1 == 1) {
n1 = n2 = n3 = 0.3;
} else if (maxPhi == (PI * 2)) {
maxPhi = PI * 12;
} else {
n1 = n2 = n3 = 1;
maxPhi = PI * 2;
}
m = 1;
}
}
private double superShape(double m, double n1, double n2, double n3, double phi) {
double r, t1, t2, a = 1, b = 1;
t1 = cos(m * phi / 4) / a;
t1 = abs(t1);
t1 = pow(t1, n2);
t2 = sin(m * phi / 4) / b;
t2 = abs(t2);
t2 = pow(t2, n3);
r = pow(t1 + t2, 1 / n1);
return abs(r) == 0 ? 0 : 1 / r;
}
}

Nenhum comentário:

Postar um comentário