I'm attempting to use Google's OR-Tools to produce a distance constraint between 2x 2D points (p0 & p1).
This becomes the non-linear expression:
(p1x - p0x)² + (p1y - p0y)² = d²
I have attempted to produce this with the CP-Sat solver using the following code:
int64_t d = 50;
int64_t d_sq = d*d;
// p0 at (10, 20)
IntVar p0x = cp_model.NewConstant(0);
IntVar p0y = cp_model.NewConstant(0);
// p1 is to be determined by solver
IntVar p1x = cp_model.NewIntVar({ int64_t_min, int64_t_max });
IntVar p1y = cp_model.NewIntVar({ int64_t_min, int64_t_max });
// x1-x0 and y1-y0
IntVar xDif = cp_model.NewIntVar({ -d, d });
IntVar yDif = cp_model.NewIntVar({ -d, d });
cp_model.AddEquality(xDif, p1x - p0x);
cp_model.AddEquality(yDif, p1y - p1y);
// xdif_sq = xdif^2 & ydif_sq = ydif^2
IntVar xDif_sq = cp_model.NewIntVar({ 0, d_sq });obvious solution is to add a tolerance range to d. But I'm having an issue
IntVar yDif_sq = cp_model.NewIntVar({ 0, d_sq });
cp_model.AddMultiplicationEquality(xDif_sq, { xDif, xDif });
cp_model.AddMultiplicationEquality(yDif_sq, { yDif, yDif });
// dif_sq_sum = xdif^2 + ydif^2
IntVar Dif_sq_sum = cp_model.NewIntVar({ 0, 2*d_sq });
cp_model.AddEquality(Dif_sq_sum, xDif_sq + yDif_sq);
// make both sides of equation equal
cp_model.AddEquality(iv_Dif_sq_sum, d_sq);
The problem is that because the solver only accepts integers, it can only calculate the position of p1 when the all parts are integer values.
For example, 3² + 4² = 5² works fine.
But I want to make it work for when a term is non-integer? a² + b² = (almost) c²
The obvious solution is to add a tolerance range to d. But I'm having an issue trying to achieve this as when I do, the status becomes infeasible. Here is the code I've tried.
IntVar iv_Error;
cp_model.AddMultiplicationEquality(iv_Dif_sq_sum, { iv_Error, iv_Error });
cp_model.AddLessThan(iv_Error, d + 1);
cp_model.AddGreaterThan(iv_Error, d - 1);
If anybody has any good suggestions I would be very grateful!
Thanks
Cross-posted from https://github.com/google/or-tools/discussions/3220.
The discussion is happening there.
Related
Background:
Heya! I'm trying to generate a circuit board which has a subset of San Francisco printed on it. Most of the pieces of this are done, and I'm generating images that look like this:
The problem is that I am rendering lines which extend outside my hardcoded cutoff boundary (I am rendering lines which one side is in and one side is out of bounds).
Question:
Given a set of lines like this:
# x1,y1, x2,y2
10,10,40,40
80,80,120,120
How can I modify the co-ordinates of each line such that it 'cuts off' at a specific bound?
In the case above, the second line (which in original form) extends to (120,120), should only extend to (100,100) assuming bounds of 100,100.
Thoughts
Based on what I remember from high-school math, I should plug something into the formula y=mx+b yeah? Even then, how would I deal with an infinite gradient or the like?
Thanks for any and all help :D Puesdocode/python/Go preferred, but explanations just as graciously recieved.
<3
Tom
Your best friend is the Cohen–Sutherland line clipping algorithm.
https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
Sat down and worked it out. My rudimentary approach was to:
Compute the slope of the line & the y-intercept
Check both points on all four sides to see if they exceed bounds, and if they do, recompute the necessary co-ordinate by plugging the bound into the formula y=mx+b.
Here is my Go code:
func boundLine(line *kcgen.Line) {
if line.Start.X == line.End.X {
panic("infinite slope not yet supported")
}
slope := (line.End.Y - line.Start.Y) / (line.End.X - line.Start.X)
b := line.End.Y - (slope * line.End.X) //y = mx + b which is equivalent to b = y - mx
if line.Start.X < (-*width/2) {
line.Start.Y = (slope * (-*width/2)) + b
line.Start.X = -*width/2
}
if line.End.X < (-*width/2) {
line.End.Y = (slope * (-*width/2)) + b
line.End.X = -*width/2
}
if line.Start.X > (*width/2) {
line.Start.Y = (slope * (*width/2)) + b
line.Start.X = *width/2
}
if line.End.X > (*width/2) {
line.End.Y = (slope * (*width/2)) + b
line.End.X = *width/2
}
if line.Start.Y < (-*height/2) {
line.Start.Y = -*height/2
line.Start.X = ((-*height/2) - b) / slope //y = mx + b equiv. (y-b)/m = x
}
if line.End.Y < (-*height/2) {
line.End.Y = -*height/2
line.End.X = ((-*height/2) - b) / slope //y = mx + b equiv. (y-b)/m = x
}
if line.Start.Y > (*height/2) {
line.Start.Y = *height/2
line.Start.X = ((*height/2) - b) / slope //y = mx + b equiv. (y-b)/m = x
}
if line.End.Y > (*height/2) {
line.End.Y = *height/2
line.End.X = ((*height/2) - b) / slope //y = mx + b equiv. (y-b)/m = x
}
}
I am trying to figure out where a bunch of line-segments clip into a window around them. I saw the Liang–Barsky algorithm, but that seems to assume the segments already clip the edges of the window, which these do not.
Say I have a window from (0,0) to (26,16), and the following segments:
(7,6) - (16,3)
(10,6) - (19,6)
(13,10) - (21,3)
(16,12) - (19,14)
Illustration:
I imagine I need to extend the segments to a certain X or Y point, till they hit the edge of the window, but I don't know how.
How would I find the points where these segments (converted to lines?) clip into the edge of the window? I will be implementing this in C#, but this is pretty language-agnostic.
If you have two line segments P and Q with points
P0 - P1
Q0 - Q1
The line equations are
P = P0 + t(P1 - P0)
Q = Q0 + r(Q1 - Q0)
then to find out where they intersect after extension you need to solve the following equation for t and r
P0 + t(P1 - P0) = Q0 + r(Q1 - Q0)
The following code can do this. ( Extracted from my own code base )
public static (double t, double r )? SolveIntersect(this Segment2D P, Segment2D Q)
{
// a-d are the entries of a 2x2 matrix
var a = P.P1.X - P.P0.X;
var b = -Q.P1.X + Q.P0.X;
var c = P.P1.Y - P.P0.Y;
var d = -Q.P1.Y + Q.P0.Y;
var det = a*d - b*c;
if (Math.Abs( det ) < Utility.ZERO_TOLERANCE)
return null;
var x = Q.P0.X - P.P0.X;
var y = Q.P0.Y - P.P0.Y;
var t = 1/det*(d*x - b*y);
var r = 1/det*(-c*x + a*y);
return (t, r);
}
If null is returned from the function then it means the lines are parallel and cannot intersect. If a result is returned then you can do.
var result = SolveIntersect( P, Q );
if (result != null)
{
var ( t, r) = result.Value;
var p = P.P0 + t * (P.P1 - P.P0);
var q = Q.P0 + t * (Q.P1 - Q.P0);
// p and q are the same point of course
}
The extended line segments will generally intersect more than one box edge but only one of those intersections will be inside the box. You can check this easily.
bool IsInBox(Point corner0, Point corner1, Point test) =>
(test.X > corner0.X && test.X < corner1.X && test.Y > corner0.Y && test.Y < corner1.Y ;
That should give you all you need to extend you lines to the edge of your box.
I managed to figure this out.
I can extend my lines to the edge of the box by first finding the equations of my lines, then solving for the X and Y of each of the sides to get their corresponding point. This requires passing the max and min Y and the max and min X into the following functions, returning 4 values. If the point is outside the bounds of the box, it can be ignored.
My code is in C#, and is making extension methods for EMGU's LineSegment2D. This is a .NET wrapper for OpenCv.
My Code:
public static float GetYIntersection(this LineSegment2D line, float x)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if(dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return m * x + b;
}
public static float GetXIntersection(this LineSegment2D line, float y)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if (dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return (y - b) / m;
}
I can then take these points, check if they are in the bounds of the box, discard the ones that are not, remove duplicate points (line goes directly into corner). This will leave me with one x and one y value, which I can then pair to the corresponding min or max Y or X values I passed into the functions to make 2 points. I can then make my new segment with the two points.
Wiki description of Liang-Barsky algorithm is not bad, but code is flaw.
Note: this algorithm intended to throw out lines without intersection as soon as possible. If most of lines intersect the rectangle, then approach from your answer might be rather effective, otherwise L-B algorithm wins.
This page describes approach in details and contains concise effective code:
// Liang-Barsky function by Daniel White # http://www.skytopia.com/project/articles/compsci/clipping.html
// This function inputs 8 numbers, and outputs 4 new numbers (plus a boolean value to say whether the clipped line is drawn at all).
//
bool LiangBarsky (double edgeLeft, double edgeRight, double edgeBottom, double edgeTop, // Define the x/y clipping values for the border.
double x0src, double y0src, double x1src, double y1src, // Define the start and end points of the line.
double &x0clip, double &y0clip, double &x1clip, double &y1clip) // The output values, so declare these outside.
{
double t0 = 0.0; double t1 = 1.0;
double xdelta = x1src-x0src;
double ydelta = y1src-y0src;
double p,q,r;
for(int edge=0; edge<4; edge++) { // Traverse through left, right, bottom, top edges.
if (edge==0) { p = -xdelta; q = -(edgeLeft-x0src); }
if (edge==1) { p = xdelta; q = (edgeRight-x0src); }
if (edge==2) { p = -ydelta; q = -(edgeBottom-y0src);}
if (edge==3) { p = ydelta; q = (edgeTop-y0src); }
if(p==0 && q<0) return false; // Don't draw line at all. (parallel line outside)
r = q/p;
if(p<0) {
if(r>t1) return false; // Don't draw line at all.
else if(r>t0) t0=r; // Line is clipped!
} else if(p>0) {
if(r<t0) return false; // Don't draw line at all.
else if(r<t1) t1=r; // Line is clipped!
}
}
x0clip = x0src + t0*xdelta;
y0clip = y0src + t0*ydelta;
x1clip = x0src + t1*xdelta;
y1clip = y0src + t1*ydelta;
return true; // (clipped) line is drawn
}
Except using look-up tables, is there another way to optimize the parameterization algorithm of a cubic Bézier curve like this? (5000 steps for a good parameterization is simply too much for a slower PC, as I need to call this function many times in 1 second):
function parameterizeCurve(path, partArc, initialT)
{
// curve length is already known and globally defined
// brute force
var STEPS = 5000; // > precision
var t = 1 / STEPS;
var aX=0;
var aY=0;
var bX=path[0], bY=path[1];
var dX=0, dY=0;
var dS = 0;
var sumArc = 0;
var arrT = new Array(Math.round(partArc));
var z = 1;
arrT[0] = -1;
var oldpartArc = partArc;
partArc = partArc - initialT;
var j = 0;
for (var i=0; i<STEPS; j = j + t) {
aX = bezierPoint(j, path[0], path[2], path[4], path[6]);
aY = bezierPoint(j, path[1], path[3], path[5], path[7]);
dX = aX - bX;
dY = aY - bY;
// deltaS. Pitagora
dS = Math.sqrt((dX * dX) + (dY * dY));
sumArc = sumArc + dS;
if (sumArc >= partArc) {
arrT[z] = j; // save current t
z++;
sumArc = 0;
partArc = oldpartArc;
}
bX = aX;
bY = aY;
i++;
}
return arrT;
}
function bezierPoint(t, o1, c1, c2, e1) {
var C1 = (e1 - (3.0 * c2) + (3.0 * c1) - o1);
var C2 = ((3.0 * c2) - (6.0 * c1) + (3.0 * o1));
var C3 = ((3.0 * c1) - (3.0 * o1));
var C4 = (o1);
return ((C1*t*t*t) + (C2*t*t) + (C3*t) + C4)
}
If I've guessed correctly, you're trying to come up with a cubic Bezier curve parameterization that moves at a constant speed along the curve.
So, why do you need 5000 steps? The minimum one can move along a curve is one pixel. A Bezier stays within the convex hull of its four control points, so the length of the curve will be less than that of the polyline P0 -> P1 -> P2 -> P3. So find that length in pixels, and use it (instead of 5000).
Let me know if that speeds things up enough.
I'd like to calculate a point on a quadratic curve. To use it with the canvas element of HTML5.
When I use the quadraticCurveTo() function in JavaScript, I have a source point, a target point and a control point.
How can I calculate a point on the created quadratic curve at let's say t=0.5 with "only" knowing this three points?
Use the quadratic Bézier formula, found, for instance, on the Wikipedia page for Bézier Curves:
In pseudo-code, that's
t = 0.5; // given example value
x = (1 - t) * (1 - t) * p[0].x + 2 * (1 - t) * t * p[1].x + t * t * p[2].x;
y = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y;
p[0] is the start point, p[1] is the control point, and p[2] is the end point. t is the parameter, which goes from 0 to 1.
In case somebody needs the cubic form:
//B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3
x = (1-t)*(1-t)*(1-t)*p0x + 3*(1-t)*(1-t)*t*p1x + 3*(1-t)*t*t*p2x + t*t*t*p3x;
y = (1-t)*(1-t)*(1-t)*p0y + 3*(1-t)*(1-t)*t*p1y + 3*(1-t)*t*t*p2y + t*t*t*p3y;
I created this demo :
// x = a * (1-t)³ + b * 3 * (1-t)²t + c * 3 * (1-t)t² + d * t³
//------------------------------------------------------------
// x = a - 3at + 3at² - at³
// + 3bt - 6bt² + 3bt³
// + 3ct² - 3ct³
// + dt³
//--------------------------------
// x = - at³ + 3bt³ - 3ct³ + dt³
// + 3at² - 6bt² + 3ct²
// - 3at + 3bt
// + a
//--------------------------------
// 0 = t³ (-a+3b-3c+d) + => A
// t² (3a-6b+3c) + => B
// t (-3a+3b) + => c
// a - x => D
//--------------------------------
var A = d - 3*c + 3*b - a,
B = 3*c - 6*b + 3*a,
C = 3*b - 3*a,
D = a-x;
// So we need to solve At³ + Bt² + Ct + D = 0
Full example here
may help someone.
I edited talkhabis answer (cubic curve) so the curve is displayed with the right coordinates. (Couldn't comment)
The Y-coordinates needed to be changed (-p[].y+150). (A new variable for that might be a nicer and more efficient solution, but you get the idea)
// Apply points to SVG and create the curve and controllers :
var path = document.getElementById('path'),
ctrl1 = document.getElementById('ctrl1'),
ctrl2 = document.getElementById('ctrl2'),
D = 'M ' + p0.x + ' ' + (-p0.y+150) +
'C ' + c0.x + ' ' + (-c0.y+150) +', ' + c1.x + ' ' + (-c1.y+150) + ', ' + p1.x + ' ' + (-p1.y+150);
path.setAttribute('d',D);
ctrl1.setAttribute('d','M'+p0.x+','+(-p0.y+150)+'L'+c0.x+','+(-c0.y+150));
ctrl2.setAttribute('d','M'+p1.x+','+(-p1.y+150)+'L'+c1.x+','+(-c1.y+150));
// Lets test the "Bezier Function"
var t = 0, point = document.getElementById('point');
setInterval(function(){
var p = Bezier(p0,c0,c1,p1,t);
point.setAttribute('cx',p.x);
point.setAttribute('cy',-p.y+150);
t += 0.01;
if(t>=1) t=0;
},50);
// OK ... Now tring to get "y" on cruve based on mouse "x" :
var svg = document.getElementById('svg'),
point2 = document.getElementById('point2');
svg.onmousemove = function(e){
var x = (e.pageX - 50)/2,
y = (e.pageY - 50)/2;
// "-50" because of "50px margin" on the left side
// and "/2" because the svg width is 300 units and 600 px => 300 = 600/2
// Get the x,y by mouse x
var p = YBX(p0,c0,c1,p1,x);
point2.setAttribute('cx',p.x);
point2.setAttribute('cy',-p.y+150);
}
http://jsfiddle.net/u214gco8/1/
I also created some C-Code to test the results for the cubic curve. Just enter the X and Y coordinates in the main function.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void bezierCurve(int x[] , int y[])
{
double xu = 0.0 , yu = 0.0 , u = 0.0 ;
int i = 0 ;
for(u = 0.0 ; u <= 1.0 ; u += 0.05)
{
xu = pow(1-u,3)*x[0]+3*u*pow(1-u,2)*x[1]+3*pow(u,2)*(1-u)*x[2]
+pow(u,3)*x[3];
yu = pow(1-u,3)*y[0]+3*u*pow(1-u,2)*y[1]+3*pow(u,2)*(1-u)*y[2]
+pow(u,3)*y[3];
printf("X: %i Y: %i \n" , (int)xu , (int)yu) ;
}
}
int main(void) {
int x[] = {0,75,50,300};
int y[] = {0,2,140,100};
bezierCurve(x,y);
return 0;
}
https://ideone.com/glLXcB
Just a note: If you are using the usual formulas presented here then don't expect t = 0.5 to return the point at half of the curve's length.. In most cases it won't.
More on this here under "§23 — Tracing a curve at fixed distance intervals" and here.
how can i extract rotation, scale and translation values from 2d transformation matrix? i mean a have a 2d transformation
matrix = [1, 0, 0, 1, 0, 0]
matrix.rotate(45 / 180 * PI)
matrix.scale(3, 4)
matrix.translate(50, 100)
matrix.rotate(30 / 180 * PI)
matrix.scale(-2, 4)
now my matrix have values [a, b, c, d, tx, ty]
lets forget about the processes above and imagine that we have only the values a, b, c, d, tx, ty
how can i find total rotation and scale values via a, b, c, d, tx, ty
sorry for my english
Thanks your advance
EDIT
I think it should be an answer somewhere...
i just tried in Flash Builder (AS3) like this
var m:Matrix = new Matrix;
m.rotate(.25 * Math.PI);
m.scale(4, 5);
m.translate(100, 50);
m.rotate(.33 * Math.PI);
m.scale(-3, 2.5);
var shape:Shape = new Shape;
shape.transform.matrix = m;
trace(shape.x, shape.y, shape.scaleX, shape.scaleY, shape.rotation);
and the output is:
x = -23.6
y = 278.8
scaleX = 11.627334873920528
scaleY = -13.54222263865791
rotation = 65.56274134518259 (in degrees)
Not all values of a,b,c,d,tx,ty will yield a valid rotation sequence. I assume the above values are part of a 3x3 homogeneous rotation matrix in 2D
| a b tx |
A = | c d ty |
| 0 0 1 |
which transforms the coordinates [x, y, 1] into:
[x', y', 1] = A * |x|
|y|
|z|
Thus set the traslation into [dx, dy]=[tx, ty]
The scale is sx = sqrt(a² + c²) and sy = sqrt(b² + d²)
The rotation angle is t = atan(c/d) or t = atan(-b/a) as also they should be the same.
Otherwise you don't have a valid rotation matrix.
The above transformation is expanded to:
x' = tx + sx (x Cos θ - y Sin θ)
y' = ty + sy (x Sin θ + y Cos θ)
when the order is rotation, followed by scale and then translation.
I ran into this problem today and found the easiest solution to transform a point using the matrix. This way, you can extract the translation first, then rotation and scaling.
This only works if x and y are always scaled the same (uniform scaling).
Given your matrix m which has undergone a series of transforms,
var translate:Point;
var rotate:Number;
var scale:Number;
// extract translation
var p:Point = new Point();
translate = m.transformPoint(p);
m.translate( -translate.x, -translate.y);
// extract (uniform) scale
p.x = 1.0;
p.y = 0.0;
p = m.transformPoint(p);
scale = p.length;
// and rotation
rotate = Math.atan2(p.y, p.x);
There you go!
The term for this is matrix decomposition. Here is a solution that includes skew as described by Frédéric Wang.
function decompose_2d_matrix(mat) {
var a = mat[0];
var b = mat[1];
var c = mat[2];
var d = mat[3];
var e = mat[4];
var f = mat[5];
var delta = a * d - b * c;
let result = {
translation: [e, f],
rotation: 0,
scale: [0, 0],
skew: [0, 0],
};
// Apply the QR-like decomposition.
if (a != 0 || b != 0) {
var r = Math.sqrt(a * a + b * b);
result.rotation = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
result.scale = [r, delta / r];
result.skew = [Math.atan((a * c + b * d) / (r * r)), 0];
} else if (c != 0 || d != 0) {
var s = Math.sqrt(c * c + d * d);
result.rotation =
Math.PI / 2 - (d > 0 ? Math.acos(-c / s) : -Math.acos(c / s));
result.scale = [delta / s, s];
result.skew = [0, Math.atan((a * c + b * d) / (s * s))];
} else {
// a = b = c = d = 0
}
return result;
}
If in scaling you'd scaled by the same amount in x and in y, then the determinant of the matrix, i.e. ad-bc, which tells you the area multiplier would tell you the linear change of scale too - it would be the square root of the determinant. atan( b/a ) or better atan2( b,a ) would tell you the total angle you have rotated through.
However, as your scaling isn't uniform, there is usually not going to be a way to condense your series of rotations and scaling to a single rotation followed by a single non-uniform scaling in x and y.