Canvas quadraticCurve center point - html

I want need to know how detect center coordinates of quadraticCurve in HTML5 canvas. I want to draw arrow in this center point of curve.
There is my draw curve method:
function draw_curve(Ax, Ay, Bx, By, M, context) {
var dx = Bx - Ax,
dy = By - Ay,
dr = Math.sqrt(dx * dx + dy * dy);
// side is either 1 or -1 depending on which side you want the curve to be on.
// Find midpoint J
var Jx = Ax + (Bx - Ax) / 2
var Jy = Ay + (By - Ay) / 2
// We need a and b to find theta, and we need to know the sign of each to make sure that the orientation is correct.
var a = Bx - Ax
var asign = (a < 0 ? -1 : 1)
var b = By - Ay
var bsign = (b < 0 ? -1 : 1)
var theta = Math.atan(b / a)
// Find the point that's perpendicular to J on side
var costheta = asign * Math.cos(theta)
var sintheta = asign * Math.sin(theta)
// Find c and d
var c = M * sintheta
var d = M * costheta
// Use c and d to find Kx and Ky
var Kx = Jx - c
var Ky = Jy + d
// context.bezierCurveTo(Kx, Ky,Bx,By, Ax, Ax);
context.quadraticCurveTo(Kx, Ky, Bx, By);
// draw the ending arrowhead
var endRadians = Math.atan((dx) / (dy));
context.stroke();
var t = 0.5; // given example value
var xx = (1 - t) * (1 - t) * Ax + 2 * (1 - t) * t * Kx + t * t * Bx;
var yy = (1 - t) * (1 - t) * Ay + 2 * (1 - t) * t * Ky + t * t * By;
var k = {};
k.x = xx;
k.y = yy;
SOLVED BY THIS CODE, T is parameter which set position on the curve:
var t = 0.5; // given example value
var xx = (1 - t) * (1 - t) * Ax + 2 * (1 - t) * t * Kx + t * t * Bx;
var yy = (1 - t) * (1 - t) * Ay + 2 * (1 - t) * t * Ky + t * t * By;
var k = {};
k.x = xx;
k.y = yy;

Related

fill a custom object defined by a path2d object

I am trying to fill a path2d object of the HTML5 canvas.context.
I have drawn a custom path that is a bezier curve according to this site:
https://javascript.info/bezier-curve
but am unable to get it to fill with a solid colour.
Here is some code in a jsfiddle to illustrate my problem. If I uncomment the line //this.ctx.stroke(this.p2d); then the outline of the bezier curve will be drawn, but I can't seem to fill the completed path.
constructor () {
this.canv = document.getElementById('canv');
this.ctx = this.canv.getContext('2d');
this.ctx.beginPath();
this.ctx.moveTo(160,350);
this.ctx.restore();
this.p2d = new Path2D();
this.t = 0;
this.currentPoint = [160,350];
this.to = setInterval(() => this.plot(), 10, this.to);
}
plot(intid) {
const p1x = 160;
const p2x = 20;
const p3x = 320;
const p4x = 160;
const p1y = 350;
const p2y = 50;
const p3y = 50;
const p4y = 350;
let t = this.t;
let x = (((1 - t)*(1 - t)*(1 - t)) * p1x) + ((3 * ((1 - t) * (1 -t))) * (t * p2x)) + ((3 * ((1 - t) * (1 -t))) * (t * p3x)) + ((t * t * t) * p4x);
let y = (((1 - t)*(1 - t)*(1 - t)) * p1y) + ((3 * ((1 - t) * (1 -t))) * (t * p2y)) + ((3 * ((1 - t) * (1 -t))) * (t * p3y)) + ((t * t * t) * p4y);
this.t = t + 0.01;
if (t <= 1.01) {
//this.p2d.fillStyle = "#1000ff";
this.p2d.moveTo(this.currentPoint[0], this.currentPoint[1]);
this.p2d.lineTo(x, y);
this.currentPoint[0] = x;
this.currentPoint[1] = y;
console.log(x + " " + y + " " + t)
}
else
{
//this.p2d.closePath();
this.ctx.lineWidth = 2;
this.ctx.strokeStyle = "blue";
this.ctx.fillStyle = "blue";
//this.ctx.stroke(this.p2d);
this.ctx.fill(this.p2d, "evenodd");
clearInterval(this.to);
}
}
}
Window.cl = new clazz();
https://jsfiddle.net/9oL4xw1b/2/
ps. this is advanced math for me, so although my formula for calculating x and y is correct, it may not be optimised properly.
Every time you move there will be nothing to fill...
All you are drawing at the end is lines with that technique, see the difference in the code below.
canv = document.getElementById('canv');
ctx = canv.getContext('2d');
ctx.fillStyle = "blue";
ctx.moveTo(10, 10);
ctx.lineTo(20, 40);
ctx.lineTo(99, 40);
ctx.lineTo(90, 30);
ctx.fill();
ctx.stroke();
ctx.moveTo(100, 10);
ctx.lineTo(120, 40);
ctx.moveTo(120, 40);
ctx.lineTo(200, 40);
ctx.moveTo(200, 40);
ctx.lineTo(190, 30);
ctx.fill();
ctx.stroke();
<canvas id="canv" width="500px" height="100px"></canvas>
So in your code just commenting the this.p2d.moveTo does it
class clazz {
constructor() {
this.canv = document.getElementById('canv');
this.ctx = this.canv.getContext('2d');
this.ctx.moveTo(160, 350);
this.p2d = new Path2D();
this.t = 0;
this.currentPoint = [160, 350];
this.to = setInterval(() => this.plot(), 5, this.to);
}
plot(intid) {
const p1x = 160; const p2x = 20;
const p3x = 320; const p4x = 160;
const p1y = 350; const p2y = 50;
const p3y = 50; const p4y = 350;
let t = this.t;
let x = (((1 - t) * (1 - t) * (1 - t)) * p1x) + ((3 * ((1 - t) * (1 - t))) * (t * p2x)) + ((3 * ((1 - t) * (1 - t))) * (t * p3x)) + ((t * t * t) * p4x);
let y = (((1 - t) * (1 - t) * (1 - t)) * p1y) + ((3 * ((1 - t) * (1 - t))) * (t * p2y)) + ((3 * ((1 - t) * (1 - t))) * (t * p3y)) + ((t * t * t) * p4y);
this.t = t + 0.01;
if (t <= 1.01) {
//this.p2d.moveTo(this.currentPoint[0], this.currentPoint[1]);
this.p2d.lineTo(x, y);
this.ctx.stroke(this.p2d);
} else {
this.ctx.lineWidth = 2;
this.ctx.strokeStyle = "blue";
this.ctx.fillStyle = "blue";
this.ctx.fill(this.p2d, "evenodd");
clearInterval(this.to);
}
}
}
Window.cl = new clazz();
<canvas id="canv" width="500px" height="500px"></canvas>

Amplitude increasing in the verlet method in backward difference

I am facing trouble in the increasing oscillation on a simple harmonic oscillator using a backward difference. here is my code in Scilab
function [x] = back(h, tf)
k = 2;
m = 1;
i = 2;
t(i - 1) = 0;
x(i - 1) = 10;
v(i - 1) = 0;
t(i) = t(i - 1) + h
v(i) = v(i - 1) - h * (k / m) * x(i - 1)
while t(i) < tf
t(i + 1) = t(i) + h
x(i + 1) = x(i - 1) - 2 * (k / m) * v(i) * h
i = i + 1
end
plot(t, x, 'b');
endfunction
From your code, I suppose that you are trying to implement the velocity-Verlet scheme. Here is its implementation for a simple oscillator with the differential equation:
function [x] = back(h, tf)
k = 2;
m = 1;
t = 0:h:tf;
x(1) = 10;
v(1) = 0;
for i=2:length(t)
x(i) = x(i - 1) + v(i - 1) * h - k / m * x(i-1) * h^2 / 2;
v(i) = v(i - 1) - k / m * (x(i) + x(i-1)) * h / 2;
end
plot(t, x, 'b');
endfunction
[x] = back(0.01, 10)
I'm not quite sure what you are trying to achieve, neither if your math is correct. But assuming that you want to solve the numerical problem of:
//coefficients of:
k = 2.;
m = 1.;
// with an initial condition of:
t(1) = 0.;
x(1) = 10.;
v(1) = 0.;
// time paramters:
N = 50;
tf = 10;
h = tf / 50.;
for ii = 2:N
t(ii) = t(ii - 1) + h;
x(ii) = x(ii - 1) - 2 * (k / m) * v(ii - 1) * h
v(ii) = v(ii - 1) - h * (k / m) * x(ii - 1)
disp(x(ii))
end
plot(t, x, 'b');
will result in:
which doesn't seem right but anyway. Please check your math again.

Type was not found or was not a compile-time constant: Raster

This is the full error message:
C:\Project Files\Good\src\views\RasterView.as(26): col: 39 Error: Type was not found or was not a compile-time constant: Raster.
C:\Project Files\Good\src\views\RasterView.as(42): col: 45 Error: Type was not found or was not a compile-time constant: Raster.
C:\Project Files\Good\src\views\RasterView.as(47): col: 45 Error: Type was not found or was not a compile-time constant: Raster.
C:\Project Files\Good\src\views\RasterView.as(62): col: 32 Error: Type was not found or was not a compile-time constant: Raster.
This is the file(it is huge)
package views
{
import flash.display.Bitmap;
import flash.geom.Point;
import views.Canvas.Raster;
/**
* ...
* #author Arthur Wulf White
*/
public class RasterView extends Bitmap
{
import views.Canvas.Raster
public static const SCHEMATIC : int = 1;
public static const SMOOTH : int = 2;
public static const RENDER_ONCE : int = 4;
public static const BOTH : int = SCHEMATIC + SMOOTH;
// protected var bitmapData : Raster;
protected var viewMode : int = 0;
protected var lineColor : int = 0xff000000;
protected var fillColor : int = 0xff808080;
protected var bgColor : int = 0xffffffff;
public function RasterView(raster : Raster)
{
this.bitmapData = raster;
}
public function getStyle():Vector.<int>
{
return new <int> [lineColor, fillColor, bgColor];
}
public function setStyle(lineColor : int, fillColor : int, bgColor : int):void
{
this.lineColor = lineColor;
this.fillColor = fillColor;
this.bgColor = bgColor;
}
public function backupView(backupRaster : Raster):void
{
backupRaster.copyPixels(bitmapData, bitmapData.rect, new Point());
}
public function loadBackup(backupRaster : Raster):void
{
bitmapData.copyPixels(backupRaster, backupRaster.rect, new Point());
}
public function getViewMode():int
{
return viewMode;
}
public function setViewMode(mode : int):void
{
viewMode = mode;
}
public function get Raster():Raster
{
return Raster(bitmapData);
}
public function refreshView():void
{
bitmapData.lock()
render();
bitmapData.unlock();
if ((getViewMode() & RENDER_ONCE) != 0)
{
setViewMode( getViewMode() ^ RENDER_ONCE);
}
}
public function render():void
{
}
protected function clear():void
{
bitmapData.fillRect(bitmapData.rect, bgColor);
}
}
}
This is the file for the Raster class(I don't think it is important for this issue)
/**
*
* Raster class
*
* #author Didier Brun aka Foxy - www.foxaweb.com
* #version 1.4
* #date 2006-01-06
* #link http://www.foxaweb.com
*
* AUTHORS ******************************************************************************
*
* authorName : Didier Brun - www.foxaweb.com
* contribution : the original class
* date : 2007-01-07
*
* authorName : Drew Cummins - http://blog.generalrelativity.org
* contribution : added bezier curves
* date : 2007-02-13
*
* authorName : Thibault Imbert - http://www.bytearray.org
* contribution : Raster now extends BitmapData, performance optimizations
* date : 2009-10-16
*
* PLEASE CONTRIBUTE ? http://www.bytearray.org/?p=67
*
* DESCRIPTION **************************************************************************
*
* Raster is an AS3 Bitmap drawing library. It provide some functions to draw directly
* into BitmapData instance.
*
* LICENSE ******************************************************************************
*
* This class is under RECIPROCAL PUBLIC LICENSE.
* http://www.opensource.org/licenses/rpl.php
*
* Please, keep this header and the list of all authors
*
*/
package views.Canvas
{
import flash.display.BitmapData;
import flash.display.Shape;
import flash.geom.Point;
import flash.geom.Rectangle;
public class Raster extends BitmapData
{
private var shape : Shape = new Shape();
private var buffer:Array = new Array();
private var r:Rectangle = new Rectangle();
public function Raster ( width:uint, height:uint, transparent:Boolean=false, color:uint=0)
{
super ( width, height, transparent, color);
}
// ------------------------------------------------
//
// ---o public methods
//
// ------------------------------------------------
public function setPoint( p : Point, color : int, size : int = 1):void
{
if(size == 1)
setPixel(p.x, p.y, color);
else if (size > 1)
drawRect(new Rectangle(p.x - size / 2, p.y - size / 2, size, size), color);
}
public function lineAA( x0:int, y0:int, x1:int, y1:int, color:uint ):void
{
var i : int = 0;
var cInt : int;
var cNum : Number;
var div : Number;
var dx : int = Math.abs(x1 - x0);
var dy : int = Math.abs(y1 - y0);
if(dx >= dy)
{
//do x
if (x0 < x1)
{
cInt = x0;
cNum = y0;
}
else
{
cInt = x1;
cNum = y1;
}
div = Number(y1 - y0) / Number(x1 - x0);
setPixel32(cInt, cNum, color);
for (; i <= dx; i++)
{
cInt++;
cNum += div;
setAAPixel(cInt, cNum, color, true, false);
}
}
else
{
//do y
if (y0 < y1)
{
cInt = y0;
cNum = x0;
}
else
{
cInt = y1;
cNum = x1;
}
div = Number(x1 - x0) / Number(y1 - y0);
setPixel32(cNum, cInt, color);
for (; i <= dy; i++)
{
cInt++;
cNum += div;
setAAPixel(cNum, cInt, color, false, true);
}
}
}
public function quadLine(x0 : int, y0 : int, cx : int, cy : int, x1 : int, y1 : int, color : int = 0xff0000ff, detail : int = 20):void
{
var epsilon : Number = 1.0 / Number(detail);
var total : Number = 0;
var x : int = 0;
var y : int = 0;
for (; total <= 1.001; total+=epsilon)
{
x = (1 - total) * ((1 - total) * x0 + total * cx) + total * (total * x1 + (1 - total) * cx);
y = (1 - total) * ((1 - total) * y0 + total * cy) + total * (total * y1 + (1 - total) * cy);
setPixel32(x, y, color);
}
}
/**
* Draws a antialias Quadratic Bezier Curve (equivalent to a DisplayObject's graphics#curveTo)
*
* #param x0 x position of first anchor
* #param y0 y position of first anchor
* #param x1 x position of control point
* #param y1 y position of control point
* #param x2 x position of second anchor
* #param y2 y position of second anchor
* #param c color
* #param resolution [optional] determines the accuracy of the curve's length (higher number = greater accuracy = longer process)
* */
public function aaQuadBezier ( anchorX0:int, anchorY0:int, controlX:int, controlY:int, anchorX1:int, anchorY1:int, c:Number, resolution:int = 3):void
{
shape.graphics.clear();
shape.graphics.lineStyle(1, c, 1);
shape.graphics.moveTo(anchorX0, anchorY0);
shape.graphics.curveTo(controlX, controlY, anchorX1, anchorY1);
this.draw(shape);
}
/**
* Draw an anti-aliased line
*
* #param x0 first point x coord
* #param y0 first point y coord
* #param x1 second point x coord
* #param y1 second point y coord
* #param c color (0xaarrvvbb)
*/
public function aaLine2( x1:int, y1:int, x2:int, y2:int, color:uint ):void
{
shape.graphics.clear();
shape.graphics.lineStyle(1, color, 1);
shape.graphics.moveTo(x1, y1);
shape.graphics.lineTo(x2, y2);
this.draw(shape);
}
/**
* Draw a line
*
* #param x0 first point x coord
* #param y0 first point y coord
* #param x1 second point x coord
* #param y1 second point y coord
* #param c color (0xaarrvvbb)
*/
public function line ( x0:int, y0:int, x1:int, y1:int, color:uint ):void
{
var dx:int;
var dy:int;
var i:int;
var xinc:int;
var yinc:int;
var cumul:int;
var x:int;
var y:int;
x = x0;
y = y0;
dx = x1 - x0;
dy = y1 - y0;
xinc = ( dx > 0 ) ? 1 : -1;
yinc = ( dy > 0 ) ? 1 : -1;
dx = dx < 0 ? -dx : dx;
dy = dy < 0 ? -dy : dy;
setPixel32(x,y,color);
if ( dx > dy )
{
cumul = dx >> 1;
for ( i = 1 ; i <= dx ; ++i )
{
x += xinc;
cumul += dy;
if (cumul >= dx)
{
cumul -= dx;
y += yinc;
}
setPixel32(x,y,color);
}
}else
{
cumul = dy >> 1;
for ( i = 1 ; i <= dy ; ++i )
{
y += yinc;
cumul += dx;
if ( cumul >= dy )
{
cumul -= dy;
x += xinc ;
}
setPixel32(x,y,color);
}
}
}
/**
* Draw a triangle
*
* #param x0 first point x coord
* #param y0 first point y coord
* #param x1 second point x coord
* #param y1 second point y coord
* #param x2 third point x coord
* #param y2 third point y coord
* #param c color (0xaarrvvbb)
*/
public function triangle ( x0:int, y0:int, x1:int, y1:int, x2:int, y2:int, color:uint ):void
{
line (x0,y0,x1,y1,color);
line (x1,y1,x2,y2,color);
line (x2,y2,x0,y0,color);
}
/**
* Draw a filled triangle
*
* #param x0 first point x coord
* #param y0 first point y coord
* #param x1 second point x coord
* #param y1 second point y coord
* #param x2 third point x coord
* #param y2 third point y coord
* #param c color (0xaarrvvbb)
*/
public function filledTri ( x0:int, y0:int, x1:int, y1:int, x2:int, y2:int, color:uint ):void
{
buffer.length = 0;
lineTri (buffer,x0,y0,x1,y1,color);
lineTri (buffer,x1,y1,x2,y2,color);
lineTri (buffer,x2,y2,x0,y0,color);
}
/**
* Draw a circle
*
* #param px first point x coord
* #param py first point y coord
* #param r radius
* #param c color (0xaarrvvbb)
*/
public function circle ( px:int, py:int, r:int, color:uint ):void
{
var x:int;
var y:int;
var d:int;
x = 0;
y = r;
d = 1-r;
setPixel32(px+x,py+y,color);
setPixel32(px+x,py-y,color);
setPixel32(px-y,py+x,color);
setPixel32(px+y,py+x,color);
while ( y > x )
{
if ( d < 0 )
{
d += (x+3) << 1;
}else
{
d += ((x - y) << 1) + 5;
y--;
}
x++;
setPixel32(px+x,py+y,color);
setPixel32(px-x,py+y,color);
setPixel32(px+x,py-y,color);
setPixel32(px-x,py-y,color);
setPixel32(px-y,py+x,color);
setPixel32(px-y,py-x,color);
setPixel32(px+y,py-x,color);
setPixel32(px+y,py+x,color);
}
}
/**
* Draw an anti-aliased circle
*
* #param px first point x coord
* #param py first point y coord
* #param r radius
* #param c color (0xaarrvvbb)
*/
public function aaCircle ( px:int, py:int, r:int, color:uint ):void
{
var vx:int;
var vy:int;
var d:int;
vx = r;
vy = 0;
var t:Number=0;
var dry:Number;
var buff:int;
setPixel(px+vx,py+vy,color);
setPixel(px-vx,py+vy,color);
setPixel(px+vy,py+vx,color);
setPixel(px+vy,py-vx,color);
while ( vx > vy+1 )
{
vy++;
buff = Math.sqrt(r*r-vy*vy)+1;
dry = buff - Math.sqrt(r*r-vy*vy);
if (dry<t) vx--;
drawAlphaPixel(px+vx,py+vy,1-dry,color)
drawAlphaPixel(px+vx-1,py+vy,dry,color)
drawAlphaPixel(px-vx,py+vy,1-dry,color)
drawAlphaPixel(px-vx+1,py+vy,dry,color)
drawAlphaPixel(px+vx,py-vy,1-dry,color)
drawAlphaPixel(px+vx-1,py-vy,dry,color)
drawAlphaPixel(px-vx,py-vy,1-dry,color)
drawAlphaPixel(px-vx+1,py-vy,dry,color)
drawAlphaPixel(px+vy,py+vx,1-dry,color)
drawAlphaPixel(px+vy,py+vx-1,dry,color)
drawAlphaPixel(px-vy,py+vx,1-dry,color)
drawAlphaPixel(px-vy,py+vx-1,dry,color)
drawAlphaPixel(px+vy,py-vx,1-dry,color)
drawAlphaPixel(px+vy,py-vx+1,dry,color)
drawAlphaPixel(px-vy,py-vx,1-dry,color)
drawAlphaPixel(px-vy,py-vx+1,dry,color)
t=dry;
}
}
/**
* Draw an anti-aliased line
*
* #param x0 first point x coord
* #param y0 first point y coord
* #param x1 second point x coord
* #param y1 second point y coord
* #param c color (0xaarrvvbb)
*/
public function aaLine ( x1:int, y1:int, x2:int, y2:int, color:uint ):void
{
var steep:Boolean = Math.abs(y2 - y1) > Math.abs(x2 - x1);
var swap:int;
if (steep)
{
swap=x1; x1=y1; y1=swap;
swap=x2; x2=y2; y2=swap;
}
if (x1 > x2)
{
swap=x1; x1=x2; x2=swap;
swap=y1; y1=y2; y2=swap;
}
var dx:int = x2 - x1;
var dy:int = y2 - y1
var gradient:Number = dy / dx;
var xend:int = x1;
var yend:Number = y1 + gradient * (xend - x1);
var xgap:Number = 1-((x1 + 0.5)%1);
var xpx1:int = xend;
var ypx1:int = yend;
var alpha:Number;
alpha = ((yend)%1) * xgap;
var intery:Number = yend + gradient;
xend = x2;
yend = y2 + gradient * (xend - x2)
xgap = (x2 + 0.5)%1;
var xpx2:int = xend;
var ypx2:int = yend;
alpha = (1-((yend)%1)) * xgap;
if (steep)
drawAlphaPixel(ypx2,xpx2,alpha,color);
else drawAlphaPixel(xpx2, ypx2,alpha,color);
alpha = ((yend)%1) * xgap;
if (steep)
drawAlphaPixel(ypx2 + 1,xpx2,alpha,color);
else drawAlphaPixel(xpx2, ypx2 + 1,alpha,color);
var x:int=xpx1;
while (x++<xpx2)
{
alpha = 1-((intery)%1);
if (steep)
drawAlphaPixel(intery,x,alpha,color);
else drawAlphaPixel(x,intery,alpha,color);
alpha=intery%1;
if (steep)
drawAlphaPixel(intery+1,x,alpha,color);
else drawAlphaPixel(x,intery+1,alpha,color);
intery = intery + gradient
}
}
/**
* Draws a Rectangle
*
* #param rect Rectangle dimensions
* #param color color
* */
public function drawRect ( rect:Rectangle, color:uint ):void
{
line ( rect.x, rect.y, rect.x+rect.width, rect.y, color );
line ( rect.x+rect.width, rect.y, rect.x+rect.width, rect.y+rect.height, color );
line ( rect.x+rect.width, rect.y+rect.height, rect.x, rect.y+rect.height, color );
line ( rect.x, rect.y+rect.height, rect.x, rect.y, color );
}
/**
* Draws a rounded Rectangle
*
* #param rect Rectangle dimensions
* #param ellipseWidth Rectangle corners width
* #param color color
* */
public function drawRoundRect ( rect:Rectangle, ellipseWidth:int, color:uint ):void
{
var arc:Number = 4/3 * (Math.sqrt(2) - 1);
var xc:Number = rect.x+rect.width-ellipseWidth;
var yc:Number = rect.y+ellipseWidth;
line( rect.x+ellipseWidth, rect.y, xc, rect.y, color );
cubicBezier(xc, rect.y, xc + ellipseWidth*arc, yc - ellipseWidth, xc + ellipseWidth, yc - ellipseWidth*arc, xc + ellipseWidth, yc, color);
xc = rect.x+rect.width-ellipseWidth;
yc = rect.y+rect.height-ellipseWidth;
line( xc + ellipseWidth, rect.y+ellipseWidth, rect.x+rect.width, yc, color );
cubicBezier(rect.x+rect.width, yc, xc + ellipseWidth, yc + ellipseWidth*arc, xc + ellipseWidth*arc, yc + ellipseWidth, xc, yc + ellipseWidth, color);
xc = rect.x+ellipseWidth;
yc = rect.y+rect.height-ellipseWidth;
line( rect.x+rect.width-ellipseWidth, rect.y+rect.height, xc, yc + ellipseWidth, color );
cubicBezier( xc, yc + ellipseWidth, xc - ellipseWidth*arc, yc + ellipseWidth, xc - ellipseWidth, yc + ellipseWidth*arc, xc - ellipseWidth, yc, color );
xc = rect.x+ellipseWidth;
yc = rect.y+ellipseWidth;
line( xc - ellipseWidth, rect.y+rect.height-ellipseWidth, rect.x, yc, color );
cubicBezier(rect.x, yc, xc - ellipseWidth, yc - ellipseWidth*arc, xc - ellipseWidth*arc, yc - ellipseWidth, xc, yc - ellipseWidth, color);
}
/**
* Draws a Quadratic Bezier Curve (equivalent to a DisplayObject's graphics#curveTo)
*
* #param x0 x position of first anchor
* #param y0 y position of first anchor
* #param x1 x position of control point
* #param y1 y position of control point
* #param x2 x position of second anchor
* #param y2 y position of second anchor
* #param c color
* #param resolution [optional] determines the accuracy of the curve's length (higher number = greater accuracy = longer process)
* */
public function quadBezier ( anchorX0:int, anchorY0:int, controlX:int, controlY:int, anchorX1:int, anchorY1:int, c:Number, resolution:int = 3):void
{
var ox:Number = anchorX0;
var oy:Number = anchorY0;
var px:int;
var py:int;
var dist:Number = 0;
var inverse:Number = 1 / resolution;
var interval:Number;
var intervalSq:Number;
var diff:Number;
var diffSq:Number;
var i:int = 0;
while( ++i <= resolution )
{
interval = inverse * i;
intervalSq = interval * interval;
diff = 1 - interval;
diffSq = diff * diff;
px = diffSq * anchorX0 + 2 * interval * diff * controlX + intervalSq * anchorX1;
py = diffSq * anchorY0 + 2 * interval * diff * controlY + intervalSq * anchorY1;
dist += Math.sqrt( ( px - ox ) * ( px - ox ) + ( py - oy ) * ( py - oy ) );
ox = px;
oy = py;
}
//approximates the length of the curve
var curveLength:int = dist;
inverse = 1 / curveLength;
var lastx:int=anchorX0;
var lasty:int=anchorY0;
i = -1;
while( ++i <= curveLength )
{
interval = inverse * i;
intervalSq = interval * interval;
diff = 1 - interval;
diffSq = diff * diff;
px = diffSq * anchorX0 + 2 * interval * diff * controlX + intervalSq * anchorX1;
py = diffSq * anchorY0 + 2 * interval * diff * controlY + intervalSq * anchorY1;
line(lastx,lasty,px,py,c);
//aaLine(lastx, lasty, px, py, c);
lastx = px;
lasty = py;
}
}
/**
* Draws a Cubic Bezier Curve
*
* TODO: Determine whether x/y params would be better named as anchor/control
*
* #param x0 x position of first anchor
* #param y0 y position of first anchor
* #param x1 x position of control point
* #param y1 y position of control point
* #param x2 x position of second control point
* #param y2 y position of second control point
* #param x3 x position of second anchor
* #param y3 y position of second anchor
* #param c color
* #param resolution [optional] determines the accuracy of the curve's length (higher number = greater accuracy = longer process)
* */
public function cubicBezier ( x0:int, y0:int, x1:int, y1:int, x2:int, y2:int, x3:int, y3:int, c:Number, resolution:int = 5 ):void
{
var ox:Number = x0;
var oy:Number = y0;
var px:int;
var py:int;
var dist:Number = 0;
var inverse:Number = 1 / resolution;
var interval:Number;
var intervalSq:Number;
var intervalCu:Number;
var diff:Number;
var diffSq:Number;
var diffCu:Number;
var i:int = 0;
while( ++i <= resolution )
{
interval = inverse * i;
intervalSq = interval * interval;
intervalCu = intervalSq * interval;
diff = 1 - interval;
diffSq = diff * diff;
diffCu = diffSq * diff;
px = diffCu * x0 + 3 * interval * diffSq * x1 + 3 * x2 * intervalSq * diff + x3 * intervalCu;
py = diffCu * y0 + 3 * interval * diffSq * y1 + 3 * y2 * intervalSq * diff + y3 * intervalCu;
dist += Math.sqrt( ( px - ox ) * ( px - ox ) + ( py - oy ) * ( py - oy ) );
ox = px;
oy = py;
}
//approximates the length of the curve
var curveLength:int = dist;
inverse = 1 / curveLength;
var lastx:int=x0;
var lasty:int=y0;
i = -1;
while( ++i <= curveLength )
{
interval = inverse * i;
intervalSq = interval * interval;
intervalCu = intervalSq * interval;
diff = 1 - interval;
diffSq = diff * diff;
diffCu = diffSq * diff;
px = diffCu * x0 + 3 * interval * diffSq * x1 + 3 * x2 * intervalSq * diff + x3 * intervalCu;
py = diffCu * y0 + 3 * interval * diffSq * y1 + 3 * y2 * intervalSq * diff + y3 * intervalCu;
line(lastx,lasty,px,py,c);
lastx = px;
lasty = py;
}
}
// ------------------------------------------------
//
// ---o private static methods
//
// ------------------------------------------------
/**
* Draw an AA pixel
*/
private function setAAPixel (x:Number, y:Number, c:Number, roundX : Boolean = false, roundY : Boolean = false):void
{
var xpos : Number = Math.floor(x);
var ypos : Number = Math.floor(y);
var xA : Number = x - xpos;
var yA : Number = y - ypos;
if (!roundX && !roundY)
{
drawAlphaPixel(xpos , ypos , (1 - xA) * (1 - yA) , c);
drawAlphaPixel(xpos + 1 , ypos , xA * (1 - yA) , c);
drawAlphaPixel(xpos , ypos + 1 , (1 - xA) * yA , c);
drawAlphaPixel(xpos + 1 , ypos + 1 , xA * yA , c);
}
else if (roundX && !roundY)
{
drawAlphaPixel(xpos , ypos , (1 - yA) , c);
drawAlphaPixel(xpos , ypos + 1 , yA , c);
}
else if (roundY && !roundX)
{
drawAlphaPixel(xpos , ypos , (1 - xA) , c);
drawAlphaPixel(xpos + 1 , ypos , xA , c);
}
else if (roundX && roundY)
{
setPixel32(xpos , ypos , c);
}
}
/**
* Draw an alpha32 pixel
*/
private function drawAlphaPixel ( x:int, y:int, a:Number, c:Number, bg : int = -1 ):void
{
//var g:uint = getPixel32(x,y);
var g : int = 0xff000000;
var r0:uint = ((g & 0x00FF0000) >> 16);
var g0:uint = ((g & 0x0000FF00) >> 8);
var b0:uint = ((g & 0x000000FF));
var r1:uint = ((c & 0x00FF0000) >> 16);
var g1:uint = ((c & 0x0000FF00) >> 8);
var b1:uint = ((c & 0x000000FF));
var ac:Number = 0xFF;
var rc:Number = r1*a+r0*(1-a);
var gc:Number = g1*a+g0*(1-a);
var bc:Number = b1*a+b0*(1-a);
var n:uint = (ac<<24)+(rc<<16)+(gc<<8)+bc;
setPixel32(x,y,n);
}
/**
* Check a triangle line
*/
private function checkLine ( o:Array, x:int, y:int, c:int, r:Rectangle ):void
{
if (o[y])
{
if (o[y]>x)
{
r.width=o[y]-x;
r.x=x;
r.y=y;
fillRect(r,c);
}else
{
r.width=x-o[y];
r.x=o[y];
r.y=y;
fillRect(r,c);
}
}else
{
o[y]=x;
}
}
/**
* Special line for filled triangle
*/
private function lineTri ( o:Array, x0:int, y0:int, x1:int, y1:int, c:Number ):void
{
var steep:Boolean= (y1-y0)*(y1-y0) > (x1-x0)*(x1-x0);
var swap:int;
if (steep)
{
swap=x0; x0=y0; y0=swap;
swap=x1; x1=y1; y1=swap;
}
if (x0>x1)
{
x0^=x1; x1^=x0; x0^=x1;
y0^=y1; y1^=y0; y0^=y1;
}
var deltax:int = x1 - x0
var deltay:int = (y1 - y0) < 0 ? -(y1 - y0) : (y1 - y0);
var error:int = 0;
var y:int = y0;
var ystep:int = y0<y1 ? 1 : -1;
var x:int = x0;
var xend:int = x1-(deltax>>1);
var fx:int = x1;
var fy:int = y1;
var px:int = 0;
r.x = 0;
r.y = 0;
r.width = 0;
r.height = 1;
while (x++<=xend)
{
if (steep)
{
checkLine(o,y,x,c,r);
if (fx != x1 && fx != xend)
checkLine(o,fy,fx+1,c,r);
}
error += deltay;
if ((error<<1) >= deltax)
{
if (!steep)
{
checkLine(o,x-px+1,y,c,r);
if (fx!=xend)
checkLine(o,fx+1,fy,c,r);
}
px = 0;
y += ystep;
fy -= ystep;
error -= deltax;
}
px++;
fx--;
}
if (!steep)
checkLine(o,x-px+1,y,c,r);
}
}
}
Looks like just a typo or copy/paste error. The import statement should not be repeated inside your class definition:
package views
{
import flash.display.Bitmap;
import flash.geom.Point;
import views.Canvas.Raster; // Yes
/**
* ...
* #author Arthur Wulf White
*/
public class RasterView extends Bitmap
{
import views.Canvas.Raster // No
Also, avoid having an accessor with the same name as a class:
public function get Raster():Raster // Call this function get MyRaster or similar
{
return Raster(bitmapData);
}
Code highlighting is your friend. Any time a property or function name other than your constructor lights up in Cyan, it means you're using an existing class name, which may cause problems.

Google Maps determine distance along Line

I am trying to determine the distance of a point along a given Polyline (from the start point) in Google maps (given that the user clicks on the Polyline and I get the point coordinates in the event).
So far, this is the only thing that comes to mind:
Iterate over all segments in the Polyline until I find one such that
d(line, point) ~= 0, keeping track of the distance covered so far.
Interpolate on the segment the point is on to find its distance
relative to the start of the segment.
Sadly, this seems rather complicated for something that should be straightforward to do.
Is there any easier way?
P.S.: I'm using API v3
So, after much searching I decided to implement the algorithm as described above. Turned out it isn't as bad as I thought. Should anyone ever land on this page, the full code is below:
var DistanceFromStart = function (/*latlng*/ markerPosition) {
var path = this.polyline.getPath();
var minValue = Infinity;
var minIndex = 0;
var x = markerPosition.lat();
var y = markerPosition.lng();
for (var i = 0; i < path.getLength() - 1; i++) {
var x1 = path.getAt(i).lat();
var y1 = path.getAt(i).lng();
var x2 = path.getAt(i + 1).lat();
var y2 = path.getAt(i + 1).lng();
var dist = pDistance(x, y, x1, y1, x2, y2);
if (dist < minValue) {
minIndex = i;
minValue = dist;
}
}
var gdist = google.maps.geometry.spherical.computeDistanceBetween;
var dinit = gdist(markerPosition, path.getAt(minIndex));
var dtotal = gdist(path.getAt(minIndex), path.getAt(minIndex + 1));
var distanceFromStart = 0;
for (var i = 0; i <= minIndex - 1; i++) {
distanceFromStart += gdist(path.getAt(i), path.getAt(i + 1));
}
distanceFromStart += dtotal * dinit / dtotal;
return distanceFromStart;
}
function pDistance(x, y, x1, y1, x2, y2) {
var A = x - x1;
var B = y - y1;
var C = x2 - x1;
var D = y2 - y1;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = dot / len_sq;
var xx, yy;
if (param < 0 || (x1 == x2 && y1 == y2)) {
xx = x1;
yy = y1;
}
else if (param > 1) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
var dx = x - xx;
var dy = y - yy;
return Math.sqrt(dx * dx + dy * dy);
}
If you see anything to improve, do let me know.
If you get the coordinates for the start and end points, then use the haversine algorithm to calculate the distance you can easily find the distance between two points taking into consideration the curvature of the earth.
Here is the formula (you may need to convert in into the language you are using):
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var lat1 = lat1.toRad();
var lat2 = lat2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
variable d is your distance.
Hope this helps

Shortest distance between a point and a line segment

I need a basic function to find the shortest distance between a point and a line segment. Feel free to write the solution in any language you want; I can translate it into what I'm using (Javascript).
EDIT: My line segment is defined by two endpoints. So my line segment AB is defined by the two points A (x1,y1) and B (x2,y2). I'm trying to find the distance between this line segment and a point C (x3,y3). My geometry skills are rusty, so the examples I've seen are confusing, I'm sorry to admit.
Eli, the code you've settled on is incorrect. A point near the line on which the segment lies but far off one end of the segment would be incorrectly judged near the segment. Update: The incorrect answer mentioned is no longer the accepted one.
Here's some correct code, in C++. It presumes a class 2D-vector class vec2 {float x,y;}, essentially, with operators to add, subract, scale, etc, and a distance and dot product function (i.e. x1 x2 + y1 y2).
float minimum_distance(vec2 v, vec2 w, vec2 p) {
// Return minimum distance between line segment vw and point p
const float l2 = length_squared(v, w); // i.e. |w-v|^2 - avoid a sqrt
if (l2 == 0.0) return distance(p, v); // v == w case
// Consider the line extending the segment, parameterized as v + t (w - v).
// We find projection of point p onto the line.
// It falls where t = [(p-v) . (w-v)] / |w-v|^2
// We clamp t from [0,1] to handle points outside the segment vw.
const float t = max(0, min(1, dot(p - v, w - v) / l2));
const vec2 projection = v + t * (w - v); // Projection falls on the segment
return distance(p, projection);
}
EDIT: I needed a Javascript implementation, so here it is, with no dependencies (or comments, but it's a direct port of the above). Points are represented as objects with x and y attributes.
function sqr(x) { return x * x }
function dist2(v, w) { return sqr(v.x - w.x) + sqr(v.y - w.y) }
function distToSegmentSquared(p, v, w) {
var l2 = dist2(v, w);
if (l2 == 0) return dist2(p, v);
var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
t = Math.max(0, Math.min(1, t));
return dist2(p, { x: v.x + t * (w.x - v.x),
y: v.y + t * (w.y - v.y) });
}
function distToSegment(p, v, w) { return Math.sqrt(distToSegmentSquared(p, v, w)); }
EDIT 2: I needed a Java version, but more important, I needed it in 3d instead of 2d.
float dist_to_segment_squared(float px, float py, float pz, float lx1, float ly1, float lz1, float lx2, float ly2, float lz2) {
float line_dist = dist_sq(lx1, ly1, lz1, lx2, ly2, lz2);
if (line_dist == 0) return dist_sq(px, py, pz, lx1, ly1, lz1);
float t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1) + (pz - lz1) * (lz2 - lz1)) / line_dist;
t = constrain(t, 0, 1);
return dist_sq(px, py, pz, lx1 + t * (lx2 - lx1), ly1 + t * (ly2 - ly1), lz1 + t * (lz2 - lz1));
}
Here, in the function parameters, <px,py,pz> is the point in question and the line segment has the endpoints <lx1,ly1,lz1> and <lx2,ly2,lz2>. The function dist_sq (which is assumed to exist) finds the square of the distance between two points.
Here is the simplest complete code in Javascript.
x, y is your target point and x1, y1 to x2, y2 is your line segment.
UPDATED: fix for 0 length line problem from comments.
function pDistance(x, y, x1, y1, x2, y2) {
var A = x - x1;
var B = y - y1;
var C = x2 - x1;
var D = y2 - y1;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = -1;
if (len_sq != 0) //in case of 0 length line
param = dot / len_sq;
var xx, yy;
if (param < 0) {
xx = x1;
yy = y1;
}
else if (param > 1) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
var dx = x - xx;
var dy = y - yy;
return Math.sqrt(dx * dx + dy * dy);
}
UPDATED: Kotlin version
fun getDistance(x: Double, y: Double, x1: Double, y1: Double, x2: Double, y2: Double): Double {
val a = x - x1
val b = y - y1
val c = x2 - x1
val d = y2 - y1
val lenSq = c * c + d * d
val param = if (lenSq != .0) { //in case of 0 length line
val dot = a * c + b * d
dot / lenSq
} else {
-1.0
}
val (xx, yy) = when {
param < 0 -> x1 to y1
param > 1 -> x2 to y2
else -> x1 + param * c to y1 + param * d
}
val dx = x - xx
val dy = y - yy
return hypot(dx, dy)
}
This is an implementation made for FINITE LINE SEGMENTS, not infinite lines like most other functions here seem to be (that's why I made this).
Implementation of theory by Paul Bourke.
Python:
def dist(x1, y1, x2, y2, x3, y3): # x3,y3 is the point
px = x2-x1
py = y2-y1
norm = px*px + py*py
u = ((x3 - x1) * px + (y3 - y1) * py) / float(norm)
if u > 1:
u = 1
elif u < 0:
u = 0
x = x1 + u * px
y = y1 + u * py
dx = x - x3
dy = y - y3
# Note: If the actual distance does not matter,
# if you only want to compare what this function
# returns to other results of this function, you
# can just return the squared distance instead
# (i.e. remove the sqrt) to gain a little performance
dist = (dx*dx + dy*dy)**.5
return dist
AS3:
public static function segmentDistToPoint(segA:Point, segB:Point, p:Point):Number
{
var p2:Point = new Point(segB.x - segA.x, segB.y - segA.y);
var something:Number = p2.x*p2.x + p2.y*p2.y;
var u:Number = ((p.x - segA.x) * p2.x + (p.y - segA.y) * p2.y) / something;
if (u > 1)
u = 1;
else if (u < 0)
u = 0;
var x:Number = segA.x + u * p2.x;
var y:Number = segA.y + u * p2.y;
var dx:Number = x - p.x;
var dy:Number = y - p.y;
var dist:Number = Math.sqrt(dx*dx + dy*dy);
return dist;
}
Java
private double shortestDistance(float x1,float y1,float x2,float y2,float x3,float y3)
{
float px=x2-x1;
float py=y2-y1;
float temp=(px*px)+(py*py);
float u=((x3 - x1) * px + (y3 - y1) * py) / (temp);
if(u>1){
u=1;
}
else if(u<0){
u=0;
}
float x = x1 + u * px;
float y = y1 + u * py;
float dx = x - x3;
float dy = y - y3;
double dist = Math.sqrt(dx*dx + dy*dy);
return dist;
}
In my own question thread how to calculate shortest 2D distance between a point and a line segment in all cases in C, C# / .NET 2.0 or Java? I was asked to put a C# answer here when I find one: so here it is, modified from http://www.topcoder.com/tc?d1=tutorials&d2=geometry1&module=Static :
//Compute the dot product AB . BC
private double DotProduct(double[] pointA, double[] pointB, double[] pointC)
{
double[] AB = new double[2];
double[] BC = new double[2];
AB[0] = pointB[0] - pointA[0];
AB[1] = pointB[1] - pointA[1];
BC[0] = pointC[0] - pointB[0];
BC[1] = pointC[1] - pointB[1];
double dot = AB[0] * BC[0] + AB[1] * BC[1];
return dot;
}
//Compute the cross product AB x AC
private double CrossProduct(double[] pointA, double[] pointB, double[] pointC)
{
double[] AB = new double[2];
double[] AC = new double[2];
AB[0] = pointB[0] - pointA[0];
AB[1] = pointB[1] - pointA[1];
AC[0] = pointC[0] - pointA[0];
AC[1] = pointC[1] - pointA[1];
double cross = AB[0] * AC[1] - AB[1] * AC[0];
return cross;
}
//Compute the distance from A to B
double Distance(double[] pointA, double[] pointB)
{
double d1 = pointA[0] - pointB[0];
double d2 = pointA[1] - pointB[1];
return Math.Sqrt(d1 * d1 + d2 * d2);
}
//Compute the distance from AB to C
//if isSegment is true, AB is a segment, not a line.
double LineToPointDistance2D(double[] pointA, double[] pointB, double[] pointC,
bool isSegment)
{
double dist = CrossProduct(pointA, pointB, pointC) / Distance(pointA, pointB);
if (isSegment)
{
double dot1 = DotProduct(pointA, pointB, pointC);
if (dot1 > 0)
return Distance(pointB, pointC);
double dot2 = DotProduct(pointB, pointA, pointC);
if (dot2 > 0)
return Distance(pointA, pointC);
}
return Math.Abs(dist);
}
I'm #SO not to answer but ask questions so I hope I don't get million down votes for some reasons but constructing critic. I just wanted (and was encouraged) to share somebody else's ideas since the solutions in this thread are either with some exotic language (Fortran, Mathematica) or tagged as faulty by somebody. The only useful one (by Grumdrig) for me is written with C++ and nobody tagged it faulty. But it's missing the methods (dot etc.) that are called.
For anyone interested, here's a trivial conversion of Joshua's Javascript code to Objective-C:
- (double)distanceToPoint:(CGPoint)p fromLineSegmentBetween:(CGPoint)l1 and:(CGPoint)l2
{
double A = p.x - l1.x;
double B = p.y - l1.y;
double C = l2.x - l1.x;
double D = l2.y - l1.y;
double dot = A * C + B * D;
double len_sq = C * C + D * D;
double param = dot / len_sq;
double xx, yy;
if (param < 0 || (l1.x == l2.x && l1.y == l2.y)) {
xx = l1.x;
yy = l1.y;
}
else if (param > 1) {
xx = l2.x;
yy = l2.y;
}
else {
xx = l1.x + param * C;
yy = l1.y + param * D;
}
double dx = p.x - xx;
double dy = p.y - yy;
return sqrtf(dx * dx + dy * dy);
}
I needed this solution to work with MKMapPoint so I will share it in case someone else needs it. Just some minor change and this will return the distance in meters :
- (double)distanceToPoint:(MKMapPoint)p fromLineSegmentBetween:(MKMapPoint)l1 and:(MKMapPoint)l2
{
double A = p.x - l1.x;
double B = p.y - l1.y;
double C = l2.x - l1.x;
double D = l2.y - l1.y;
double dot = A * C + B * D;
double len_sq = C * C + D * D;
double param = dot / len_sq;
double xx, yy;
if (param < 0 || (l1.x == l2.x && l1.y == l2.y)) {
xx = l1.x;
yy = l1.y;
}
else if (param > 1) {
xx = l2.x;
yy = l2.y;
}
else {
xx = l1.x + param * C;
yy = l1.y + param * D;
}
return MKMetersBetweenMapPoints(p, MKMapPointMake(xx, yy));
}
In F#, the distance from the point c to the line segment between a and b is given by:
let pointToLineSegmentDistance (a: Vector, b: Vector) (c: Vector) =
let d = b - a
let s = d.Length
let lambda = (c - a) * d / s
let p = (lambda |> max 0.0 |> min s) * d / s
(a + p - c).Length
The vector d points from a to b along the line segment. The dot product of d/s with c-a gives the parameter of the point of closest approach between the infinite line and the point c. The min and max function are used to clamp this parameter to the range 0..s so that the point lies between a and b. Finally, the length of a+p-c is the distance from c to the closest point on the line segment.
Example use:
pointToLineSegmentDistance (Vector(0.0, 0.0), Vector(1.0, 0.0)) (Vector(-1.0, 1.0))
In Mathematica
It uses a parametric description of the segment, and projects the point into the line defined by the segment. As the parameter goes from 0 to 1 in the segment, if the projection is outside this bounds, we compute the distance to the corresponding enpoint, instead of the straight line normal to the segment.
Clear["Global`*"];
distance[{start_, end_}, pt_] :=
Module[{param},
param = ((pt - start).(end - start))/Norm[end - start]^2; (*parameter. the "."
here means vector product*)
Which[
param < 0, EuclideanDistance[start, pt], (*If outside bounds*)
param > 1, EuclideanDistance[end, pt],
True, EuclideanDistance[pt, start + param (end - start)] (*Normal distance*)
]
];
Plotting result:
Plot3D[distance[{{0, 0}, {1, 0}}, {xp, yp}], {xp, -1, 2}, {yp, -1, 2}]
Plot those points nearer than a cutoff distance:
Contour Plot:
Hey, I just wrote this yesterday. It's in Actionscript 3.0, which is basically Javascript, though you might not have the same Point class.
//st = start of line segment
//b = the line segment (as in: st + b = end of line segment)
//pt = point to test
//Returns distance from point to line segment.
//Note: nearest point on the segment to the test point is right there if we ever need it
public static function linePointDist( st:Point, b:Point, pt:Point ):Number
{
var nearestPt:Point; //closest point on seqment to pt
var keyDot:Number = dot( b, pt.subtract( st ) ); //key dot product
var bLenSq:Number = dot( b, b ); //Segment length squared
if( keyDot <= 0 ) //pt is "behind" st, use st
{
nearestPt = st
}
else if( keyDot >= bLenSq ) //pt is "past" end of segment, use end (notice we are saving twin sqrts here cuz)
{
nearestPt = st.add(b);
}
else //pt is inside segment, reuse keyDot and bLenSq to get percent of seqment to move in to find closest point
{
var keyDotToPctOfB:Number = keyDot/bLenSq; //REM dot product comes squared
var partOfB:Point = new Point( b.x * keyDotToPctOfB, b.y * keyDotToPctOfB );
nearestPt = st.add(partOfB);
}
var dist:Number = (pt.subtract(nearestPt)).length;
return dist;
}
Also, there's a pretty complete and readable discussion of the problem here: notejot.com
One line solution using arctangents:
The idea is to move A to (0, 0) and rotate triangle clockwise to make C lay on X axis,
when this happen, By will be the distance.
a angle = Atan(Cy - Ay, Cx - Ax);
b angle = Atan(By - Ay, Bx - Ax);
AB length = Sqrt( (Bx - Ax)^2 + (By - Ay)^2 )
By = Sin ( bAngle - aAngle) * ABLength
C#
public double Distance(Point a, Point b, Point c)
{
// normalize points
Point cn = new Point(c.X - a.X, c.Y - a.Y);
Point bn = new Point(b.X - a.X, b.Y - a.Y);
double angle = Math.Atan2(bn.Y, bn.X) - Math.Atan2(cn.Y, cn.X);
double abLength = Math.Sqrt(bn.X*bn.X + bn.Y*bn.Y);
return Math.Sin(angle)*abLength;
}
One line C# (to be converted to SQL)
double distance = Math.Sin(Math.Atan2(b.Y - a.Y, b.X - a.X) - Math.Atan2(c.Y - a.Y, c.X - a.X)) * Math.Sqrt((b.X - a.X) * (b.X - a.X) + (b.Y - a.Y) * (b.Y - a.Y))
For the lazy, here's my Objective-C port of #Grumdrig's solution above:
CGFloat sqr(CGFloat x) { return x*x; }
CGFloat dist2(CGPoint v, CGPoint w) { return sqr(v.x - w.x) + sqr(v.y - w.y); }
CGFloat distanceToSegmentSquared(CGPoint p, CGPoint v, CGPoint w)
{
CGFloat l2 = dist2(v, w);
if (l2 == 0.0f) return dist2(p, v);
CGFloat t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0.0f) return dist2(p, v);
if (t > 1.0f) return dist2(p, w);
return dist2(p, CGPointMake(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)));
}
CGFloat distanceToSegment(CGPoint point, CGPoint segmentPointV, CGPoint segmentPointW)
{
return sqrtf(distanceToSegmentSquared(point, segmentPointV, segmentPointW));
}
Couldn't resist coding it in python :)
from math import sqrt, fabs
def pdis(a, b, c):
t = b[0]-a[0], b[1]-a[1] # Vector ab
dd = sqrt(t[0]**2+t[1]**2) # Length of ab
t = t[0]/dd, t[1]/dd # unit vector of ab
n = -t[1], t[0] # normal unit vector to ab
ac = c[0]-a[0], c[1]-a[1] # vector ac
return fabs(ac[0]*n[0]+ac[1]*n[1]) # Projection of ac to n (the minimum distance)
print pdis((1,1), (2,2), (2,0)) # Example (answer is 1.414)
Ditto for fortran :)
real function pdis(a, b, c)
real, dimension(0:1), intent(in) :: a, b, c
real, dimension(0:1) :: t, n, ac
real :: dd
t = b - a ! Vector ab
dd = sqrt(t(0)**2+t(1)**2) ! Length of ab
t = t/dd ! unit vector of ab
n = (/-t(1), t(0)/) ! normal unit vector to ab
ac = c - a ! vector ac
pdis = abs(ac(0)*n(0)+ac(1)*n(1)) ! Projection of ac to n (the minimum distance)
end function pdis
program test
print *, pdis((/1.0,1.0/), (/2.0,2.0/), (/2.0,0.0/)) ! Example (answer is 1.414)
end program test
Here is a more complete spelling out of Grumdrig's solution. This version also returns the closest point itself.
#include "stdio.h"
#include "math.h"
class Vec2
{
public:
float _x;
float _y;
Vec2()
{
_x = 0;
_y = 0;
}
Vec2( const float x, const float y )
{
_x = x;
_y = y;
}
Vec2 operator+( const Vec2 &v ) const
{
return Vec2( this->_x + v._x, this->_y + v._y );
}
Vec2 operator-( const Vec2 &v ) const
{
return Vec2( this->_x - v._x, this->_y - v._y );
}
Vec2 operator*( const float f ) const
{
return Vec2( this->_x * f, this->_y * f );
}
float DistanceToSquared( const Vec2 p ) const
{
const float dX = p._x - this->_x;
const float dY = p._y - this->_y;
return dX * dX + dY * dY;
}
float DistanceTo( const Vec2 p ) const
{
return sqrt( this->DistanceToSquared( p ) );
}
float DotProduct( const Vec2 p ) const
{
return this->_x * p._x + this->_y * p._y;
}
};
// return minimum distance between line segment vw and point p, and the closest point on the line segment, q
float DistanceFromLineSegmentToPoint( const Vec2 v, const Vec2 w, const Vec2 p, Vec2 * const q )
{
const float distSq = v.DistanceToSquared( w ); // i.e. |w-v|^2 ... avoid a sqrt
if ( distSq == 0.0 )
{
// v == w case
(*q) = v;
return v.DistanceTo( p );
}
// consider the line extending the segment, parameterized as v + t (w - v)
// we find projection of point p onto the line
// it falls where t = [(p-v) . (w-v)] / |w-v|^2
const float t = ( p - v ).DotProduct( w - v ) / distSq;
if ( t < 0.0 )
{
// beyond the v end of the segment
(*q) = v;
return v.DistanceTo( p );
}
else if ( t > 1.0 )
{
// beyond the w end of the segment
(*q) = w;
return w.DistanceTo( p );
}
// projection falls on the segment
const Vec2 projection = v + ( ( w - v ) * t );
(*q) = projection;
return p.DistanceTo( projection );
}
float DistanceFromLineSegmentToPoint( float segmentX1, float segmentY1, float segmentX2, float segmentY2, float pX, float pY, float *qX, float *qY )
{
Vec2 q;
float distance = DistanceFromLineSegmentToPoint( Vec2( segmentX1, segmentY1 ), Vec2( segmentX2, segmentY2 ), Vec2( pX, pY ), &q );
(*qX) = q._x;
(*qY) = q._y;
return distance;
}
void TestDistanceFromLineSegmentToPoint( float segmentX1, float segmentY1, float segmentX2, float segmentY2, float pX, float pY )
{
float qX;
float qY;
float d = DistanceFromLineSegmentToPoint( segmentX1, segmentY1, segmentX2, segmentY2, pX, pY, &qX, &qY );
printf( "line segment = ( ( %f, %f ), ( %f, %f ) ), p = ( %f, %f ), distance = %f, q = ( %f, %f )\n",
segmentX1, segmentY1, segmentX2, segmentY2, pX, pY, d, qX, qY );
}
void TestDistanceFromLineSegmentToPoint()
{
TestDistanceFromLineSegmentToPoint( 0, 0, 1, 1, 1, 0 );
TestDistanceFromLineSegmentToPoint( 0, 0, 20, 10, 5, 4 );
TestDistanceFromLineSegmentToPoint( 0, 0, 20, 10, 30, 15 );
TestDistanceFromLineSegmentToPoint( 0, 0, 20, 10, -30, 15 );
TestDistanceFromLineSegmentToPoint( 0, 0, 10, 0, 5, 1 );
TestDistanceFromLineSegmentToPoint( 0, 0, 0, 10, 1, 5 );
}
Consider this modification to Grumdrig's answer above. Many times you'll find that floating point imprecision can cause problems. I'm using doubles in the version below, but you can easily change to floats. The important part is that it uses an epsilon to handle the "slop". In addition, you'll many times want to know WHERE the intersection happened, or if it happened at all. If the returned t is < 0.0 or > 1.0, no collision occurred. However, even if no collision occurred, many times you'll want to know where the closest point on the segment to P is, and thus I use qx and qy to return this location.
double PointSegmentDistanceSquared( double px, double py,
double p1x, double p1y,
double p2x, double p2y,
double& t,
double& qx, double& qy)
{
static const double kMinSegmentLenSquared = 0.00000001; // adjust to suit. If you use float, you'll probably want something like 0.000001f
static const double kEpsilon = 1.0E-14; // adjust to suit. If you use floats, you'll probably want something like 1E-7f
double dx = p2x - p1x;
double dy = p2y - p1y;
double dp1x = px - p1x;
double dp1y = py - p1y;
const double segLenSquared = (dx * dx) + (dy * dy);
if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared)
{
// segment is a point.
qx = p1x;
qy = p1y;
t = 0.0;
return ((dp1x * dp1x) + (dp1y * dp1y));
}
else
{
// Project a line from p to the segment [p1,p2]. By considering the line
// extending the segment, parameterized as p1 + (t * (p2 - p1)),
// we find projection of point p onto the line.
// It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2
t = ((dp1x * dx) + (dp1y * dy)) / segLenSquared;
if (t < kEpsilon)
{
// intersects at or to the "left" of first segment vertex (p1x, p1y). If t is approximately 0.0, then
// intersection is at p1. If t is less than that, then there is no intersection (i.e. p is not within
// the 'bounds' of the segment)
if (t > -kEpsilon)
{
// intersects at 1st segment vertex
t = 0.0;
}
// set our 'intersection' point to p1.
qx = p1x;
qy = p1y;
// Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
// we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)).
}
else if (t > (1.0 - kEpsilon))
{
// intersects at or to the "right" of second segment vertex (p2x, p2y). If t is approximately 1.0, then
// intersection is at p2. If t is greater than that, then there is no intersection (i.e. p is not within
// the 'bounds' of the segment)
if (t < (1.0 + kEpsilon))
{
// intersects at 2nd segment vertex
t = 1.0;
}
// set our 'intersection' point to p2.
qx = p2x;
qy = p2y;
// Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
// we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)).
}
else
{
// The projection of the point to the point on the segment that is perpendicular succeeded and the point
// is 'within' the bounds of the segment. Set the intersection point as that projected point.
qx = p1x + (t * dx);
qy = p1y + (t * dy);
}
// return the squared distance from p to the intersection point. Note that we return the squared distance
// as an optimization because many times you just need to compare relative distances and the squared values
// works fine for that. If you want the ACTUAL distance, just take the square root of this value.
double dpqx = px - qx;
double dpqy = py - qy;
return ((dpqx * dpqx) + (dpqy * dpqy));
}
}
I'm assuming you want to find the shortest distance between the point and a line segment; to do this, you need to find the line (lineA) which is perpendicular to your line segment (lineB) which goes through your point, determine the intersection between that line (lineA) and your line which goes through your line segment (lineB); if that point is between the two points of your line segment, then the distance is the distance between your point and the point you just found which is the intersection of lineA and lineB; if the point is not between the two points of your line segment, you need to get the distance between your point and the closer of two ends of the line segment; this can be done easily by taking the square distance (to avoid a square root) between the point and the two points of the line segment; whichever is closer, take the square root of that one.
Here it is using Swift
/* Distance from a point (p1) to line l1 l2 */
func distanceFromPoint(p: CGPoint, toLineSegment l1: CGPoint, and l2: CGPoint) -> CGFloat {
let A = p.x - l1.x
let B = p.y - l1.y
let C = l2.x - l1.x
let D = l2.y - l1.y
let dot = A * C + B * D
let len_sq = C * C + D * D
let param = dot / len_sq
var xx, yy: CGFloat
if param < 0 || (l1.x == l2.x && l1.y == l2.y) {
xx = l1.x
yy = l1.y
} else if param > 1 {
xx = l2.x
yy = l2.y
} else {
xx = l1.x + param * C
yy = l1.y + param * D
}
let dx = p.x - xx
let dy = p.y - yy
return sqrt(dx * dx + dy * dy)
}
Grumdrig's C++/JavaScript implementation was very useful to me, so I have provided a Python direct port that I am using. The complete code is here.
class Point(object):
def __init__(self, x, y):
self.x = float(x)
self.y = float(y)
def square(x):
return x * x
def distance_squared(v, w):
return square(v.x - w.x) + square(v.y - w.y)
def distance_point_segment_squared(p, v, w):
# Segment length squared, |w-v|^2
d2 = distance_squared(v, w)
if d2 == 0:
# v == w, return distance to v
return distance_squared(p, v)
# Consider the line extending the segment, parameterized as v + t (w - v).
# We find projection of point p onto the line.
# It falls where t = [(p-v) . (w-v)] / |w-v|^2
t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / d2;
if t < 0:
# Beyond v end of the segment
return distance_squared(p, v)
elif t > 1.0:
# Beyond w end of the segment
return distance_squared(p, w)
else:
# Projection falls on the segment.
proj = Point(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y))
# print proj.x, proj.y
return distance_squared(p, proj)
And now my solution as well......
(Javascript)
It is very fast because I try to avoid any Math.pow functions.
As you can see, at the end of the function I have the distance of the line.
code is from the lib http://www.draw2d.org/graphiti/jsdoc/#!/example
/**
* Static util function to determine is a point(px,py) on the line(x1,y1,x2,y2)
* A simple hit test.
*
* #return {boolean}
* #static
* #private
* #param {Number} coronaWidth the accepted corona for the hit test
* #param {Number} X1 x coordinate of the start point of the line
* #param {Number} Y1 y coordinate of the start point of the line
* #param {Number} X2 x coordinate of the end point of the line
* #param {Number} Y2 y coordinate of the end point of the line
* #param {Number} px x coordinate of the point to test
* #param {Number} py y coordinate of the point to test
**/
graphiti.shape.basic.Line.hit= function( coronaWidth, X1, Y1, X2, Y2, px, py)
{
// Adjust vectors relative to X1,Y1
// X2,Y2 becomes relative vector from X1,Y1 to end of segment
X2 -= X1;
Y2 -= Y1;
// px,py becomes relative vector from X1,Y1 to test point
px -= X1;
py -= Y1;
var dotprod = px * X2 + py * Y2;
var projlenSq;
if (dotprod <= 0.0) {
// px,py is on the side of X1,Y1 away from X2,Y2
// distance to segment is length of px,py vector
// "length of its (clipped) projection" is now 0.0
projlenSq = 0.0;
} else {
// switch to backwards vectors relative to X2,Y2
// X2,Y2 are already the negative of X1,Y1=>X2,Y2
// to get px,py to be the negative of px,py=>X2,Y2
// the dot product of two negated vectors is the same
// as the dot product of the two normal vectors
px = X2 - px;
py = Y2 - py;
dotprod = px * X2 + py * Y2;
if (dotprod <= 0.0) {
// px,py is on the side of X2,Y2 away from X1,Y1
// distance to segment is length of (backwards) px,py vector
// "length of its (clipped) projection" is now 0.0
projlenSq = 0.0;
} else {
// px,py is between X1,Y1 and X2,Y2
// dotprod is the length of the px,py vector
// projected on the X2,Y2=>X1,Y1 vector times the
// length of the X2,Y2=>X1,Y1 vector
projlenSq = dotprod * dotprod / (X2 * X2 + Y2 * Y2);
}
}
// Distance to line is now the length of the relative point
// vector minus the length of its projection onto the line
// (which is zero if the projection falls outside the range
// of the line segment).
var lenSq = px * px + py * py - projlenSq;
if (lenSq < 0) {
lenSq = 0;
}
return Math.sqrt(lenSq)<coronaWidth;
};
C#
Adapted from #Grumdrig
public static double MinimumDistanceToLineSegment(this Point p,
Line line)
{
var v = line.StartPoint;
var w = line.EndPoint;
double lengthSquared = DistanceSquared(v, w);
if (lengthSquared == 0.0)
return Distance(p, v);
double t = Math.Max(0, Math.Min(1, DotProduct(p - v, w - v) / lengthSquared));
var projection = v + t * (w - v);
return Distance(p, projection);
}
public static double Distance(Point a, Point b)
{
return Math.Sqrt(DistanceSquared(a, b));
}
public static double DistanceSquared(Point a, Point b)
{
var d = a - b;
return DotProduct(d, d);
}
public static double DotProduct(Point a, Point b)
{
return (a.X * b.X) + (a.Y * b.Y);
}
Matlab code, with built-in "self test" if they call the function with no arguments:
function r = distPointToLineSegment( xy0, xy1, xyP )
% r = distPointToLineSegment( xy0, xy1, xyP )
if( nargin < 3 )
selfTest();
r=0;
else
vx = xy0(1)-xyP(1);
vy = xy0(2)-xyP(2);
ux = xy1(1)-xy0(1);
uy = xy1(2)-xy0(2);
lenSqr= (ux*ux+uy*uy);
detP= -vx*ux + -vy*uy;
if( detP < 0 )
r = norm(xy0-xyP,2);
elseif( detP > lenSqr )
r = norm(xy1-xyP,2);
else
r = abs(ux*vy-uy*vx)/sqrt(lenSqr);
end
end
function selfTest()
%#ok<*NASGU>
disp(['invalid args, distPointToLineSegment running (recursive) self-test...']);
ptA = [1;1]; ptB = [-1;-1];
ptC = [1/2;1/2]; % on the line
ptD = [-2;-1.5]; % too far from line segment
ptE = [1/2;0]; % should be same as perpendicular distance to line
ptF = [1.5;1.5]; % along the A-B but outside of the segment
distCtoAB = distPointToLineSegment(ptA,ptB,ptC)
distDtoAB = distPointToLineSegment(ptA,ptB,ptD)
distEtoAB = distPointToLineSegment(ptA,ptB,ptE)
distFtoAB = distPointToLineSegment(ptA,ptB,ptF)
figure(1); clf;
circle = #(x, y, r, c) rectangle('Position', [x-r, y-r, 2*r, 2*r], ...
'Curvature', [1 1], 'EdgeColor', c);
plot([ptA(1) ptB(1)],[ptA(2) ptB(2)],'r-x'); hold on;
plot(ptC(1),ptC(2),'b+'); circle(ptC(1),ptC(2), 0.5e-1, 'b');
plot(ptD(1),ptD(2),'g+'); circle(ptD(1),ptD(2), distDtoAB, 'g');
plot(ptE(1),ptE(2),'k+'); circle(ptE(1),ptE(2), distEtoAB, 'k');
plot(ptF(1),ptF(2),'m+'); circle(ptF(1),ptF(2), distFtoAB, 'm');
hold off;
axis([-3 3 -3 3]); axis equal;
end
end
coded in t-sql
the point is (#px, #py) and the line segment runs from (#ax, #ay) to (#bx, #by)
create function fn_sqr (#NumberToSquare decimal(18,10))
returns decimal(18,10)
as
begin
declare #Result decimal(18,10)
set #Result = #NumberToSquare * #NumberToSquare
return #Result
end
go
create function fn_Distance(#ax decimal (18,10) , #ay decimal (18,10), #bx decimal(18,10), #by decimal(18,10))
returns decimal(18,10)
as
begin
declare #Result decimal(18,10)
set #Result = (select dbo.fn_sqr(#ax - #bx) + dbo.fn_sqr(#ay - #by) )
return #Result
end
go
create function fn_DistanceToSegmentSquared(#px decimal(18,10), #py decimal(18,10), #ax decimal(18,10), #ay decimal(18,10), #bx decimal(18,10), #by decimal(18,10))
returns decimal(18,10)
as
begin
declare #l2 decimal(18,10)
set #l2 = (select dbo.fn_Distance(#ax, #ay, #bx, #by))
if #l2 = 0
return dbo.fn_Distance(#px, #py, #ax, #ay)
declare #t decimal(18,10)
set #t = ((#px - #ax) * (#bx - #ax) + (#py - #ay) * (#by - #ay)) / #l2
if (#t < 0)
return dbo.fn_Distance(#px, #py, #ax, #ay);
if (#t > 1)
return dbo.fn_Distance(#px, #py, #bx, #by);
return dbo.fn_Distance(#px, #py, #ax + #t * (#bx - #ax), #ay + #t * (#by - #ay))
end
go
create function fn_DistanceToSegment(#px decimal(18,10), #py decimal(18,10), #ax decimal(18,10), #ay decimal(18,10), #bx decimal(18,10), #by decimal(18,10))
returns decimal(18,10)
as
begin
return sqrt(dbo.fn_DistanceToSegmentSquared(#px, #py , #ax , #ay , #bx , #by ))
end
go
--example execution for distance from a point at (6,1) to line segment that runs from (4,2) to (2,1)
select dbo.fn_DistanceToSegment(6, 1, 4, 2, 2, 1)
--result = 2.2360679775
--example execution for distance from a point at (-3,-2) to line segment that runs from (0,-2) to (-2,1)
select dbo.fn_DistanceToSegment(-3, -2, 0, -2, -2, 1)
--result = 2.4961508830
--example execution for distance from a point at (0,-2) to line segment that runs from (0,-2) to (-2,1)
select dbo.fn_DistanceToSegment(0,-2, 0, -2, -2, 1)
--result = 0.0000000000
Looks like just about everyone else on StackOverflow has contributed an answer (23 answers so far), so here's my contribution for C#. This is mostly based on the answer by M. Katz, which in turn is based on the answer by Grumdrig.
public struct MyVector
{
private readonly double _x, _y;
// Constructor
public MyVector(double x, double y)
{
_x = x;
_y = y;
}
// Distance from this point to another point, squared
private double DistanceSquared(MyVector otherPoint)
{
double dx = otherPoint._x - this._x;
double dy = otherPoint._y - this._y;
return dx * dx + dy * dy;
}
// Find the distance from this point to a line segment (which is not the same as from this
// point to anywhere on an infinite line). Also returns the closest point.
public double DistanceToLineSegment(MyVector lineSegmentPoint1, MyVector lineSegmentPoint2,
out MyVector closestPoint)
{
return Math.Sqrt(DistanceToLineSegmentSquared(lineSegmentPoint1, lineSegmentPoint2,
out closestPoint));
}
// Same as above, but avoid using Sqrt(), saves a new nanoseconds in cases where you only want
// to compare several distances to find the smallest or largest, but don't need the distance
public double DistanceToLineSegmentSquared(MyVector lineSegmentPoint1,
MyVector lineSegmentPoint2, out MyVector closestPoint)
{
// Compute length of line segment (squared) and handle special case of coincident points
double segmentLengthSquared = lineSegmentPoint1.DistanceSquared(lineSegmentPoint2);
if (segmentLengthSquared < 1E-7f) // Arbitrary "close enough for government work" value
{
closestPoint = lineSegmentPoint1;
return this.DistanceSquared(closestPoint);
}
// Use the magic formula to compute the "projection" of this point on the infinite line
MyVector lineSegment = lineSegmentPoint2 - lineSegmentPoint1;
double t = (this - lineSegmentPoint1).DotProduct(lineSegment) / segmentLengthSquared;
// Handle the two cases where the projection is not on the line segment, and the case where
// the projection is on the segment
if (t <= 0)
closestPoint = lineSegmentPoint1;
else if (t >= 1)
closestPoint = lineSegmentPoint2;
else
closestPoint = lineSegmentPoint1 + (lineSegment * t);
return this.DistanceSquared(closestPoint);
}
public double DotProduct(MyVector otherVector)
{
return this._x * otherVector._x + this._y * otherVector._y;
}
public static MyVector operator +(MyVector leftVector, MyVector rightVector)
{
return new MyVector(leftVector._x + rightVector._x, leftVector._y + rightVector._y);
}
public static MyVector operator -(MyVector leftVector, MyVector rightVector)
{
return new MyVector(leftVector._x - rightVector._x, leftVector._y - rightVector._y);
}
public static MyVector operator *(MyVector aVector, double aScalar)
{
return new MyVector(aVector._x * aScalar, aVector._y * aScalar);
}
// Added using ReSharper due to CodeAnalysis nagging
public bool Equals(MyVector other)
{
return _x.Equals(other._x) && _y.Equals(other._y);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is MyVector && Equals((MyVector) obj);
}
public override int GetHashCode()
{
unchecked
{
return (_x.GetHashCode()*397) ^ _y.GetHashCode();
}
}
public static bool operator ==(MyVector left, MyVector right)
{
return left.Equals(right);
}
public static bool operator !=(MyVector left, MyVector right)
{
return !left.Equals(right);
}
}
And here's a little test program.
public static class JustTesting
{
public static void Main()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 10000000; i++)
{
TestIt(1, 0, 0, 0, 1, 1, 0.70710678118654757);
TestIt(5, 4, 0, 0, 20, 10, 1.3416407864998738);
TestIt(30, 15, 0, 0, 20, 10, 11.180339887498949);
TestIt(-30, 15, 0, 0, 20, 10, 33.541019662496844);
TestIt(5, 1, 0, 0, 10, 0, 1.0);
TestIt(1, 5, 0, 0, 0, 10, 1.0);
}
stopwatch.Stop();
TimeSpan timeSpan = stopwatch.Elapsed;
}
private static void TestIt(float aPointX, float aPointY,
float lineSegmentPoint1X, float lineSegmentPoint1Y,
float lineSegmentPoint2X, float lineSegmentPoint2Y,
double expectedAnswer)
{
// Katz
double d1 = DistanceFromPointToLineSegment(new MyVector(aPointX, aPointY),
new MyVector(lineSegmentPoint1X, lineSegmentPoint1Y),
new MyVector(lineSegmentPoint2X, lineSegmentPoint2Y));
Debug.Assert(d1 == expectedAnswer);
/*
// Katz using squared distance
double d2 = DistanceFromPointToLineSegmentSquared(new MyVector(aPointX, aPointY),
new MyVector(lineSegmentPoint1X, lineSegmentPoint1Y),
new MyVector(lineSegmentPoint2X, lineSegmentPoint2Y));
Debug.Assert(Math.Abs(d2 - expectedAnswer * expectedAnswer) < 1E-7f);
*/
/*
// Matti (optimized)
double d3 = FloatVector.DistanceToLineSegment(new PointF(aPointX, aPointY),
new PointF(lineSegmentPoint1X, lineSegmentPoint1Y),
new PointF(lineSegmentPoint2X, lineSegmentPoint2Y));
Debug.Assert(Math.Abs(d3 - expectedAnswer) < 1E-7f);
*/
}
private static double DistanceFromPointToLineSegment(MyVector aPoint,
MyVector lineSegmentPoint1, MyVector lineSegmentPoint2)
{
MyVector closestPoint; // Not used
return aPoint.DistanceToLineSegment(lineSegmentPoint1, lineSegmentPoint2,
out closestPoint);
}
private static double DistanceFromPointToLineSegmentSquared(MyVector aPoint,
MyVector lineSegmentPoint1, MyVector lineSegmentPoint2)
{
MyVector closestPoint; // Not used
return aPoint.DistanceToLineSegmentSquared(lineSegmentPoint1, lineSegmentPoint2,
out closestPoint);
}
}
As you can see, I tried to measure the difference between using the version that avoids the Sqrt() method and the normal version. My tests indicate you can maybe save about 2.5%, but I'm not even sure of that - the variations within the various test runs were of the same order of magnitude. I also tried measuring the version posted by Matti (plus an obvious optimization), and that version seems to be about 4% slower than the version based on Katz/Grumdrig code.
Edit: Incidentally, I've also tried measuring a method that finds the distance to an infinite line (not a line segment) using a cross product (and a Sqrt()), and it's about 32% faster.
Here is devnullicus's C++ version converted to C#. For my implementation I needed to know the point of intersection and found his solution to work well.
public static bool PointSegmentDistanceSquared(PointF point, PointF lineStart, PointF lineEnd, out double distance, out PointF intersectPoint)
{
const double kMinSegmentLenSquared = 0.00000001; // adjust to suit. If you use float, you'll probably want something like 0.000001f
const double kEpsilon = 1.0E-14; // adjust to suit. If you use floats, you'll probably want something like 1E-7f
double dX = lineEnd.X - lineStart.X;
double dY = lineEnd.Y - lineStart.Y;
double dp1X = point.X - lineStart.X;
double dp1Y = point.Y - lineStart.Y;
double segLenSquared = (dX * dX) + (dY * dY);
double t = 0.0;
if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared)
{
// segment is a point.
intersectPoint = lineStart;
t = 0.0;
distance = ((dp1X * dp1X) + (dp1Y * dp1Y));
}
else
{
// Project a line from p to the segment [p1,p2]. By considering the line
// extending the segment, parameterized as p1 + (t * (p2 - p1)),
// we find projection of point p onto the line.
// It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2
t = ((dp1X * dX) + (dp1Y * dY)) / segLenSquared;
if (t < kEpsilon)
{
// intersects at or to the "left" of first segment vertex (lineStart.X, lineStart.Y). If t is approximately 0.0, then
// intersection is at p1. If t is less than that, then there is no intersection (i.e. p is not within
// the 'bounds' of the segment)
if (t > -kEpsilon)
{
// intersects at 1st segment vertex
t = 0.0;
}
// set our 'intersection' point to p1.
intersectPoint = lineStart;
// Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
// we were doing PointLineDistanceSquared, then intersectPoint.X would be (lineStart.X + (t * dx)) and intersectPoint.Y would be (lineStart.Y + (t * dy)).
}
else if (t > (1.0 - kEpsilon))
{
// intersects at or to the "right" of second segment vertex (lineEnd.X, lineEnd.Y). If t is approximately 1.0, then
// intersection is at p2. If t is greater than that, then there is no intersection (i.e. p is not within
// the 'bounds' of the segment)
if (t < (1.0 + kEpsilon))
{
// intersects at 2nd segment vertex
t = 1.0;
}
// set our 'intersection' point to p2.
intersectPoint = lineEnd;
// Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
// we were doing PointLineDistanceSquared, then intersectPoint.X would be (lineStart.X + (t * dx)) and intersectPoint.Y would be (lineStart.Y + (t * dy)).
}
else
{
// The projection of the point to the point on the segment that is perpendicular succeeded and the point
// is 'within' the bounds of the segment. Set the intersection point as that projected point.
intersectPoint = new PointF((float)(lineStart.X + (t * dX)), (float)(lineStart.Y + (t * dY)));
}
// return the squared distance from p to the intersection point. Note that we return the squared distance
// as an optimization because many times you just need to compare relative distances and the squared values
// works fine for that. If you want the ACTUAL distance, just take the square root of this value.
double dpqX = point.X - intersectPoint.X;
double dpqY = point.Y - intersectPoint.Y;
distance = ((dpqX * dpqX) + (dpqY * dpqY));
}
return true;
}
A 2D and 3D solution
Consider a change of basis such that the line segment becomes (0, 0, 0)-(d, 0, 0) and the point (u, v, 0). The shortest distance occurs in that plane and is given by
u ≤ 0 -> d(A, C)
0 ≤ u ≤ d -> |v|
d ≤ u -> d(B, C)
(the distance to one of the endpoints or to the supporting line, depending on the projection to the line. The iso-distance locus is made of two half-circles and two line segments.)
In the above expression, d is the length of the segment AB, and u, v are respectivey the scalar product and (modulus of the) cross product of AB/d (unit vector in the direction of AB) and AC. Hence vectorially,
AB.AC ≤ 0 -> |AC|
0 ≤ AB.AC ≤ AB² -> |ABxAC|/|AB|
AB² ≤ AB.AC -> |BC|
see the Matlab GEOMETRY toolbox in the following website:
http://people.sc.fsu.edu/~jburkardt/m_src/geometry/geometry.html
ctrl+f and type "segment" to find line segment related functions. the functions "segment_point_dist_2d.m" and "segment_point_dist_3d.m" are what you need.
The GEOMETRY codes are available in a C version and a C++ version and a FORTRAN77 version and a FORTRAN90 version and a MATLAB version.
AutoHotkeys version based on Joshua's Javascript:
plDist(x, y, x1, y1, x2, y2) {
A:= x - x1
B:= y - y1
C:= x2 - x1
D:= y2 - y1
dot:= A*C + B*D
sqLen:= C*C + D*D
param:= dot / sqLen
if (param < 0 || ((x1 = x2) && (y1 = y2))) {
xx:= x1
yy:= y1
} else if (param > 1) {
xx:= x2
yy:= y2
} else {
xx:= x1 + param*C
yy:= y1 + param*D
}
dx:= x - xx
dy:= y - yy
return sqrt(dx*dx + dy*dy)
}
the accepted answer does not work
(e.g. distance between 0,0 and (-10,2,10,2) should be 2).
here's code that works:
def dist2line2(x,y,line):
x1,y1,x2,y2=line
vx = x1 - x
vy = y1 - y
ux = x2-x1
uy = y2-y1
length = ux * ux + uy * uy
det = (-vx * ux) + (-vy * uy) #//if this is < 0 or > length then its outside the line segment
if det < 0:
return (x1 - x)**2 + (y1 - y)**2
if det > length:
return (x2 - x)**2 + (y2 - y)**2
det = ux * vy - uy * vx
return det**2 / length
def dist2line(x,y,line): return math.sqrt(dist2line2(x,y,line))
Didn't see a Java implementation here, so I translated the Javascript function from the accepted answer to Java code:
static double sqr(double x) {
return x * x;
}
static double dist2(DoublePoint v, DoublePoint w) {
return sqr(v.x - w.x) + sqr(v.y - w.y);
}
static double distToSegmentSquared(DoublePoint p, DoublePoint v, DoublePoint w) {
double l2 = dist2(v, w);
if (l2 == 0) return dist2(p, v);
double t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0) return dist2(p, v);
if (t > 1) return dist2(p, w);
return dist2(p, new DoublePoint(
v.x + t * (w.x - v.x),
v.y + t * (w.y - v.y)
));
}
static double distToSegment(DoublePoint p, DoublePoint v, DoublePoint w) {
return Math.sqrt(distToSegmentSquared(p, v, w));
}
static class DoublePoint {
public double x;
public double y;
public DoublePoint(double x, double y) {
this.x = x;
this.y = y;
}
}
WPF version:
public class LineSegment
{
private readonly Vector _offset;
private readonly Vector _vector;
public LineSegment(Point start, Point end)
{
_offset = (Vector)start;
_vector = (Vector)(end - _offset);
}
public double DistanceTo(Point pt)
{
var v = (Vector)pt - _offset;
// first, find a projection point on the segment in parametric form (0..1)
var p = (v * _vector) / _vector.LengthSquared;
// and limit it so it lays inside the segment
p = Math.Min(Math.Max(p, 0), 1);
// now, find the distance from that point to our point
return (_vector * p - v).Length;
}
}
I've made an interactive Desmos graph to demonstrate how to achieve this:
https://www.desmos.com/calculator/kswrm8ddum
The red point is A, the green point is B, and the point C is blue.
You can drag the points in the graph to see the values change.
On the left, the value 's' is the parameter of the line segment (i.e. s = 0 means the point A, and s = 1 means the point B).
The value 'd' is the distance from the third point to the line through A and B.
EDIT:
Fun little insight: the coordinate (s, d) is the coordinate of the third point C in the coordinate system where AB is the unit x-axis, and the unit y-axis is perpendicular to AB.
Python Numpy implementation for 2D coordinate array:
import numpy as np
def dist2d(p1, p2, coords):
''' Distance from points to a finite line btwn p1 -> p2 '''
assert coords.ndim == 2 and coords.shape[1] == 2, 'coords is not 2 dim'
dp = p2 - p1
st = dp[0]**2 + dp[1]**2
u = ((coords[:, 0] - p1[0]) * dp[0] + (coords[:, 1] - p1[1]) * dp[1]) / st
u[u > 1.] = 1.
u[u < 0.] = 0.
dx = (p1[0] + u * dp[0]) - coords[:, 0]
dy = (p1[1] + u * dp[1]) - coords[:, 1]
return np.sqrt(dx**2 + dy**2)
# Usage:
p1 = np.array([0., 0.])
p2 = np.array([0., 10.])
# List of coordinates
coords = np.array(
[[0., 0.],
[5., 5.],
[10., 10.],
[20., 20.]
])
d = dist2d(p1, p2, coords)
# Single coordinate
coord = np.array([25., 25.])
d = dist2d(p1, p2, coord[np.newaxis, :])