New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

tangram

Package Overview
Dependencies
Maintainers
2
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tangram - npm Package Compare versions

Comparing version 0.18.0 to 0.18.1

2

package.json
{
"name": "tangram",
"version": "0.18.0",
"version": "0.18.1",
"description": "WebGL Maps for Vector Tiles",

@@ -5,0 +5,0 @@ "repository": {

// Point builders
import { default_uvs } from './common';
// Build a billboard sprite quad centered on a point. Sprites are intended to be drawn in screenspace, and have
// properties for width, height, angle, and a scale factor that can be used to interpolate the screenspace size
// of a sprite between two zoom levels.
export function buildQuadsForPoints (points, vertex_data, vertex_template,
{ texcoord_index, position_index, shape_index, offset_index, offsets_index, pre_angles_index, angles_index },
{ quad, quad_normalize, offset, offsets, pre_angles, angle, angles, curve, texcoord_scale, texcoord_normalize, pre_angles_normalize, angles_normalize, offsets_normalize }) {
quad_normalize = quad_normalize || 1;
let w2 = quad[0] / 2 * quad_normalize;
let h2 = quad[1] / 2 * quad_normalize;
let scaling = [
[-w2, -h2],
[w2, -h2],
[w2, h2],
[-w2, h2]
];
// Scaling values to encode fractional values with fixed-point integer attributes
const pre_angles_normalize = 128 / Math.PI;
const angles_normalize = 16384 / Math.PI;
const offsets_normalize = 64;
const texcoord_normalize = 65535;
const size_normalize = 128; // width/height are 8.8 fixed-point, but are halved (so multiply by 128 instead of 256)
let vertex_elements = vertex_data.vertex_elements;
let element_offset = vertex_data.vertex_count;
// These index values map a 4-element vertex position counter from this pattern (used for size and UVs):
// [min_x, min_y, max_x, max_y]
// to this pattern:
// [min_x, min_y],
// [max_x, min_y],
// [max_x, max_y],
// [min_x, max_y]
const ix = [0, 2, 2, 0];
const iy = [1, 1, 3, 3];
const shape = new Array(4); // single, reusable allocation
let texcoords;
if (texcoord_index) {
texcoord_normalize = texcoord_normalize || 1;
// Build a billboard sprite quad centered on a point. Sprites are intended to be drawn in screenspace, and have
// properties for width, height, angle, and texture UVs. Curved label segment sprites have additional properties
// for interpolating their position and angle across zooms.
export function buildQuadForPoint (
point,
vertex_data,
vertex_template,
vindex,
size,
offset,
offsets,
pre_angles,
angle,
angles,
texcoords,
curve) {
var [min_u, min_v, max_u, max_v] = texcoord_scale || default_uvs;
// Half-sized point dimensions in fixed point
const w2 = size[0] * size_normalize;
const h2 = size[1] * size_normalize;
shape[0] = -w2;
shape[1] = -h2;
shape[2] = w2;
shape[3] = h2;
texcoords = [
[min_u, min_v],
[max_u, min_v],
[max_u, max_v],
[min_u, max_v]
];
}
const uvs = texcoords || default_uvs;
var geom_count = 0;
let num_points = points.length;
for (let p=0; p < num_points; p++) {
let point = points[p];
const vertex_elements = vertex_data.vertex_elements;
let element_offset = vertex_data.vertex_count;
for (let pos=0; pos < 4; pos++) {
// Add texcoords
if (texcoord_index) {
vertex_template[texcoord_index + 0] = texcoords[pos][0] * texcoord_normalize;
vertex_template[texcoord_index + 1] = texcoords[pos][1] * texcoord_normalize;
}
for (let p=0; p < 4; p++) {
vertex_template[vindex.a_position + 0] = point[0];
vertex_template[vindex.a_position + 1] = point[1];
vertex_template[position_index + 0] = point[0];
vertex_template[position_index + 1] = point[1];
vertex_template[vindex.a_shape + 0] = shape[ix[p]];
vertex_template[vindex.a_shape + 1] = shape[iy[p]];
vertex_template[vindex.a_shape + 2] = angle;
vertex_template[shape_index + 0] = scaling[pos][0];
vertex_template[shape_index + 1] = scaling[pos][1];
vertex_template[shape_index + 2] = angle;
vertex_template[vindex.a_offset + 0] = offset[0];
vertex_template[vindex.a_offset + 1] = offset[1];
vertex_template[offset_index + 0] = offset[0];
vertex_template[offset_index + 1] = offset[1];
// Add texcoords
if (vindex.a_texcoord) {
vertex_template[vindex.a_texcoord + 0] = uvs[ix[p]] * texcoord_normalize;
vertex_template[vindex.a_texcoord + 1] = uvs[iy[p]] * texcoord_normalize;
}
if (curve){
// 1 byte (signed) range: [-127, 128]
// actual range: [-2pi, 2pi]
// total: multiply by 128 / (2 PI)
vertex_template[pre_angles_index + 0] = pre_angles_normalize * pre_angles[0];
vertex_template[pre_angles_index + 1] = pre_angles_normalize * pre_angles[1];
vertex_template[pre_angles_index + 2] = pre_angles_normalize * pre_angles[2];
vertex_template[pre_angles_index + 3] = pre_angles_normalize * pre_angles[3];
// Add curved label segment props
if (curve) {
// 1 byte (signed) range: [-127, 128]
// actual range: [-2pi, 2pi]
// total: multiply by 128 / (2 PI)
vertex_template[vindex.a_pre_angles + 0] = pre_angles_normalize * pre_angles[0];
vertex_template[vindex.a_pre_angles + 1] = pre_angles_normalize * pre_angles[1];
vertex_template[vindex.a_pre_angles + 2] = pre_angles_normalize * pre_angles[2];
vertex_template[vindex.a_pre_angles + 3] = pre_angles_normalize * pre_angles[3];
// 2 byte (signed) of resolution [-32767, 32768]
// actual range: [-2pi, 2pi]
// total: multiply by 32768 / (2 PI) = 16384 / PI
vertex_template[angles_index + 0] = angles_normalize * angles[0];
vertex_template[angles_index + 1] = angles_normalize * angles[1];
vertex_template[angles_index + 2] = angles_normalize * angles[2];
vertex_template[angles_index + 3] = angles_normalize * angles[3];
// 2 byte (signed) of resolution [-32767, 32768]
// actual range: [-2pi, 2pi]
// total: multiply by 32768 / (2 PI) = 16384 / PI
vertex_template[vindex.a_angles + 0] = angles_normalize * angles[0];
vertex_template[vindex.a_angles + 1] = angles_normalize * angles[1];
vertex_template[vindex.a_angles + 2] = angles_normalize * angles[2];
vertex_template[vindex.a_angles + 3] = angles_normalize * angles[3];
// offset range can be [0, 65535]
// actual range: [0, 1024]
vertex_template[offsets_index + 0] = offsets_normalize * offsets[0];
vertex_template[offsets_index + 1] = offsets_normalize * offsets[1];
vertex_template[offsets_index + 2] = offsets_normalize * offsets[2];
vertex_template[offsets_index + 3] = offsets_normalize * offsets[3];
}
vertex_data.addVertex(vertex_template);
// offset range can be [0, 65535]
// actual range: [0, 1024]
vertex_template[vindex.a_offsets + 0] = offsets_normalize * offsets[0];
vertex_template[vindex.a_offsets + 1] = offsets_normalize * offsets[1];
vertex_template[vindex.a_offsets + 2] = offsets_normalize * offsets[2];
vertex_template[vindex.a_offsets + 3] = offsets_normalize * offsets[3];
}
vertex_elements.push(element_offset + 0);
vertex_elements.push(element_offset + 1);
vertex_elements.push(element_offset + 2);
vertex_elements.push(element_offset + 2);
vertex_elements.push(element_offset + 3);
vertex_elements.push(element_offset + 0);
element_offset += 4;
geom_count += 2;
vertex_data.addVertex(vertex_template);
}
return geom_count;
vertex_elements.push(element_offset + 0);
vertex_elements.push(element_offset + 1);
vertex_elements.push(element_offset + 2);
vertex_elements.push(element_offset + 2);
vertex_elements.push(element_offset + 3);
vertex_elements.push(element_offset + 0);
return 2; // geom count is always two triangles, for one quad
}

@@ -22,34 +22,26 @@ // Geometry building functions

const DEFAULT = {
MITER_LIMIT: 3,
TEXCOORD_NORMALIZE: 1,
TEXCOORD_RATIO: 1,
MIN_FAN_WIDTH: 5 // Width of line in tile units to place 1 triangle per fan
};
const DEFAULT_MITER_LIMIT = 3;
const MIN_FAN_WIDTH = 5; // Width of line in tile units to place 1 triangle per fan
const TEXCOORD_NORMALIZE = 65536; // Scaling factor for UV attribute values
// Scaling factor to add precision to line texture V coordinate packed as normalized short
const v_scale_adjust = Geo.tile_scale;
const V_SCALE_ADJUST = Geo.tile_scale;
const zero_v = [0, 0], one_v = [1, 0], mid_v = [0.5, 0]; // reusable instances, updated with V coordinate
export function buildPolylines (lines, width, vertex_data, vertex_template,
{
closed_polygon,
remove_tile_edges,
tile_edge_tolerance,
texcoord_index,
texcoord_width,
texcoord_ratio,
texcoord_normalize,
extrude_index,
offset_index,
join, cap,
miter_limit,
offset
}) {
var cap_type = cap ? CAP_TYPE[cap] : CAP_TYPE.butt;
var join_type = join ? JOIN_TYPE[join] : JOIN_TYPE.miter;
export function buildPolylines (
lines,
style,
vertex_data,
vertex_template,
vindex,
closed_polygon,
remove_tile_edges,
tile_edge_tolerance) {
var cap_type = style.cap ? CAP_TYPE[style.cap] : CAP_TYPE.butt;
var join_type = style.join ? JOIN_TYPE[style.join] : JOIN_TYPE.miter;
// Configure miter limit
if (join_type === JOIN_TYPE.miter) {
miter_limit = miter_limit || DEFAULT.MITER_LIMIT; // default miter limit
const miter_limit = style.miter_limit || DEFAULT_MITER_LIMIT; // default miter limit
var miter_len_sq = miter_limit * miter_limit;

@@ -60,6 +52,4 @@ }

var v_scale;
if (texcoord_index) {
texcoord_normalize = texcoord_normalize || DEFAULT.TEXCOORD_NORMALIZE;
texcoord_ratio = texcoord_ratio || DEFAULT.TEXCOORD_RATIO;
v_scale = 1 / (texcoord_width * texcoord_ratio * v_scale_adjust); // scales line texture as a ratio of the line's width
if (vindex.a_texcoord) {
v_scale = 1 / (style.texcoord_width * V_SCALE_ADJUST); // scales line texture as a ratio of the line's width
}

@@ -77,10 +67,9 @@

vertex_template,
half_width: width / 2,
extrude_index,
offset_index,
half_width: style.width / 2,
extrude_index: vindex.a_extrude,
offset_index: vindex.a_offset,
v_scale,
texcoord_index,
texcoord_width,
texcoord_normalize,
offset,
texcoord_index: vindex.a_texcoord,
texcoord_width: style.texcoord_width,
offset: style.offset,
geom_count: 0

@@ -90,4 +79,4 @@ };

// Process lines
for (let index = 0; index < lines.length; index++) {
buildPolyline(lines[index], context);
for (let i = 0; i < lines.length; i++) {
buildPolyline(lines[i], context);
}

@@ -97,4 +86,4 @@

if (context.extra_lines) {
for (let index = 0; index < context.extra_lines.length; index++) {
buildPolyline(context.extra_lines[index], context);
for (let i = 0; i < context.extra_lines.length; i++) {
buildPolyline(context.extra_lines[i], context);
}

@@ -460,4 +449,4 @@ }

if (context.texcoord_index != null) {
vertex_template[context.texcoord_index + 0] = u * context.texcoord_normalize;
vertex_template[context.texcoord_index + 1] = v * context.texcoord_normalize;
vertex_template[context.texcoord_index + 0] = u * TEXCOORD_NORMALIZE;
vertex_template[context.texcoord_index + 1] = v * TEXCOORD_NORMALIZE;
}

@@ -654,3 +643,3 @@

var numTriangles = (width > 2 * DEFAULT.MIN_FAN_WIDTH) ? Math.log2(width / DEFAULT.MIN_FAN_WIDTH) : 1;
var numTriangles = (width > 2 * MIN_FAN_WIDTH) ? Math.log2(width / MIN_FAN_WIDTH) : 1;
return Math.ceil(angle / Math.PI * numTriangles);

@@ -657,0 +646,0 @@ }

@@ -8,6 +8,7 @@ import Label from './label';

constructor (position, size, layout) {
constructor (position, size, layout, angle = 0) {
super(size, layout);
this.type = 'point';
this.position = [position[0], position[1]];
this.angle = angle;
this.parent = this.layout.parent;

@@ -55,12 +56,14 @@ this.update();

// fudge width value as text may overflow bounding box if it has italic, bold, etc style
if (this.layout.italic){
if (this.layout.italic) {
width += 5 * this.unit_scale;
}
let p = [
// make bounding boxes
this.obb = new OBB(
this.position[0] + (this.offset[0] * this.unit_scale),
this.position[1] - (this.offset[1] * this.unit_scale)
];
this.obb = new OBB(p[0], p[1], 0, width, height);
this.position[1] - (this.offset[1] * this.unit_scale),
-this.angle, // angle is negative because tile system y axis is pointing down
width,
height
);
this.aabb = this.obb.getExtent();

@@ -67,0 +70,0 @@

import PointAnchor from './point_anchor';
import {boxIntersectsList} from './intersect';
import Utils from '../utils/utils';
import OBB from '../utils/obb';

@@ -16,2 +15,3 @@ import Geo from '../utils/geo';

this.position = null;
this.angle = 0;
this.anchor = Array.isArray(this.layout.anchor) ? this.layout.anchor[0] : this.layout.anchor; // initial anchor

@@ -34,2 +34,3 @@ this.placed = null;

position: this.position,
angle: this.angle,
size: this.size,

@@ -84,9 +85,6 @@ offset: this.offset,

inTileBounds () {
let min = [ this.aabb[0], this.aabb[1] ];
let max = [ this.aabb[2], this.aabb[3] ];
if (!Utils.pointInTile(min) || !Utils.pointInTile(max)) {
if ((this.aabb[0] >= 0 && this.aabb[1] > -Geo.tile_scale && this.aabb[0] < Geo.tile_scale && this.aabb[1] <= 0) ||
(this.aabb[2] >= 0 && this.aabb[3] > -Geo.tile_scale && this.aabb[2] < Geo.tile_scale && this.aabb[3] <= 0)) {
return false;
}
return true;

@@ -140,6 +138,7 @@ }

Label.id = 0;
Label.id_prefix = ''; // id prefix scoped to worker thread
Label.id_prefix = 0; // id prefix scoped to worker thread
Label.id_multiplier = 0; // multiplier to keep label ids distinct across threads
Label.nextLabelId = function () {
return Label.id_prefix + '/' + (Label.id++);
return Label.id_prefix + ((Label.id++) * Label.id_multiplier);
};

@@ -146,0 +145,0 @@

@@ -11,6 +11,70 @@ import Label from './label';

export default function mainThreadLabelCollisionPass (tiles, view_zoom, hide_breach = false) {
export default async function mainThreadLabelCollisionPass (tiles, view_zoom, hide_breach = false) {
// Swap/reset visible label set
prev_visible = visible; // save last visible label set
visible = {}; // initialize new visible label set
// Build label containers from tiles
let containers = buildLabels(tiles, view_zoom);
// Collide all labels in a single group
// TODO: maybe rename tile and style to group/subgroup?
Collision.startTile('main', { apply_repeat_groups: true, return_hidden: true });
Collision.addStyle('main', 'main');
const labels = await Collision.collide(containers, 'main', 'main');
// Update label visiblity
let meshes = [];
labels.forEach(container => {
// Hide breach labels (those that cross tile boundaries) while tiles are loading, unless they
// were previously visible (otherwise fully loaded/collided breach labels will flicker in and out
// when new tiles load, even if they aren't adjacent)
let show = 0;
if (container.show === true &&
(!hide_breach || !container.label.breach || prev_visible[container.label.id])) {
show = 1;
}
if (show) {
visible[container.label.id] = true; // track visible labels
}
let changed = true; // check if label visibility changed on this collision pass
container.ranges.forEach(r => {
if (!changed) {
return; // skip rest of label if state hasn't changed
}
let mesh = container.mesh;
if (!mesh.valid) {
return;
}
let off = mesh.vertex_layout.offset.a_shape; // byte offset (within each vertex) of attribute
let stride = mesh.vertex_layout.stride; // byte stride per vertex
for (let i=0; i < r[1]; i++) {
// NB: +6 is because attribute is a short int (2 bytes each), and we're skipping to 3rd element, 6=3*2
if (mesh.vertex_data[r[0] + i * stride + off + 6] === show) {
changed = false;
return; // label hasn't changed states, skip further updates
}
mesh.vertex_data[r[0] + i * stride + off + 6] = show;
}
if (meshes.indexOf(mesh) === -1) {
meshes.push(mesh);
}
});
});
// Upload updated meshes and make them visible
meshes.forEach(mesh => mesh.upload());
tiles.forEach(t => t.swapPendingLabels());
return { labels, containers }; // currently returned for debugging
}
function buildLabels (tiles, view_zoom) {
const labels = {};

@@ -23,3 +87,3 @@ let containers = {};

const zoom_scale = Math.pow(2, view_zoom - tile.style_z); // adjust label size by view zoom
const size_scale = units_per_meter * zoom_scale; // scale from tile units to zoom-adjusted meters
const size_scale = units_per_meter * zoom_scale; // scale from tile units to zoom-adjusted meters
const meters_per_pixel = Geo.metersPerPixel(view_zoom);

@@ -46,3 +110,3 @@

const ranges = mesh.labels[label_id].ranges;
const debug = Object.assign({}, mesh.labels[label_id].debug, {tile, params, label_id});
const debug = Object.assign({}, mesh.labels[label_id].debug, { tile, params, label_id });

@@ -58,3 +122,2 @@ let label = labels[label_id] = {};

label.layout.repeat_distance /= size_scale; // TODO: where should this be scaled?
label.position = [ // don't overwrite referenced values

@@ -74,6 +137,6 @@ label.position[0] / units_per_meter + tile.min.x,

// NB: this is a very rough approximation of curved label collision at intermediate zooms,
// becuase the position/scale of each collision box isn't correctly updated; however,
// because the position/scale of each collision box isn't correctly updated; however,
// it's good enough to provide some additional label coverage, with less overhead
const obbs = params.obbs.map(o => {
let {x, y, a, w, h} = o;
let { x, y, a, w, h } = o;
x = x / units_per_meter + tile.min.x;

@@ -113,59 +176,3 @@ y = y / units_per_meter + tile.min.y;

containers = Object.keys(containers).map(k => containers[k]);
// Collide all labels in a single group
// TODO: maybe rename tile and style to group/subgroup?
Collision.startTile('main', { apply_repeat_groups: true, return_hidden: true });
Collision.addStyle('main', 'main');
return Collision.collide(containers, 'main', 'main').then(labels => {
let meshes = [];
labels.forEach(container => {
// Hide breach labels (those that cross tile boundaries) while tiles are loading, unless they
// were previously visible (otherwise fully loaded/collided breach labels will flicker in and out
// when new tiles load, even if they aren't adjacent)
let show = 0;
if (container.show === true &&
(!hide_breach || !container.label.breach || prev_visible[container.label.id])) {
show = 1;
}
if (show) {
visible[container.label.id] = true; // track visible labels
}
let changed = true; // check if label visibility changed on this collision pass
container.ranges.forEach(r => {
if (!changed) {
return; // skip rest of label if state hasn't changed
}
let mesh = container.mesh;
if (!mesh.valid) {
return;
}
let off = mesh.vertex_layout.offset.a_shape; // byte offset (within each vertex) of attribute
let stride = mesh.vertex_layout.stride; // byte stride per vertex
for (let i=0; i < r[1]; i++) {
// NB: +6 is because attribute is a short int (2 bytes each), and we're skipping to 3rd element, 6=3*2
if (mesh.vertex_data[r[0] + i * stride + off + 6] === show) {
changed = false;
return; // label hasn't changed states, skip further updates
}
mesh.vertex_data[r[0] + i * stride + off + 6] = show;
}
if (meshes.indexOf(mesh) === -1) {
meshes.push(mesh);
}
});
});
meshes.forEach(mesh => mesh.upload());
tiles.forEach(t => t.swapPendingLabels());
return { labels, containers }; // currently returned for debugging
});
return containers;
}

@@ -172,0 +179,0 @@

@@ -9,9 +9,9 @@ // Logic for placing point labels along a line geometry

export default function placePointsOnLine (line, size, options) {
let labels = [];
let strategy = options.placement;
let min_length = Math.max(size[0], size[1]) * options.placement_min_length_ratio * options.units_per_pixel;
export default function placePointsOnLine (line, size, layout) {
const labels = [];
const strategy = layout.placement;
const min_length = Math.max(size[0], size[1]) * layout.placement_min_length_ratio * layout.units_per_pixel;
if (strategy === PLACEMENT.SPACED) {
let result = getPositionsAndAngles(line, min_length, options);
let result = getPositionsAndAngles(line, min_length, layout);
// false will be returned if line have no length

@@ -24,9 +24,7 @@ if (!result) {

let angles = result.angles;
for (let i = 0; i < positions.length; i++){
for (let i = 0; i < positions.length; i++) {
let position = positions[i];
let angle = angles[i];
if (options.tile_edges === true || !isCoordOutsideTile(position)) {
let label = new LabelPoint(position, size, options);
label.angle = angle;
labels.push(label);
if (layout.tile_edges === true || !isCoordOutsideTile(position)) {
labels.push(new LabelPoint(position, size, layout, angle));
}

@@ -36,10 +34,9 @@ }

else if (strategy === PLACEMENT.VERTEX) {
let p, q, label;
for (let i = 0; i < line.length - 1; i++){
let p, q;
for (let i = 0; i < line.length - 1; i++) {
p = line[i];
q = line[i + 1];
if (options.tile_edges === true || !isCoordOutsideTile(p)) {
label = new LabelPoint(p, size, options);
label.angle = getAngle(p, q, options.angle);
labels.push(label);
if (layout.tile_edges === true || !isCoordOutsideTile(p)) {
const angle = getAngle(p, q, layout.angle);
labels.push(new LabelPoint(p, size, layout, angle));
}

@@ -49,8 +46,7 @@ }

// add last endpoint
label = new LabelPoint(q, size, options);
label.angle = getAngle(p, q, options.angle);
labels.push(label);
const angle = getAngle(p, q, layout.angle);
labels.push(new LabelPoint(q, size, layout, angle));
}
else if (strategy === PLACEMENT.MIDPOINT) {
for (let i = 0; i < line.length - 1; i++){
for (let i = 0; i < line.length - 1; i++) {
let p = line[i];

@@ -62,7 +58,6 @@ let q = line[i + 1];

];
if (options.tile_edges === true || !isCoordOutsideTile(position)) {
if (layout.tile_edges === true || !isCoordOutsideTile(position)) {
if (!min_length || norm(p, q) > min_length) {
let label = new LabelPoint(position, size, options);
label.angle = getAngle(p, q, options.angle);
labels.push(label);
const angle = getAngle(p, q, layout.angle);
labels.push(new LabelPoint(position, size, layout, angle));
}

@@ -75,5 +70,5 @@ }

function getPositionsAndAngles(line, min_length, options){
let upp = options.units_per_pixel;
let spacing = (options.placement_spacing || default_spacing) * upp;
function getPositionsAndAngles(line, min_length, layout) {
let upp = layout.units_per_pixel;
let spacing = (layout.placement_spacing || default_spacing) * upp;

@@ -91,4 +86,4 @@ let length = getLineLength(line);

let distance = 0.5 * remainder;
for (let i = 0; i < num_labels; i++){
let {position, angle} = interpolateLine(line, distance, min_length, options);
for (let i = 0; i < num_labels; i++) {
let {position, angle} = interpolateLine(line, distance, min_length, layout);
if (position != null && angle != null) {

@@ -104,9 +99,9 @@ positions.push(position);

function getAngle(p, q, angle = 0){
function getAngle(p, q, angle = 0) {
return (angle === 'auto') ? Math.atan2(q[0] - p[0], q[1] - p[1]) : angle;
}
function getLineLength(line){
function getLineLength(line) {
let distance = 0;
for (let i = 0; i < line.length - 1; i++){
for (let i = 0; i < line.length - 1; i++) {
distance += norm(line[i], line[i+1]);

@@ -117,3 +112,3 @@ }

function norm(p, q){
function norm(p, q) {
return Math.sqrt(Math.pow(p[0] - q[0], 2) + Math.pow(p[1] - q[1], 2));

@@ -124,6 +119,6 @@ }

// you don't have to start from the first index every time for placement
function interpolateLine(line, distance, min_length, options){
function interpolateLine(line, distance, min_length, layout) {
let sum = 0;
let position, angle;
for (let i = 0; i < line.length-1; i++){
for (let i = 0; i < line.length-1; i++) {
let p = line[i];

@@ -139,5 +134,5 @@ let q = line[i+1];

if (sum > distance){
if (sum > distance) {
position = interpolateSegment(p, q, sum - distance);
angle = getAngle(p, q, options.angle);
angle = getAngle(p, q, layout.angle);
break;

@@ -149,3 +144,3 @@ }

function interpolateSegment(p, q, distance){
function interpolateSegment(p, q, distance) {
let length = norm(p, q);

@@ -152,0 +147,0 @@ let ratio = distance / length;

@@ -291,5 +291,5 @@ import ShaderProgram from '../gl/shader_program';

// Move light's world position into camera space
let [x, y] = Geo.latLngToMeters(this.position);
this.position_eye[0] = x - this.view.camera.position_meters[0];
this.position_eye[1] = y - this.view.camera.position_meters[1];
const m = Geo.latLngToMeters([...this.position]);
this.position_eye[0] = m[0] - this.view.camera.position_meters[0];
this.position_eye[1] = m[1] - this.view.camera.position_meters[1];

@@ -296,0 +296,0 @@ this.position_eye[2] = StyleParser.convertUnits(this.position[2],

@@ -42,2 +42,3 @@ /*jshint worker: true*/

Label.id_prefix = worker_id;
Label.id_multiplier = num_workers;
return worker_id;

@@ -266,3 +267,5 @@ },

let features = [];
let tiles = tile_keys.map(t => this.tiles[t]).filter(t => t);
let tiles = tile_keys
.map(t => this.tiles[t])
.filter(t => t && t.loaded);

@@ -269,0 +272,0 @@ // Compile feature filter

@@ -1336,2 +1336,3 @@ import log from '../utils/log';

if ((this.render_count_changed || this.generation !== this.last_complete_generation) &&
!this.building &&
!this.tile_manager.isLoadingVisibleTiles() &&

@@ -1338,0 +1339,0 @@ this.tile_manager.allVisibleTilesLabeled()) {

@@ -201,4 +201,4 @@ import Geo from '../utils/geo';

// Center of viewport in meters, and tile
let [x, y] = Geo.latLngToMeters([this.center.lng, this.center.lat]);
this.center.meters = { x, y };
const m = Geo.latLngToMeters([this.center.lng, this.center.lat]);
this.center.meters = { x: m[0], y: m[1] };

@@ -205,0 +205,0 @@ this.center.tile = Geo.tileForMeters([this.center.meters.x, this.center.meters.y], this.tile_zoom);

@@ -117,7 +117,3 @@ /*jshint worker: true */

var feature = source.layers[t].features[f];
Geo.transformGeometry(feature.geometry, coord => {
var [x, y] = Geo.latLngToMeters(coord);
coord[0] = x;
coord[1] = y;
});
Geo.transformGeometry(feature.geometry, this.projectCoord);
}

@@ -131,2 +127,6 @@ }

static projectCoord (coord) {
Geo.latLngToMeters(coord);
}
/**

@@ -422,3 +422,3 @@ Re-scale geometries within each source to internal tile units

if (!min) {
min = bounds.tiles.min[coords.z] = Geo.wrapTile(Geo.tileForMeters(bounds.meters.min, coords.z));
min = bounds.tiles.min[coords.z] = Geo.tileForMeters(bounds.meters.min, coords.z);
}

@@ -428,3 +428,3 @@

if (!max) {
max = bounds.tiles.max[coords.z] = Geo.wrapTile(Geo.tileForMeters(bounds.meters.max, coords.z));
max = bounds.tiles.max[coords.z] = Geo.tileForMeters(bounds.meters.max, coords.z);
}

@@ -431,0 +431,0 @@

@@ -570,19 +570,9 @@ // Line rendering style

lines,
style.width,
style,
vertex_data,
vertex_template,
{
cap: style.cap,
join: style.join,
miter_limit: style.miter_limit,
extrude_index: vertex_layout.index.a_extrude,
offset_index: vertex_layout.index.a_offset,
texcoord_index: vertex_layout.index.a_texcoord,
texcoord_width: style.texcoord_width,
texcoord_normalize: 65535, // scale UVs to unsigned shorts
closed_polygon: options && options.closed_polygon,
remove_tile_edges: !style.tile_edges && options && options.remove_tile_edges,
tile_edge_tolerance: Geo.tile_scale * context.tile.pad_scale * 2,
offset: style.offset
}
vertex_layout.index,
(options && options.closed_polygon), // closed_polygon
(!style.tile_edges && options && options.remove_tile_edges), // remove_tile_edges
(Geo.tile_scale * context.tile.pad_scale * 2) // tile_edge_tolerance
);

@@ -589,0 +579,0 @@ },

@@ -8,6 +8,5 @@ // Point + text label rendering style

import VertexLayout from '../../gl/vertex_layout';
import {buildQuadsForPoints} from '../../builders/points';
import { buildQuadForPoint } from '../../builders/points';
import Texture from '../../gl/texture';
import Geo from '../../utils/geo';
import Vector from '../../utils/vector';
import Collision from '../../labels/collision';

@@ -25,7 +24,2 @@ import LabelPoint from '../../labels/label_point';

const pre_angles_normalize = 128 / Math.PI;
const angles_normalize = 16384 / Math.PI;
const offsets_normalize = 64;
const texcoord_normalize = 65535;
export const Points = Object.create(Style);

@@ -380,3 +374,2 @@

style.size = text_info.size.logical_size;
style.angle = 0; // text attached to point is always upright
style.texcoords = text_info.align[q.label.align].texcoords;

@@ -543,7 +536,7 @@ style.label_texture = textures[text_info.align[q.label.align].texture_id];

// Builds one or more point labels for a geometry
buildLabels (size, geometry, options) {
buildLabels (size, geometry, layout) {
let labels = [];
if (geometry.type === 'Point') {
labels.push(new LabelPoint(geometry.coordinates, size, options));
labels.push(new LabelPoint(geometry.coordinates, size, layout, layout.angle));
}

@@ -554,3 +547,3 @@ else if (geometry.type === 'MultiPoint') {

let point = points[i];
labels.push(new LabelPoint(point, size, options));
labels.push(new LabelPoint(point, size, layout, layout.angle));
}

@@ -560,3 +553,3 @@ }

let line = geometry.coordinates;
let point_labels = placePointsOnLine(line, size, options);
let point_labels = placePointsOnLine(line, size, layout);
for (let i = 0; i < point_labels.length; ++i) {

@@ -570,3 +563,3 @@ labels.push(point_labels[i]);

let line = lines[ln];
let point_labels = placePointsOnLine(line, size, options);
let point_labels = placePointsOnLine(line, size, layout);
for (let i = 0; i < point_labels.length; ++i) {

@@ -579,6 +572,6 @@ labels.push(point_labels[i]);

// Point at polygon centroid (of outer ring)
if (options.placement === PLACEMENT.CENTROID) {
if (layout.placement === PLACEMENT.CENTROID) {
let centroid = Geo.centroid(geometry.coordinates);
if (centroid) { // skip degenerate polygons
labels.push(new LabelPoint(centroid, size, options));
labels.push(new LabelPoint(centroid, size, layout, layout.angle));
}

@@ -590,3 +583,3 @@ }

for (let ln = 0; ln < rings.length; ln++) {
let point_labels = placePointsOnLine(rings[ln], size, options);
let point_labels = placePointsOnLine(rings[ln], size, layout);
for (let i = 0; i < point_labels.length; ++i) {

@@ -599,6 +592,6 @@ labels.push(point_labels[i]);

else if (geometry.type === 'MultiPolygon') {
if (options.placement === PLACEMENT.CENTROID) {
if (layout.placement === PLACEMENT.CENTROID) {
let centroid = Geo.multiCentroid(geometry.coordinates);
if (centroid) { // skip degenerate polygons
labels.push(new LabelPoint(centroid, size, options));
labels.push(new LabelPoint(centroid, size, layout, layout.angle));
}

@@ -611,3 +604,3 @@ }

for (let ln = 0; ln < rings.length; ln++) {
let point_labels = placePointsOnLine(rings[ln], size, options);
let point_labels = placePointsOnLine(rings[ln], size, layout);
for (let i = 0; i < point_labels.length; ++i) {

@@ -629,38 +622,61 @@ labels.push(point_labels[i]);

makeVertexTemplate(style, mesh) {
let color = style.color || StyleParser.defaults.color;
let vertex_layout = mesh.vertex_data.vertex_layout;
let i = 0;
// position - x & y coords will be filled in per-vertex below
this.fillVertexTemplate(vertex_layout, 'a_position', 0, { size: 2 });
this.fillVertexTemplate(vertex_layout, 'a_position', style.z || 0, { size: 1, offset: 2 });
// layer order - w coord of 'position' attribute (for packing efficiency)
this.fillVertexTemplate(vertex_layout, 'a_position', this.scaleOrder(style.order), { size: 1, offset: 3 });
// a_position.xyz - vertex position
// a_position.w - layer order
this.vertex_template[i++] = 0;
this.vertex_template[i++] = 0;
this.vertex_template[i++] = style.z || 0;
this.vertex_template[i++] = this.scaleOrder(style.order);
// scaling vector - (x, y) components per pixel, z = angle, w = show/hide
this.fillVertexTemplate(vertex_layout, 'a_shape', 0, { size: 4 });
this.fillVertexTemplate(vertex_layout, 'a_shape', style.label.layout.collide ? 0 : 1, { size: 1, offset: 3 }); // set initial label hide/show state
// a_shape.xy - size of point in pixels (scaling vector)
// a_shape.z - angle of point
// a_shape.w - show/hide flag
this.vertex_template[i++] = 0;
this.vertex_template[i++] = 0;
this.vertex_template[i++] = 0;
this.vertex_template[i++] = style.label.layout.collide ? 0 : 1; // set initial label hide/show state
// texture coords
this.fillVertexTemplate(vertex_layout, 'a_texcoord', 0, { size: 2 });
// a_texcoord.xy - texture coords
if (!mesh.variant.shader_point) {
this.vertex_template[i++] = 0;
this.vertex_template[i++] = 0;
}
// offsets
this.fillVertexTemplate(vertex_layout, 'a_offset', 0, { size: 2 });
// a_offset.xy - offset of point from center, in pixels
this.vertex_template[i++] = 0;
this.vertex_template[i++] = 0;
// color
this.fillVertexTemplate(vertex_layout, 'a_color', Vector.mult(color, 255), { size: 4 });
// a_color.rgba - feature color
const color = style.color || StyleParser.defaults.color;
this.vertex_template[i++] = color[0] * 255;
this.vertex_template[i++] = color[1] * 255;
this.vertex_template[i++] = color[2] * 255;
this.vertex_template[i++] = color[3] * 255;
// outline (can be static or dynamic depending on style)
if (this.defines.TANGRAM_HAS_SHADER_POINTS && mesh.variant.shader_point) {
let outline_color = style.outline_color || StyleParser.defaults.outline.color;
this.fillVertexTemplate(vertex_layout, 'a_outline_color', Vector.mult(outline_color, 255), { size: 4 });
this.fillVertexTemplate(vertex_layout, 'a_outline_edge', style.outline_edge_pct || StyleParser.defaults.outline.width, { size: 1 });
// a_selection_color.rgba - selection color
if (mesh.variant.selection) {
this.vertex_template[i++] = style.selection_color[0] * 255;
this.vertex_template[i++] = style.selection_color[1] * 255;
this.vertex_template[i++] = style.selection_color[2] * 255;
this.vertex_template[i++] = style.selection_color[3] * 255;
}
// selection color
this.fillVertexTemplate(vertex_layout, 'a_selection_color', Vector.mult(style.selection_color, 255), { size: 4 });
// point outline
if (mesh.variant.shader_point) {
// a_outline_color.rgba - outline color
const outline_color = style.outline_color || StyleParser.defaults.outline.color;
this.vertex_template[i++] = outline_color[0] * 255;
this.vertex_template[i++] = outline_color[1] * 255;
this.vertex_template[i++] = outline_color[2] * 255;
this.vertex_template[i++] = outline_color[3] * 255;
// a_outline_edge - point outline edge (as % of point size where outline begins)
this.vertex_template[i++] = style.outline_edge_pct || StyleParser.defaults.outline.width;
}
return this.vertex_template;
},
buildQuad(points, size, angle, angles, pre_angles, offset, offsets, texcoord_scale, curve, vertex_data, vertex_template) {
buildQuad(point, size, angle, angles, pre_angles, offset, offsets, texcoords, curve, vertex_data, vertex_template) {
if (size[0] <= 0 || size[1] <= 0) {

@@ -670,30 +686,15 @@ return 0; // size must be positive

return buildQuadsForPoints(
points,
return buildQuadForPoint(
point,
vertex_data,
vertex_template,
{
texcoord_index: vertex_data.vertex_layout.index.a_texcoord,
position_index: vertex_data.vertex_layout.index.a_position,
shape_index: vertex_data.vertex_layout.index.a_shape,
offset_index: vertex_data.vertex_layout.index.a_offset,
offsets_index: vertex_data.vertex_layout.index.a_offsets,
pre_angles_index: vertex_data.vertex_layout.index.a_pre_angles,
angles_index: vertex_data.vertex_layout.index.a_angles
},
{
quad: size,
quad_normalize: 256, // values have an 8-bit fraction
offset,
offsets,
pre_angles: pre_angles,
angle: angle * 4096, // values have a 12-bit fraction
angles: angles,
curve,
texcoord_scale,
texcoord_normalize,
pre_angles_normalize,
angles_normalize,
offsets_normalize
}
vertex_data.vertex_layout.index,
size,
offset,
offsets,
pre_angles,
angle * 4096, // angle values have a 12-bit fraction
angles,
texcoords,
curve
);

@@ -716,3 +717,2 @@ },

let vertex_template = this.makeVertexTemplate(style, mesh);
let angle = label.angle || style.angle;

@@ -752,5 +752,5 @@ let size, texcoords;

let geom_count = this.buildQuad(
[label.position], // position
label.position, // position
size, // size in pixels
angle, // angle in radians
label.angle, // angle in radians
null, // placeholder for multiple angles

@@ -774,3 +774,2 @@ null, // placeholder for multiple pre_angles

let mesh, vertex_template;
let angle = label.angle;
let geom_count = 0;

@@ -805,5 +804,5 @@

let seg_count = this.buildQuad(
[position], // position
position, // position
size, // size in pixels
angle, // angle in degrees
label.angle, // angle in degrees
angles, // angles per segment

@@ -848,5 +847,5 @@ pre_angles, // pre_angle array (rotation applied before offseting)

let seg_count = this.buildQuad(
[position], // position
position, // position
size, // size in pixels
angle, // angle in degrees
label.angle, // angle in degrees
angles, // angles per segment

@@ -925,8 +924,8 @@ pre_angles, // pre_angle array (rotation applied before offseting)

{ name: 'a_shape', size: 4, type: gl.SHORT, normalized: false },
{ name: 'a_texcoord', size: 2, type: gl.UNSIGNED_SHORT, normalized: true },
{ name: 'a_texcoord', size: 2, type: gl.UNSIGNED_SHORT, normalized: true, static: (variant.shader_point ? [0, 0] : null) },
{ name: 'a_offset', size: 2, type: gl.SHORT, normalized: false },
{ name: 'a_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
{ name: 'a_selection_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true, static: (variant.selection ? null : [0, 0, 0, 0]) },
{ name: 'a_outline_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true, static: (variant.shader_point ? null : [0, 0, 0, 0]) },
{ name: 'a_outline_edge', size: 1, type: gl.FLOAT, normalized: false, static: (variant.shader_point ? null : 0) },
{ name: 'a_selection_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
{ name: 'a_outline_edge', size: 1, type: gl.FLOAT, normalized: false, static: (variant.shader_point ? null : 0) }
];

@@ -947,2 +946,3 @@

key,
selection: 1, // TODO: make this vary by draw params
shader_point: (texture === SHADER_POINT_VARIANT), // is shader point

@@ -949,0 +949,0 @@ blend_order: draw.blend_order,

@@ -113,17 +113,2 @@ // Rendering styles

fillVertexTemplate(vertex_layout, attribute, value, { size, offset }) {
offset = (offset === undefined) ? 0 : offset;
let index = vertex_layout.index[attribute];
if (index === undefined) {
log('warn', `Style: in style '${this.name}', no index found in vertex layout for attribute '${attribute}'`);
return;
}
for (let i = 0; i < size; ++i) {
let v = value.length > i ? value[i] : value;
this.vertex_template[index + i + offset] = v;
}
},
/*** Style parsing and geometry construction ***/

@@ -130,0 +115,0 @@

@@ -14,2 +14,4 @@ // Text rendering style

TextStyle.vertex_layouts = {}; // vertex layouts by variant key
Object.assign(TextStyle, {

@@ -42,6 +44,10 @@ name: 'text',

let vertex_layout = mesh.vertex_data.vertex_layout;
let i = vertex_layout.index.a_pre_angles;
this.fillVertexTemplate(vertex_layout, 'a_pre_angles', 0, { size: 4 });
this.fillVertexTemplate(vertex_layout, 'a_offsets', 0, { size: 4 });
this.fillVertexTemplate(vertex_layout, 'a_angles', 0, { size: 4 });
// a_pre_angles.xyzw - rotation of entire curved label
// a_angles.xyzw - angle of each curved label segment
// a_offsets.xyzw - offset of each curved label segment
for (let j=0; j < 12; j++) {
this.vertex_template[i++] = 0;
}

@@ -262,4 +268,5 @@ return this.vertex_template;

// Create or return vertex layout
vertexLayoutForMeshVariant () {
if (this.vertex_layout == null) {
vertexLayoutForMeshVariant(variant) {
// Vertex layout only depends on shader point flag, so using it as layout key to avoid duplicate layouts
if (TextStyle.vertex_layouts[variant.shader_point] == null) {
// TODO: could make selection, offset, and curved label attribs optional, but may not be worth it

@@ -273,11 +280,11 @@ // since text points generally don't consume much memory anyway

{ name: 'a_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
{ name: 'a_selection_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true, static: (variant.selection ? null : [0, 0, 0, 0]) },
{ name: 'a_pre_angles', size: 4, type: gl.BYTE, normalized: false },
{ name: 'a_angles', size: 4, type: gl.SHORT, normalized: false },
{ name: 'a_offsets', size: 4, type: gl.UNSIGNED_SHORT, normalized: false },
{ name: 'a_pre_angles', size: 4, type: gl.BYTE, normalized: false },
{ name: 'a_selection_color', size: 4, type: gl.UNSIGNED_BYTE, normalized: true },
];
this.vertex_layout = new VertexLayout(attribs);
TextStyle.vertex_layouts[variant.shader_point] = new VertexLayout(attribs);
}
return this.vertex_layout;
return TextStyle.vertex_layouts[variant.shader_point];
},

@@ -284,0 +291,0 @@ });

@@ -174,8 +174,13 @@ import Tile from './tile';

type: 'tileManagerUpdateLabels',
run: (task) => {
return mainThreadLabelCollisionPass(tiles, this.collision.zoom, this.isLoadingVisibleTiles()).then(results => {
this.collision.task = null;
Task.finish(task, results);
this.updateTileStates().then(() => this.scene.immediateRedraw());
});
run: async task => {
// Do collision pass, then update view
const results = await mainThreadLabelCollisionPass(tiles, this.collision.zoom, this.isLoadingVisibleTiles());
this.scene.requestRedraw();
// Clear state to allow another collision pass to start
this.collision.task = null;
Task.finish(task, results);
// Check if tiles changed during previous collision pass - will start new pass if so
this.updateTileStates();
}

@@ -182,0 +187,0 @@ };

@@ -165,3 +165,3 @@ import log from '../utils/log';

tile.debug.rendering = +new Date();
tile.debug.building = +new Date();
tile.debug.feature_count = 0;

@@ -189,3 +189,3 @@ tile.debug.layers = null;

// Render features in layer
// Build features in layer
for (let s=0; s < source_layers.length; s++) {

@@ -215,3 +215,3 @@ let source_layer = source_layers[s];

// Render draw groups
// Build draw groups
for (let group_name in draw_groups) {

@@ -243,9 +243,9 @@ let group = draw_groups[group_name];

}
tile.debug.rendering = +new Date() - tile.debug.rendering;
tile.debug.building = +new Date() - tile.debug.building;
// Send styles back to main thread as they finish building, in two groups: collision vs. non-collision
let tile_styles = this.stylesForTile(tile, styles).map(s => styles[s]);
Tile.sendStyleGroups(tile, tile_styles, { scene_id }, style => style.collision ? 'collision' : 'non-collision');
// Tile.sendStyleGroups(tile, tile_styles, { scene_id }, style => style.name); // call for each style
// Tile.sendStyleGroups(tile, tile_styles, { scene_id }, style => 'styles'); // all styles in single call (previous behavior)
Tile.buildStyleGroups(tile, tile_styles, scene_id, style => style.collision ? 'collision' : 'non-collision');
// Tile.buildStyleGroups(tile, tile_styles, scene_id, style => style.name); // call for each style
// Tile.buildStyleGroups(tile, tile_styles, scene_id, style => 'styles'); // all styles in single call (previous behavior)
}

@@ -263,63 +263,61 @@

// Send groups of styles back to main thread, asynchronously (as they finish building),
// grouped by the provided function
static sendStyleGroups(tile, styles, { scene_id }, group_by) {
// Group styles
let groups = {};
styles.forEach(s => {
let group_name = group_by(s);
groups[group_name] = groups[group_name] || [];
groups[group_name].push(s);
});
// Build styles (grouped by the provided function) and send back to main thread as they finish building
static buildStyleGroups(tile, styles, scene_id, group_by) {
// Group the styles; each group will be sent to the main thread when the styles in the group finish building.
const groups = styles.reduce((groups, style) => {
const group = group_by(style);
groups[group] = groups[group] || [];
groups[group].push(style);
return groups;
}, {});
if (Object.keys(groups).length > 0) {
let progress = { start: true };
tile.mesh_data = {};
// If nothing to build, return empty tile to main thread
if (Object.keys(groups).length === 0) {
WorkerBroker.postMessage(
`TileManager_${scene_id}.buildTileStylesCompleted`,
WorkerBroker.withTransferables({ tile: Tile.slice(tile), progress: { start: true, done: true } })
);
Collision.resetTile(tile.id); // clear collision if we're done with the tile
return;
}
for (let group_name in groups) {
let group = groups[group_name];
// Build each group of styles
const progress = {};
for (const group_name in groups) {
Tile.buildStyleGroup({ group_name, groups, tile, progress, scene_id });
}
}
Promise.all(
group.map(style => {
return style.endData(tile)
.then(style_data => {
if (style_data) {
tile.mesh_data[style.name] = style_data;
}
});
}))
.then(() => {
log('trace', `Finished style group '${group_name}' for tile ${tile.key}`);
// Build a single group of styles
static async buildStyleGroup({ group_name, groups, tile, progress, scene_id }) {
const group = groups[group_name];
const mesh_data = {};
try {
// For each group, build all styles in the group
await Promise.all(group.map(async (style) => {
const style_data = await style.endData(tile);
if (style_data) {
mesh_data[style.name] = style_data;
}
}));
// Clear group and check if all groups finished
groups[group_name] = [];
if (Object.keys(groups).every(g => groups[g].length === 0)) {
progress.done = true;
}
// Mark the group as done, and check if all groups have finished
log('trace', `Finished style group '${group_name}' for tile ${tile.key}`);
groups[group_name] = null;
if (Object.keys(groups).every(g => groups[g] == null)) {
progress.done = true;
}
// Send meshes to main thread
WorkerBroker.postMessage(
`TileManager_${scene_id}.buildTileStylesCompleted`,
WorkerBroker.withTransferables({ tile: Tile.slice(tile, ['mesh_data']), progress })
);
progress.start = null;
tile.mesh_data = {}; // reset so each group sends separate set of style meshes
if (progress.done) {
Collision.resetTile(tile.id); // clear collision if we're done with the tile
}
})
.catch((e) => {
log('error', `Error for style group '${group_name}' for tile ${tile.key}`, (e && e.stack) || e);
});
}
}
else {
// Nothing to build, return empty tile to main thread
// Send meshes to main thread
WorkerBroker.postMessage(
`TileManager_${scene_id}.buildTileStylesCompleted`,
WorkerBroker.withTransferables({ tile: Tile.slice(tile), progress: { start: true, done: true } })
WorkerBroker.withTransferables({ tile: { ...Tile.slice(tile), mesh_data }, progress })
);
Collision.resetTile(tile.id); // clear collision if we're done with the tile
if (progress.done) {
Collision.resetTile(tile.id); // clear collision if we're done with the tile
}
}
catch (e) {
log('error', `Error for style group '${group_name}' for tile ${tile.key}`, (e && e.stack) || e);
}
}

@@ -326,0 +324,0 @@

@@ -71,30 +71,28 @@ // Miscellaneous geo functions

/**
Convert mercator meters to lat-lng
Convert mercator meters to lat-lng, in-place
*/
Geo.metersToLatLng = function ([x, y]) {
Geo.metersToLatLng = function (c) {
c[0] /= Geo.half_circumference_meters;
c[1] /= Geo.half_circumference_meters;
x /= Geo.half_circumference_meters;
y /= Geo.half_circumference_meters;
c[1] = (2 * Math.atan(Math.exp(c[1] * Math.PI)) - (Math.PI / 2)) / Math.PI;
y = (2 * Math.atan(Math.exp(y * Math.PI)) - (Math.PI / 2)) / Math.PI;
c[0] *= 180;
c[1] *= 180;
x *= 180;
y *= 180;
return [x, y];
return c;
};
/**
Convert lat-lng to mercator meters
Convert lat-lng to mercator meters, in-place
*/
Geo.latLngToMeters = function([x, y]) {
Geo.latLngToMeters = function (c) {
// Latitude
y = Math.log(Math.tan(y*Math.PI/360 + Math.PI/4)) / Math.PI;
y *= Geo.half_circumference_meters;
c[1] = Math.log(Math.tan(c[1] * Math.PI / 360 + Math.PI / 4)) / Math.PI;
c[1] *= Geo.half_circumference_meters;
// Longitude
x *= Geo.half_circumference_meters / 180;
c[0] *= Geo.half_circumference_meters / 180;
return [x, y];
return c;
};

@@ -108,6 +106,3 @@

coord[1] = (coord[1] / units_per_meter) + min.y;
let [x, y] = Geo.metersToLatLng(coord);
coord[0] = x;
coord[1] = y;
Geo.metersToLatLng(coord);
});

@@ -114,0 +109,0 @@ return geometry;

import Vector from './vector';
// single-allocation, reusable objects
const ZERO_AXES = [[1, 0], [0, 1]];
const proj_a = [], proj_b = [];
let d0, d1, d2, d3;
export default class OBB {
constructor (x, y, a, w, h) {
this.dimension = [w, h];
this.dimension = [w / 2, h / 2]; // store half-dimension as that's what's needed in calculations below
this.angle = a;
this.centroid = [x, y];
this.quad = [];
this.axes = [];
this.quad = null;
this.axis_0 = null;
this.axis_1 = null;

@@ -26,59 +32,92 @@ this.update();

getExtent () {
let aabb = [Infinity, Infinity, -Infinity, -Infinity];
for (let i = 0; i < 4; ++i) {
aabb[0] = Math.min(this.quad[i][0], aabb[0]);
aabb[1] = Math.min(this.quad[i][1], aabb[1]);
aabb[2] = Math.max(this.quad[i][0], aabb[2]);
aabb[3] = Math.max(this.quad[i][1], aabb[3]);
// special handling to skip calculations for 0-angle
if (this.angle === 0) {
return [
this.quad[0], this.quad[1], // lower-left
this.quad[4], this.quad[5] // upper-right
];
}
let aabb = [
Math.min(this.quad[0], this.quad[2], this.quad[4], this.quad[6]), // min x
Math.min(this.quad[1], this.quad[3], this.quad[5], this.quad[7]), // min y
Math.max(this.quad[0], this.quad[2], this.quad[4], this.quad[6]), // max x
Math.max(this.quad[1], this.quad[3], this.quad[5], this.quad[7]) // max y
];
return aabb;
}
perpAxes () {
this.axes[0] = Vector.normalize(Vector.sub(this.quad[2], this.quad[3]));
this.axes[1] = Vector.normalize(Vector.sub(this.quad[2], this.quad[1]));
updateAxes () {
// upper-left to upper-right
this.axis_0 = Vector.normalize([this.quad[4] - this.quad[6], this.quad[5] - this.quad[7]]);
// lower-right to upper-right
this.axis_1 = Vector.normalize([this.quad[4] - this.quad[2], this.quad[5] - this.quad[3]]);
}
update () {
let x = [ Math.cos(this.angle), Math.sin(this.angle)];
let y = [-Math.sin(this.angle), Math.cos(this.angle)];
const c = this.centroid;
const w2 = this.dimension[0];
const h2 = this.dimension[1];
x = Vector.mult(x, this.dimension[0] / 2.0);
y = Vector.mult(y, this.dimension[1] / 2.0);
// special handling to skip calculations for 0-angle
if (this.angle === 0) {
// quad is a flat array storing 4 [x, y] vectors
this.quad = [
c[0] - w2, c[1] - h2, // lower-left
c[0] + w2, c[1] - h2, // lower-right
c[0] + w2, c[1] + h2, // upper-right
c[0] - w2, c[1] + h2 // upper-left
];
this.quad[0] = Vector.sub(Vector.sub(this.centroid, x), y); // lower-left
this.quad[1] = Vector.sub(Vector.add(this.centroid, x), y); // lower-right
this.quad[2] = Vector.add(Vector.add(this.centroid, x), y); // uper-right
this.quad[3] = Vector.add(Vector.sub(this.centroid, x), y); // uper-left
this.axis_0 = ZERO_AXES[0];
this.axis_1 = ZERO_AXES[1];
}
// calculate axes and enclosing quad
else {
let x0 = Math.cos(this.angle) * w2;
let x1 = Math.sin(this.angle) * w2;
this.perpAxes();
}
let y0 = -Math.sin(this.angle) * h2;
let y1 = Math.cos(this.angle) * h2;
static projectToAxis (obb, axis) {
let min = Infinity;
let max = -Infinity;
// quad is a flat array storing 4 [x, y] vectors
this.quad = [
c[0] - x0 - y0, c[1] - x1 - y1, // lower-left
c[0] + x0 - y0, c[1] + x1 - y1, // lower-right
c[0] + x0 + y0, c[1] + x1 + y1, // upper-right
c[0] - x0 + y0, c[1] - x1 + y1 // upper-left
];
let quad = obb.quad;
this.updateAxes();
}
}
static projectToAxis (obb, axis, proj) {
// for each axis, project obb quad to it and find min and max values
for (let i = 0; i < 4; ++i) {
let d = Vector.dot(quad[i], axis);
min = Math.min(min, d);
max = Math.max(max, d);
}
let quad = obb.quad;
d0 = quad[0] * axis[0] + quad[1] * axis[1];
d1 = quad[2] * axis[0] + quad[3] * axis[1];
d2 = quad[4] * axis[0] + quad[5] * axis[1];
d3 = quad[6] * axis[0] + quad[7] * axis[1];
return [min, max];
proj[0] = Math.min(d0, d1, d2, d3);
proj[1] = Math.max(d0, d1, d2, d3);
return proj;
}
static axisCollide (obb_a, obb_b, axes) {
for (let i = 0; i < 2; ++i) {
let a_proj = OBB.projectToAxis(obb_a, axes[i]);
let b_proj = OBB.projectToAxis(obb_b, axes[i]);
static axisCollide(obb_a, obb_b, axis_0, axis_1) {
OBB.projectToAxis(obb_a, axis_0, proj_a);
OBB.projectToAxis(obb_b, axis_0, proj_b);
if (proj_b[0] > proj_a[1] || proj_b[1] < proj_a[0]) {
return false;
}
if (b_proj[0] > a_proj[1] || b_proj[1] < a_proj[0]) {
return false;
}
OBB.projectToAxis(obb_a, axis_1, proj_a);
OBB.projectToAxis(obb_b, axis_1, proj_b);
if (proj_b[0] > proj_a[1] || proj_b[1] < proj_a[0]) {
return false;
}
return true;

@@ -88,5 +127,6 @@ }

static intersect(obb_a, obb_b) {
return OBB.axisCollide(obb_a, obb_b, obb_a.axes) && OBB.axisCollide(obb_a, obb_b, obb_b.axes);
return OBB.axisCollide(obb_a, obb_b, obb_a.axis_0, obb_a.axis_1) &&
OBB.axisCollide(obb_a, obb_b, obb_b.axis_0, obb_b.axis_1);
}
}

@@ -7,3 +7,2 @@ // Miscellaneous utilities

import WorkerBroker from './worker_broker';
import Geo from './geo';

@@ -241,5 +240,1 @@ export default Utils;

};
Utils.pointInTile = function (point) {
return point[0] >= 0 && point[1] > -Geo.tile_scale && point[0] < Geo.tile_scale && point[1] <= 0;
};

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc