Skip to content

Instantly share code, notes, and snippets.

@nraynaud
Created January 10, 2014 14:56
Show Gist options
  • Save nraynaud/8355721 to your computer and use it in GitHub Desktop.
Save nraynaud/8355721 to your computer and use it in GitHub Desktop.
testing js clipper float difference.
<!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