D3.js 在矩形中填充预定义的圆

D3.js 在矩形中填充预定义的圆,d3.js,D3.js,我尝试了一个d3.js代码,但这段代码适用于随机半径,但根据算法或我希望它的工作方式,是使用预定义的半径,如10,15,20,30。。。我怎样才能做到这一点。请帮忙 当我检查代码使用的半径时,它们不是预定义的半径,而是随机生成的半径或半径,用于减少矩形面积 // =========================== // ancillary geometric classes // =========================== var Point = function(x, y) {

我尝试了一个d3.js代码,但这段代码适用于随机半径,但根据算法或我希望它的工作方式,是使用预定义的半径,如10,15,20,30。。。我怎样才能做到这一点。请帮忙

当我检查代码使用的
半径
时,它们不是预定义的
半径
,而是随机生成的
半径
半径
,用于减少矩形面积

// ===========================
// 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);
}