Допустим, у меня есть пятиугольник (и мы нумеруем стороны, как движемся вокруг часов):
Как вычислить положение точки в этих точках, начиная с центра многоугольника (вдоль линии края многоугольника):
Знание того, как вычислить координаты x/y относительно центра, будет означать, что я могу строить точки вдоль отрезков прямых линий произвольного многоугольника (скажем, от 3 до 20 сторон). Я очень долго обдумываю, как это сделать, не говоря уже о том, чтобы заставить это работать в коде. Не имеет значения, на каком языке он написан, но желательно на JavaScript/TypeScript или Python или C (или псевдокод, говорящий сам за себя).
Вот комбинация того, что я придумал. Компоновка многоугольников работает правильно, но позиционирование точек не работает. Как вы расположите эти 3 пункта?
const ANGLE = -Math.PI / 2 // Start the first vertex at the top center
function computePolygonPoints({
width,
height,
sides,
strokeWidth = 0,
rotation = 0,
}) {
const centerX = width / 2 + strokeWidth / 2
const centerY = height / 2 + strokeWidth / 2
const radiusX = width / 2 - strokeWidth / 2
const radiusY = height / 2 - strokeWidth / 2
const offsetX = strokeWidth / 2
const offsetY = strokeWidth / 2
const rotationRad = (rotation * Math.PI) / 180
const points = Array.from({ length: sides }, (_, i) => {
const angle = (i * 2 * Math.PI) / sides + ANGLE
const x = centerX + radiusX * Math.cos(angle)
const y = centerY + radiusY * Math.sin(angle)
// Apply rotation around the center
const rotatedX =
centerX +
(x - centerX) * Math.cos(rotationRad) -
(y - centerY) * Math.sin(rotationRad)
const rotatedY =
centerY +
(x - centerX) * Math.sin(rotationRad) +
(y - centerY) * Math.cos(rotationRad)
return { x: rotatedX, y: rotatedY }
})
const minX = Math.min(...points.map(p => p.x))
const minY = Math.min(...points.map(p => p.y))
const adjustedPoints = points.map(p => ({
x: offsetX + p.x - minX,
y: offsetY + p.y - minY,
}))
return adjustedPoints
}
function vertexCoordinates(n, R, vertexIndex) {
const angle = 2 * Math.PI * vertexIndex / n - Math.PI / 2; // Adjusting to start from the top
return {
x: R * Math.cos(angle),
y: R * Math.sin(angle),
}
}
function midpointCoordinates(x1, y1, x2) {
return {
x: (x1 + x2.x) / 2,
y: (y1 + x2.y) / 2,
}
}
function fractionalPoint(x1, y1, x2, fraction) {
return {
x: x1 + fraction * (x2.x - x1),
y: y1 + fraction * (x2.y - y1),
}
}
const pentagonPoints = computePolygonPoints({ width: 300, height: 300, sides: 5 })
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", 300)
svg.setAttribute("height", 300);
const pentagon = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
pentagon.setAttribute('fill', 'cyan')
pentagon.setAttribute('points', pentagonPoints
.map((p) => `${p.x},${p.y}`)
.join(" "))
svg.appendChild(pentagon)
document.body.appendChild(svg);
const n = 5 // Number of sides for a pentagon
const width = 300; // Width of the pentagon
const R = width / (2 * Math.cos(Math.PI / n)); // Radius of the circumscribed circle
const centerX = 150; // Center of the canvas
const centerY = 150;
// Vertex between sides 2 and 3
const vertex23 = vertexCoordinates(n, R, 2)
const vertex23Adjusted = {
x: centerX + vertex23.x, // subtract radius too?
y: centerY + vertex23.y
};
console.info('Vertex between sides 2 and 3:', vertex23Adjusted)
const circle23 = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle23.setAttribute('fill', 'magenta')
circle23.setAttribute('r', 16)
circle23.setAttribute('cx', vertex23Adjusted.x)
circle23.setAttribute('cy', vertex23Adjusted.y)
svg.appendChild(circle23)
// Midpoint of side 4
const vertex4_1 = vertexCoordinates(n, R, 3)
const vertex4_2 = vertexCoordinates(n, R, 4)
const mid4 = midpointCoordinates(vertex4_1.x, vertex4_1.y, vertex4_2)
console.info('Midpoint of side 4:', mid4)
const mid4Circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
mid4Circle.setAttribute('fill', 'magenta')
mid4Circle.setAttribute('r', 16)
mid4Circle.setAttribute('cx', mid4.x)
mid4Circle.setAttribute('cy', mid4.y)
svg.appendChild(mid4Circle)
// Point 2/3 across side 3, moving clockwise
const vertex3_1 = vertexCoordinates(n, R, 2)
const vertex3_2 = vertexCoordinates(n, R, 3)
const frac3 = fractionalPoint(
vertex3_1.x,
vertex3_1.y,
vertex3_2,
2 / 3,
)
console.info('Point 2/3 across side 3:', frac3)
const frac3Circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
frac3Circle.setAttribute('fill', 'magenta')
frac3Circle.setAttribute('r', 16)
frac3Circle.setAttribute('cx', frac3.x)
frac3Circle.setAttribute('cy', frac3.y)
svg.appendChild(frac3Circle)Я хотел бы иметь возможность решить эту проблему для любого многоугольника от 3 до 20 сторон, а не только для пятиугольника.
Итак 1. В вершине между сторонами 2 и 3 (это максимальное расстояние от центра). на самом деле должно быть pentagonPoints[2] (3-я точка из полученного массива точек многоугольника). Может быть, вы пытаетесь найти точки на многоугольнике под определенными углами?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Проблема с вашими тригонометрическими вычислениями заключается в том, что описанная окружность слишком велика. Вы можете увидеть это, если добавите
<circle cx = "«centerX»" cy = "«centerY»" r = "«R»"/>
к вашей схеме:
Но вам не обязательно выполнять вычисления самостоятельно, вы можете позволить SVG сделать это за вас с помощью функции path.getPointAtLength(length). В ваших случаях
length=2length=3.5length=2.67.var path = document.querySelector("polygon");
var circle = document.querySelector("circle");
var control = document.querySelector("input[type=range]");
function init(n) {
control.setAttribute("max", n);
var d = [];
for (var i = 0; i < n; i++) {
var alpha = 2 * i * Math.PI / n;
d.push(`${Math.sin(alpha)},${-Math.cos(alpha)}`);
}
path.setAttribute("points", d.join(" "));
control.value = 0;
move();
}
function move() {
var p = path.getPointAtLength(path.getTotalLength() * control.value / control.getAttribute("max"));
circle.setAttribute("cx", p.x);
circle.setAttribute("cy", p.y);
control.nextElementSibling.textContent = control.value;
}
init(5);polygon {
fill: yellow;
stroke: black;
stroke-width: 0.01;
}
circle {
fill: red;
}<input type = "number" value = "5" onchange = "init(this.value)" />
<svg viewBox = "-1 -1 2 2">
<polygon/>
<circle r = "0.1"/>
</svg>
<input type = "range" step = "0.01" onchange = "move()" /><span></span>Я надеялся получить более абстрактные вычисления, так как я не хочу располагать их точно на краю многоугольника, а смещать их от края на определенный зазор. Думаю, я мог бы просто нарисовать еще один скрытый многоугольник... Но было бы неплохо знать, как это сделать, не полагаясь на помощники SVG. Мой код также всегда помещает плоский край внизу.
Вот ответ, я наконец понял это.
export function calculatePolygonDotPosition({
polygonRadius,
polygonSideCount,
polygonEdgeNumber,
polygonEdgePositionRatio, // from 0 to 1
gap = 0,
dotRadius,
rotation = 0,
offset = 0,
}: {
polygonRadius: number
polygonSideCount: number
polygonEdgeNumber: number
polygonEdgePositionRatio: number
gap?: number
dotRadius: number
rotation?: number
offset?: number
}) {
const n = polygonSideCount
const R = polygonRadius
const e = polygonEdgeNumber
const t = polygonEdgePositionRatio
const o = gap
const rotationAngle = (rotation * Math.PI) / 180
const V1 = rotatePoint(getPolygonVertex(e - 1, n, R), rotationAngle)
const V2 = rotatePoint(getPolygonVertex(e % n, n, R), rotationAngle) // Wrap around using modulo
// Interpolate position along the edge
const P = {
x: (1 - t) * V1.x + t * V2.x,
y: (1 - t) * V1.y + t * V2.y,
}
// Calculate the edge vector and the normal vector
const dx = V2.x - V1.x
const dy = V2.y - V1.y
const edgeLength = Math.sqrt(dx * dx + dy * dy)
// Unit normal vector (rotate by 90 degrees counter-clockwise)
const normal = {
x: -dy / edgeLength,
y: dx / edgeLength,
}
// Offset the point by the gap distance
const P_offset = {
x: P.x + (-o - dotRadius + offset) * normal.x,
y: P.y + (-o - dotRadius + offset) * normal.y,
}
return {
x: P_offset.x + R,
y: -P_offset.y + R,
}
}
// Calculate vertex positions
export function getPolygonVertex(i: number, n: number, R: number) {
const angle = (2 * Math.PI * i) / n + Math.PI / 2
return { x: R * Math.cos(angle), y: R * Math.sin(angle) }
}
function rotatePoint(
{ x, y }: { x: number; y: number },
angle: number,
) {
const cos = Math.cos(angle)
const sin = Math.sin(angle)
return {
x: x * cos - y * sin,
y: x * sin + y * cos,
}
}
Использование:
calculatePolygonDotPosition({
polygonRadius: 300,
polygonSideCount: 5,
polygonEdgeNumber: 2,
polygonEdgePositionRatio: 0.5,
gap: 4,
dotRadius: 3,
offset: 4, // strokeWidth
})
Вот 4 точки, расположенные сразу за внешним краем.
Простите, но я так и не понял, какие именно точки вы пытаетесь вычислить. У вас уже есть координаты вершин при создании пятиугольника. Таким образом, вы можете линейно интерполировать точки между всеми краями на основе начальной и конечной точки (вершины) при заданном значении t. См. код: codepen.io/herrstrietzel/pen/ZENLmGv?editors=0010