具有N个角的正多边形的圆近似

时间:2019-02-12 05:56:33

标签: javascript d3.js svg

我必须实现圆形逼近的想法,该逼近是具有N个角的规则多边形,而N由用户定义。

例如,如果N = 3,我将有一个三角形。在n = 5的情况下,我将开始一个类似于圆形的形状。随着N的增加,我将越来越接近圆形。

这个想法与以下问题/解决方案中的询问和回答非常相似: Draw regular polygons inscribed in a circle,但是他们使用raphael.js而不是D3.js。

我试图做的事情

var vis = d3.select("body").append("svg")
         .attr("width", 1000)
         .attr("height", 667);


var svg = d3.select('svg');
var originX = 200;
var originY = 200;
var outerCircleRadius = 60;


var outerCircle = svg.append("circle").attr({
    cx: originX,
    cy: originY,
    r: outerCircleRadius,
    fill: "none",
    stroke: "black"
});

var chairWidth = 10;

var chairOriginX = originX + ((outerCircleRadius) * Math.sin(0));
var chairOriginY = originY - ((outerCircleRadius) * Math.cos(0));



var chair = svg.append("rect").attr({
    x: chairOriginX - (chairWidth / 2),
    y: chairOriginY - (chairWidth / 2),
    width: chairWidth,
    opacity: 1,
    height: 20,
    fill: "none",
    stroke: "blue"
});

var n_number = 5
var n_angles = 360/n_number
var angle_start=0;
var angle_next;

console.log(chair.node().getBBox().x);
console.log(chair.node().getBBox().y);
chair.attr("transform", "rotate(" + (angle_start+n_angles+n_angles) + ", 200, 200)");



        var circle = svg.append("circle")
                    .attr("cx", 195)
                    .attr("cy", 135)
                    .attr("r", 50)
                    .attr("fill", "red");




var chairOriginX2 = originX + ((outerCircleRadius) * Math.sin(0));
var chairOriginY2 = originY - ((outerCircleRadius) * Math.cos(0));


var chair2 = svg.append("rect").attr({
    x: chairOriginX2 - (chairWidth / 2),
    y: chairOriginY2 - (chairWidth / 2),
    width: chairWidth,
    opacity: 1,
    height: 20,
    fill: "none",
    stroke: "blue"
});

console.log(chair2.node().getBBox().x);
console.log(chair2.node().getBBox().y);

我的想法没有用,我试图创建一个圆(“ outerCircle”),我将在基于N的圆的圆周(“ chair.attr(” transform“ ...”)中滑动,获得几个不同的(x,y)坐标。 然后,我将(x,y)坐标输入到多边形。

我认为我针对此问题的方法是错误的。另外,我卡住的部分是我无法顺着圆周移动并存储每个不同的(x,y)坐标。我尝试了“ console.log(chair2.node()。getBBox()。x);”但是它总是存储相同的坐标,它是原点。

2 个答案:

答案 0 :(得分:2)

为了清楚起见,我简化了您的代码。要获取圆上某点的x,请使用Math.cos(angle),对于y,请使用Math.sin(angle)。这是你的错误。现在,您可以更改n_number

的值

var SVG_NS = 'http://www.w3.org/2000/svg';

var originX = 200;
var originY = 200;
var outerCircleRadius = 60;

var polygon = document.createElementNS(SVG_NS, 'polygon');
svg.appendChild(polygon);

let points="";

var n_number = 5;
var n_angles = 2*Math.PI/n_number
// building the value of the `points` attribute for the polygon
for(let i = 0; i < n_number; i++){
  let x = originX + outerCircleRadius * Math.cos(i*n_angles);
  let y = originY + outerCircleRadius * Math.sin(i*n_angles);
  points += ` ${x},${y} `;
}
// setting the value of the points attribute of the polygon
polygon.setAttributeNS(null,"points",points)
svg{border:1px solid;width:90vh;}

polygon{fill: none;
    stroke: blue}
<svg id="svg" viewBox = "100 100 200 200" >
  <circle cx="200" cy="200" r="60" fill="none" stroke="black" />  
</svg>

这是另一个演示,我在其中使用输入类型范围来更改n_number变量

var SVG_NS = 'http://www.w3.org/2000/svg';

var originX = 200;
var originY = 200;
var outerCircleRadius = 60;

var polygon = document.createElementNS(SVG_NS, 'polygon');
svg.appendChild(polygon);

let points="";

var n_number = 5;

setPoints(n_number);


theRange.addEventListener("input", ()=>{
  n_number = theRange.value;
  setPoints(n_number)
});


function setPoints(n_number){
var n_angles = 2*Math.PI/n_number;
  points = ""
// building the value of the `points` attribute for the polygon
for(let i = 0; i < n_number; i++){
  let x = originX + outerCircleRadius * Math.cos(i*n_angles);
  let y = originY + outerCircleRadius * Math.sin(i*n_angles);
  points += ` ${x},${y} `;
}
// setting the value of the points attribute of the polygon
polygon.setAttributeNS(null,"points",points);
}
svg{border:1px solid; width:90vh;}

polygon{fill: none;
    stroke: blue}
<p><input type="range" min="3" max="50" value="5" id="theRange" /></p>
<svg id="svg" viewBox = "100 100 200 200" >
  <circle cx="200" cy="200" r="60" fill="none" stroke="black" />  
</svg>

答案 1 :(得分:1)

answer提供的enxaneta非常好,并且无疑是经典的方法。但是,我经常喜欢让浏览器执行三角函数,而不是独自执行。典型示例包括我的answer"Complex circle diagram"one"SVG marker - can I set length and angle?"。我什至不确定它们是否胜过更经典的,但尽管如此,我还是喜欢它们的简单性。


我的解决方案集中在SVGGeometryElement及其方法.getTotalLength().getPointAtLength()上。由于SVGCircleElement接口扩展了该接口,因此这些方法可用于具有以下含义的SVG圈:

  1. .getTotalLength():圆的圆周。

  2. .getPointAtLength():圆上x- / y坐标上给定长度的点。 definition的测量从3点钟位置开始,然后顺时针进行。

基于这些解释,很明显,您可以将圆的总长度(即圆的周长)除以近似点的数量。这使您可以沿圆弧到下一个点的步距。通过汇总这些距离,您可以使用第二种方法来获取每个点的x / y坐标。

可以按照以下几行进行编码:

// Calculate step length as circumference / number of points.
const step = circleElement.getTotalLength() / count; 

// Build an array of points on the circle.
const data = Array.from({length: count}, (_, i) => {
  const point = circleElement.getPointAtLength(i * step);   // Get coordinates of next point.
  return `${point.x},${point.y}`; 
});
polygon.attr("points", data.join(" "));

光滑轻松!不涉及三角函数。

最后,一个完整的工作演示:

// Just setup, not related to problem.
const svg = d3.select("body")
  .append("svg")
    .attr("width", 500)
    .attr("height", 500);
    
const circle = svg.append("circle")
    .attr("cx", "150")
    .attr("cy", "150")
    .attr("r", "100")
    .attr("fill", "none")
    .attr("stroke", "black");
    
const polygon = svg.append("polygon")
    .attr("fill", "none")
    .attr("stroke", "blue");
    
const circleElement = circle.node();
const ranger = d3.select("#ranger").on("input", update);
const label = d3.select("label");

// This function contains all the relevant logic.
function update() {
  let count = ranger.node().value;
  label.text(count);
  // Calculate step length as circumference / number of points.
  const step = circleElement.getTotalLength() / count; 
  // Build an array of all points on the circle.
  const data = Array.from({length: count}, (_, i) => {
    const point = circleElement.getPointAtLength(i * step);   // Get coordinates of next point.
    return `${point.x},${point.y}`; 
  });
  polygon.attr("points", data.join(" "));
}

update();
<script src="https://d3js.org/d3.v5.js"></script>
<p>
  <input id="ranger" type="range" min="3" max="15" value="5">
  <label for="ranger"></label>
</p>