I tried to create the fake gravity universe.
The creative coding can create the fake gravity universe.
It's a creative coding animation made with 'Processing' programming language. It draws the stars that move with their (fake) gravity.
Fake gravity.
It is not a real physical model. It's just a simple fake gravity vector calculation.
float nD = dist(r.x, r.y, p.x, p.y);
if (r != p && nD > _limit) {
// calc fake garavity vector
float vR = atan2(p.y - r.y, p.x - r.x);
r.x += cos(vR) * range / nD / nD;
r.y += sin(vR) * range / nD / nD;
}
Discrete moving.
And I did not define speed but step to calculate star moving. So stars may not collide to another star but jump to the other side of that star. It adds some interesting results to this animation.
Example code of the 'Processing'.
This code does not display any images on the screen but generates image files in frames directory. You can make an animation with these files.
Please feel free to use this example code under the terms of the GPL. To see other works based on my code is my pleasure. And my honor.
/**
* Cosmic Blues.
* It draws the stars that move with their (fake) gravity.
*
* Processing 3.5.3
* @author @deconbatch
* @version 0.1
* created 0.1 2020.05.23
*/
/**
* Node : draw and hold location and color.
*/
public class Node {
public float x, y; // coordinate of node
private float hueVal; // hue value of node
private float satVal; // saturation value of node
private float briVal; // brightness value of node
Node(float _x, float _y, float _c, float _s, float _b) {
x = _x;
y = _y;
hueVal = _c;
satVal = _s;
briVal = _b;
}
}
void setup() {
size(720, 720);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
smooth();
noLoop();
}
void draw() {
int sceneMax = 2; // draw 2 different scenes
int frmMax = 24 * 8; // for 24fps x 8s animation
int nodeMax = 5000;
float nodeGap = 5.0;
float baseHue = random(360.0);
for (int sceneCnt = 0; sceneCnt < sceneMax; ++sceneCnt) {
baseHue += 180.0;
float limit = random(2.0, 5.0); // bigger : many cluster
float step = random(0.1, 1.0); // random(0.2, 1.2);bigger : big cluster
ArrayList<Node> rnds = plotNodes(nodeMax, nodeGap, baseHue);
for (int frmCnt = 0; frmCnt < frmMax; ++frmCnt) {
float frmRatio = map(frmCnt, 0, frmMax, 0.0, 1.0);
float easeRatio = easeInOutCubic(frmRatio);
background(baseHue % 360.0, 100.0, 20.0, 100.0);
blendMode(ADD);
drawNodes(rnds);
blendMode(BLEND);
casing();
if (frmCnt == 0) {
for (int i = 0; i < 12; i++) {
saveFrame("frames/" + String.format("%02d", sceneCnt) + ".00." + String.format("%04d", i) + ".png");
}
}
saveFrame("frames/" + String.format("%02d", sceneCnt) + ".01." + String.format("%04d", frmCnt) + ".png");
moveNodes(rnds, limit * (1.0 - easeRatio * 0.25), step * frmRatio);
}
}
exit();
}
/**
* moveNodes : move Nodes with (fake) gravity calculation.
* NOT A PHYSICAL MODEL.
* @param ArrayList<Node> _nodes : Nodes to move.
* @param _limit : max distance that ignore gravity.
* @param _step : moving step
*/
public void moveNodes(ArrayList<Node> _nodes, float _limit, float _step) {
float range = max(width, height) * _step;
for (Node r : _nodes) {
ArrayList<PVector> vectors = new ArrayList<PVector>();
for (Node p : _nodes) {
float nD = dist(r.x, r.y, p.x, p.y);
if (r != p && nD > _limit) {
// calc fake garavity vector
float vR = atan2(p.y - r.y, p.x - r.x);
r.x += cos(vR) * range / nD / nD;
r.y += sin(vR) * range / nD / nD;
}
}
}
}
/**
* drawNodes : draw each Node.
* @param ArrayList<Node> _nodes : Nodes to draw.
*/
public void drawNodes(ArrayList<Node> _nodes) {
noStroke();
for (Node n : _nodes) {
fill(n.hueVal, n.satVal, n.briVal, 100.0);
ellipse(n.x, n.y, 3.0, 3.0);
}
}
/**
* plotNodes : locate Nodes with some distance each other.
* @param _nodeMax : maximum number of Nodes.
* @param _gap : gap between each Node.
* @param _baseHue : base color of Nodes.
* @return ArrayList<Node> : holds nodes.
*/
public ArrayList<Node> plotNodes(float _nodeMax, float _gap, float _baseHue) {
ArrayList<Node> nodes = new ArrayList<Node>();
for (int i = 0; i < _nodeMax; i++) {
// locate in round range
float rRadius = random(0.6, 1.0) * min(width, height) * 0.45;
float rRadian = random(TWO_PI);
float rX = width * 0.5 + rRadius * cos(rRadian);
float rY = height * 0.5 + rRadius * sin(rRadian);
// add new node
boolean inner = false;
for (Node n : nodes) {
if (dist(rX, rY, n.x, n.y) < _gap) {
inner = true;
break;
}
}
if (!inner) {
int pixIndex = floor(rY * width + rX);
nodes.add(new Node(
rX,
rY,
(_baseHue + noise(10.0, rX * 0.05, rY * 0.05) * 120.0 + 300.0) % 360.0,
noise(20.0, rX * 0.05, rY * 0.05) * 60.0 + 40.0,
noise(30.0, rX * 0.05, rY * 0.05) * 20.0 + 50.0
));
}
}
return nodes;
}
/**
* casing : draw fancy casing
*/
public void casing() {
fill(0.0, 0.0, 0.0, 0.0);
strokeWeight(24.0);
stroke(0.0, 0.0, 00.0, 100.0);
rect(0.0, 0.0, width, height);
strokeWeight(1.0);
stroke(0.0, 0.0, 60.0, 100.0);
rect(8.0, 8.0, width - 16.0, height - 16.0);
noStroke();
noFill();
noStroke();
}
/**
* easeInOutCubic easing function.
* @param t 0.0 - 1.0 : linear value.
* @return float 0.0 - 1.0 : eased value.
*/
public float easeInOutCubic(float t) {
t *= 2.0;
if (t < 1.0) {
return pow(t, 3) / 2.0;
}
t -= 2.0;
return (pow(t, 3) + 2.0) / 2.0;
}
/*
Copyright (C) 2020- deconbatch
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/