Аппроксимация окружности, представляющая собой правильный многоугольник с N углами - PullRequest
0 голосов
/ 12 февраля 2019

Мне нужно реализовать идею кругового приближения, представляющего собой правильный многоугольник с N углами, тогда как N определяется пользователем.

Например, если N = 3, у меня будет треугольник.При n = 5 я бы получил форму, которая начинает напоминать круг.Когда я увеличиваю N, я становлюсь все ближе и ближе к форме круга.

Эта идея очень похожа на то, что задавали и отвечали на следующий вопрос / решение: Рисуем правильные многоугольникивписанные в кружок , однако они использовали 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);

Моя идея, что это не сработало,пытался создать круг ("externalCircle"), который я бы скользил внутри окружности ("chair.attr (" transform "...") круга, основываясь на N, получая несколько различных (x, y) координат).Затем я передал бы координаты (x, y) в многоугольник.

Я считаю, что мой подход к этой проблеме неправильный. Кроме того, я застрял в том, что я не могу продолжать скольжениебез учета окружности и сохранения каждой отдельной (x, y) координаты. Я попытался "console.log (chair2.node (). getBBox (). x);", но он всегда хранит одну и ту же координату, которая имеет исходную точку.

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Ответ , предоставленный enxaneta , прекрасно подходит и, безусловно, является классическим подходом к этому.Однако я часто предпочитаю позволить браузеру выполнять тригонометрию, а не делать это самостоятельно.Типичные примеры включают мой ответ до «Комплексная круговая диаграмма» или один до »маркер SVG - можетЯ установил длину и угол? ".Я даже не уверен, превосходят ли они более классические, но тем не менее они мне нравятся за их простоту.


Мое решение сосредоточено на SVGGeometryElement и его методах .getTotalLength() и .getPointAtLength().Поскольку интерфейс SVGCircleElement расширяет этот интерфейс, эти методы доступны для круга SVG, имеющего следующие значения:

  1. .getTotalLength(): окружность круга.

  2. .getPointAtLength(): Точка в x- / y-координатах на окружности на заданной длине.Измерение по определению начинается в позиции 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(" "));

Slickи легко!Нет тригонометрии.

Наконец, полная рабочая демонстрация:

// 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>
0 голосов
/ 12 февраля 2019

Я упростил ваш код для ясности.Чтобы получить 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...