@versatiles/png-renderer 2.4.0 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +7 -0
- package/dist/index.js +82 -12
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2741,6 +2741,12 @@ declare class Feature implements Feature$1 {
|
|
|
2741
2741
|
* clipped edges along the tile border are not part of the outline.
|
|
2742
2742
|
*/
|
|
2743
2743
|
readonly outline?: Geometry;
|
|
2744
|
+
/**
|
|
2745
|
+
* The polygon as an opaque fill draws it — when it differs from the polygon's rings, i.e.
|
|
2746
|
+
* when the polygon was clipped to its tile: clipped a little beyond the tile instead, so
|
|
2747
|
+
* that it overlaps its continuation in the next tile, and no seam shows between them.
|
|
2748
|
+
*/
|
|
2749
|
+
readonly fillGeometry?: Geometry;
|
|
2744
2750
|
/**
|
|
2745
2751
|
* For each line of the geometry, how far along the line in its tile it starts, in pixels
|
|
2746
2752
|
* — where the line was clipped to its tile, which cut off its start. A dash pattern
|
|
@@ -2754,6 +2760,7 @@ declare class Feature implements Feature$1 {
|
|
|
2754
2760
|
patterns?: Patterns;
|
|
2755
2761
|
geometry: Geometry;
|
|
2756
2762
|
outline?: Geometry;
|
|
2763
|
+
fillGeometry?: Geometry;
|
|
2757
2764
|
lineStarts?: number[];
|
|
2758
2765
|
});
|
|
2759
2766
|
getBbox(): Bbox;
|
package/dist/index.js
CHANGED
|
@@ -119,6 +119,12 @@ class Feature {
|
|
|
119
119
|
* clipped edges along the tile border are not part of the outline.
|
|
120
120
|
*/
|
|
121
121
|
outline;
|
|
122
|
+
/**
|
|
123
|
+
* The polygon as an opaque fill draws it — when it differs from the polygon's rings, i.e.
|
|
124
|
+
* when the polygon was clipped to its tile: clipped a little beyond the tile instead, so
|
|
125
|
+
* that it overlaps its continuation in the next tile, and no seam shows between them.
|
|
126
|
+
*/
|
|
127
|
+
fillGeometry;
|
|
122
128
|
/**
|
|
123
129
|
* For each line of the geometry, how far along the line in its tile it starts, in pixels
|
|
124
130
|
* — where the line was clipped to its tile, which cut off its start. A dash pattern
|
|
@@ -133,6 +139,7 @@ class Feature {
|
|
|
133
139
|
this.patterns = opt.patterns;
|
|
134
140
|
this.geometry = opt.geometry;
|
|
135
141
|
this.outline = opt.outline;
|
|
142
|
+
this.fillGeometry = opt.fillGeometry;
|
|
136
143
|
this.lineStarts = opt.lineStarts;
|
|
137
144
|
}
|
|
138
145
|
getBbox() {
|
|
@@ -2090,20 +2097,28 @@ function readUtf8$1(buf, pos, end) {
|
|
|
2090
2097
|
*/
|
|
2091
2098
|
/** Whether any point lies outside of the square [min, max]². */
|
|
2092
2099
|
function exceedsSquare(rings, min, max) {
|
|
2093
|
-
return rings
|
|
2100
|
+
return exceedsBox(rings, [min, min, max, max]);
|
|
2101
|
+
}
|
|
2102
|
+
/** Whether any point lies outside of the box. */
|
|
2103
|
+
function exceedsBox(rings, [minX, minY, maxX, maxY]) {
|
|
2104
|
+
return rings.some((ring) => ring.some(({ x, y }) => x < minX || x > maxX || y < minY || y > maxY));
|
|
2094
2105
|
}
|
|
2095
2106
|
/**
|
|
2096
2107
|
* Clips the rings of a polygon to the square [min, max]² (Sutherland–Hodgman). Rings that
|
|
2097
2108
|
* vanish are dropped.
|
|
2098
2109
|
*/
|
|
2099
2110
|
function clipPolygon(rings, min, max) {
|
|
2111
|
+
return clipPolygonToBox(rings, [min, min, max, max]);
|
|
2112
|
+
}
|
|
2113
|
+
/** Clips the rings of a polygon to the box, like `clipPolygon`. */
|
|
2114
|
+
function clipPolygonToBox(rings, [minX, minY, maxX, maxY]) {
|
|
2100
2115
|
const result = [];
|
|
2101
2116
|
for (const ring of rings) {
|
|
2102
2117
|
let points = ring;
|
|
2103
|
-
points = clipAgainst(points, (p) => p.x >=
|
|
2104
|
-
points = clipAgainst(points, (p) => p.x <=
|
|
2105
|
-
points = clipAgainst(points, (p) => p.y >=
|
|
2106
|
-
points = clipAgainst(points, (p) => p.y <=
|
|
2118
|
+
points = clipAgainst(points, (p) => p.x >= minX, (a, b) => lerpAtX(a, b, minX));
|
|
2119
|
+
points = clipAgainst(points, (p) => p.x <= maxX, (a, b) => lerpAtX(a, b, maxX));
|
|
2120
|
+
points = clipAgainst(points, (p) => p.y >= minY, (a, b) => lerpAtY(a, b, minY));
|
|
2121
|
+
points = clipAgainst(points, (p) => p.y <= maxY, (a, b) => lerpAtY(a, b, maxY));
|
|
2107
2122
|
if (points.length >= 3)
|
|
2108
2123
|
result.push(points);
|
|
2109
2124
|
}
|
|
@@ -7276,7 +7291,7 @@ function decodeVectorTile(buffer, encoding) {
|
|
|
7276
7291
|
return new VectorTile(new PbfReader(buffer)).layers;
|
|
7277
7292
|
}
|
|
7278
7293
|
/** Adds the features of one vector tile's layers, projected to the screen. */
|
|
7279
|
-
function addTileFeatures(layers, { project, clipToTile, pixelsPerUnit }, layerFeatures, width, height) {
|
|
7294
|
+
function addTileFeatures(layers, { project, clipToTile, pixelsPerUnit, fillBox }, layerFeatures, width, height) {
|
|
7280
7295
|
for (const [name, layer] of Object.entries(layers)) {
|
|
7281
7296
|
// Geometry is in units of the layer's extent, which MapLibre scales to its own.
|
|
7282
7297
|
const scale = layer.extent > 0 ? TILE_EXTENT / layer.extent : 1;
|
|
@@ -7309,12 +7324,16 @@ function addTileFeatures(layers, { project, clipToTile, pixelsPerUnit }, layerFe
|
|
|
7309
7324
|
if (scale !== 1)
|
|
7310
7325
|
rings = rings.map((ring) => ring.map((p) => ({ x: p.x * scale, y: p.y * scale })));
|
|
7311
7326
|
let outline;
|
|
7327
|
+
let fillGeometry;
|
|
7312
7328
|
let lineStarts;
|
|
7313
7329
|
// Clip polygons and lines to the tile, like MapLibre's stencil clipping: otherwise
|
|
7314
7330
|
// the parts in the tile buffer are drawn twice, which shows when translucent.
|
|
7315
7331
|
if (clipToTile && type !== 'Point' && exceedsSquare(rings, 0, TILE_EXTENT)) {
|
|
7316
7332
|
if (type === 'Polygon') {
|
|
7317
7333
|
outline = project('LineString', clipPolygonOutline(rings, 0, TILE_EXTENT));
|
|
7334
|
+
if (fillBox) {
|
|
7335
|
+
fillGeometry = project('Polygon', exceedsBox(rings, fillBox) ? clipPolygonToBox(rings, fillBox) : rings);
|
|
7336
|
+
}
|
|
7318
7337
|
rings = clipPolygon(rings, 0, TILE_EXTENT);
|
|
7319
7338
|
}
|
|
7320
7339
|
else {
|
|
@@ -7346,6 +7365,7 @@ function addTileFeatures(layers, { project, clipToTile, pixelsPerUnit }, layerFe
|
|
|
7346
7365
|
type,
|
|
7347
7366
|
geometry,
|
|
7348
7367
|
outline,
|
|
7368
|
+
fillGeometry,
|
|
7349
7369
|
lineStarts,
|
|
7350
7370
|
id: featureSrc.id,
|
|
7351
7371
|
properties: featureSrc.properties,
|
|
@@ -7396,19 +7416,59 @@ function getTileProjections(source, job) {
|
|
|
7396
7416
|
return tiles.map(({ x, y, offsetX, offsetY }) => {
|
|
7397
7417
|
const offset = new Point2D(offsetX + (width - covered.width) / 2, offsetY + (height - covered.height) / 2);
|
|
7398
7418
|
const scale = tileSize / TILE_EXTENT;
|
|
7419
|
+
const project = (_type, rings) => rings.map((ring) => ring.map((point) => {
|
|
7420
|
+
const p = new Point2D(point.x, point.y).scale(scale).translate(offset);
|
|
7421
|
+
return moved ? moved.fromNorthUp(p.x, p.y) : p;
|
|
7422
|
+
}));
|
|
7399
7423
|
return {
|
|
7400
7424
|
x,
|
|
7401
7425
|
y,
|
|
7402
7426
|
z: zoomLevel,
|
|
7403
7427
|
clipToTile: true,
|
|
7404
7428
|
pixelsPerUnit: scale,
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
return moved ? moved.fromNorthUp(p.x, p.y) : p;
|
|
7408
|
-
})),
|
|
7429
|
+
fillBox: pixelAlignedBox(project),
|
|
7430
|
+
project,
|
|
7409
7431
|
};
|
|
7410
7432
|
});
|
|
7411
7433
|
}
|
|
7434
|
+
/**
|
|
7435
|
+
* The tile, widened to whole pixels on the screen, in tile units, or undefined where that is
|
|
7436
|
+
* the tile itself, or where the tile is turned on the screen.
|
|
7437
|
+
*
|
|
7438
|
+
* Opaque fills are clipped to this box instead of the tile. Where a polygon goes on in the
|
|
7439
|
+
* next tile, a fill clipped to the tile ends within a pixel: it covers part of the pixel,
|
|
7440
|
+
* and so does every other fill ending there, e.g. a land polygon over a sea polygon, both
|
|
7441
|
+
* covering the whole tile. Each is antialiased, so what is below shows through a little, and
|
|
7442
|
+
* the tile border shows as a faint line. MapLibre fills the inside of a polygon without
|
|
7443
|
+
* antialiasing, so it shows none. Ending on a pixel border, a fill covers each pixel entirely
|
|
7444
|
+
* or not at all; the next tile, drawn later, covers the pixels the two tiles share.
|
|
7445
|
+
*/
|
|
7446
|
+
function pixelAlignedBox(project) {
|
|
7447
|
+
const [corners] = project('LineString', [
|
|
7448
|
+
[
|
|
7449
|
+
{ x: 0, y: 0 },
|
|
7450
|
+
{ x: TILE_EXTENT, y: 0 },
|
|
7451
|
+
{ x: 0, y: TILE_EXTENT },
|
|
7452
|
+
],
|
|
7453
|
+
]);
|
|
7454
|
+
const [origin, right, down] = corners;
|
|
7455
|
+
// The tile's x axis must be the screen's, and its y axis the screen's.
|
|
7456
|
+
const epsilon = 1e-6;
|
|
7457
|
+
if (Math.abs(right.y - origin.y) > epsilon || Math.abs(down.x - origin.x) > epsilon) {
|
|
7458
|
+
return undefined;
|
|
7459
|
+
}
|
|
7460
|
+
const toTile = (screen, start, end) => ((screen - start) / (end - start)) * TILE_EXTENT;
|
|
7461
|
+
const widen = (start, end) => {
|
|
7462
|
+
const a = toTile(Math.floor(Math.min(start, end)), start, end);
|
|
7463
|
+
const b = toTile(Math.ceil(Math.max(start, end)), start, end);
|
|
7464
|
+
return [Math.min(a, b), Math.max(a, b)];
|
|
7465
|
+
};
|
|
7466
|
+
const [minX, maxX] = widen(origin.x, right.x);
|
|
7467
|
+
const [minY, maxY] = widen(origin.y, down.y);
|
|
7468
|
+
if (minX === 0 && minY === 0 && maxX === TILE_EXTENT && maxY === TILE_EXTENT)
|
|
7469
|
+
return undefined;
|
|
7470
|
+
return [minX, minY, maxX, maxY];
|
|
7471
|
+
}
|
|
7412
7472
|
|
|
7413
7473
|
//#region src/reference/v8.json
|
|
7414
7474
|
var v8_default = {
|
|
@@ -20274,7 +20334,13 @@ class SVGRenderer {
|
|
|
20274
20334
|
currentFillKey = translucent ? undefined : key;
|
|
20275
20335
|
}
|
|
20276
20336
|
const group = fillGroups[fillGroups.length - 1];
|
|
20277
|
-
|
|
20337
|
+
// An opaque color fills a polygon clipped to its tile a little beyond the tile, so
|
|
20338
|
+
// that it overlaps its continuation in the next tile without a seam. What is drawn
|
|
20339
|
+
// twice there must not show: not with a translucent fill, nor with a pattern.
|
|
20340
|
+
const fillRings = feature.fillGeometry && !translucent && !patternPaint
|
|
20341
|
+
? feature.fillGeometry.map((ring) => ring.map((p) => roundXY(p.x, p.y)))
|
|
20342
|
+
: rings;
|
|
20343
|
+
for (const ring of fillRings)
|
|
20278
20344
|
group.segments.push(ring);
|
|
20279
20345
|
}
|
|
20280
20346
|
// fill-antialias outline, in fill-outline-color or else the fill color. For an
|
|
@@ -21711,9 +21777,13 @@ class CanvasRenderer {
|
|
|
21711
21777
|
});
|
|
21712
21778
|
}
|
|
21713
21779
|
else if (color.alpha > 0) {
|
|
21780
|
+
// An opaque color fills a polygon clipped to its tile a little beyond the tile, so
|
|
21781
|
+
// that it overlaps its continuation in the next tile without a seam. What is drawn
|
|
21782
|
+
// twice there must not show: not with a translucent fill, nor with a pattern.
|
|
21783
|
+
const geometry = (translucent ? undefined : feature.fillGeometry) ?? feature.geometry;
|
|
21714
21784
|
this.#paint(this.ctx, style.translate, style.opacity, (ctx) => {
|
|
21715
21785
|
ctx.fillStyle = color.hex;
|
|
21716
|
-
this.#trace(ctx, toSegments(
|
|
21786
|
+
this.#trace(ctx, toSegments(geometry), true);
|
|
21717
21787
|
ctx.fill();
|
|
21718
21788
|
});
|
|
21719
21789
|
}
|