【问题标题】:Circle approximation that is a regular polygon with N corners圆近似,是具有 N 个角的正多边形
【发布时间】:2019-12-18 00:57:34
【问题描述】:

我必须实现一个圆近似的想法,它是一个有 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”),我将在圆的圆周(“chair.attr(“transform”...”))内滑动,基于 N , 获得几个不同的 (x,y) 坐标。 然后,我会将 (x,y) 坐标提供给多边形。

我相信我解决这个问题的方法是错误的。此外,我卡住的部分是我无法继续在圆周上滑动并存储每个不同的 (x,y) 坐标。我试过“console.log(chair2.node().getBBox().x);”但它总是存储相同的坐标,它是原点。

【问题讨论】:

标签: javascript d3.js svg


【解决方案1】:

为了清楚起见,我已经简化了您的代码。要获得圆上某个点的 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_numbervariable

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>

【讨论】:

    【解决方案2】:

    enxaneta 提供的answer 非常好,当然是解决此问题的经典方法。然而,我经常喜欢让浏览器做三角函数而不是我自己做。典型的例子包括我的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>

    【讨论】:

    • 确实很流畅,很简单!
    猜你喜欢
    • 2020-02-01
    • 1970-01-01
    • 2018-04-17
    • 1970-01-01
    • 1970-01-01
    • 2015-01-15
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多