我正在寻找一种算法,该算法实质上返回一系列点,这些点定义了由液体形成的污点的形状。
我不需要任何方向性影响,它可能是垂直落下的水滴产生的污渍。
我开始从一个中心以两对的方式传播点,一个靠近中心一点,这给了我一个星球。但后来我的想法离开了我。我怎样才能获得交叉点等但主要是:对于没有任何花哨侧面的单一形状解决方案,应该有一个我不知道的算法?那么任何想法/解决方案?
染色的例子我的意思是(我只需要中央/主要形状的课程): related stains on Google-Images
答案 0 :(得分:3)
您可以通过制作Bézier曲线来优化星形逼近。如果你看一下搜索的例子,你基本上会看到两种飞溅模式:细小的尖刺和更大的水滴形状。
我们可以在圆圈上随机分散飞溅并确定飞溅长度。然后我们根据该长度决定要绘制哪种形状。我们需要的控制点是:
下面的代码尝试对其进行建模。功能splash
返回以原点为中心的飞溅坐标列表。对于3*n + 1
Bézier段的闭合曲线,该列表具有n
个点。 (n
是随机确定的。)
代码远非完美,也有太多辅助内容,可以改进,但可能会给你一个想法:
var rnd = {
uniform: function(n) {
return Math.floor(n * Math.random());
},
range: function(from, to) {
return from + this.uniform(to - from);
},
float: function(from, to) {
return from + (to - from) * Math.random();
}
}
var coord = {
radiants: function(x) {
return Math.PI * x / 180.0;
},
degrees: function(x) {
return 180.0 * x / Math.PI;
},
cartesian: function(P) {
return {
x: P.r * Math.cos(P.phi),
y: P.r * Math.sin(P.phi)
}
},
mid: function(P, Q, a) {
if (!a) a = 0.5;
return {
x: (1 - a) * P.x + a * Q.x,
y: (1 - a) * P.y + a * Q.y
};
},
normal: function(P, len) {
if (!len) len = 1;
var l = Math.sqrt(P.x*P.x + P.y*P.y);
return {
x: len * P.y / l,
y: -len * P.x / l
};
},
add: function(P, Q) {
return {
x: P.x + Q.x,
y: P.y + Q.y
};
},
mul: function(P, a) {
return {
x: a * P.x,
y: a * P.y
};
},
dist: function(P, Q) {
var dx = P.x - Q.x;
var dy = P.y - Q.y;
var l = Math.sqrt(dx*dx + dy*dy);
},
normalize: function(P, len) {
if (!len) len = 1;
var l = Math.sqrt(P.x*P.x + P.y*P.y);
return {
x: len * P.x / l,
y: len * P.y / l
};
}
}
function get(param, value, dflt) {
if (value in param) return param[value];
return dflt;
}
function splash(param) {
var r = get(param, "r", 10);
var minangle = get(param, "minangle", 5);
var maxangle = get(param, "maxangle", 30);
var ratio = get(param, "ratio", 2.4);
var n = get(param, "n", 2);
var radial = [];
var phi = 0;
while (phi < 2 * Math.PI) {
radial.push({
phi: phi,
r: r * (1 + (ratio - 1) * Math.pow(Math.random(), n))
});
phi += coord.radiants(rnd.float(minangle, maxangle, 30));
}
var phi0 = coord.radiants(rnd.float(0, 10));
for (var i = 0; i < radial.length; i++) {
var rr = radial[i];
rr.phi = 2 * rr.phi * Math.PI / phi + phi0;
}
var res = [];
var prev = radial[radial.length - 1];
var curr = radial[0];
var C = {x: 0, y: 0};
for (var i = 0; i < radial.length; i++) {
var next = radial[(i + 1) % radial.length];
var ML = coord.cartesian(prev);
var MR = coord.cartesian(next);
var M = coord.cartesian(curr);
var L = coord.mid(C, coord.mid(ML, M));
var R = coord.mid(C, coord.mid(MR, M));
if (i == 0) res.push(L);
var dphi = (next.phi - prev.phi);
if (dphi < 0) dphi += 2 * Math.PI;
var dr = 0.5 * r * dphi;
var NL = coord.normal(L, -dr * rnd.float(0.3, 0.45));
res.push(coord.add(L, NL));
console.log((curr.r - r) / (ratio - 1));
if (Math.random() > (curr.r - r) / r / (ratio - 1)) {
// little splash
var MM = coord.mid(C, M, rnd.float(0.75, 0.95));
res.push(MM);
res.push(M);
res.push(MM);
} else {
// drop-shaped splash
var s = dr * rnd.float(0.2, 0.5);
var t = dr * rnd.float(0.02, 0.2);
var MM = coord.mid(coord.mid(L, M), coord.mid(R, M));
var Mpos = coord.normalize(M, s);
var Mneg = coord.normalize(M, -s);
var MT = coord.add(M, Mpos);
var NML = coord.normal(M, s);
var NLL = coord.normal(M, t);
var MML = coord.add(MM, NLL);
var ML = coord.add(M, NML);
var NMR = coord.normal(M, -s);
var NRR = coord.normal(M, -t);
var MMR = coord.add(MM, NRR);
var MR = coord.add(M, NMR);
res.push(coord.mid(C, MML, 0.8));
res.push(MML);
res.push(coord.mid(C, MML, 1.25));
res.push(coord.add(ML, coord.mul(Mneg, 0.55)));
res.push(ML);
res.push(coord.add(ML, coord.mul(Mpos, 0.55)));
res.push(coord.add(MT, coord.mul(NML, 0.55)));
res.push(MT);
res.push(coord.add(MT, coord.mul(NMR, 0.55)));
res.push(coord.add(MR, coord.mul(Mpos, 0.55)));
res.push(MR);
res.push(coord.add(MR, coord.mul(Mneg, 0.55)));
res.push(coord.mid(C, MMR, 1.25));
res.push(MMR);
res.push(coord.mid(C, MMR, 0.8));
}
var NR = coord.normal(R, dr * rnd.float(0.3, 0.45));
res.push(coord.add(R, NR));
res.push(R);
prev = curr;
curr = next;
}
return res;
}
以及如何使用该代码的示例:
window.onload = function() {
var cv = document.getElementById("plot");
var cx = cv.getContext("2d");
var p = splash({
r: 100,
ratio: 1.6,
n: 1
});
cx.fillStyle = "tomato";
cx.translate(300, 300);
cx.beginPath();
cx.moveTo(p[0].x, p[0].y);
for (var i = 1; i < p.length; i++) {
var p1 = p[i++];
var p2 = p[i++];
var p3 = p[i];
cx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
}
cx.closePath();
cx.fill();
}