【问题标题】:How can I reshape polygon without change line lengths如何在不改变线长的情况下重塑多边形
【发布时间】:2018-02-08 17:37:03
【问题描述】:

我可以使用 sn-p 中的代码用鼠标重塑多边形。绘制多边形后,用户可以通过移动点来改变形状。但我想在不改变线长的情况下修改形状。点将尽可能改变,但线的长度将保持不变。

我该怎么做?

var canvas, ctx;
var canvasIsMouseDown = false;
var radius = 6;
var pointIndex = -1;

var points = [
    { x: 10, y: 10 },
    { x: 100, y: 50 },
    { x: 150, y: 100 },
    { x: 60, y: 110 },
    { x: 30, y: 160 }
];
function start() {
    canvas = document.getElementById("cnPolygon");
    ctx = canvas.getContext("2d");
    canvas.addEventListener("mousemove", canvasMouseMove);
    canvas.addEventListener("mousedown", canvasMouseDown);
    canvas.addEventListener("mouseup", canvasMouseUp);
    draw();
}
function canvasMouseMove(ev) {
if (!canvasIsMouseDown || pointIndex === -1) return;
    points[pointIndex].x = ev.pageX - this.offsetLeft;
    points[pointIndex].y = ev.pageY - this.offsetTop;
    draw();
}
function canvasMouseDown(ev) {
    canvasIsMouseDown = true;
    var x = ev.pageX - this.offsetLeft;
    var y = ev.pageY - this.offsetTop;
    pointIndex = -1;
    var dist;
    for (var i = 0; i < points.length; i++) {
        dist = Math.sqrt(Math.pow((x - points[i].x), 2) + Math.pow((y - points[i].y), 2));
        if (dist <= radius) {
            pointIndex = i;
            break;
        }
    }
}
function canvasMouseUp(ev) {
        canvasIsMouseDown = false;
}
function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);
    for (var i = 0; i < points.length; i++) {
        ctx.lineTo(points[i].x, points[i].y);
    }
    ctx.closePath();
    ctx.stroke();
    for (var i = 0; i < points.length; i++) {
        ctx.beginPath();
        ctx.arc(points[i].x, points[i].y, radius, 0, Math.PI * 2);
        ctx.stroke();
    }
}
document.addEventListener("DOMContentLoaded", start);
&lt;canvas id="cnPolygon" width="200" height="200" style="border:solid 1px silver"&gt;&lt;/canvas&gt;

【问题讨论】:

  • 你是指线宽还是线长?您在问题中使用了这两个短语。当您说线宽时,我将其理解为线宽,而线长是两点之间的距离。
  • 我的意思是行长。我更正了文字。谢谢。
  • 这是一个有趣的问题。在某些多边形中,不可能在不改变线条长度的情况下移动单个点,例如考虑三角形。在其他情况下,例如正方形,您可以同时移动两个点。我会通过使用刚体模拟器来解决这个问题,例如brm.io/matter-js
  • matterjs 看起来令人印象深刻!

标签: javascript polygon


【解决方案1】:

看看这个

var canvas, ctx;
var canvasIsMouseDown = false;
var radius = 3;
var pointIndex = -1;
var points = [
    { x: 10, y: 10 },
    { x: 100, y: 50 },
    { x: 150, y: 100 },
    { x: 60, y: 110 },
    { x: 30, y: 160 }
];

// PHYSICS START ----------------
var stiffness = 0.25 // defines how elastic the contrainst should be
var oscillations = 10 // defines how many iterations should be made, more iterations mean higher precision

function getAngle(x1,y1,x2,y2){
	return Math.atan2(y2-y1,x2-x1) + Math.PI/2
}
function getConstraintPos(tx,ty,ox,oy,dist){
	var rot = getAngle(tx,ty,ox,oy)
	var x = tx+Math.sin(rot)*dist
	var y = ty-Math.cos(rot)*dist
	return [x,y]
}
function applyContraintForce(point,pos){
	point.x += (pos[0] - point.x)*stiffness
    point.y += (pos[1] - point.y)*stiffness
}
function defineDistances(){
	for (var i = 0; i < points.length; i++) {
		var next_point = points[(i+1)%points.length]
		points[i].distance = Math.sqrt(Math.pow((next_point.x - points[i].x), 2) + Math.pow((next_point.y - points[i].y), 2))
	}
}
function updateContraints(){
	// forward pass
	for (var i=0;i<points.length;i++) 
    {
       if(i==pointIndex) continue
       	var j = (+i+1)%points.length
       	var pos = getConstraintPos(points[j].x,points[j].y,points[i].x,points[i].y,points[i].distance)
       	applyContraintForce(points[i],pos)
    }
    //backward pass
    for (var i=points.length-1;i>=0;i--) 
    {
       if(i==pointIndex) continue
       	var j = (i-1)
       	j = j<0 ? points.length+j : j
       	var pos = getConstraintPos(points[j].x,points[j].y,points[i].x,points[i].y,points[j].distance)
       	applyContraintForce(points[i],pos)
    }
}
// PHYSICS END ----------------



function start() {
    canvas = document.getElementById("cnPolygon");
    ctx = canvas.getContext("2d");
    canvas.addEventListener("mousemove", canvasMouseMove);
    canvas.addEventListener("mousedown", canvasMouseDown);
    canvas.addEventListener("mouseup", canvasMouseUp);
    defineDistances()
    draw();
}
function canvasMouseMove(ev) {
    if (!canvasIsMouseDown || pointIndex === -1) return;
    points[pointIndex].x = ev.pageX - this.offsetLeft;
    points[pointIndex].y = ev.pageY - this.offsetTop;
    for(var i=0;i<oscillations;i++){
    	updateContraints()
    }
    draw();
}
function canvasMouseDown(ev) {
    canvasIsMouseDown = true;
    var x = ev.pageX - this.offsetLeft;
    var y = ev.pageY - this.offsetTop;
    pointIndex = -1;
    var dist;
    for (var i = 0; i < points.length; i++) {
        dist = Math.abs(Math.sqrt(Math.pow((x - points[i].x), 2) + Math.pow((y - points[i].y), 2)));
        if (dist <= radius) {
            pointIndex = i;
            break;
        }
    }
}
function canvasMouseUp(ev) {
    canvasIsMouseDown = false;
}
function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);
    for (var i = 0; i < points.length; i++) {
        ctx.lineTo(points[i].x, points[i].y);
    }
    ctx.closePath();
    ctx.stroke();
    for (var i = 0; i < points.length; i++) {
        ctx.beginPath();
        ctx.arc(points[i].x, points[i].y, radius * 2, 0, Math.PI * 2);
        ctx.stroke();
    }
}

document.addEventListener("DOMContentLoaded", start);
&lt;canvas id="cnPolygon" width="500" height="300" style="border:solid 1px silver"&gt;&lt;/canvas&gt;

它的作用是计算两个给定点之间的角度,并根据该角度和距离增量施加力。施加的力乘以stiffness

这必须向前 (point A -> point B) 和向后 (point A 进行,以说明两者之间的差异最后到链中的第一个点。

注意这不是 100% 准确的。精度可以通过迭代次数来提高,但正如@bhspencer 已经指出的那样,在某些情况下这是不可能的,仅仅是因为几何。

【讨论】:

  • 这是一个很好的答案。谢谢。可能只需要进行一些更改以适应项目。我不确定是否有必要,但我可能需要一种情况,只有漂移点和相邻点的位置尽可能地变化。
猜你喜欢
  • 1970-01-01
  • 2022-01-17
  • 2021-03-16
  • 1970-01-01
  • 2018-06-08
  • 1970-01-01
  • 1970-01-01
  • 2023-01-15
  • 1970-01-01
相关资源
最近更新 更多