计算静态线和移动线之间最近的垂直点
Calculate the closest perpendicular point between a static line and a movable line
描述
给定的是一条蓝线,可以任意放置,可以鼠标移动。
通过移动线条,从原始位置绘制渐变线(此处为浅粉色)。现在这一行有以下要求:
假设蓝线的左边缘是点 1(红圈)并且
假设蓝线原始位置的左边缘为点 2(石灰圆)
假设蓝线的右侧是点 3(绿色圆圈)
从点 1 到 point2/point3.
的角度需要为 90 度或 -90 度
我相信这个术语是:渐变的粉色线和蓝色线要垂直。
蓝线的方向是不变的,只是它的位置!
我的问题
通过移动蓝线,我可以计算出其原始位置的度数并绘制渐变的亮粉色线。然而,我无法计算出离蓝线最近的点,这将使渐变热粉色线垂直于原始蓝线和蓝线的新位置。如果有人能指出我正确的公式或正确的术语来解决这个问题,我将不胜感激。
视觉示例(已删除 代码部分)
以下是我一起删除的一个简单示例。可以移动蓝线,但我不能强制点 1 与 point2/point3.
保持一定的 90/-90 度角
//REM: Current moving element
var _currentElement = null;
//REM: Drawing for quicker access
var _Drawing = null;
//REM: Starting the drag
function _onDown(event){
if(event.target && event.target.tagName === 'line'){
let tMatrix = _getMatrix(event.target);
_currentElement = {
Element: event.target,
startX: event.clientX,
startY: event.clientY,
startE: tMatrix.e,
startF: tMatrix.f,
X1: Number(event.target.getAttribute('x1')),
Y1: Number(event.target.getAttribute('y1')),
X2: Number(event.target.getAttribute('x2')),
Y2: Number(event.target.getAttribute('y2')),
Ratio: 0.4
}
}
}
//REM: Dragging
function _onMove(event){
if(_currentElement){
_currentElement.endE = _currentElement.startE + ((event.clientX - _currentElement.startX) * _currentElement.Ratio);
_currentElement.endF = _currentElement.startF + ((event.clientY - _currentElement.startY) * _currentElement.Ratio);
console.log(
'Angle (3)',
_getAngleBetweenThreePoints(
{x: _currentElement.X1 + _currentElement.endE, y: _currentElement.Y1 + _currentElement.endF},
{x: _currentElement.X1, y: _currentElement.Y1},
{x: _currentElement.X2 + _currentElement.endE, y: _currentElement.Y2 + _currentElement.endF}
)
);
_setMatrix(_currentElement.Element, 1, 0, 0, 1, _currentElement.endE, _currentElement.endF)
}
}
//REM: Ending the drag
function _onUp(){
_currentElement = null
}
//REM: Returns the elements matrix
function _getMatrix(element){
if(element){
return element.transform.baseVal.numberOfItems ?
element.transform.baseVal.getItem(0).matrix :
element.transform.baseVal.appendItem(_Drawing.createSVGTransform()).matrix
}
}
//REM: Sets the elements matrix
function _setMatrix(element, a, b, c, d, e, f){
if(element){
let tMatrix = _getMatrix(element);
if(tMatrix){
tMatrix.a = (typeof a === 'number') ? a : tMatrix.a;
tMatrix.b = (typeof b === 'number') ? b : tMatrix.b;
tMatrix.c = (typeof c === 'number') ? c : tMatrix.c;
tMatrix.d = (typeof d === 'number') ? d : tMatrix.d;
tMatrix.e = (typeof e === 'number') ? e : tMatrix.e;
tMatrix.f = (typeof f === 'number') ? f : tMatrix.f;
element.transform.baseVal.getItem(0).setMatrix(tMatrix)
}
}
}
//REM: Transforms client-coords to svg-coords
function _getSVGCoords(clientX, clientY){
var tCTM = _Drawing.getScreenCTM();
return tCTM ? {x: (clientX - tCTM.e) / tCTM.a, y: (clientY - tCTM.f ) / tCTM.d} : {x: clientX, y: clientY}
}
//REM: Returns angle from p1 to p2 and p3
function _getAngleBetweenThreePoints(p1, p2, p3){
let tAngle = 0;
if(p1 && p2 && p3){
let tTemplate = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tTemplate.setAttribute('r', 1);
let tC1 = _Drawing.appendChild(tTemplate.cloneNode(false));
tC1.setAttribute('fill', 'red');
tC1.setAttribute('cx', p1.x);
tC1.setAttribute('cy', p1.y);
let tC2 = _Drawing.appendChild(tTemplate.cloneNode(false));
tC2.setAttribute('fill', 'lime');
tC2.setAttribute('cx', p2.x);
tC2.setAttribute('cy', p2.y);
let tC3 = _Drawing.appendChild(tTemplate.cloneNode(false));
tC3.setAttribute('fill', 'green');
tC3.setAttribute('cx', p3.x);
tC3.setAttribute('cy', p3.y);
let tLine = document.getElementById('line1');
if(!tLine){
tLine = _Drawing.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'line'));
tLine.id = 'line1';
tLine.setAttribute('stroke-dasharray', '5,5')
};
tLine.setAttribute('x1', p1.x);
tLine.setAttribute('y1', p1.y);
tLine.setAttribute('x2', p2.x);
tLine.setAttribute('y2', p2.y);
tAngle = (Math.atan2(p3.y - p1.y, p3.x - p1.x) - Math.atan2(p2.y - p1.y, p2.x - p1.x)) * 180 / Math.PI
}
return tAngle
};
//REM: Assiging events
window.onload = function(){
_Drawing = document.querySelector('svg');
document.body.addEventListener('mousedown', _onDown, false);
document.body.addEventListener('mousemove', _onMove, false);
document.body.addEventListener('mouseup', _onUp, false);
document.body.addEventListener('mouseleave', _onUp, false)
};
line,
circle{
pointer-events: none;
stroke-width: 1
}
#movable{
pointer-events: all;
stroke-width: 10
}
#line1{
stroke: hotpink
}
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '0 0 300 300'>
<line x1 = '50' y1 = '50' x2 = '160' y2 = '110' stroke = 'blue' id = 'movable'></line>
</svg>
预期结果
通过移动蓝线,我画了一条渐变的亮粉色线。该渐变线必须垂直于蓝线的原始位置以及蓝线的新位置。蓝线本身的方向可能不会改变,只是它的位置。如果线是水平的,则只允许直线向上和直线向下。如果线是垂直的,则只允许向左和向右笔直。如果该线是对角线,则仅允许沿该线对齐的直线运动(如铁路)。因此,例如,通过我的对角线蓝线并直线向上移动,我想计算离左边缘最近的位置,这将使渐变热粉色线垂直于蓝线的原始位置和蓝线的新位置.
这是一种与 Adobe PDF Measuring Tool 非常相似的行为。
要使运动垂直于蓝线,请执行以下操作:
- 获取蓝线方向的向量。
- 将其旋转 90 度(垂直于其表面)。
- 规范化,例如使其长度等于
1
(称之为 n
)。
- 现在获取鼠标的移动向量(
current - start
,称之为m
)。
- 现在计算
n * dot(n,m)
。那是你在 n
方向的运动矢量。
我制作了一个 class 可以为您提供需要放置蓝线的位置的 x、y。
class cTracker {
constructor(x1, y1, x2, y2) {
x1 = parseInt(x1);
y1 = parseInt(y1);
x2 = parseInt(x2);
y2 = parseInt(y2);
const extendLine = 10000;
let blueLineRadian = Math.atan2(x1 - x2, y1 - y2);
this.m_blueLineRadian = blueLineRadian;
let magentaLineRadian = blueLineRadian + (Math.PI / 2);
this.m_blueLineCos = Math.cos(blueLineRadian) * extendLine;
this.m_blueLineSin = Math.sin(blueLineRadian) * extendLine;
this.m_magentaLineX1 = x1 + Math.sin(magentaLineRadian) * extendLine;
this.m_magentaLineY1 = y1 + Math.cos(magentaLineRadian) * extendLine;
this.m_magentaLineX2 = x1 + Math.sin(magentaLineRadian + Math.PI) * extendLine;
this.m_magentaLineY2 = y1 + Math.cos(magentaLineRadian + Math.PI) * extendLine;
}
// -------------------------------------------------------------------------
//
// -------------------------------------------------------------------------
getCursorPosition(x, y) {
this.m_x1 = x - this.m_blueLineSin;
this.m_y1 = y - this.m_blueLineCos;
this.m_x2 = x + this.m_blueLineSin;
this.m_y2 = y + this.m_blueLineCos;
return this.intersect(this.m_magentaLineX1, this.m_magentaLineY1, this.m_magentaLineX2, this.m_magentaLineY2, this.m_x1, this.m_y1, this.m_x2, this.m_y2);
}
// -------------------------------------------------------------------------
//
// -------------------------------------------------------------------------
intersect(x1, y1, x2, y2, x3, y3, x4, y4) {
var denominator = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1));
var a = y1 - y3;
var b = x1 - x3;
var numerator1 = ((x4 - x3) * a) - ((y4 - y3) * b);
var numerator2 = ((x2 - x1) * a) - ((y2 - y1) * b);
a = numerator1 / denominator;
return {m_x : x1 + (a * (x2 - x1)), m_y : y1 + (a * (y2 - y1))};
}
}
要使用它,请用蓝线的 x1、y1、x2、y2 创建一个瞬间
let movableLine = document.getElementById('movable');
let x1 = parseInt(movableLine.getAttribute("x1"));
let y1 = parseInt(movableLine.getAttribute("y1"));
let x2 = parseInt(movableLine.getAttribute("x2"));
let y2 = parseInt(movableLine.getAttribute("y2"));
this.m_tracker = new cTracker(x1, y1, x2, y2);
要获得放置蓝线位置的 X、Y,您只需执行以下操作...
let xySVG = this._getSVGCoords(event.clientX, event.clientY);
let xy = this.m_tracker.getCursorPosition(xySVG.x, xySVG.y);
console.log(xy); // This is where the blue line needs to be placed...
这是一个有效的 fiddle:https://jsfiddle.net/syxfv63z/2/
描述
给定的是一条蓝线,可以任意放置,可以鼠标移动。 通过移动线条,从原始位置绘制渐变线(此处为浅粉色)。现在这一行有以下要求:
假设蓝线的左边缘是点 1(红圈)并且 假设蓝线原始位置的左边缘为点 2(石灰圆) 假设蓝线的右侧是点 3(绿色圆圈) 从点 1 到 point2/point3.
的角度需要为 90 度或 -90 度我相信这个术语是:渐变的粉色线和蓝色线要垂直。 蓝线的方向是不变的,只是它的位置!
我的问题
通过移动蓝线,我可以计算出其原始位置的度数并绘制渐变的亮粉色线。然而,我无法计算出离蓝线最近的点,这将使渐变热粉色线垂直于原始蓝线和蓝线的新位置。如果有人能指出我正确的公式或正确的术语来解决这个问题,我将不胜感激。
视觉示例(已删除 代码部分)
以下是我一起删除的一个简单示例。可以移动蓝线,但我不能强制点 1 与 point2/point3.
保持一定的 90/-90 度角//REM: Current moving element
var _currentElement = null;
//REM: Drawing for quicker access
var _Drawing = null;
//REM: Starting the drag
function _onDown(event){
if(event.target && event.target.tagName === 'line'){
let tMatrix = _getMatrix(event.target);
_currentElement = {
Element: event.target,
startX: event.clientX,
startY: event.clientY,
startE: tMatrix.e,
startF: tMatrix.f,
X1: Number(event.target.getAttribute('x1')),
Y1: Number(event.target.getAttribute('y1')),
X2: Number(event.target.getAttribute('x2')),
Y2: Number(event.target.getAttribute('y2')),
Ratio: 0.4
}
}
}
//REM: Dragging
function _onMove(event){
if(_currentElement){
_currentElement.endE = _currentElement.startE + ((event.clientX - _currentElement.startX) * _currentElement.Ratio);
_currentElement.endF = _currentElement.startF + ((event.clientY - _currentElement.startY) * _currentElement.Ratio);
console.log(
'Angle (3)',
_getAngleBetweenThreePoints(
{x: _currentElement.X1 + _currentElement.endE, y: _currentElement.Y1 + _currentElement.endF},
{x: _currentElement.X1, y: _currentElement.Y1},
{x: _currentElement.X2 + _currentElement.endE, y: _currentElement.Y2 + _currentElement.endF}
)
);
_setMatrix(_currentElement.Element, 1, 0, 0, 1, _currentElement.endE, _currentElement.endF)
}
}
//REM: Ending the drag
function _onUp(){
_currentElement = null
}
//REM: Returns the elements matrix
function _getMatrix(element){
if(element){
return element.transform.baseVal.numberOfItems ?
element.transform.baseVal.getItem(0).matrix :
element.transform.baseVal.appendItem(_Drawing.createSVGTransform()).matrix
}
}
//REM: Sets the elements matrix
function _setMatrix(element, a, b, c, d, e, f){
if(element){
let tMatrix = _getMatrix(element);
if(tMatrix){
tMatrix.a = (typeof a === 'number') ? a : tMatrix.a;
tMatrix.b = (typeof b === 'number') ? b : tMatrix.b;
tMatrix.c = (typeof c === 'number') ? c : tMatrix.c;
tMatrix.d = (typeof d === 'number') ? d : tMatrix.d;
tMatrix.e = (typeof e === 'number') ? e : tMatrix.e;
tMatrix.f = (typeof f === 'number') ? f : tMatrix.f;
element.transform.baseVal.getItem(0).setMatrix(tMatrix)
}
}
}
//REM: Transforms client-coords to svg-coords
function _getSVGCoords(clientX, clientY){
var tCTM = _Drawing.getScreenCTM();
return tCTM ? {x: (clientX - tCTM.e) / tCTM.a, y: (clientY - tCTM.f ) / tCTM.d} : {x: clientX, y: clientY}
}
//REM: Returns angle from p1 to p2 and p3
function _getAngleBetweenThreePoints(p1, p2, p3){
let tAngle = 0;
if(p1 && p2 && p3){
let tTemplate = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tTemplate.setAttribute('r', 1);
let tC1 = _Drawing.appendChild(tTemplate.cloneNode(false));
tC1.setAttribute('fill', 'red');
tC1.setAttribute('cx', p1.x);
tC1.setAttribute('cy', p1.y);
let tC2 = _Drawing.appendChild(tTemplate.cloneNode(false));
tC2.setAttribute('fill', 'lime');
tC2.setAttribute('cx', p2.x);
tC2.setAttribute('cy', p2.y);
let tC3 = _Drawing.appendChild(tTemplate.cloneNode(false));
tC3.setAttribute('fill', 'green');
tC3.setAttribute('cx', p3.x);
tC3.setAttribute('cy', p3.y);
let tLine = document.getElementById('line1');
if(!tLine){
tLine = _Drawing.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'line'));
tLine.id = 'line1';
tLine.setAttribute('stroke-dasharray', '5,5')
};
tLine.setAttribute('x1', p1.x);
tLine.setAttribute('y1', p1.y);
tLine.setAttribute('x2', p2.x);
tLine.setAttribute('y2', p2.y);
tAngle = (Math.atan2(p3.y - p1.y, p3.x - p1.x) - Math.atan2(p2.y - p1.y, p2.x - p1.x)) * 180 / Math.PI
}
return tAngle
};
//REM: Assiging events
window.onload = function(){
_Drawing = document.querySelector('svg');
document.body.addEventListener('mousedown', _onDown, false);
document.body.addEventListener('mousemove', _onMove, false);
document.body.addEventListener('mouseup', _onUp, false);
document.body.addEventListener('mouseleave', _onUp, false)
};
line,
circle{
pointer-events: none;
stroke-width: 1
}
#movable{
pointer-events: all;
stroke-width: 10
}
#line1{
stroke: hotpink
}
<svg xmlns = 'http://www.w3.org/2000/svg' viewBox = '0 0 300 300'>
<line x1 = '50' y1 = '50' x2 = '160' y2 = '110' stroke = 'blue' id = 'movable'></line>
</svg>
预期结果
通过移动蓝线,我画了一条渐变的亮粉色线。该渐变线必须垂直于蓝线的原始位置以及蓝线的新位置。蓝线本身的方向可能不会改变,只是它的位置。如果线是水平的,则只允许直线向上和直线向下。如果线是垂直的,则只允许向左和向右笔直。如果该线是对角线,则仅允许沿该线对齐的直线运动(如铁路)。因此,例如,通过我的对角线蓝线并直线向上移动,我想计算离左边缘最近的位置,这将使渐变热粉色线垂直于蓝线的原始位置和蓝线的新位置.
这是一种与 Adobe PDF Measuring Tool 非常相似的行为。
要使运动垂直于蓝线,请执行以下操作:
- 获取蓝线方向的向量。
- 将其旋转 90 度(垂直于其表面)。
- 规范化,例如使其长度等于
1
(称之为n
)。 - 现在获取鼠标的移动向量(
current - start
,称之为m
)。 - 现在计算
n * dot(n,m)
。那是你在n
方向的运动矢量。
我制作了一个 class 可以为您提供需要放置蓝线的位置的 x、y。
class cTracker {
constructor(x1, y1, x2, y2) {
x1 = parseInt(x1);
y1 = parseInt(y1);
x2 = parseInt(x2);
y2 = parseInt(y2);
const extendLine = 10000;
let blueLineRadian = Math.atan2(x1 - x2, y1 - y2);
this.m_blueLineRadian = blueLineRadian;
let magentaLineRadian = blueLineRadian + (Math.PI / 2);
this.m_blueLineCos = Math.cos(blueLineRadian) * extendLine;
this.m_blueLineSin = Math.sin(blueLineRadian) * extendLine;
this.m_magentaLineX1 = x1 + Math.sin(magentaLineRadian) * extendLine;
this.m_magentaLineY1 = y1 + Math.cos(magentaLineRadian) * extendLine;
this.m_magentaLineX2 = x1 + Math.sin(magentaLineRadian + Math.PI) * extendLine;
this.m_magentaLineY2 = y1 + Math.cos(magentaLineRadian + Math.PI) * extendLine;
}
// -------------------------------------------------------------------------
//
// -------------------------------------------------------------------------
getCursorPosition(x, y) {
this.m_x1 = x - this.m_blueLineSin;
this.m_y1 = y - this.m_blueLineCos;
this.m_x2 = x + this.m_blueLineSin;
this.m_y2 = y + this.m_blueLineCos;
return this.intersect(this.m_magentaLineX1, this.m_magentaLineY1, this.m_magentaLineX2, this.m_magentaLineY2, this.m_x1, this.m_y1, this.m_x2, this.m_y2);
}
// -------------------------------------------------------------------------
//
// -------------------------------------------------------------------------
intersect(x1, y1, x2, y2, x3, y3, x4, y4) {
var denominator = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1));
var a = y1 - y3;
var b = x1 - x3;
var numerator1 = ((x4 - x3) * a) - ((y4 - y3) * b);
var numerator2 = ((x2 - x1) * a) - ((y2 - y1) * b);
a = numerator1 / denominator;
return {m_x : x1 + (a * (x2 - x1)), m_y : y1 + (a * (y2 - y1))};
}
}
要使用它,请用蓝线的 x1、y1、x2、y2 创建一个瞬间
let movableLine = document.getElementById('movable');
let x1 = parseInt(movableLine.getAttribute("x1"));
let y1 = parseInt(movableLine.getAttribute("y1"));
let x2 = parseInt(movableLine.getAttribute("x2"));
let y2 = parseInt(movableLine.getAttribute("y2"));
this.m_tracker = new cTracker(x1, y1, x2, y2);
要获得放置蓝线位置的 X、Y,您只需执行以下操作...
let xySVG = this._getSVGCoords(event.clientX, event.clientY);
let xy = this.m_tracker.getCursorPosition(xySVG.x, xySVG.y);
console.log(xy); // This is where the blue line needs to be placed...
这是一个有效的 fiddle:https://jsfiddle.net/syxfv63z/2/