D3.js 在矩形中填充预定义的圆
我尝试了一个d3.js代码,但这段代码适用于随机半径,但根据算法或我希望它的工作方式,是使用预定义的半径,如10,15,20,30。。。我怎样才能做到这一点。请帮忙 当我检查代码使用的D3.js 在矩形中填充预定义的圆,d3.js,D3.js,我尝试了一个d3.js代码,但这段代码适用于随机半径,但根据算法或我希望它的工作方式,是使用预定义的半径,如10,15,20,30。。。我怎样才能做到这一点。请帮忙 当我检查代码使用的半径时,它们不是预定义的半径,而是随机生成的半径或半径,用于减少矩形面积 // =========================== // ancillary geometric classes // =========================== var Point = function(x, y) {
半径
时,它们不是预定义的半径
,而是随机生成的半径
或半径
,用于减少矩形面积
// ===========================
// ancillary geometric classes
// ===========================
var Point = function(x, y) {
this.x = x;
this.y = y;
};
Point.prototype = {
dist: function(p) {
return this.vect(p).norm();
},
vect: function(p) {
return new Point(p.x - this.x, p.y - this.y);
},
norm: function(p) {
return Math.sqrt(this.x * this.x + this.y * this.y);
},
add: function(v) {
return new Point(this.x + v.x, this.y + v.y);
},
mult: function(a) {
return new Point(this.x * a, this.y * a);
}
};
var Circle = function(radius, center) {
this.r = radius;
this.c = center;
};
Circle.prototype = {
surface: function() {
return Math.PI * this.r * this.r;
},
distance: function(circle) {
return this.c.dist(circle.c) - this.r - circle.r;
}
};
// =========================
// circle packer lives here!
// =========================
var Packer = function(circles, ratio) {
this.circles = circles;
this.ratio = ratio || 1;
this.list = this.solve();
};
Packer.prototype = {
// try to fit all circles into a rectangle of a given surface
compute: function(surface) {
// check if a circle is inside our rectangle
function in_rect(radius, center) {
if (center.x - radius < -w / 2) return false;
if (center.x + radius > w / 2) return false;
if (center.y - radius < -h / 2) return false;
if (center.y + radius > h / 2) return false;
return true;
}
// approximate a segment with an "infinite" radius circle
function bounding_circle(x0, y0, x1, y1) {
var xm = Math.abs((x1 - x0) * w);
var ym = Math.abs((y1 - y0) * h);
var m = xm > ym ? xm : ym;
var theta = Math.asin(m / 4 / bounding_r);
var r = bounding_r * Math.cos(theta);
return new Circle(
bounding_r,
new Point(
(r * (y0 - y1)) / 2 + ((x0 + x1) * w) / 4,
(r * (x1 - x0)) / 2 + ((y0 + y1) * h) / 4
)
);
}
// return the corner placements for two circles
function corner(radius, c1, c2) {
var u = c1.c.vect(c2.c); // c1 to c2 vector
var A = u.norm();
if (A == 0) return []; // same centers
u = u.mult(1 / A); // c1 to c2 unary vector
// compute c1 and c2 intersection coordinates in (u,v) base
var B = c1.r + radius;
var C = c2.r + radius;
if (A > B + C) return []; // too far apart
var x = (A + (B * B - C * C) / A) / 2;
var y = Math.sqrt(B * B - x * x);
var base = c1.c.add(u.mult(x));
var res = [];
var p1 = new Point(base.x - u.y * y, base.y + u.x * y);
var p2 = new Point(base.x + u.y * y, base.y - u.x * y);
if (in_rect(radius, p1)) res.push(new Circle(radius, p1));
if (in_rect(radius, p2)) res.push(new Circle(radius, p2));
return res;
}
/////////////////////////////////////////////////////////////////
// deduce starting dimensions from surface
var bounding_r = Math.sqrt(surface) * 100; // "infinite" radius
var w = (this.w = Math.sqrt(surface * this.ratio));
var h = (this.h = this.w / this.ratio);
// place our bounding circles
var placed = [
bounding_circle(1, 1, 1, -1),
bounding_circle(1, -1, -1, -1),
bounding_circle(-1, -1, -1, 1),
bounding_circle(-1, 1, 1, 1)
];
// Initialize our rectangles list
var unplaced = this.circles.slice(0); // clones the array
while (unplaced.length > 0) {
// compute all possible placements of the unplaced circles
var lambda = {};
var circle = {};
for (var i = 0; i != unplaced.length; i++) {
var lambda_min = 1e10;
lambda[i] = -1e10;
// match current circle against all possible pairs of placed circles
for (var j = 0; j < placed.length; j++)
for (var k = j + 1; k < placed.length; k++) {
// find corner placement
if (k > 3) {
zog = 1;
}
var corners = corner(unplaced[i], placed[j], placed[k]);
// check each placement
for (var c = 0; c != corners.length; c++) {
// check for overlap and compute min distance
var d_min = 1e10;
for (var l = 0; l != placed.length; l++) {
// skip the two circles used for the placement
if (l == j || l == k) continue;
// compute distance from current circle
var d = placed[l].distance(corners[c]);
if (d < 0) break; // circles overlap
if (d < d_min) d_min = d;
}
if (l == placed.length) {
// no overlap
if (d_min < lambda_min) {
lambda_min = d_min;
lambda[i] = 1 - d_min / unplaced[i];
circle[i] = corners[c];
}
}
}
}
}
// select the circle with maximal gain
var lambda_max = -1e10;
var i_max = -1;
for (var i = 0; i != unplaced.length; i++) {
if (lambda[i] > lambda_max) {
lambda_max = lambda[i];
i_max = i;
}
}
// failure if no circle fits
if (i_max == -1) break;
// place the selected circle
unplaced.splice(i_max, 1);
placed.push(circle[i_max]);
}
// return all placed circles except the four bounding circles
this.tmp_bounds = placed.splice(0, 4);
return placed;
},
// find the smallest rectangle to fit all circles
solve: function() {
// compute total surface of the circles
var surface = 0;
for (var i = 0; i != this.circles.length; i++) {
surface += Math.PI * Math.pow(this.circles[i], 2);
}
// set a suitable precision
var limit = surface / 1000;
var step = surface / 2;
var res = [];
while (step > limit) {
var placement = this.compute.call(this, surface);
console.log(
"placed",
placement.length,
"out of",
this.circles.length,
"for surface",
surface
);
if (placement.length != this.circles.length) {
surface += step;
} else {
res = placement;
this.bounds = this.tmp_bounds;
surface -= step;
}
step /= 2;
}
return res;
}
};
// ====
// demo
// ====
function draw_result(packer) {
function draw_circle(circle) {
ctx.beginPath();
ctx.arc(
(circle.c.x + dx) * zoom + mx,
(circle.c.y + dy) * zoom + my,
circle.r * zoom,
0,
2 * Math.PI
);
m = circle.r;
//console.log("radius",m);
ctx.closePath();
ctx.stroke();
}
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
canvas.width += 0; // clear canvas
var margin_factor = 0.1;
var mx = (canvas.width * margin_factor) / 2;
var my = (canvas.height * margin_factor) / 2;
var dx = packer.w / 2;
var dy = packer.h / 2;
var zx = (canvas.width * (1 - margin_factor)) / packer.w;
var zy = (canvas.height * (1 - margin_factor)) / packer.h;
var zoom = zx < zy ? zx : zy;
// draw all circles
ctx.strokeStyle = "black";
for (var i = 0; i != packer.list.length; i++) draw_circle(packer.list[i]);
v = packer.list;
console.log("circles", v);
// draw bounding circles
ctx.strokeStyle = "red";
for (var i = 0; i != packer.bounds.length; i++) draw_circle(packer.bounds[i]);
// draw rectangle
ctx.strokeStyle = "orange";
ctx.beginPath();
ctx.rect(
(-packer.w / 2 + dx) * zoom + mx,
(-packer.h / 2 + dy) * zoom + my,
packer.w * zoom,
packer.h * zoom
);
let a = packer.w * zoom;
let b = packer.h * zoom;
console.log("width", a, "length", b);
ctx.closePath();
ctx.stroke();
}
function draw() {
var circles = parseInt(document.getElementById("c").value);
var ratio = parseFloat(document.getElementById("r").value);
var min_r = parseInt(document.getElementById("a").value);
var max_r = parseInt(document.getElementById("b").value);
var radiuses = [];
for (var i = 0; i != circles; i++)
radiuses.push(Math.random() * (max_r - min_r) + min_r);
var packer = new Packer(radiuses, ratio);
draw_result(packer);
}
window.onload = draw;
//===========================
//辅助几何类
// ===========================
变量点=函数(x,y){
这个.x=x;
这个。y=y;
};
Point.prototype={
地区:功能(p){
返回这个.vect(p.norm();
},
向量:函数(p){
返回新点(p.x-this.x,p.y-this.y);
},
常模:函数(p){
返回Math.sqrt(this.x*this.x+this.y*this.y);
},
加:功能(五){
返回新点(this.x+v.x,this.y+v.y);
},
mult:功能(a){
返回新点(this.x*a,this.y*a);
}
};
变量圆=函数(半径、中心){
r=半径;
c=中心;
};
圆圈。原型={
曲面:函数(){
返回Math.PI*this.r*this.r;
},
距离:功能(圆){
返回this.c.dist(circle.c)-this.r-circle.r;
}
};
// =========================
//圈包装工住在这里!
// =========================
var封隔器=功能(圆圈、比率){
这个圆=圆;
这个比率=比率| | 1;
this.list=this.solve();
};
Packer.prototype={
//尝试将所有圆拟合到给定曲面的矩形中
计算:函数(曲面){
//检查矩形内是否有一个圆
直线函数(半径、中心){
如果(中心x-半径<-w/2)返回false;
如果(中心x+半径>w/2)返回false;
如果(中心y-半径<-h/2)返回false;
如果(中心y+半径>h/2)返回false;
返回true;
}
//用“无限”半径圆近似一段
函数边界_圆(x0,y0,x1,y1){
var xm=数学绝对值((x1-x0)*w);
var ym=数学绝对值((y1-y0)*h);
var m=xm>ym?xm:ym;
varθ=数学asin(m/4/边界);
var r=边界_r*数学cos(θ);
回归新的圈子(
边界,
新点(
(r*(y0-y1))/2+((x0+x1)*w)/4,
(r*(x1-x0))/2+((y0+y1)*h)/4
)
);
}
//返回两个圆的拐角位置
功能角(半径,c1,c2){
var u=c1.c.vect(c2.c);//c1到c2向量
var A=u.norm();
如果(A==0)返回[];//相同的中心
u=u.mult(1/A);//c1到c2一元向量
//计算(u,v)基准中的c1和c2交点坐标
var B=c1.r+半径;
var C=c2.r+半径;
如果(A>B+C)返回[];//距离太远
VarX=(A+(B*B-C*C)/A)/2;
变量y=数学sqrt(B*B-x*x);
var base=c1.c.add(u.mult(x));
var-res=[];
变量p1=新点(基准x-u.y*y,基准y+u.x*y);
变量p2=新点(基准x+u.y*y,基准y-u.x*y);
if(in_rect(radius,p1))res.push(新圆(radius,p1));
if(in_rect(radius,p2))res.push(新圆(radius,p2));
返回res;
}
/////////////////////////////////////////////////////////////////
//从曲面推断起始尺寸
var bounding_r=数学sqrt(曲面)*100;/“无限”半径
var w=(this.w=Math.sqrt(surface*this.ratio));
var h=(this.h=this.w/this.ratio);
//放置我们的边界圆
放置的变量=[
边界圆(1,1,1,-1),
边界圆(1,-1,-1,-1),
边界圆(-1,-1,-1,1),
边界圆(-1,1,1,1)
];
//初始化我们的矩形列表
var unplace=this.circles.slice(0);//克隆数组
while(未放置。长度>0){
//计算未放置圆的所有可能位置
var lambda={};
var循环={};
对于(变量i=0;i!=unplaced.length;i++){
var lambda_min=1e10;
lambda[i]=-1e10;
//将当前圆与所有可能放置的圆对进行匹配
对于(var j=0;j3){
zog=1;
}
var角点=角点(未放置[i],放置[j],放置[k]);
//检查每个位置
对于(var c=0;c!=corners.length;c++){
//检查重叠并计算最小距离
var d_min=1e10;
for(var l=0;l!=placed.length;l++){
//跳过用于放置的两个圆
如果(l==j | l==k)继续;
//计算距当前圆的距离
var d=放置的[l]。距离(角[c]);
如果(d<0)中断;//圆圈重叠
如果(dλ_max){
lambda_max=lambda[i];
i_max=i;
}
}
//如果没有适合的圆,则失败
如果(i_max==-1)中断;
//放置选定的圆
未定位拼接(i_max,1);
放置。推(圈[i_max]);
}
//返回除四个边界圆以外的所有放置圆
this.tmp_界限=放置的拼接(0,4);
退货;
},
//找到适合的最小矩形
function draw() {
var circles = 10; //parseInt (document.getElementById('c').value);
var ratio = 1; //parseFloat(document.getElementById('r').value);
var min_r = 1; //parseInt (document.getElementById('a').value);
var max_r = 10; //parseInt (document.getElementById('b').value);
console.log(circles, ratio, min_r, max_r);
var radiuses = [];
// for (var i = 0 ; i != circles ; i++)
// radiuses.push (Math.random() * (max_r-min_r) + min_r);
radiuses = [
3.9583054608672303,
5.108187765437406,
5.8970449014304505,
6.176802256787828,
2.875371765313655,
8.017411283036802,
8.751854615569854,
2.7833816354701018,
2.408464192175587,
3.8428318045023144
];
var packer = new Packer(radiuses, ratio);
draw_result(packer);
}