我一直试图解决这个问题。
要解决的问题..
说我有3分..
P1 ---------- P2, and P3 can be anywhere around P1 and P2
要计算的公式是什么,以便将P3插值到P1和P2之间的线上?
我需要一个公式来计算P3的新X,Y坐标,它位于P1和P2之间的线上。
到目前为止我的代码..
public Point lerp(Point P0, Point P1, Point P)
{
double y1 = P0.Y + (P1.Y - P0.Y) * ((P.X - P0.X) / (P1.X - P0.X));
double x1 = P.X;
double y2 = P.Y;
double x2 = P0.X + (P1.X - P0.X) * ((P.Y - P0.Y) / (P1.Y - P0.Y));
return new Point((x1 + x2) / 2, (y1 + y2) / 2);
}
我的参考.. http://en.wikipedia.org/wiki/Linear_interpolation
上面的代码很接近,但稍微关闭......
以下是Corey Ogburn的转换后的javascript代码
public Point _pointOnLine(Point pt1, Point pt2, Point pt)
{
bool isValid = false;
var r = new Point(0, 0);
if (pt1.Y == pt2.Y && pt1.X == pt2.X) { pt1.Y -= 0.00001; }
var U = ((pt.Y - pt1.Y) * (pt2.Y - pt1.Y)) + ((pt.X - pt1.X) * (pt2.X - pt1.X));
var Udenom = Math.Pow(pt2.Y - pt1.Y, 2) + Math.Pow(pt2.X - pt1.X, 2);
U /= Udenom;
r.Y = pt1.Y + (U * (pt2.Y - pt1.Y));
r.X = pt1.X + (U * (pt2.X - pt1.X));
double minx, maxx, miny, maxy;
minx = Math.Min(pt1.X, pt2.X);
maxx = Math.Max(pt1.X, pt2.X);
miny = Math.Min(pt1.Y, pt2.Y);
maxy = Math.Max(pt1.Y, pt2.Y);
isValid = (r.X >= minx && r.X <= maxx) && (r.Y >= miny && r.Y <= maxy);
return isValid ? r : new Point();
}
答案 0 :(得分:5)
这是我们在工作中使用的一些javascript代码(一家GIS公司),在用户希望通过向其添加顶点来分割线条的情况下,找出鼠标旁边一条线上的最近点。应该很容易转移到C#:
function _pointOnLine(line1, line2, pt) {
var isValid = false;
var r = new Microsoft.Maps.Location(0, 0);
if (line1.latitude == line2.latitude && line1.longitude == line2.longitude) line1.latitude -= 0.00001;
var U = ((pt.latitude - line1.latitude) * (line2.latitude - line1.latitude)) + ((pt.longitude - line1.longitude) * (line2.longitude - line1.longitude));
var Udenom = Math.pow(line2.latitude - line1.latitude, 2) + Math.pow(line2.longitude - line1.longitude, 2);
U /= Udenom;
r.latitude = line1.latitude + (U * (line2.latitude - line1.latitude));
r.longitude = line1.longitude + (U * (line2.longitude - line1.longitude));
var minx, maxx, miny, maxy;
minx = Math.min(line1.latitude, line2.latitude);
maxx = Math.max(line1.latitude, line2.latitude);
miny = Math.min(line1.longitude, line2.longitude);
maxy = Math.max(line1.longitude, line2.longitude);
isValid = (r.latitude >= minx && r.latitude <= maxx) && (r.longitude >= miny && r.longitude <= maxy);
return isValid ? r : null;
}
line1
是一个具有纬度和经度的点,表示该行的一个端点,相当于您的P1。 line2
是另一个端点:P2。 pt
是你的P3。这将返回P3垂直穿过的线上的点。如果P3超过该行的任一端,则返回null,这意味着两个端点中的一个是与P3最近的点。
为清楚起见:
答案 1 :(得分:3)
问题是你的Point有X和Y的整数值,因此你正在进行整数除法。尝试将您的值转换为float
或double
,进行计算,然后将它们返回到整数。
请注意,当您这样做时: (P1.Y - P0.Y)*((P.X - P0.X)/(P1.X - P0.X)) 你实际上失去了精度,因为5/2的结果是2,而不是2.5但是当你的值是实数时,那么5.0 / 2.0确实是2.5。
你应该试试这个:
double y1 = P0.Y + (double)(P1.Y - P0.Y) * ((double)(P.X - P0.X) / (double)(P1.X - P0.X));
double x1 = P.X; //these two are implicit casts
double y2 = P.Y;
double x2 = P0.X + (double)(P1.X - P0.X) * ((double)(P.Y - P0.Y) / (double)(P1.Y - P0.Y));
return new Point((x1 + x2) / 2.0, (y1 + y2) / 2.0); //put 2.0 for any case even though x1+x2 is `double`
另外,那么你是从double转换为int,数字的小数部分会自动切断,所以例如3.87将变为3.如果你可以使用它,你的最后一行应该更精确:
return new Point((x1 + x2) / 2.0 + 0.5, (y1 + y2) / 2.0 + 0.5);
将有效地将double值舍入为更接近的整数值。
修改强>
但是如果你只是想在两点之间的线上找到点p3,那么使用这种方法会更容易:
public Point lerp(Point P0, Point P1)
{
double x = ((double)P0.X + P1.X)/2.0;
double y = (double)P0.Y + (double)(P1.Y - P0.Y) * ((double)(x - P0.X) / (double)(P1.X - P0.X));
return new Point(x + 0.5, y + 0.5);
}
答案 2 :(得分:0)
这是一个古老的问题,我发现Corey Ogburn解决方案非常有用。但是我认为发布其他较少“地图”版本的javascript代码(在画布绘图中使用过)可能会有所帮助。
export const pointOnLine = (p0, p1, q) => {
// p0 and p1 define the line segment
// q is the reference point (aka mouse)
// returns point on the line closest to px
if (p0.x == p1.x && p0.y == p1.y) p0.x -= 0.00001;
const Unumer = ((q.x - p0.x) * (p1.x - p0.x)) + ((q.y - p0.y) * (p1.y - p0.y));
const Udenom = Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2);
const U = Unumer / Udenom;
const r = {
x: p0.x + (U * (p1.x - p0.x)),
y: p0.y + (U * (p1.y - p0.y))
}
const minx = Math.min(p0.x, p1.x);
const maxx = Math.max(p0.x, p1.x);
const miny = Math.min(p0.y, p1.y);
const maxy = Math.max(p0.y, p1.y);
const isValid = (r.x >= minx && r.x <= maxx) && (r.y >= miny && r.y <= maxy);
return isValid ? r : null;
}