按距另一点的距离沿弧线移动一个点
Moving a Point Along an Arc by Distance from another Point
我有一个180°的弧度。我有两个点(V3 和 V4)按弧长沿弧绘制。如何将 V4 的位置设置为相对于它与 V3 而不是 V1 的距离?我不想使用 angular 距离,而是 x 和 y 中的距离。我仍然希望 V4 沿着同一弧线移动,但我希望能够拖动一个滑块,其中正值向 V2 移动,负值向 V1 移动,具体取决于 V3 的位置。
任何帮助都会很棒。谢谢!
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var percentSlider = document.getElementById("percentSlider");
var distanceSlider = document.getElementById("distanceSlider");
var pReadout = document.querySelector(".readout.percent");
var dReadout = document.querySelector(".readout.distance");
var distance = document.getElementById("distance");
class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
function drawDot(v, txt, color) {
ctx.beginPath();
r = 4;
ctx.arc(v.x, v.y, r, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fillStyle = color;
ctx.fill();
ctx.font = '10px sans-serif';
ctx.fillText(txt, v.x - 6, v.y - 12);
}
function clamp(n, minn, maxn) {
return Math.max(Math.min(maxn, n), minn);
}
function PosByPercent(cx, cy, d, radius, perc) {
angle = perc * Math.PI;
let newX = cx + radius * Math.cos(angle);
let newY = cy - radius * Math.sin(angle);
return new Vector(newX, newY)
}
function PosByDistance(cx, cy, d, radius) {
a = 1 - Math.pow(d / radius, 2) / 2;
a = clamp(a, -1, 1);
angle = Math.acos(a);
let newX = cx + radius * Math.cos(angle);
let newY = cy - radius * Math.sin(angle);
return new Vector(newX, newY)
}
function drawAll() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let angle = 0;
let d = distanceSlider.value;
let p = percentSlider.value;
let cx = canvas.width / 2;
let cy = canvas.height / 2;
let radius = 70;
v1 = new Vector(cx + radius, cy);
v2 = new Vector(cx - radius, cy);
v3 = PosByPercent(cx, cy, d, radius, p);
v4 = PosByDistance(cx, cy, d, radius, p);
ctx.beginPath();
ctx.arc(cx, cy, radius, Math.PI, 0, false);
ctx.lineWidth = 3;
ctx.strokeStyle = "#3f3f3f";
ctx.stroke();
drawDot(v1, "V1", "#40e4ff");
drawDot(v2, "V2", "#40e4ff");
drawDot(v3, "V3", "#FF0000");
drawDot(v4, "V4", "#00ff48");
}
function getDistances() {
let a = v4.x - v3.x;
let b = v4.y - v3.y;
let c = Math.sqrt(a * a + b * b);
distance.innerHTML = "<div><span>dist - v3 & v4: </span>" + parseFloat(c).toFixed(1) + "</div>";
}
var v1, v2, v3, v4;
drawAll();
getDistances();
dReadout.innerHTML = distanceSlider.value;
pReadout.innerHTML = percentSlider.value;
percentSlider.oninput = function() {
pReadout.innerHTML = percentSlider.value;
drawAll();
getDistances();
}
distanceSlider.oninput = function() {
dReadout.innerHTML = distanceSlider.value;
drawAll();
getDistances();
}
body {
margin: 0px;
font-family: sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#canvas {
background-color: #2b2b2b;
border: 1px solid rgba(0, 0, 0, 0.2);
width: 200px;
height: 200px;
margin-bottom: 4px;
}
#controls {
background-color: white;
position: fixed;
top: 0;
right: 0;
padding: 10px;
width: 200px;
box-sizing: border-box;
}
.row {
display: block;
}
label {
display: block;
text-align: left;
}
label span {
font-size: 10px;
}
.readout {
width: 80px;
font-size: 12px;
text-align: left;
padding-left: 2px;
box-sizing: border-box;
}
#myRange {
width: 130px;
}
#distance {
font-size: 13px;
position: fixed;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
text-align: left;
top: 0;
left: 0;
background: #333;
color: white;
padding: 10px;
}
#distance span {
color: rgba(255, 255, 255, 0.6);
}
<canvas id="canvas" width=200 height=200></canvas>
<div id="controls">
<div class="row" id="perc">
<label for="percentSlider">V3 <span>(%)</span></label>
<input type="range" step="0.01" min="0" max="1" value="0.75" class="slider" id="percentSlider">
<span class="readout percent"></span>
</div>
<div class="row" id="dist">
<label for="distanceSlider">V4 <span>(dist from V1)</span></label>
<input type="range" step="0.1" min="0" max="360" value="0" class="slider" id="distanceSlider">
<span class="readout distance"></span>
</div>
</div>
<div id="distance"></div>
*** 编辑 ***
我最初将 V3 设置为 Math.PI 的百分比(如上面的代码片段所示),但通过使用 arc_length/radius.
设置两个点的位置得到了预期的结果
当您计算 v3
位置时,您知道它的 angle
作为中间结果。
记住这个角度并将其用于计算 v4
位置,如下所示:
function PosByDistanceFromV3(cx, cy, d, radius, v3angle) {
a = 1 - Math.pow(d / radius, 2) / 2;
a = clamp(a, -1, 1);
angle = Math.acos(a) + v3angle;
angle = clamp(angle, 0, Math.PI);
let newX = cx + radius * Math.cos(angle);
let newY = cy - radius * Math.sin(angle);
return new Vector(newX, newY)
}
我能够通过使用 arc_length 和半径获取角度来获得正确的位置:
function PosByDistance(cx, cy, arc_length, radius)
{
angle = arc_length / radius;
angle = clamp(angle,0,Math.PI); //limits the dots to the arc
newX = cx + radius * Math.cos(angle);
newY = cy - radius * Math.sin(angle);
return new Vector(newX, newY);
}
我有一个180°的弧度。我有两个点(V3 和 V4)按弧长沿弧绘制。如何将 V4 的位置设置为相对于它与 V3 而不是 V1 的距离?我不想使用 angular 距离,而是 x 和 y 中的距离。我仍然希望 V4 沿着同一弧线移动,但我希望能够拖动一个滑块,其中正值向 V2 移动,负值向 V1 移动,具体取决于 V3 的位置。
任何帮助都会很棒。谢谢!
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var percentSlider = document.getElementById("percentSlider");
var distanceSlider = document.getElementById("distanceSlider");
var pReadout = document.querySelector(".readout.percent");
var dReadout = document.querySelector(".readout.distance");
var distance = document.getElementById("distance");
class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
function drawDot(v, txt, color) {
ctx.beginPath();
r = 4;
ctx.arc(v.x, v.y, r, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fillStyle = color;
ctx.fill();
ctx.font = '10px sans-serif';
ctx.fillText(txt, v.x - 6, v.y - 12);
}
function clamp(n, minn, maxn) {
return Math.max(Math.min(maxn, n), minn);
}
function PosByPercent(cx, cy, d, radius, perc) {
angle = perc * Math.PI;
let newX = cx + radius * Math.cos(angle);
let newY = cy - radius * Math.sin(angle);
return new Vector(newX, newY)
}
function PosByDistance(cx, cy, d, radius) {
a = 1 - Math.pow(d / radius, 2) / 2;
a = clamp(a, -1, 1);
angle = Math.acos(a);
let newX = cx + radius * Math.cos(angle);
let newY = cy - radius * Math.sin(angle);
return new Vector(newX, newY)
}
function drawAll() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let angle = 0;
let d = distanceSlider.value;
let p = percentSlider.value;
let cx = canvas.width / 2;
let cy = canvas.height / 2;
let radius = 70;
v1 = new Vector(cx + radius, cy);
v2 = new Vector(cx - radius, cy);
v3 = PosByPercent(cx, cy, d, radius, p);
v4 = PosByDistance(cx, cy, d, radius, p);
ctx.beginPath();
ctx.arc(cx, cy, radius, Math.PI, 0, false);
ctx.lineWidth = 3;
ctx.strokeStyle = "#3f3f3f";
ctx.stroke();
drawDot(v1, "V1", "#40e4ff");
drawDot(v2, "V2", "#40e4ff");
drawDot(v3, "V3", "#FF0000");
drawDot(v4, "V4", "#00ff48");
}
function getDistances() {
let a = v4.x - v3.x;
let b = v4.y - v3.y;
let c = Math.sqrt(a * a + b * b);
distance.innerHTML = "<div><span>dist - v3 & v4: </span>" + parseFloat(c).toFixed(1) + "</div>";
}
var v1, v2, v3, v4;
drawAll();
getDistances();
dReadout.innerHTML = distanceSlider.value;
pReadout.innerHTML = percentSlider.value;
percentSlider.oninput = function() {
pReadout.innerHTML = percentSlider.value;
drawAll();
getDistances();
}
distanceSlider.oninput = function() {
dReadout.innerHTML = distanceSlider.value;
drawAll();
getDistances();
}
body {
margin: 0px;
font-family: sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#canvas {
background-color: #2b2b2b;
border: 1px solid rgba(0, 0, 0, 0.2);
width: 200px;
height: 200px;
margin-bottom: 4px;
}
#controls {
background-color: white;
position: fixed;
top: 0;
right: 0;
padding: 10px;
width: 200px;
box-sizing: border-box;
}
.row {
display: block;
}
label {
display: block;
text-align: left;
}
label span {
font-size: 10px;
}
.readout {
width: 80px;
font-size: 12px;
text-align: left;
padding-left: 2px;
box-sizing: border-box;
}
#myRange {
width: 130px;
}
#distance {
font-size: 13px;
position: fixed;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
text-align: left;
top: 0;
left: 0;
background: #333;
color: white;
padding: 10px;
}
#distance span {
color: rgba(255, 255, 255, 0.6);
}
<canvas id="canvas" width=200 height=200></canvas>
<div id="controls">
<div class="row" id="perc">
<label for="percentSlider">V3 <span>(%)</span></label>
<input type="range" step="0.01" min="0" max="1" value="0.75" class="slider" id="percentSlider">
<span class="readout percent"></span>
</div>
<div class="row" id="dist">
<label for="distanceSlider">V4 <span>(dist from V1)</span></label>
<input type="range" step="0.1" min="0" max="360" value="0" class="slider" id="distanceSlider">
<span class="readout distance"></span>
</div>
</div>
<div id="distance"></div>
*** 编辑 *** 我最初将 V3 设置为 Math.PI 的百分比(如上面的代码片段所示),但通过使用 arc_length/radius.
设置两个点的位置得到了预期的结果当您计算 v3
位置时,您知道它的 angle
作为中间结果。
记住这个角度并将其用于计算 v4
位置,如下所示:
function PosByDistanceFromV3(cx, cy, d, radius, v3angle) {
a = 1 - Math.pow(d / radius, 2) / 2;
a = clamp(a, -1, 1);
angle = Math.acos(a) + v3angle;
angle = clamp(angle, 0, Math.PI);
let newX = cx + radius * Math.cos(angle);
let newY = cy - radius * Math.sin(angle);
return new Vector(newX, newY)
}
我能够通过使用 arc_length 和半径获取角度来获得正确的位置:
function PosByDistance(cx, cy, arc_length, radius)
{
angle = arc_length / radius;
angle = clamp(angle,0,Math.PI); //limits the dots to the arc
newX = cx + radius * Math.cos(angle);
newY = cy - radius * Math.sin(angle);
return new Vector(newX, newY);
}