Немного сложно понять, в чем проблема на самом деле. Clipper (также при использовании SimplifyPolygons или SimplifyPolygon) может создавать только слабо простые многоугольники, что означает, что могут быть псевдопублицированные точки: хотя последовательные координаты гарантируются не идентичными, некоторые из следующих точек могут иметь одну и ту же координату. Также координата может быть на линии между двумя точками.
После упрощения (или любой другой логической операции) вы можете выполнить шаг очистки, используя смещение с небольшим отрицательным значением: https://sourceforge.net/p/jsclipper/wiki/documentation/#clipperlibclipperoffsetexecute .
Это, возможно, удаляет все псевдопублицированные точки.
Я также сделал поплавковую версию Clipper ( http://jsclipper.sourceforge.net/6.4.2.2_fpoint/ ). Он тщательно протестирован, но поскольку Ангус Джонсон, автор оригинальной C # Clipper (с которого портирована JS-версия), считает, что использование float вызывает проблемы с устойчивостью, хотя, по моим тестам, таких проблем нет, оригинальный C # float версия не существует. Версия с плавающей точкой проще в использовании, и вы можете попробовать небольшое отрицательное смещение: например. -0,001 или -0,01.
Вы также можете попробовать PolyTree или ExPolygons ( https://sourceforge.net/p/jsclipper/wiki/ExPolygons%20and%20PolyTree%206/ ). ExPolygons может использоваться для получения отверстий и контуров, а PolyTree может использоваться для получения полного родителя-дочернего отношения отверстий и контуров.
1117] Последнее средство - функция сломанной ручки. Он обнаруживает все псевдодублирующиеся точки и создает для них эффект разбитого пера, чтобы в результате не было дубликатов. Прикрепленные изображения показывают, что означает этот эффект, используя большое значение nib-effect, чтобы сделать эффект более понятным. Триангуляция многоугольника Three.js не удалась в псевдопубликатных точках . Существует обсуждение https://github.com/mrdoob/three.js/issues/3386 этой темы.
// Make polygons to simple by making "a broken pen tip" effect on each semi-adjacent (duplicate) vertex
// ORIGPOLY can be a contour
// or exPolygon structure
function BreakPenNibs(ORIGPOLY, dist, scale)
{
if (!dist || dist < 0) return;
var sqrt = Math.sqrt;
var allpoints = {}, point = {};
var key = "";
var currX = 0.0,
currY = 0.0;
var prevX = 0.0,
prevY = 0.0;
var nextX = 0.0,
nextY;
var x = 0.0,
y = 0.0,
length = 0.0,
i = 0,
duplcount = 0,
j = 0;
var prev_i = 0,
next_i = 0,
last_i;
var extra_vertices = new Array(100),
moved_vertices = new Array(100);
// Get first all duplicates
var duplicates = new Array(100),
indexi = "",
indexstr = "",
arraystr = "",
polys, outer, holes;
if (ORIGPOLY instanceof Array)
{
outer = ORIGPOLY;
}
else if (ORIGPOLY.outer instanceof Array)
{
outer = ORIGPOLY.outer;
}
else return;
if (ORIGPOLY.holes instanceof Array) holes = ORIGPOLY.holes;
else holes = [];
polys = [outer].concat(holes);
var polys_length = polys.length;
// Get first max lenght of arrays
var max_index_len = 0;
var arr_len;
i = polys_length;
while (i--)
{
arr_len = polys[i].length;
if (arr_len > max_index_len) max_index_len = arr_len;
}
max_index_len = max_index_len.toString().length;
var max_polys_length = polys_length.toString().length;
var poly;
j = polys_length;
var scaling = scale/10;
while (j--)
{
poly = polys[j];
ilen = poly.length;
i = ilen;
while (i--)
{
point = poly[i];
//key = Math.round(point.X) + ":" + Math.round(point.Y);
key = (Math.round(point.X / scaling) * scaling)
+ ":" + (Math.round(point.Y / scaling) * scaling);
indexi = allpoints[key];
if (typeof (indexi) != "undefined")
{
// first found duplicate
duplicates[duplcount] = indexi;
duplcount++;
arraystr = j.toString();
while (arraystr.length < max_polys_length) arraystr = "0" + arraystr;
indexstr = i.toString();
while (indexstr.length < max_index_len) indexstr = "0" + indexstr;
duplicates[duplcount] = arraystr + "." + indexstr;
duplcount++;
}
arraystr = j.toString();
while (arraystr.length < max_polys_length) arraystr = "0" + arraystr;
indexstr = i.toString();
while (indexstr.length < max_index_len) indexstr = "0" + indexstr;
allpoints[key] = arraystr + "." + indexstr;
}
}
if (!duplcount) return;
duplicates.length = duplcount;
duplicates.sort();
//console.log(JSON.stringify(duplicates));
var splitted, poly_index = 0,
nth_dupl = 0;
var prev_poly_index = -1;
poly_index = 0;
for (j = 0; j < duplcount; j++)
{
splitted = duplicates[j].split(".");
poly_index = parseInt(splitted[0], 10);
if (poly_index != prev_poly_index) nth_dupl = 0;
else nth_dupl++;
i = parseInt(splitted[1], 10);
poly = polys[poly_index];
len = poly.length;
if (poly[0].X === poly[len - 1].X &&
poly[0].Y === poly[len - 1].Y)
{
last_i = len - 2;
}
else
{
last_i = len - 1;
}
point = poly[i];
// Calculate "broken pen tip" effect
// for current point by finding
// a coordinate at a distance dist
// along the edge between current and
// previous point
// This is inlined to maximize speed
currX = point.X;
currY = point.Y;
if (i === 0) prev_i = last_i; // last element in array
else prev_i = i - 1;
prevX = poly[prev_i].X;
prevY = poly[prev_i].Y;
x=0;y=0;
if (!point.Collinear)
{
length = sqrt((-currX + prevX) * (-currX + prevX) + (currY - prevY) * (currY - prevY));
//console.log(length);
x = currX - (dist * (currX - prevX)) / length;
y = currY - (dist * (currY - prevY)) / length;
}
// save the found (calculated) point
moved_vertices[j] = {
X: x,
Y: y,
Collinear:point.Collinear,
index: i,
poly_index: poly_index
};
// "broken nib effect" for next point also
if (i == len - 1) next_i = 0;
else next_i = i + 1;
nextX = poly[next_i].X;
nextY = poly[next_i].Y;
x=0;y=0;
if (!point.Collinear)
{
length = sqrt((-currX + nextX) * (-currX + nextX) + (currY - nextY) * (currY - nextY));
x = currX - (dist * (currX - nextX)) / length;
y = currY - (dist * (currY - nextY)) / length;
}
// save the found (calculated) point
extra_vertices[j] = {
X: x,
Y: y,
Collinear:point.Collinear,
index: i + nth_dupl,
poly_index: poly_index
};
prev_poly_index = poly_index;
}
moved_vertices.length = extra_vertices.length = duplcount;
//console.log("MOVED:" + JSON.stringify(moved_vertices));
//console.log("EXTRA:" + JSON.stringify(extra_vertices));
// Update moved coordinates
i = duplcount;
var point2;
while (i--)
{
point = moved_vertices[i];
x = point.X;
y = point.Y;
// Faster than isNaN: http://jsperf.com/isnan-alternatives
if (x != x || x == Infinity || x == -Infinity) continue;
if (y != y || y == Infinity || y == -Infinity) continue;
point2 = polys[point.poly_index][point.index];
point2.X = point.X;
point2.Y = point.Y;
point2.Collinear = point.Collinear;
}
// Add an extra vertex
// This is needed to remain the angle of the next edge
for (i = 0; i < duplcount; i++)
{
point = extra_vertices[i];
x = point.X;
y = point.Y;
// Faster than isNaN: http://jsperf.com/isnan-alternatives
if (x != x || x == Infinity || x == -Infinity) continue;
if (y != y || y == Infinity || y == -Infinity) continue;
polys[point.poly_index].splice(point.index + 1, 0,
{
X: point.X,
Y: point.Y,
Collinear: point.Collinear
});
}
// Remove collinear points
// and for some reason coming
// sequential duplicates
// TODO: check why seq. duplicates becomes
j = polys.length;
var prev_point = null;
while (j--)
{
poly = polys[j];
ilen = poly.length;
i = ilen;
while (i--)
{
point = poly[i];
if(prev_point!=null && point.X == prev_point.X && point.Y == prev_point.Y) poly.splice(i, 1);
else
if(point.Collinear) poly.splice(i, 1);
prev_point = point;
}
}
//console.log(JSON.stringify(polys));
// because original array is modified, no need to return anything
}
var BreakPenNipsOfExPolygons = function (exPolygons, dist, scale)
{
var i = 0,
j = 0,
ilen = exPolygons.length,
jlen = 0;
for (; i < ilen; i++)
{
//if(i!=4) continue;
BreakPenNibs(exPolygons[i], dist, scale);
}
};
[1119 ]
They're using a combination of CSS and JavaScript. The flip animation is powered by a CSS Sprite-like technique.
First of all, they have a very tall image called filmstrip.png
that contains every flip "state" for each number (0 to 9; have a look at a scaled-down detail and you'll see what I mean).
Then, in the HTML, each digit is made up of three nested elements:
The first is a container element, which has its width
and height
set to the dimensions of a single flip "state", and its overflow
set to hidden
. This element is positioned relatively.
The second element is positioned absolutely (and because the first element is positioned relatively, this second element is positioned absolutely relative to the first element).
The third element has its background-image
set to filmstrip.png
, and its width
and height
set to the dimensions of this image.
The JavaScript then seems to rapidly change the top
property of the second element, causing different parts of filmstrip.png
to be exposed one after another, thus resulting in a flip animation.
Steve