用高斯随机函数来设置半径大小,然后循环一周画一个整圆,最后利用vertex相连。生成的圆看似无序,但每圈上高峰和低估的比例都近似相同。
//cx,cy控制中心位置,n控制点个数
function randomline0423_1(cx, cy, n) {
strokeWeight(1.5);
translate(width / 2, height / 2);
for (let i = 0; i < 250; i += 10) {
//stroke(random(255), 255, 255);
stroke(random(20, 255), random(20, 255), random(20, 255));
Gaussian(cx, cy, sqrt(i) * 15, 10 - i / 30, n);
Gaussian(cx, cy, i, 10, n);
}
}
function Gaussian(cx, cy, mean, sd, n) {
beginShape();
for (let i = 0; i < n; i++) {
let theta = TWO_PI * i / n;
let radius = abs(randomGaussian(mean, sd));
let v = p5.Vector.fromAngle(theta, radius);
curveVertex(cx + v.x, cy + v.y);
}
endShape(CLOSE);
}
//起始点与中心点,i代表粒子在全局数组x,y中的序号,part表示斥力半径,g代表重力系数,mag表示引力系数
function randomline0423_2(s_x,s_y,c_x,c_y,i,part,g,mag)
{
var distance = dist(c_x,c_y,x[i], y[i]);
if(distance > part){
ax[i] = mag * (c_x - x[i]) / (distance*distance );
ay[i] = mag * (c_y - y[i]) / (distance * distance);
}
else
{
ax[i] = -random(1,3)*mag * (c_x - x[i]) / (distance * distance);
ay[i] = -random(1,3)*mag * (c_y - y[i]) / (distance * distance);
}
vx[i] += ax[i]*g*random(1,3);
vy[i] += ay[i]*g*random(1,3);
x[i] += vx[i];
y[i] += vy[i];
var sokudo = dist(0,0,vx[i],vy[i]);
var r = map(sokudo, 0, 5, 0, 255);
var g = map(sokudo, 0,5, 64, 255);
var b = map(sokudo, 0,5, 128, 255);
fill(r, g, b+random(100), 100);
ellipse(x[i],y[i],1,1);
}
使用粒子间的引力公式来计算加速度,然后根据设定的距离来给接近的粒子一个斥力,模拟原子核收到粒子撞击的情况。通过对随机位置以及角度的射入计算可得,粒子反射位置的最高点可以构成若干个圆,且圆的位置由原子核位置以及随机初始点位置决定。而且,由于随机性满足均匀分布,在原子核附近会生成类似原子核的一层粒子层,且有粒子会在该粒子层不断环绕。
下列结果可以证实。
有没有黑洞的感觉
把y的距离计算上升到高阶时
if(distance > part){
ax[i] = mag * (c_x - x[i]) / (distance*distance );
ay[i] = mag * (c_y - y[i]) / (distance * distance* distance);
}
先介绍一下Voronoi图理论
沃罗诺伊图(Voronoi Diagram,也称作Dirichlet tessellation,狄利克雷镶嵌)是由俄国数学家Georgy Fedoseevich Voronoi建立的空间分割算法,其空间划分思想来源于笛卡尔用凸域分割空间理论,也就是说,Voronoi图实际是一种空间划分方法,这种划分方法解决了这样一个问题:如何根据已知点划分空间,使得晶胞与点一一对应,并使晶胞内任取一点都与最近的已知点围在一起,或者换一种说法就是沃罗诺伊图基于一组特征点将空间分割成不同区域,而每一区域又仅包含唯一的特征点,并且该区域内任意位置到该特征点的距离比到其它的特征点都要更近。所以Voronoi图有以下特性(以二维平面为例):
(1)每个V多边形内有一个特征点;
(2)每个V多边形内点到该特征点距离短于到其它特征点的距离;
(3)多边形边界上的点到生成此边界的特征点距离相等;
(4)邻接图形的Voronoi多边形界线以原邻接界线作为子集。
实验中使用的沃罗诺伊噪声,基本思路是先随机生成初始点位置,然后通过沃罗诺伊噪声来随机化角度。具体的使用方式是通过噪声来改变梯度的运算方向,因此,每一根线都是连续的,然后形成连续的线来形成整个图案。由于控制了梯度的范围,所以当梯度小于一定值时,导数方向相同,就使得一块区域内的线按照相似的轨迹运动。
//粒子系统以及噪声生成
function voronoi_noise(x, y) {
var max_step = Math.max(x_grid_step, y_grid_step);
var x_idx = Math.floor(x / x_grid_step);
x_idx = Math.min(Math.max(0, x_idx), grid_resolution-1);
var y_idx = Math.floor(y / y_grid_step);
y_idx = Math.min(Math.max(0, y_idx), grid_resolution-1);
var idx = to_idx(x_idx, y_idx);
var min_dist = Math.hypot(points[idx][0] - x, points[idx][1] - y);
var min_dist_idx = 0;
var x_extent = Math.ceil(x_grid_step / max_step);
var y_extent = Math.ceil(y_grid_step / max_step);
for(var i = -x_extent; i <= x_extent; i++){
for(var j = -y_extent; j <= y_extent; j++){
var x1_idx = x_idx + i;
var y1_idx = y_idx + j;
if(x1_idx < 0 || x1_idx >= grid_resolution || y1_idx < 0 || y1_idx >= grid_resolution){
continue;
}
idx = to_idx(x1_idx, y1_idx);
var dist = Math.hypot(points[idx][0] - x, points[idx][1] - y);
if(dist < min_dist){
min_dist = dist;
min_dist_idx = idx;
}
}
}
return min_dist;
}
function Particle(x, y){
this.dir = createVector(0, 0);
this.vel = createVector(0, 0);
this.pos = createVector(x, y);
this.speed = 1;
this.move = function()
{
var angle = voronoi_noise(this.pos.x, this.pos.y)*TWO_PI/angle_scale;
this.dir.x = cos(angle);
this.dir.y = sin(angle);
this.vel = this.dir.copy();
this.vel.mult(this.speed);
this.pos.add(this.vel);
}
this.display = function(r)
{
ellipse(this.pos.x, this.pos.y, r, r);
}
}
角度范围100 梯度5
角度范围100 梯度1
角度范围20 梯度6
角度范围50 梯度10
还有更多有趣的图案等你探索
鼠标点击变换参数
曼德勃罗集合(Mandelbrot Set)或曼德勃罗复数集合。是一种在复平面上组成分形的点的集合,因由曼德勃罗提出而得名。曼德博集合能够使复二次多项式 进行迭代来获得。
当中,c是一个复參数。对于每个c。从 z = 0 開始对fc(z)进行迭代。序列 的值或者延伸到无限大,或者仅仅停留在有限半径的圆盘内(这与不同的參数c有关)。曼德布洛特集合就是使以上序列不延伸至无限大的全部c点的集合。
可视化图像是通过反向绘制得到的。具体思路是从曼德勃罗集合外部随机选择n个点出发,遇到边界就停止运动。
由于该集合的可迭代性,所以无论如何放大,图像都会呈现出相似但不同的另一个图像。这是由于每次迭代过程中的变换矩阵所满足的概率分布条件,使得它永远不会出现两个相同的变换结果。
//num代表初始化点个数,Imax表示最大迭代次数,Zmax表示最大距离
function mandelbrot(num,Imax,Zmax)
{
if (one) {
for (I = 0 ; I < num ; I++) {
Set(Imax, Zmax);
}
one = false;
}
for (I = 0 ; I < num ; I++) {
DON = 1;
KX = CX[I] + (L * cos(CA[I] - AA));
KY = CY[I] + (L * sin(CA[I] - AA));
check(Imax, Zmax);
KDL = KD;
KX = CX[I] + (L * cos(CA[I] + AA));
KY = CY[I] + (L * sin(CA[I] + AA));
check(Imax, Zmax);
KDR = KD;
if (KDL > KDR) {
CA[I] = CA[I] - AA;
}
if (KDL < KDR) {
CA[I] = CA[I] + AA;
}
if (KDL == KDR) {
CA[I] = CA[I];
}
NX = CX[I] + (L * cos(CA[I]));
NY = CY[I] + (L * sin(CA[I]));
if (DON == 0) {
stroke(random(0, 150), random(150, 250), random(0, 250), random(50, 100));
line((CX[I] - Xmin) * 700 / (Xmax - Xmin), (CY[I] - Ymin) * 700 / (Ymax - Ymin), (NX - Xmin) * 700 / (Xmax - Xmin), (NY - Ymin) * 700 / (Ymax - Ymin));
}
CX[I] = NX;
CY[I] = NY;
if (COL[I] > 0 && DON == 0) {
COL[I] = COL[I] - 2;
}
else {
Set(Imax,Zmax);
}
}
fill(255, 255, 255);
text(B, 90, 640);
}
function Set(Imax, Zmax) {
DON = 0;
var III = 0;
while (DON == 0 && III < 1000) {
CX[I] = random(Xmin, Xmax);
CY[I] = random(Ymin, Ymax);
KX = CX[I];
KY = CY[I];
DON = 1;
check(Imax, Zmax);
III = III + 1;
}
CA[I] = random(-PI, +PI);
KX = CX[I];
KY = CY[I];
check();
D[I] = KD;
COL[I] = int(random(150, 255));
}
function check(Imax,Zmax) {
X = 0;
Y = 0;
for (var II = 0 ; II < Imax ; II++) {
NX = (X * X) - (Y * Y) + KX;
NY = (2 * X * Y) + KY;
X = NX;
Y = NY;
KD = sqrt((X * X) + (Y * Y));
if (KD > Zmax) {
DON = 0;
II = Imax;
}
}
}
鼠标控制放大,可以查看不同迭代次数下的结果。
可放大次数取决于你最大的迭代次数。