Created
January 10, 2014 14:56
-
-
Save nraynaud/8355721 to your computer and use it in GitHub Desktop.
testing js clipper float difference.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Simple Bentley Ottmann with mock underlying structures</title> | |
<link rel="stylesheet" href="qunit-1.12.0.css"> | |
<style> | |
.svgTest td { | |
border: solid 1px green; | |
} | |
.svgTest th { | |
text-align: center !important; /*some qunit selector use an #id, so we have to force it*/ | |
} | |
path { | |
fill: none; | |
stroke: black; | |
vector-effect: non-scaling-stroke; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="clipper_unminified_6.1.3.1_fpoint.js"></script> | |
<script> | |
//https://github.com/bjornharrtell/jsts/blob/master/src/jsts/algorithm/RobustDeterminant.js | |
function signOfDet2x2(x1, y1, x2, y2) { | |
//returns -1 if the determinant is negative, | |
// returns 1 if the determinant is positive, | |
// returns 0 if the determinant is null. | |
var sign, swap, k, count; | |
count = 0; | |
sign = 1; | |
/* | |
* testing null entries | |
*/ | |
if ((x1 === 0.0) || (y2 === 0.0)) { | |
if ((y1 === 0.0) || (x2 === 0.0)) { | |
return 0; | |
} | |
else if (y1 > 0) { | |
if (x2 > 0) { | |
return -sign; | |
} | |
else { | |
return sign; | |
} | |
} | |
else { | |
if (x2 > 0) { | |
return sign; | |
} | |
else { | |
return -sign; | |
} | |
} | |
} | |
if ((y1 === 0.0) || (x2 === 0.0)) { | |
if (y2 > 0) { | |
if (x1 > 0) { | |
return sign; | |
} | |
else { | |
return -sign; | |
} | |
} | |
else { | |
if (x1 > 0) { | |
return -sign; | |
} | |
else { | |
return sign; | |
} | |
} | |
} | |
/* | |
* making y coordinates positive and permuting the entries | |
*/ | |
/* | |
* so that y2 is the biggest one | |
*/ | |
if (0.0 < y1) { | |
if (0.0 < y2) { | |
if (y1 > y2) { | |
sign = -sign; | |
swap = x1; | |
x1 = x2; | |
x2 = swap; | |
swap = y1; | |
y1 = y2; | |
y2 = swap; | |
} | |
} | |
else { | |
if (y1 <= -y2) { | |
sign = -sign; | |
x2 = -x2; | |
y2 = -y2; | |
} | |
else { | |
swap = x1; | |
x1 = -x2; | |
x2 = swap; | |
swap = y1; | |
y1 = -y2; | |
y2 = swap; | |
} | |
} | |
} | |
else { | |
if (0.0 < y2) { | |
if (-y1 <= y2) { | |
sign = -sign; | |
x1 = -x1; | |
y1 = -y1; | |
} | |
else { | |
swap = -x1; | |
x1 = x2; | |
x2 = swap; | |
swap = -y1; | |
y1 = y2; | |
y2 = swap; | |
} | |
} | |
else { | |
if (y1 >= y2) { | |
x1 = -x1; | |
y1 = -y1; | |
x2 = -x2; | |
y2 = -y2; | |
} | |
else { | |
sign = -sign; | |
swap = -x1; | |
x1 = -x2; | |
x2 = swap; | |
swap = -y1; | |
y1 = -y2; | |
y2 = swap; | |
} | |
} | |
} | |
/* | |
* making x coordinates positive | |
*/ | |
/* | |
* if |x2| < |x1| one can conclude | |
*/ | |
if (0.0 < x1) { | |
if (0.0 < x2) { | |
if (x1 > x2) { | |
return sign; | |
} | |
} | |
else { | |
return sign; | |
} | |
} | |
else { | |
if (0.0 < x2) { | |
return -sign; | |
} | |
else { | |
if (x1 >= x2) { | |
sign = -sign; | |
x1 = -x1; | |
x2 = -x2; | |
} | |
else { | |
return -sign; | |
} | |
} | |
} | |
/* | |
* all entries strictly positive x1 <= x2 and y1 <= y2 | |
*/ | |
while (true) { | |
count = count + 1; | |
k = Math.floor(x2 / x1); | |
x2 = x2 - k * x1; | |
y2 = y2 - k * y1; | |
/* | |
* testing if R (new U2) is in U1 rectangle | |
*/ | |
if (y2 < 0.0) { | |
return -sign; | |
} | |
if (y2 > y1) { | |
return sign; | |
} | |
/* | |
* finding R' | |
*/ | |
if (x1 > x2 + x2) { | |
if (y1 < y2 + y2) { | |
return sign; | |
} | |
} | |
else { | |
if (y1 > y2 + y2) { | |
return -sign; | |
} | |
else { | |
x2 = x1 - x2; | |
y2 = y1 - y2; | |
sign = -sign; | |
} | |
} | |
if (y2 === 0.0) { | |
if (x2 === 0.0) { | |
return 0; | |
} | |
else { | |
return -sign; | |
} | |
} | |
if (x2 === 0.0) { | |
return sign; | |
} | |
/* | |
* exchange 1 and 2 role. | |
*/ | |
k = Math.floor(x1 / x2); | |
x1 = x1 - k * x2; | |
y1 = y1 - k * y2; | |
/* | |
* testing if R (new U1) is in U2 rectangle | |
*/ | |
if (y1 < 0.0) { | |
return sign; | |
} | |
if (y1 > y2) { | |
return -sign; | |
} | |
/* | |
* finding R' | |
*/ | |
if (x2 > x1 + x1) { | |
if (y2 < y1 + y1) { | |
return -sign; | |
} | |
} | |
else { | |
if (y2 > y1 + y1) { | |
return sign; | |
} | |
else { | |
x1 = x2 - x1; | |
y1 = y2 - y1; | |
sign = -sign; | |
} | |
} | |
if (y1 === 0.0) { | |
if (x1 === 0.0) { | |
return 0; | |
} | |
else { | |
return sign; | |
} | |
} | |
if (x1 === 0.0) { | |
return -sign; | |
} | |
} | |
} | |
function orientationIndex(p1, p2, q) { | |
/** | |
* MD - 9 Aug 2010 It seems that the basic algorithm is slightly orientation | |
* dependent, when computing the orientation of a point very close to a | |
* line. This is possibly due to the arithmetic in the translation to the | |
* origin. | |
* | |
* For instance, the following situation produces identical results in spite | |
* of the inverse orientation of the line segment: | |
* | |
* Coordinate p0 = new Coordinate(219.3649559090992, 140.84159161824724); | |
* Coordinate p1 = new Coordinate(168.9018919682399, -5.713787599646864); | |
* | |
* Coordinate p = new Coordinate(186.80814046338352, 46.28973405831556); int | |
* orient = orientationIndex(p0, p1, p); int orientInv = | |
* orientationIndex(p1, p0, p); | |
* | |
* | |
*/ | |
var dx1 = p2.X - p1.X; | |
var dy1 = p2.Y - p1.Y; | |
var dx2 = q.X - p2.X; | |
var dy2 = q.Y - p2.Y; | |
return signOfDet2x2(dx1, dy1, dx2, dy2); | |
} | |
</script> | |
<script> | |
function subtractRectangles(smallNumber1, smallNumber2, smallNumber3, bigNumber) { | |
if (!(smallNumber1 < smallNumber2 && smallNumber2 < smallNumber3 && smallNumber3 < bigNumber )) | |
throw new Error('order has to be respected'); | |
var y1 = 10; | |
var y2 = 20; | |
var subj_paths = [ | |
[ | |
{X: smallNumber1, Y: y1}, | |
{X: smallNumber3, Y: y1}, | |
{X: smallNumber3, Y: y2}, | |
{X: smallNumber1, Y: y2} | |
] | |
]; | |
var clip_paths = [ | |
[ | |
{X: smallNumber2, Y: y1}, | |
{X: bigNumber, Y: y1}, | |
{X: bigNumber, Y: y2}, | |
{X: smallNumber2, Y: y2} | |
] | |
]; | |
var cpr = new ClipperLib.Clipper(); | |
cpr.AddPaths(subj_paths, ClipperLib.PolyType.ptSubject, true); // true means closed path | |
cpr.AddPaths(clip_paths, ClipperLib.PolyType.ptClip, true); | |
var solution_paths = new ClipperLib.Paths(); | |
cpr.Execute(ClipperLib.ClipType.ctDifference, solution_paths, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero); | |
return solution_paths; | |
} | |
console.log("computed with IsAlmostEqual (1)", subtractRectangles(1, 2, 3, Number.MAX_VALUE / 2)); | |
var minVal = 2.220446049250313e-16; | |
console.log("computed with IsAlmostEqual (epsilon)", subtractRectangles(minVal, minVal * 2, minVal * 3, Number.MAX_VALUE / 2)); | |
console.log("computed with IsAlmostEqual (Number.MIN_VALUE)", subtractRectangles(Number.MIN_VALUE, Number.MIN_VALUE * 2, Number.MIN_VALUE * 3, Number.MAX_VALUE / 2)); | |
var oldSlopesEquals = ClipperLib.ClipperBase.SlopesEqual; | |
ClipperLib.ClipperBase.SlopesEqual = function () { | |
if (arguments.length != 3) | |
return oldSlopesEquals.apply(this, arguments); | |
else { | |
console.log('using orientationIndex'); | |
return !orientationIndex(arguments[0], arguments[1], arguments[2]); | |
} | |
}; | |
console.log("computed with robust determinant (Number.MIN_VALUE)", subtractRectangles(Number.MIN_VALUE, Number.MIN_VALUE * 2, Number.MIN_VALUE * 3, Number.MAX_VALUE / 2)); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment