@tileflow/core 0.1.0-alpha.36 → 0.1.0-alpha.37
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/README.md +12 -11
- package/dist/browser.d.ts +183 -9
- package/dist/browser.js +1553 -74
- package/dist/build.d.ts +2 -2
- package/dist/build.js +1382 -694
- package/dist/config-reference.json +3315 -1044
- package/dist/{icon-composition-sN3JkXdb.d.ts → icon-composition-C5X3Q9Ya.d.ts} +332 -6
- package/dist/index.d.ts +153 -154
- package/dist/index.js +3711 -2667
- package/dist/worker/buildings-3d-worker.global.js +1 -1
- package/docs/buildings-3d.md +105 -13
- package/docs/data-is-separate-from-design.md +12 -31
- package/docs/nautical-marks.md +200 -0
- package/docs/public-api-and-browser-subpath.md +4 -1
- package/package.json +1 -1
package/dist/browser.js
CHANGED
|
@@ -4269,23 +4269,23 @@ function matrix3d(matrix) {
|
|
|
4269
4269
|
var tileflowAtmosphereMetadataKey = "tileflow:atmosphere";
|
|
4270
4270
|
function readTileflowAtmosphere(value) {
|
|
4271
4271
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
4272
|
-
const
|
|
4273
|
-
if (
|
|
4272
|
+
const record2 = value;
|
|
4273
|
+
if (record2.version !== 1 || Object.keys(record2).length !== 7) return void 0;
|
|
4274
4274
|
for (const key of ["skyColor", "horizonColor", "fogColor", "spaceColor"]) {
|
|
4275
|
-
if (typeof
|
|
4275
|
+
if (typeof record2[key] !== "string" || !/^#[0-9a-f]{6}$/i.test(record2[key])) return void 0;
|
|
4276
4276
|
}
|
|
4277
4277
|
for (const key of ["starIntensity", "starParallax"]) {
|
|
4278
|
-
const number =
|
|
4278
|
+
const number = record2[key];
|
|
4279
4279
|
if (typeof number !== "number" || !Number.isFinite(number) || number < 0 || number > 1)
|
|
4280
4280
|
return void 0;
|
|
4281
4281
|
}
|
|
4282
4282
|
return {
|
|
4283
|
-
skyColor:
|
|
4284
|
-
horizonColor:
|
|
4285
|
-
fogColor:
|
|
4286
|
-
spaceColor:
|
|
4287
|
-
starIntensity:
|
|
4288
|
-
starParallax:
|
|
4283
|
+
skyColor: record2.skyColor,
|
|
4284
|
+
horizonColor: record2.horizonColor,
|
|
4285
|
+
fogColor: record2.fogColor,
|
|
4286
|
+
spaceColor: record2.spaceColor,
|
|
4287
|
+
starIntensity: record2.starIntensity,
|
|
4288
|
+
starParallax: record2.starParallax
|
|
4289
4289
|
};
|
|
4290
4290
|
}
|
|
4291
4291
|
function atmosphereSpaceOpacity(zoom) {
|
|
@@ -4832,6 +4832,16 @@ var tileflowModelParts = Object.freeze({
|
|
|
4832
4832
|
accent: 3,
|
|
4833
4833
|
roof: 4
|
|
4834
4834
|
});
|
|
4835
|
+
var tileflowModelLightingRoles = Object.freeze({
|
|
4836
|
+
unclassified: 0,
|
|
4837
|
+
wall: 1,
|
|
4838
|
+
door: 2,
|
|
4839
|
+
roof: 3,
|
|
4840
|
+
window: 4,
|
|
4841
|
+
lamp: 5,
|
|
4842
|
+
logo: 6
|
|
4843
|
+
});
|
|
4844
|
+
var tileflowModelDefaultLightingRoles = Object.freeze([1, 1, 4, 1, 3]);
|
|
4835
4845
|
var tileflowModelLimits = Object.freeze({
|
|
4836
4846
|
/** Levels of detail, nearest first. */
|
|
4837
4847
|
levels: 3,
|
|
@@ -4954,6 +4964,83 @@ function tileMetresAt(latitude, zoom) {
|
|
|
4954
4964
|
return 40075016686e-3 * Math.cos(latitude * Math.PI / 180) / 2 ** zoom;
|
|
4955
4965
|
}
|
|
4956
4966
|
|
|
4967
|
+
// src/building-illumination.ts
|
|
4968
|
+
var defaultBuildingFloodLight = Object.freeze({
|
|
4969
|
+
intensity: 0,
|
|
4970
|
+
brightnessScale: 1,
|
|
4971
|
+
color: "#f5cfa8",
|
|
4972
|
+
groundRadius: [30, 100],
|
|
4973
|
+
minGroundHeight: 30,
|
|
4974
|
+
minWallHeight: 200,
|
|
4975
|
+
wallHeightRatio: 1 / 3
|
|
4976
|
+
});
|
|
4977
|
+
var defaultModelEmissionGradient = [0, 1, 1, 1, 0];
|
|
4978
|
+
function readBuildingFloodLight(value) {
|
|
4979
|
+
if (!record(value)) return void 0;
|
|
4980
|
+
const defaults = defaultBuildingFloodLight;
|
|
4981
|
+
const radius = value.groundRadius;
|
|
4982
|
+
return {
|
|
4983
|
+
intensity: bounded(value.intensity, 0, 1, 0),
|
|
4984
|
+
brightnessScale: bounded(value.brightnessScale, 0, 16, 1),
|
|
4985
|
+
color: typeof value.color === "string" ? value.color : defaults.color,
|
|
4986
|
+
groundRadius: Array.isArray(radius) && radius.length === 2 && radius.every((v2) => typeof v2 === "number" && Number.isFinite(v2) && v2 >= 0 && v2 <= 2048) && radius[0] <= radius[1] ? [radius[0], radius[1]] : defaults.groundRadius,
|
|
4987
|
+
minGroundHeight: bounded(value.minGroundHeight, 0, 6e3, defaults.minGroundHeight),
|
|
4988
|
+
minWallHeight: bounded(value.minWallHeight, 0, 6e3, defaults.minWallHeight),
|
|
4989
|
+
wallHeightRatio: bounded(value.wallHeightRatio, 0, 1, defaults.wallHeightRatio)
|
|
4990
|
+
};
|
|
4991
|
+
}
|
|
4992
|
+
function readModelRoleLighting(value) {
|
|
4993
|
+
if (!record(value)) return void 0;
|
|
4994
|
+
const result = {};
|
|
4995
|
+
for (const role of Object.keys(tileflowModelLightingRoles)) {
|
|
4996
|
+
const entry = value[role];
|
|
4997
|
+
if (!record(entry)) continue;
|
|
4998
|
+
const gradient = entry.emissiveHeight;
|
|
4999
|
+
result[role] = {
|
|
5000
|
+
emissiveStrength: bounded(entry.emissiveStrength, 0, 5, 0),
|
|
5001
|
+
...entry.emissiveVariation === void 0 ? {} : { emissiveVariation: bounded(entry.emissiveVariation, 0, 1, 0) },
|
|
5002
|
+
...typeof entry.emissiveColor === "string" ? { emissiveColor: entry.emissiveColor } : {},
|
|
5003
|
+
...typeof entry.color === "string" ? { color: entry.color } : {},
|
|
5004
|
+
...entry.colorMix === void 0 ? {} : { colorMix: bounded(entry.colorMix, 0, 1, 1) },
|
|
5005
|
+
...entry.metallic === void 0 ? {} : { metallic: bounded(entry.metallic, 0, 1, 0) },
|
|
5006
|
+
...entry.roughness === void 0 ? {} : { roughness: bounded(entry.roughness, 0, 1, 1) },
|
|
5007
|
+
...readRoleColorVariation(entry.colorVariation),
|
|
5008
|
+
emissiveHeight: Array.isArray(gradient) && gradient.length === 5 && gradient.every(
|
|
5009
|
+
(v2, i2) => typeof v2 === "number" && Number.isFinite(v2) && v2 <= 1 && v2 >= (i2 === 4 ? -1 : 0)
|
|
5010
|
+
) ? gradient : defaultModelEmissionGradient
|
|
5011
|
+
};
|
|
5012
|
+
}
|
|
5013
|
+
return Object.keys(result).length ? result : void 0;
|
|
5014
|
+
}
|
|
5015
|
+
function readRoleColorVariation(value) {
|
|
5016
|
+
if (!record(value)) return {};
|
|
5017
|
+
for (const [key, max2] of [
|
|
5018
|
+
["hue", 360],
|
|
5019
|
+
["saturation", 100],
|
|
5020
|
+
["lightness", 100]
|
|
5021
|
+
]) {
|
|
5022
|
+
const range = value[key];
|
|
5023
|
+
if (!Array.isArray(range) || range.length !== 2 || range[0] > range[1] || !range.every((v2) => typeof v2 === "number" && Number.isFinite(v2) && v2 >= 0 && v2 <= max2))
|
|
5024
|
+
return {};
|
|
5025
|
+
}
|
|
5026
|
+
if (value.space !== void 0 && value.space !== "srgb" && value.space !== "linear") return {};
|
|
5027
|
+
return { colorVariation: value };
|
|
5028
|
+
}
|
|
5029
|
+
function buildingFloodLightShare(sun, brightnessScale = 1) {
|
|
5030
|
+
const luminance2 = (rgb) => {
|
|
5031
|
+
const linear = rgb.map((c) => c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
|
|
5032
|
+
return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2];
|
|
5033
|
+
};
|
|
5034
|
+
const brightness = (luminance2(sun.ambient.rgb) * sun.ambient.intensity + luminance2(sun.rgb) * sun.intensity * Math.max(0, sun.altitude / 90)) / 2;
|
|
5035
|
+
return Math.min(1, Math.max(0, (0.026 - brightness * brightnessScale) / (0.026 - 0.015)));
|
|
5036
|
+
}
|
|
5037
|
+
function bounded(value, min, max2, fallback) {
|
|
5038
|
+
return typeof value === "number" && Number.isFinite(value) && value >= min && value <= max2 ? value : fallback;
|
|
5039
|
+
}
|
|
5040
|
+
function record(value) {
|
|
5041
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5042
|
+
}
|
|
5043
|
+
|
|
4957
5044
|
// src/buildings-3d-design.ts
|
|
4958
5045
|
var tileflowBuildingsMetadataKey = "tileflow:buildings";
|
|
4959
5046
|
var tileflowDefaultBuildings3DDesign = Object.freeze({
|
|
@@ -4968,7 +5055,8 @@ var tileflowDefaultBuildings3DDesign = Object.freeze({
|
|
|
4968
5055
|
nightLight: 0,
|
|
4969
5056
|
windows: false,
|
|
4970
5057
|
shadows: true,
|
|
4971
|
-
occlusion: true
|
|
5058
|
+
occlusion: true,
|
|
5059
|
+
modelOcclusion: 0.75
|
|
4972
5060
|
});
|
|
4973
5061
|
function readTileflowBuildings3D(metadata) {
|
|
4974
5062
|
if (!isRecord6(metadata)) return void 0;
|
|
@@ -4980,6 +5068,8 @@ function readTileflowBuildings3D(metadata) {
|
|
|
4980
5068
|
if (typeof color !== "string" && !Array.isArray(color)) return void 0;
|
|
4981
5069
|
const within = (number, high, fallback) => typeof number === "number" && number >= 0 && number <= high ? number : fallback;
|
|
4982
5070
|
const defaults = tileflowDefaultBuildings3DDesign;
|
|
5071
|
+
const floodLight = readBuildingFloodLight(value.floodLight);
|
|
5072
|
+
const modelLighting = readModelRoleLighting(value.modelLighting);
|
|
4983
5073
|
return {
|
|
4984
5074
|
source,
|
|
4985
5075
|
layers: {
|
|
@@ -4994,9 +5084,12 @@ function readTileflowBuildings3D(metadata) {
|
|
|
4994
5084
|
verticalGradient: value.verticalGradient !== false,
|
|
4995
5085
|
...typeof value.edgeColor === "string" ? { edgeColor: value.edgeColor } : {},
|
|
4996
5086
|
nightLight: within(value.nightLight, 2, 0),
|
|
5087
|
+
...floodLight ? { floodLight } : {},
|
|
5088
|
+
...modelLighting ? { modelLighting } : {},
|
|
4997
5089
|
windows: value.windows === true,
|
|
4998
5090
|
shadows: typeof value.shadows === "boolean" ? value.shadows : defaults.shadows,
|
|
4999
5091
|
occlusion: typeof value.occlusion === "boolean" ? value.occlusion : defaults.occlusion,
|
|
5092
|
+
modelOcclusion: within(value.modelOcclusion, 1, defaults.modelOcclusion),
|
|
5000
5093
|
...modelsOf(value.models),
|
|
5001
5094
|
...modelColorsOf(value.modelColors),
|
|
5002
5095
|
...landmarksOf(value.landmarks)
|
|
@@ -5004,6 +5097,17 @@ function readTileflowBuildings3D(metadata) {
|
|
|
5004
5097
|
}
|
|
5005
5098
|
var modelId = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
5006
5099
|
var tileflowBuildingModelPlacementLimit = 256;
|
|
5100
|
+
var teamModelPrefix = "tileflow://team-models/";
|
|
5101
|
+
function tileflowTeamModelUrl(model) {
|
|
5102
|
+
if (!modelId.test(model) || model.length > 64)
|
|
5103
|
+
throw new RangeError(`"${model}" is not a 3D model name.`);
|
|
5104
|
+
return `${teamModelPrefix}${model}`;
|
|
5105
|
+
}
|
|
5106
|
+
function readTileflowTeamModelName(url) {
|
|
5107
|
+
if (!url.startsWith(teamModelPrefix)) return void 0;
|
|
5108
|
+
const model = url.slice(teamModelPrefix.length);
|
|
5109
|
+
return modelId.test(model) && model.length <= 64 ? model : void 0;
|
|
5110
|
+
}
|
|
5007
5111
|
function modelsOf(value) {
|
|
5008
5112
|
if (!Array.isArray(value)) return {};
|
|
5009
5113
|
const models = [];
|
|
@@ -6028,8 +6132,9 @@ function meshTileflowBuildings(layer, options) {
|
|
|
6028
6132
|
}
|
|
6029
6133
|
used = true;
|
|
6030
6134
|
tallest = Math.max(tallest, height);
|
|
6031
|
-
const
|
|
6032
|
-
const
|
|
6135
|
+
const identified = Number.isSafeInteger(feature.id);
|
|
6136
|
+
const seed = identified ? hash(feature.id * 0.0731) : hash(cx * 0.0731 + cy * 0.19137 + index * 1.618);
|
|
6137
|
+
const jitter = !identified && rings[0].some((value) => value <= 0 || value >= tileflowBuildingExtent) ? hash(height * 7.31 + base * 3.17 + tone) : seed;
|
|
6033
6138
|
const building = { base, top: height, jitter, seed, tone, group, use, cx, cy, index };
|
|
6034
6139
|
const rounded = rings.map(
|
|
6035
6140
|
(ring, at) => roundCorners(ring, at, edgeRadius, options.cornerAngle)
|
|
@@ -6578,7 +6683,7 @@ function browserWorker() {
|
|
|
6578
6683
|
if (typeof Worker === "undefined" || false)
|
|
6579
6684
|
return;
|
|
6580
6685
|
const url = URL.createObjectURL(
|
|
6581
|
-
new Blob(['"use strict";(()=>{var An=new Set,Dn=!1;function kn(n,e,t=2){let s=e&&e.length,o=s?e[0]*t:n.length;An.size&&An.clear();let r=he(n,0,o,t,!0),i=[];if(!r||r.next===r.prev)return i;let u=0,l=0,c=0;if(s&&(r=_e(n,e,r,t)),n.length>80*t){u=n[0],l=n[1];let a=u,f=l;for(let h=t;h<o;h+=t){let m=n[h],b=n[h+1];m<u&&(u=m),b<l&&(l=b),m>a&&(a=m),b>f&&(f=b)}c=Math.max(a-u,f-l),c=c!==0?32767/c:0}return Wn(r,i,u,l,c),i}function he(n,e,t,s,o){let r=null;if(o===Ye(n,e,t,s)>0)for(let i=e;i<t;i+=s)r=fe(i/s|0,n[i],n[i+1],r);else for(let i=t-s;i>=e;i-=s)r=fe(i/s|0,n[i],n[i+1],r);return r&&In(r,r.next)&&(Tn(r),r=r.next),r}function fn(n,e=n){let t=e===n,s=n,o;do o=!1,s!==s.next&&(An.size===0||!An.has(s))&&(In(s,s.next)||O(s.prev,s,s.next)===0)?((t||s===e)&&(e=s.prev),Dn=!0,Tn(s),s=s.prev,o=!0):(t||s!==e)&&(s=s.next,o=!t);while(o||s!==e);return e}function Wn(n,e,t,s,o){o&&Se(n,t,s,o);let r=n,i=!1;for(;n.prev!==n.next;){let u=n.prev,l=n.next;if(O(u,n,l)<0&&(o?ke(n,t,s,o):Te(n))){e.push(u.i,n.i,l.i),Tn(n),n=l,r=l;continue}if(n=l,n===r){if(Dn=!1,n=fn(n),Dn){r=n;continue}if(!i){n=Be(n,e),r=n,i=!0;continue}Fe(n,e,t,s,o);break}}}function Te(n){let e=n.prev,t=n,s=n.next,o=e.x,r=t.x,i=s.x,u=e.y,l=t.y,c=s.y,a=Math.min(o,r,i),f=Math.min(u,l,c),h=Math.max(o,r,i),m=Math.max(u,l,c),b=s.next;for(;b!==e;){if(b.x>=a&&b.x<=h&&b.y>=f&&b.y<=m&&!(o===b.x&&u===b.y)&&Vn(o,u,r,l,i,c,b.x,b.y)&&O(b.prev,b,b.next)>=0)return!1;b=b.next}return!0}function ke(n,e,t,s){let o=n.prev,r=n,i=n.next,u=o.x,l=r.x,c=i.x,a=o.y,f=r.y,h=i.y,m=Math.min(u,l,c),b=Math.min(a,f,h),d=Math.max(u,l,c),x=Math.max(a,f,h),I=$n(m,b,e,t,s),B=$n(d,x,e,t,s),M=n.prevZ;for(;M&&M.z>=I;){if(M.x>=m&&M.x<=d&&M.y>=b&&M.y<=x&&M!==i&&!(u===M.x&&a===M.y)&&Vn(u,a,l,f,c,h,M.x,M.y)&&O(M.prev,M,M.next)>=0)return!1;M=M.prevZ}let y=n.nextZ;for(;y&&y.z<=B;){if(y.x>=m&&y.x<=d&&y.y>=b&&y.y<=x&&y!==i&&!(u===y.x&&a===y.y)&&Vn(u,a,l,f,c,h,y.x,y.y)&&O(y.prev,y,y.next)>=0)return!1;y=y.nextZ}return!0}function Be(n,e){let t=n,s=!1;do{let o=t.prev,r=t.next.next;xe(o,t,t.next,r,!1)&&Un(o,r)&&Un(r,o)&&(e.push(o.i,t.i,r.i),Tn(t),Tn(t.next),t=n=r,s=!0),t=t.next}while(t!==n);return s?fn(t):t}function Fe(n,e,t,s,o){let r=n;do{let i=r.next.next;for(;i!==r.prev;){if(r.i!==i.i&&He(r,i)){let u=de(r,i);r=fn(r,r.next),u=fn(u,u.next),Wn(r,e,t,s,o),Wn(u,e,t,s,o);return}i=i.next}r=r.next}while(r!==n)}var Kn=!1;function _e(n,e,t,s){let o=[];for(let r=0,i=e.length;r<i;r++){let u=e[r]*s,l=r<i-1?e[r+1]*s:n.length,c=he(n,u,l,s,!1);c===c.next&&An.add(c),o.push(Ee(c))}o.sort(Pe),Le(n.length/s,e.length),be(t,t),Kn=!0;for(let r=0;r<o.length;r++)t=Oe(o[r],t);return Kn=!1,fn(t)}function Pe(n,e){return n.x-e.x||n.y-e.y||(n.next.y-n.y)/(n.next.x-n.x)-(e.next.y-e.y)/(e.next.x-e.x)}function Oe(n,e){let t=ze(n,e);if(!t)return e;let s=de(t,n),o=s.next;return be(t,o.next),fn(s,s.next),fn(t,t.next)}var me=16,_=new Float64Array(0),Nn=0,Jn=[],Qn=[];function Le(n,e){let t=Math.ceil((n+2*e)/me)+e+2;_.length<t*4&&(_=new Float64Array(t*4)),Nn=0}function be(n,e){let t=n;do{let s=Nn++;Jn[s]=t;let o=t.x,r=t.y,i=t.x,u=t.y,l=0;do{let a=t.next;t.z=s,a.x<o&&(o=a.x),a.x>i&&(i=a.x),a.y<r&&(r=a.y),a.y>u&&(u=a.y),t=a}while(++l<me&&t!==e);Qn[s]=t;let c=s*4;_[c]=o,_[c+1]=r,_[c+2]=i,_[c+3]=u}while(t!==e)}function Ce(n,e){let t=n.z*4;e.x<_[t]&&(_[t]=e.x),e.y<_[t+1]&&(_[t+1]=e.y),e.x>_[t+2]&&(_[t+2]=e.x),e.y>_[t+3]&&(_[t+3]=e.y)}function ce(n){let e=Qn[n];for(;e.prev.next!==e;)e=e.next;return Qn[n]=e,e}function ae(n){let e=Jn[n];for(;e.prev.next!==e;)e=e.next;return Jn[n]=e,e}function ze(n,e){let t=e,s=n.x,o=n.y,r=-1/0,i;if(In(n,t))return t;for(let h=0,m=0;h<Nn;h++,m+=4){if(o<_[m+1]||o>_[m+3]||_[m]>s||_[m+2]<=r)continue;let b=ce(h);t=ae(h);do{if(t.prev.next===t){if(In(n,t.next))return t.next;if(o<=t.y&&o>=t.next.y&&t.next.y!==t.y){let d=t.x+(o-t.y)*(t.next.x-t.x)/(t.next.y-t.y);if(d<=s&&d>r&&(r=d,i=t.x<t.next.x?t:t.next,d===s))return i}}t=t.next}while(t!==b)}if(!i)return null;let u=i.x,l=i.y,c=Math.min(o,l),a=Math.max(o,l),f=1/0;for(let h=0,m=0;h<Nn;h++,m+=4){if(_[m+2]<u||_[m]>s||_[m+3]<c||_[m+1]>a)continue;let b=ce(h);t=ae(h);do{if(t.prev.next===t&&s>=t.x&&t.x>=u&&s!==t.x&&Vn(o<l?s:r,o,u,l,o<l?r:s,o,t.x,t.y)){let d=Math.abs(o-t.y)/(s-t.x);(Un(t,n)||t.y===o&&t.next.y===o&&t.next.x>s)&&(d<f||d===f&&(t.x>i.x||t.x===i.x&&Ze(i,t)))&&(i=t,f=d)}t=t.next}while(t!==b)}return i}function Ze(n,e){return O(n.prev,n,e.prev)<0&&O(e.next,n,n.next)<0}var K=[],dn=[],an=new Uint32Array(0),vn=new Uint32Array(0),Rn=new Uint32Array(256);function Se(n,e,t,s){let o=n,r=0;do o.z=$n(o.x,o.y,e,t,s),K[r++]=o,o=o.next;while(o!==n);je(r);let i=null;for(let u=0;u<r;u++){let l=K[u];l.prevZ=i,i&&(i.nextZ=l),i=l}i.nextZ=null,K.fill(null,0,r),dn.fill(null,0,r)}function je(n){if(n<=32){for(let e=1;e<n;e++){let t=K[e],s=t.z,o=e-1;for(;o>=0&&K[o].z>s;)K[o+1]=K[o],o--;K[o+1]=t}return}an.length<n&&(an=new Uint32Array(n),vn=new Uint32Array(n),dn=new Array(n));for(let e=0;e<n;e++)an[e]=K[e].z;En(n,K,an,dn,vn,0),En(n,dn,vn,K,an,8),En(n,K,an,dn,vn,16),En(n,dn,vn,K,an,24)}function En(n,e,t,s,o,r){Rn.fill(0);for(let u=0;u<n;u++)Rn[t[u]>>>r&255]++;let i=0;for(let u=0;u<256;u++){let l=Rn[u];Rn[u]=i,i+=l}for(let u=0;u<n;u++){let l=t[u],c=Rn[l>>>r&255]++;s[c]=e[u],o[c]=l}}function $n(n,e,t,s,o){return n=(n-t)*o|0,e=(e-s)*o|0,n=(n|n<<8)&16711935,n=(n|n<<4)&252645135,n=(n|n<<2)&858993459,n=(n|n<<1)&1431655765,e=(e|e<<8)&16711935,e=(e|e<<4)&252645135,e=(e|e<<2)&858993459,e=(e|e<<1)&1431655765,n|e<<1}function Ee(n){let e=n,t=n;do(e.x<t.x||e.x===t.x&&e.y<t.y)&&(t=e),e=e.next;while(e!==n);return t}function Vn(n,e,t,s,o,r,i,u){return(o-i)*(e-u)>=(n-i)*(r-u)&&(n-i)*(s-u)>=(t-i)*(e-u)&&(t-i)*(r-u)>=(o-i)*(s-u)}function He(n,e){let t=In(n,e)&&O(n.prev,n,n.next)>0&&O(e.prev,e,e.next)>0;return n.next.i!==e.i&&(t||Un(n,e)&&Un(e,n)&&(O(n.prev,n,e.prev)!==0||O(n,e.prev,e)!==0))&&!Ne(n,e)&&(t||Ve(n,e))}function O(n,e,t){return(e.y-n.y)*(t.x-e.x)-(e.x-n.x)*(t.y-e.y)}function In(n,e){return n.x===e.x&&n.y===e.y}function xe(n,e,t,s,o=!0){let r=O(n,e,t),i=O(n,e,s),u=O(t,s,n),l=O(t,s,e);return(r>0&&i<0||r<0&&i>0)&&(u>0&&l<0||u<0&&l>0)?!0:o?!!(r===0&&Hn(n,t,e)||i===0&&Hn(n,s,e)||u===0&&Hn(t,n,s)||l===0&&Hn(t,e,s)):!1}function Hn(n,e,t){return e.x<=Math.max(n.x,t.x)&&e.x>=Math.min(n.x,t.x)&&e.y<=Math.max(n.y,t.y)&&e.y>=Math.min(n.y,t.y)}function Ne(n,e){let t=Math.min(n.x,e.x),s=Math.max(n.x,e.x),o=Math.min(n.y,e.y),r=Math.max(n.y,e.y),i=n;do{let u=i.next;if(i.x>s&&u.x>s||i.x<t&&u.x<t||i.y>r&&u.y>r||i.y<o&&u.y<o){i=u;continue}if(i.i!==n.i&&u.i!==n.i&&i.i!==e.i&&u.i!==e.i&&xe(i,u,n,e))return!0;i=u}while(i!==n);return!1}function Un(n,e){return O(n.prev,n,n.next)<0?O(n,e,n.next)>=0&&O(n,n.prev,e)>=0:O(n,e,n.prev)<0||O(n,n.next,e)<0}function Ve(n,e){let t=n,s=!1,o=(n.x+e.x)/2,r=(n.y+e.y)/2;do{let i=t.next;t.y>r!=i.y>r&&o<(i.x-t.x)*(r-t.y)/(i.y-t.y)+t.x&&(s=!s),t=i}while(t!==n);return s}function de(n,e){let t=qn(n.i,n.x,n.y),s=qn(e.i,e.x,e.y),o=n.next,r=e.prev;return n.next=e,e.prev=n,t.next=o,o.prev=t,s.next=t,t.prev=s,r.next=s,s.prev=r,s}function fe(n,e,t,s){let o=qn(n,e,t);return s?(o.next=s.next,o.prev=s,s.next.prev=o,s.next=o):(o.prev=o,o.next=o),o}function Tn(n){n.next.prev=n.prev,n.prev.next=n.next,n.prevZ&&(n.prevZ.nextZ=n.nextZ),n.nextZ&&(n.nextZ.prevZ=n.prevZ),Kn&&Ce(n.prev,n.next)}function qn(n,e,t){return{i:n,x:e,y:t,prev:null,next:null,z:0,prevZ:null,nextZ:null}}function Ye(n,e,t,s){let o=0;for(let r=e,i=t-s;r<t;r+=s)o+=(n[i]-n[r])*(n[r+1]+n[i+1]),i=r;return o}var Xe=.7,Ge=Math.tan(Math.PI/6),De=Math.tan(35*Math.PI/180),We=Math.tan(Math.PI/18),Bn=.65,Fn=.4;function Me(n,e,t,s,o,r,i){let u=String(t.roof_shape??"");if(!u||u==="flat"||u==="sawtooth"||qe(n,i))return;let l=Qe(n);if(!l||Math.abs(ne(n))<Xe*4*l.halfU*l.halfV)return;let c=l.u,a=l.v,f=l.halfU*r,h=l.halfV*r;f<h&&([c,a,f,h]=[a,c,h,f]);let m=ge(t.roof_direction),b=m===void 0?void 0:[Math.sin(m*Math.PI/180),-Math.cos(m*Math.PI/180)];(b!==void 0?Math.abs(b[0]*c[0]+b[1]*c[1])>Math.abs(b[0]*a[0]+b[1]*a[1]):t.roof_orientation==="across")&&u!=="skillion"&&([c,a,f,h]=[a,c,h,f]);let x=Je(u,f,h,b&&[pe(b,c),pe(b,a)]);if(!x)return;let I=Math.max(0,o-s),B=ge(t.roof_height),M,y;if(B!==void 0&&B>0)y=o,M=Math.max(s+Math.min(2,I/2),o-B);else{let p=Math.min(Ke(u,h),I*.6,30);t.height_source==="height"?(y=o,M=o-p):(M=o,y=o+p)}let k=y-M;if(!(k>=.3))return;let[R,N]=l.centre,F=(p,A)=>{let v=(p-R)*r,g=(A-N)*r;return[v*c[0]+g*c[1],v*a[0]+g*a[1]]},S=(p,A)=>[R+(p*c[0]+A*a[0])/r,N+(p*c[1]+A*a[1])/r],J=(p,A)=>{let v=1/0;for(let[g,E,H]of x)v=Math.min(v,g*p+E*A+H);return Math.max(0,v)},Q=(p,A)=>M+k*J(...F(p,A)),L=[];for(let p=0;p+1<e.length;p+=2)L.push(...F(e[p],e[p+1]));ne(L)<0&&nt(L);let C=1.01,$=[-f*C,-h*C,f*C,-h*C,f*C,h*C,-f*C,h*C],V=[];for(let[p,A]of x.entries()){let v=$;for(let[T,j]of x.entries()){if(T===p)continue;let z=A[0]-j[0],nn=A[1]-j[1],en=A[2]-j[2];if(Math.abs(z)+Math.abs(nn)+Math.abs(en)<1e-9){T<p&&(v=[]);continue}if(v=ye(v,z,nn,en),v.length<6)break}if(v.length<6)continue;let g=L,E=v.length/2;for(let T=0;T<E&&g.length>=6;T+=1){let[j,z]=[v[T*2],v[T*2+1]],[nn,en]=[v[(T+1)%E*2],v[(T+1)%E*2+1]];g=ye(g,en-z,j-nn,-(en-z)*j-(j-nn)*z)}if(g.length<6||Math.abs(ne(g))<1e-6)continue;let H=[];for(let T=0;T<g.length;T+=2)H.push(...S(g[T],g[T+1]));let q=kn(H);if(q.length===0)continue;let on=k*A[0],Y=k*A[1],w=on*c[0]+Y*a[0],U=on*c[1]+Y*a[1],rn=Math.hypot(w,U,1);V.push({ring:H,triangles:q,normal:[-w/rn,-U/rn]})}if(V.length!==0)return{eaves:M,ridge:y,faces:V,heightAt:Q,breaks(p,A,v,g){let[E,H]=F(p,A),[q,on]=F(v,g),Y=[];for(let w=0;w<x.length;w+=1)for(let U=w+1;U<x.length;U+=1){let rn=(Z,P)=>(x[w][0]-x[U][0])*Z+(x[w][1]-x[U][1])*P+(x[w][2]-x[U][2]),T=rn(E,H),j=rn(q,on);if(T===j||T*j>0)continue;let z=T/(T-j);if(z<=1e-4||z>=1-1e-4)continue;let nn=E+(q-E)*z,en=H+(on-H)*z;x[w][0]*nn+x[w][1]*en+x[w][2]<=J(nn,en)+1e-6&&Y.push(z)}return[...new Set(Y.map(w=>Math.round(w*1e6)/1e6))].sort((w,U)=>w-U)}}}function Ke(n,e){switch(n){case"skillion":return 2*e*We;case"pyramidal":return e*De;case"gambrel":case"mansard":return e*.8;case"dome":case"onion":case"round":return e;default:return e*Ge}}function Je(n,e,t,s){let o=e,r=t,i=[[0,-1/r,1],[0,1/r,1]],u=c=>c.flatMap(a=>[1,-1].flatMap(f=>{let h=a==="v"?r:o,m=Bn/(Fn*r)*f,b=(1-Bn)/((1-Fn)*r)*f,d=x=>a==="v"?[0,-x]:[-x,0];return[[...d(m),Bn/(Fn*r)*h],[...d(b),Bn+(1-Bn)/((1-Fn)*r)*(h-Fn*r)]]})),l=[.99,.8,.5,.2];switch(n){case"gabled":return i;case"saltbox":{let c=r/3;return[[0,-1/(r-c),1+c/(r-c)],[0,1/(r+c),1-c/(r+c)]]}case"hipped":return[...i,[-1/r,0,o/r],[1/r,0,o/r]];case"half_hipped":return[...i,[-1/r,0,.5+o/r],[1/r,0,.5+o/r]];case"pyramidal":return[...i,[-1/o,0,1],[1/o,0,1]];case"gambrel":return u(["v"]);case"mansard":return u(["v","u"]);case"skillion":{let[c,a]=s??[0,1],f=Math.abs(c)*o+Math.abs(a)*r;return f>0?[[-c/(2*f),-a/(2*f),.5]]:void 0}case"round":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return[1,-1].map(f=>[0,-f*c/(r*a),1/a])})];case"dome":case"onion":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return Array.from({length:12},(f,h)=>{let m=h*Math.PI/6;return[-c*Math.cos(m)/(o*a),-c*Math.sin(m)/(r*a),1/a]})})];default:return}}function ye(n,e,t,s){let o=n.length/2,r=[];for(let i=0;i<o;i+=1){let u=n[i*2],l=n[i*2+1],c=n[(i+1)%o*2],a=n[(i+1)%o*2+1],f=e*u+t*l+s,h=e*c+t*a+s;if(f<=0&&r.push(u,l),f<0&&h>0||f>0&&h<0){let m=f/(f-h);r.push(u+(c-u)*m,l+(a-l)*m)}}return r}function Qe(n){let e=$e(n);if(e.length<3)return;let t,s=1/0;for(let o=0;o<e.length;o+=1){let[r,i]=e[o],[u,l]=e[(o+1)%e.length],c=Math.hypot(u-r,l-i);if(c===0)continue;let a=[(u-r)/c,(l-i)/c],f=[-a[1],a[0]],[h,m,b,d]=[1/0,-1/0,1/0,-1/0];for(let[I,B]of e){let M=I*a[0]+B*a[1],y=I*f[0]+B*f[1];h=Math.min(h,M),m=Math.max(m,M),b=Math.min(b,y),d=Math.max(d,y)}let x=(m-h)*(d-b);if(x<s){s=x;let I=(h+m)/2,B=(b+d)/2;t={centre:[I*a[0]+B*f[0],I*a[1]+B*f[1]],u:a,v:f,halfU:(m-h)/2,halfV:(d-b)/2}}}return t&&t.halfU>0&&t.halfV>0?t:void 0}function $e(n){let e=[];for(let r=0;r+1<n.length;r+=2)e.push([n[r],n[r+1]]);e.sort((r,i)=>r[0]-i[0]||r[1]-i[1]);let t=(r,i,u)=>(i[0]-r[0])*(u[1]-r[1])-(i[1]-r[1])*(u[0]-r[0]),s=[];for(let r of e){for(;s.length>=2&&t(s.at(-2),s.at(-1),r)<=0;)s.pop();s.push(r)}let o=[];for(let r of e.reverse()){for(;o.length>=2&&t(o.at(-2),o.at(-1),r)<=0;)o.pop();o.push(r)}return[...s.slice(0,-1),...o.slice(0,-1)]}function qe(n,e){let[t,s,o,r]=[1/0,-1/0,1/0,-1/0];for(let u=0;u+1<n.length;u+=2)t=Math.min(t,n[u]),s=Math.max(s,n[u]),o=Math.min(o,n[u+1]),r=Math.max(r,n[u+1]);let i=n.length/2;for(let u=0;u<i;u+=1){let l=n[u*2],c=n[u*2+1],a=n[(u+1)%i*2],f=n[(u+1)%i*2+1];if(t<0&&l===t&&a===t||s>e&&l===s&&a===s||o<0&&c===o&&f===o||r>e&&c===r&&f===r)return!0}return!1}function ne(n){let e=0,t=n.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;e+=n[s*2]*n[o*2+1]-n[o*2]*n[s*2+1]}return e/2}function nt(n){let e=[];for(let t=0;t<n.length;t+=2)e.push([n[t],n[t+1]]);e.reverse(),n.splice(0,n.length,...e.flat())}function pe(n,e){return n[0]*e[0]+n[1]*e[1]}function ge(n){let e=typeof n=="string"?Number(n):n;return typeof e=="number"&&Number.isFinite(e)?e:void 0}var gt=Object.freeze({surface:0,secondary:1,glass:2,accent:3,roof:4}),Mt=Object.freeze({levels:3,vertices:2e5,triangles:2e5,footprintPoints:4096,radius:2e3,height:1e3,models:512,bytes:16*1024*1024});function ee(n,e,t){for(let s of n){let o=!1;for(let r=0,i=s.length-2;r<s.length;i=r,r+=2){let u=s[r],l=s[r+1],c=s[i],a=s[i+1];l>t!=a>t&&e<(c-u)*(t-l)/(a-l)+u&&(o=!o)}if(o)return!0}return!1}function Re(n,e,t){for(let s of e){let[o,r,i,u]=ve(s);for(let l of n){let[c,a,f,h]=ve(l);if(!(c>i-t||f<o+t||a>u-t||h<r+t)&&(we(l,s,t)||we(s,l,t)))return!0}}return!1}function we(n,e,t){let s=(o,r)=>ee([e],o,r)&&tt(e,o,r)>t;for(let o=0;o+1<n.length;o+=2)if(s(n[o],n[o+1]))return!0;for(let o=0,r=n.length-2;o<n.length;r=o,o+=2){let[i,u,l,c]=[n[r],n[r+1],n[o],n[o+1]],a=[0,1];for(let f=0,h=e.length-2;f<e.length;h=f,f+=2){let m=et(i,u,l,c,e[h],e[h+1],e[f],e[f+1]);m!==void 0&&a.push(m)}if(a.length!==2){a.sort((f,h)=>f-h);for(let f=1;f<a.length;f+=1){let h=(a[f-1]+a[f])/2;if(s(i+(l-i)*h,u+(c-u)*h))return!0}}}return!1}function et(n,e,t,s,o,r,i,u){let l=t-n,c=s-e,a=i-o,f=u-r,h=l*f-c*a;if(h===0)return;let m=((o-n)*f-(r-e)*a)/h,b=((o-n)*c-(r-e)*l)/h;return m>0&&m<1&&b>=0&&b<=1?m:void 0}function tt(n,e,t){let s=1/0;for(let o=0,r=n.length-2;o<n.length;r=o,o+=2){let[i,u,l,c]=[n[r],n[r+1],n[o],n[o+1]],a=(l-i)**2+(c-u)**2,f=a>0?Math.max(0,Math.min(1,((e-i)*(l-i)+(t-u)*(c-u))/a)):0;s=Math.min(s,Math.hypot(e-(i+(l-i)*f),t-(u+(c-u)*f)))}return s}function ve(n){let[e,t,s,o]=[1/0,1/0,-1/0,-1/0];for(let r=0;r+1<n.length;r+=2)e=Math.min(e,n[r]),s=Math.max(s,n[r]),t=Math.min(t,n[r+1]),o=Math.max(o,n[r+1]);return[e,t,s,o]}var tn=8192,te=28,ot={commercial:1,active:2,destination:3},rt={residential:1,...Object.fromEntries(["civic","commercial","education","entertainment","medical","transportation"].map(n=>[n,2])),...Object.fromEntries(["agricultural","industrial","military","outbuilding","religious","service"].map(n=>[n,3]))},st={dining:1,entertainment:2,nightlife:3},oe={...Object.fromEntries(["alcoholic_beverage_venue","bar","beer","biergarten","brewery","casino","cocktail_bar","dance_club","karaoke","lounge","music_venue","night_club","nightclub","pub","wine_bar"].map(n=>[n,3])),...Object.fromEntries(["arts_centre","bowling_alley","cinema","comedy_club","concert_hall","movie_theater","opera","performing_arts_venue","theater","theatre","theatre_venue"].map(n=>[n,2])),...Object.fromEntries(["casual_eatery","fast_food","fast_food_restaurant","food_court","restaurant"].map(n=>[n,1]))},ut=6,it=.5,lt=.5;function Ue(n,e){let t=tn/n.extent,s=e.tileMetres/tn,o=e.defaultHeight??5,r=Number.isFinite(e.edgeRadius)?Math.max(0,Math.min(1,e.edgeRadius))/s:0,i=new re,u=new Yn,l=new Yn,c=[],a=[],f=0,h=ht(n,e);for(let[y,k]of n.features.entries()){if(k.type!=="Polygon"||c.length>=65535)continue;let R=k.properties,N=R.hide_3d;if(N===!0||N===1||N==="1"||N==="true")continue;let F=Math.min(6e3,Ae(R.render_height)??o),S=Math.min(F,Ae(R.render_min_height)??0),J=ot[String(R.building_tone??"")]??0,Q=rt[String(R.class??"")]??0,L=h.get(y)??0,C=c.length,$=!1;for(let V of mt(k.geometry,t)){let p=V.map(xt).filter(w=>w.length>=6);if(p.length===0||Xn(p[0])===0)continue;let[A,v]=bt(p[0]);if(e.masks?.length&&(ee(e.masks,A,v)||Re(p,e.masks,lt/s)))continue;let g={cell:Math.min(7,Math.max(0,Math.floor(v/1024)))*8+Math.min(7,Math.max(0,Math.floor(A/1024))),bounds:[1/0,1/0,-1/0,-1/0,F+.1],roofsFrom:u.length,roofsCount:0,wallsFrom:l.length,wallsCount:0};for(let w of p)for(let U=0;U<w.length;U+=2)g.bounds[0]=Math.min(g.bounds[0],w[U]-1),g.bounds[1]=Math.min(g.bounds[1],w[U+1]-1),g.bounds[2]=Math.max(g.bounds[2],w[U]+1),g.bounds[3]=Math.max(g.bounds[3],w[U+1]+1);$=!0,f=Math.max(f,F);let E=Ie(A*.0731+v*.19137+C*1.618),H=p[0].some(w=>w<=0||w>=tn)?Ie(F*7.31+S*3.17+J):E,q={base:S,top:F,jitter:H,seed:E,tone:J,group:Q,use:L,cx:A,cy:v,index:C},on=p.map((w,U)=>ft(w,U,r,e.cornerAngle));p=on.map(({ring:w})=>w);let Y=V.length===1&&p.length===1?Me(V[0],p[0],R,S,F,s,tn):void 0;Y?(f=Math.max(f,Y.ridge),g.bounds[4]=Math.max(g.bounds[4],Y.ridge+.1),at(Y,q,i,u)):ct(p,q,i,u);for(let[w,U]of p.entries()){let rn=Xn(U)>0,T=w===0?rn?-1:1:rn?1:-1,j=U.length/2,z=Z=>{let P=(Z%j+j)%j;return[U[P*2],U[P*2+1]]},nn=Z=>{let[P,D]=z(Z-1),[X,G]=z(Z),[un,mn]=z(Z+1);if(hn(P,D,X,G)||hn(X,G,un,mn))return!1;let bn=Math.hypot(X-P,G-D),ln=Math.hypot(un-X,mn-G);if(!bn||!ln)return!1;let _n=-(G-D)/bn*T,Pn=(X-P)/bn*T;return(_n*(un-X)+Pn*(mn-G))/ln>it},en=(Z,P)=>Y?Y.heightAt(Z,P):F,pn=0;for(let Z=0;Z<j;Z+=1){let[P,D]=z(Z),[X,G]=z(Z+1),un=Math.hypot(X-P,G-D);if(un===0||hn(P,D,X,G))continue;let mn=-(G-D)/un*T,bn=(X-P)/un*T,ln=on[w].normals[Z]??[mn,bn],_n=on[w].normals[(Z+1)%j]??[mn,bn],Pn=W=>{let gn=ln[0]+(_n[0]-ln[0])*W,Mn=ln[1]+(_n[1]-ln[1])*W,cn=Math.hypot(gn,Mn)||1;return[gn/cn,Mn/cn]},sn=[0,...Y?Y.breaks(P,D,X,G):[],1];for(let W=0;W+1<sn.length;W+=1){let gn=W===0,Mn=W+2===sn.length,cn=P+(X-P)*sn[W],On=D+(G-D)*sn[W],Ln=P+(X-P)*sn[W+1],Cn=D+(G-D)*sn[W+1],wn=Math.hypot(Ln-cn,Cn-On)/tn*e.tileMetres,zn=(gn&&nn(Z)?1:0)|(Mn&&nn(Z+1)?2:0)|(w>0?4:0),Zn=(gn&&se(P,D)?1:0)|(Mn&&se(X,G)?2:0),Gn=pn+wn,Sn=Pn(sn[W]),jn=Pn(sn[W+1]),ie=en(cn,On),le=en(Ln,Cn),xn=i.write(q,cn,On,0,Sn[0],Sn[1],1,pn,zn,wn,0,Zn,ie);i.write(q,Ln,Cn,0,jn[0],jn[1],1,Gn,zn,wn,1,Zn,le),i.write(q,cn,On,1,Sn[0],Sn[1],1,pn,zn,wn,0,Zn,ie),i.write(q,Ln,Cn,1,jn[0],jn[1],1,Gn,zn,wn,1,Zn,le),l.push(xn,xn+1,xn+2,xn+1,xn+3,xn+2),pn=Gn}}}g.roofsCount=u.length-g.roofsFrom,g.wallsCount=l.length-g.wallsFrom,a.push(g)}$&&c.push(R)}if(i.count===0)return;let m=new Uint32Array(u.length+l.length),b=[],d=0,x=u.length,I=u.values(),B=l.values();a.sort((y,k)=>y.cell-k.cell);let M=-1;for(let y of a){y.cell!==M&&(M=y.cell,b.push({bounds:[...y.bounds],roofsFrom:d,roofsCount:0,wallsFrom:x,wallsCount:0}));let k=b.at(-1);for(let R=0;R<5;R+=1)k.bounds[R]=R<2?Math.min(k.bounds[R],y.bounds[R]):Math.max(k.bounds[R],y.bounds[R]);m.set(I.subarray(y.roofsFrom,y.roofsFrom+y.roofsCount),d),m.set(B.subarray(y.wallsFrom,y.wallsFrom+y.wallsCount),x),d+=y.roofsCount,x+=y.wallsCount,k.roofsCount+=y.roofsCount,k.wallsCount+=y.wallsCount}return{groups:b,vertices:i.bytes(),indices:m,wallsFrom:u.length,top:f,features:c}}function ct(n,e,t,s){let o=[],r=[];for(let l of n)r.push(o.length/2),o.push(...l);r.push(o.length/2);let i=(l,c)=>{let a=r.findIndex((d,x)=>l>=d&&l<r[x+1]),[f,h]=[r[a],r[a+1]-1];if(c<f||c>h)return!1;let[m,b]=l<c?[l,c]:[c,l];return b-m!==1&&!(m===f&&b===h)?!1:!hn(o[l*2],o[l*2+1],o[c*2],o[c*2+1])},u=kn(o,r.slice(1,-1),2);for(let l=0;l+2<u.length;l+=3){let c=[u[l],u[l+1],u[l+2]],a=0;for(let f=0;f<3;f+=1)i(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries())s.push(t.write(e,o[h*2],o[h*2+1],1,0,0,0,0,0,0,f,a))}}function at(n,e,t,s){for(let o of n.faces){let r=o.ring,i=r.length/2,u=(l,c)=>{let[a,f]=l<c?[l,c]:[c,l];return f-a!==1&&!(a===0&&f===i-1)?!1:!hn(r[l*2],r[l*2+1],r[c*2],r[c*2+1])};for(let l=0;l+2<o.triangles.length;l+=3){let c=[o.triangles[l],o.triangles[l+1],o.triangles[l+2]],a=0;for(let f=0;f<3;f+=1)u(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries()){let m=r[h*2],b=r[h*2+1];s.push(t.write(e,m,b,1,o.normal[0],o.normal[1],0,0,0,0,f,a,n.heightAt(m,b)))}}}}function ft(n,e,t,s=Math.PI/6){if(t<1)return{ring:n,normals:[]};let o=n.length/2,r=Xn(n)>0,i=e===0?r?-1:1:r?1:-1,u=[],l=[],c=(a,f,h)=>{a=yn(a),f=yn(f),!(u.at(-2)===a&&u.at(-1)===f)&&(u.push(a,f),l.push(h))};for(let a=0;a<o;a+=1){let f=(a+o-1)%o,h=(a+1)%o,[m,b]=[n[f*2],n[f*2+1]],[d,x]=[n[a*2],n[a*2+1]],[I,B]=[n[h*2],n[h*2+1]],M=Math.hypot(d-m,x-b),y=Math.hypot(I-d,B-x);if(!M||!y||se(d,x)||hn(m,b,d,x)||hn(d,x,I,B)){c(d,x);continue}let[k,R]=[(d-m)/M,(x-b)/M],[N,F]=[(I-d)/y,(B-x)/y],S=Math.atan2(k*F-R*N,k*N+R*F);if(S*i>=-.08||Math.abs(S)>Math.PI*.85){c(d,x);continue}let J=Math.tan(Math.abs(S)/2),Q=Math.min(t*J,M*.2,y*.2),L=Q/J;if(L<1){c(d,x);continue}let[C,$]=[-R*i,k*i],[V,p]=[d-k*Q-C*L,x-R*Q-$*L],A=Math.atan2($,C),v=Math.max(s<=Math.PI/6?2:1,Math.ceil(Math.abs(S)/Math.max(Math.PI/6,Math.min(Math.PI/3,s))));for(let g=0;g<=v;g+=1){let E=A+S*g/v,H=[Math.cos(E),Math.sin(E)];c(V+H[0]*L,p+H[1]*L,H)}}return u[0]===u.at(-2)&&u[1]===u.at(-1)&&(u.splice(-2),l.pop()),u.length>=6?{ring:u,normals:l}:{ring:n,normals:[]}}var re=class{#n=new ArrayBuffer(4096*te);#e=new DataView(this.#n);count=0;write(e,t,s,o,r,i,u,l,c,a,f=0,h=0,m=e.top){let b=te;if((this.count+1)*b>this.#n.byteLength){let I=new ArrayBuffer(this.#n.byteLength*2);new Uint8Array(I).set(new Uint8Array(this.#n)),this.#n=I,this.#e=new DataView(I)}let d=this.#e,x=this.count*b;return d.setUint16(x,yn(t),!0),d.setUint16(x+2,yn(s),!0),d.setUint16(x+4,Math.min(65535,Math.round(e.base*10)),!0),d.setUint16(x+6,Math.min(65535,Math.max(0,Math.round(m*10))),!0),d.setInt8(x+8,Math.round(r*127)),d.setInt8(x+9,Math.round(i*127)),d.setUint8(x+10,o),d.setUint8(x+11,u),d.setUint16(x+12,Math.round(l*10)%65536,!0),d.setUint8(x+14,Math.round(e.jitter*255)),d.setUint8(x+15,Math.round(e.seed*255)),d.setUint16(x+16,yn(e.cx),!0),d.setUint16(x+18,yn(e.cy),!0),d.setUint8(x+20,e.tone+4*e.group),d.setUint8(x+21,e.use),d.setUint8(x+22,c),d.setUint8(x+23,Math.min(255,Math.round(a))),d.setUint16(x+24,e.index,!0),d.setUint8(x+26,f),d.setUint8(x+27,h),this.count+=1,this.count-1}bytes(){return new Uint8Array(this.#n.slice(0,this.count*te))}},Yn=class{#n=new Uint32Array(8192);length=0;push(...e){if(this.length+e.length>this.#n.length){let t=new Uint32Array(Math.max(this.length+e.length,this.#n.length*2));t.set(this.#n),this.#n=t}for(let t of e)this.#n[this.length++]=t}values(){return this.#n.subarray(0,this.length)}};function ht(n,e){let t=new Map;if(n.features.some(u=>u.properties.night_use!==void 0)){for(let[u,l]of n.features.entries()){let c=st[String(l.properties.night_use??"")];c&&t.set(u,c)}return t}let s=e.places;if(!s||s.features.length===0)return t;let o=ut/e.tileMetres*n.extent,r=[];for(let[u,l]of n.features.entries()){if(l.type!=="Polygon")continue;let c=l.properties.hide_3d;if(c===!0||c===1||c==="1"||c==="true")continue;let a=[1/0,1/0,-1/0,-1/0];for(let f of l.geometry)for(let h=0;h+1<f.length;h+=2)a[0]=Math.min(a[0],f[h]),a[1]=Math.min(a[1],f[h+1]),a[2]=Math.max(a[2],f[h]),a[3]=Math.max(a[3],f[h+1]);r.push({index:u,rings:l.geometry,box:a})}let i=n.extent/s.extent;for(let u of s.features){if(u.type!=="Point")continue;let l=u.properties,c=oe[String(l.type??"")]??oe[String(l.subclass??"")]??oe[String(l.class??"")];if(c)for(let a of u.geometry)for(let f=0;f+1<a.length;f+=2){let h=a[f]*i,m=a[f+1]*i,b,d,x=o;for(let B of r){let[M,y,k,R]=B.box;if(h<M-o||h>k+o||m<y-o||m>R+o)continue;let N=!1;for(let F of B.rings)for(let S=0,J=F.length-2;S+1<F.length;J=S,S+=2){let Q=F[J],L=F[J+1],C=F[S],$=F[S+1];$>m!=L>m&&h<(Q-C)*(m-$)/(L-$)+C&&(N=!N);let V=C-Q,p=$-L,A=V*V+p*p,v=A?Math.min(1,Math.max(0,((h-Q)*V+(m-L)*p)/A)):0,g=Math.hypot(h-Q-v*V,m-L-v*p);g<x&&(x=g,d=B.index)}if(N){b=B.index;break}}let I=b??d;I!==void 0&&t.set(I,Math.max(t.get(I)??0,c))}}return t}function yn(n){return Math.min(tn,Math.max(0,Math.round(n)))}function Ae(n){let e=Number(n);return Number.isFinite(e)&&e>0?e:void 0}function mt(n,e){let t=[],s;for(let o of n){let r=[];for(let u=0;u+1<o.length;u+=2){let l=o[u]*e,c=o[u+1]*e;r.length&&l===r.at(-2)&&c===r.at(-1)||r.push(l,c)}if(r.length>=6&&r[0]===r.at(-2)&&r[1]===r.at(-1)&&(r.length-=2),r.length<6)continue;let i=Math.sign(Xn(r));i&&(s??=i,i===s||t.length===0?t.push([r]):t.at(-1).push(r))}return t}function Xn(n){let e=0,t=n.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;e+=n[s*2]*n[o*2+1]-n[o*2]*n[s*2+1]}return e}function bt(n){let e=0,t=0,s=n.length/2;for(let o=0;o<s;o+=1)e+=n[o*2],t+=n[o*2+1];return[e/s,t/s]}function se(n,e){return n<=0||e<=0||n>=tn||e>=tn}function hn(n,e,t,s){let o=tn;return n<=0&&t<=0||n>=o&&t>=o||e<=0&&s<=0||e>=o&&s>=o}function xt(n){let e=tn,t=[...n],s=[[0,0,o=>o>=0],[0,e,o=>o<=e],[1,0,o=>o>=0],[1,e,o=>o<=e]];for(let[o,r,i]of s){let u=[],l=t.length/2;for(let c=0;c<l;c+=1){let a=[t[c*2],t[c*2+1]],f=[t[(c+1)%l*2],t[(c+1)%l*2+1]],h=i(a[o]),m=i(f[o]);if(h&&u.push(a[0],a[1]),h!==m){let b=(r-a[o])/(f[o]-a[o]);u.push(a[0]+(f[0]-a[0])*b,a[1]+(f[1]-a[1])*b)}}if(t=u,t.length<6)return[]}return t}function Ie(n){let e=Math.sin(n*12.9898+78.233)*43758.5453;return e-Math.floor(e)}var ue=globalThis;ue.onmessage=({data:n})=>{try{let e=Ue(n.layer,n.options);ue.postMessage({mesh:e},e?[e.vertices.buffer,e.indices.buffer]:[])}catch(e){ue.postMessage({error:String(e)})}};})();\n'], { type: "text/javascript" })
|
|
6686
|
+
new Blob(['"use strict";(()=>{var Re=new Set,De=!1;function Ue(e,n,t=2){let s=n&&n.length,o=s?n[0]*t:e.length;Re.size&&Re.clear();let r=bn(e,0,o,t,!0),u=[];if(!r||r.next===r.prev)return u;let i=0,l=0,c=0;if(s&&(r=Pn(e,n,r,t)),e.length>80*t){i=e[0],l=e[1];let a=i,f=l;for(let h=t;h<o;h+=t){let m=e[h],b=e[h+1];m<i&&(i=m),b<l&&(l=b),m>a&&(a=m),b>f&&(f=b)}c=Math.max(a-i,f-l),c=c!==0?32767/c:0}return We(r,u,i,l,c),u}function bn(e,n,t,s,o){let r=null;if(o===Xn(e,n,t,s)>0)for(let u=n;u<t;u+=s)r=mn(u/s|0,e[u],e[u+1],r);else for(let u=t-s;u>=n;u-=s)r=mn(u/s|0,e[u],e[u+1],r);return r&&Ae(r,r.next)&&(Te(r),r=r.next),r}function ae(e,n=e){let t=n===e,s=e,o;do o=!1,s!==s.next&&(Re.size===0||!Re.has(s))&&(Ae(s,s.next)||P(s.prev,s,s.next)===0)?((t||s===n)&&(n=s.prev),De=!0,Te(s),s=s.prev,o=!0):(t||s!==n)&&(s=s.next,o=!t);while(o||s!==n);return n}function We(e,n,t,s,o){o&&jn(e,t,s,o);let r=e,u=!1;for(;e.prev!==e.next;){let i=e.prev,l=e.next;if(P(i,e,l)<0&&(o?Bn(e,t,s,o):kn(e))){n.push(i.i,e.i,l.i),Te(e),e=l,r=l;continue}if(e=l,e===r){if(De=!1,e=ae(e),De){r=e;continue}if(!u){e=Fn(e,n),r=e,u=!0;continue}_n(e,n,t,s,o);break}}}function kn(e){let n=e.prev,t=e,s=e.next,o=n.x,r=t.x,u=s.x,i=n.y,l=t.y,c=s.y,a=Math.min(o,r,u),f=Math.min(i,l,c),h=Math.max(o,r,u),m=Math.max(i,l,c),b=s.next;for(;b!==n;){if(b.x>=a&&b.x<=h&&b.y>=f&&b.y<=m&&!(o===b.x&&i===b.y)&&Ne(o,i,r,l,u,c,b.x,b.y)&&P(b.prev,b,b.next)>=0)return!1;b=b.next}return!0}function Bn(e,n,t,s){let o=e.prev,r=e,u=e.next,i=o.x,l=r.x,c=u.x,a=o.y,f=r.y,h=u.y,m=Math.min(i,l,c),b=Math.min(a,f,h),d=Math.max(i,l,c),x=Math.max(a,f,h),U=$e(m,b,n,t,s),k=$e(d,x,n,t,s),M=e.prevZ;for(;M&&M.z>=U;){if(M.x>=m&&M.x<=d&&M.y>=b&&M.y<=x&&M!==u&&!(i===M.x&&a===M.y)&&Ne(i,a,l,f,c,h,M.x,M.y)&&P(M.prev,M,M.next)>=0)return!1;M=M.prevZ}let y=e.nextZ;for(;y&&y.z<=k;){if(y.x>=m&&y.x<=d&&y.y>=b&&y.y<=x&&y!==u&&!(i===y.x&&a===y.y)&&Ne(i,a,l,f,c,h,y.x,y.y)&&P(y.prev,y,y.next)>=0)return!1;y=y.nextZ}return!0}function Fn(e,n){let t=e,s=!1;do{let o=t.prev,r=t.next.next;yn(o,t,t.next,r,!1)&&Ie(o,r)&&Ie(r,o)&&(n.push(o.i,t.i,r.i),Te(t),Te(t.next),t=e=r,s=!0),t=t.next}while(t!==e);return s?ae(t):t}function _n(e,n,t,s,o){let r=e;do{let u=r.next.next;for(;u!==r.prev;){if(r.i!==u.i&&Nn(r,u)){let i=pn(r,u);r=ae(r,r.next),i=ae(i,i.next),We(r,n,t,s,o),We(i,n,t,s,o);return}u=u.next}r=r.next}while(r!==e)}var Ke=!1;function Pn(e,n,t,s){let o=[];for(let r=0,u=n.length;r<u;r++){let i=n[r]*s,l=r<u-1?n[r+1]*s:e.length,c=bn(e,i,l,s,!1);c===c.next&&Re.add(c),o.push(Hn(c))}o.sort(On),zn(e.length/s,n.length),dn(t,t),Ke=!0;for(let r=0;r<o.length;r++)t=Ln(o[r],t);return Ke=!1,ae(t)}function On(e,n){return e.x-n.x||e.y-n.y||(e.next.y-e.y)/(e.next.x-e.x)-(n.next.y-n.y)/(n.next.x-n.x)}function Ln(e,n){let t=Sn(e,n);if(!t)return n;let s=pn(t,e),o=s.next;return dn(t,o.next),ae(s,s.next),ae(t,t.next)}var xn=16,_=new Float64Array(0),He=0,Je=[],Qe=[];function zn(e,n){let t=Math.ceil((e+2*n)/xn)+n+2;_.length<t*4&&(_=new Float64Array(t*4)),He=0}function dn(e,n){let t=e;do{let s=He++;Je[s]=t;let o=t.x,r=t.y,u=t.x,i=t.y,l=0;do{let a=t.next;t.z=s,a.x<o&&(o=a.x),a.x>u&&(u=a.x),a.y<r&&(r=a.y),a.y>i&&(i=a.y),t=a}while(++l<xn&&t!==n);Qe[s]=t;let c=s*4;_[c]=o,_[c+1]=r,_[c+2]=u,_[c+3]=i}while(t!==n)}function Cn(e,n){let t=e.z*4;n.x<_[t]&&(_[t]=n.x),n.y<_[t+1]&&(_[t+1]=n.y),n.x>_[t+2]&&(_[t+2]=n.x),n.y>_[t+3]&&(_[t+3]=n.y)}function fn(e){let n=Qe[e];for(;n.prev.next!==n;)n=n.next;return Qe[e]=n,n}function hn(e){let n=Je[e];for(;n.prev.next!==n;)n=n.next;return Je[e]=n,n}function Sn(e,n){let t=n,s=e.x,o=e.y,r=-1/0,u;if(Ae(e,t))return t;for(let h=0,m=0;h<He;h++,m+=4){if(o<_[m+1]||o>_[m+3]||_[m]>s||_[m+2]<=r)continue;let b=fn(h);t=hn(h);do{if(t.prev.next===t){if(Ae(e,t.next))return t.next;if(o<=t.y&&o>=t.next.y&&t.next.y!==t.y){let d=t.x+(o-t.y)*(t.next.x-t.x)/(t.next.y-t.y);if(d<=s&&d>r&&(r=d,u=t.x<t.next.x?t:t.next,d===s))return u}}t=t.next}while(t!==b)}if(!u)return null;let i=u.x,l=u.y,c=Math.min(o,l),a=Math.max(o,l),f=1/0;for(let h=0,m=0;h<He;h++,m+=4){if(_[m+2]<i||_[m]>s||_[m+3]<c||_[m+1]>a)continue;let b=fn(h);t=hn(h);do{if(t.prev.next===t&&s>=t.x&&t.x>=i&&s!==t.x&&Ne(o<l?s:r,o,i,l,o<l?r:s,o,t.x,t.y)){let d=Math.abs(o-t.y)/(s-t.x);(Ie(t,e)||t.y===o&&t.next.y===o&&t.next.x>s)&&(d<f||d===f&&(t.x>u.x||t.x===u.x&&Zn(u,t)))&&(u=t,f=d)}t=t.next}while(t!==b)}return u}function Zn(e,n){return P(e.prev,e,n.prev)<0&&P(n.next,e,e.next)<0}var J=[],de=[],ce=new Uint32Array(0),we=new Uint32Array(0),ve=new Uint32Array(256);function jn(e,n,t,s){let o=e,r=0;do o.z=$e(o.x,o.y,n,t,s),J[r++]=o,o=o.next;while(o!==e);En(r);let u=null;for(let i=0;i<r;i++){let l=J[i];l.prevZ=u,u&&(u.nextZ=l),u=l}u.nextZ=null,J.fill(null,0,r),de.fill(null,0,r)}function En(e){if(e<=32){for(let n=1;n<e;n++){let t=J[n],s=t.z,o=n-1;for(;o>=0&&J[o].z>s;)J[o+1]=J[o],o--;J[o+1]=t}return}ce.length<e&&(ce=new Uint32Array(e),we=new Uint32Array(e),de=new Array(e));for(let n=0;n<e;n++)ce[n]=J[n].z;je(e,J,ce,de,we,0),je(e,de,we,J,ce,8),je(e,J,ce,de,we,16),je(e,de,we,J,ce,24)}function je(e,n,t,s,o,r){ve.fill(0);for(let i=0;i<e;i++)ve[t[i]>>>r&255]++;let u=0;for(let i=0;i<256;i++){let l=ve[i];ve[i]=u,u+=l}for(let i=0;i<e;i++){let l=t[i],c=ve[l>>>r&255]++;s[c]=n[i],o[c]=l}}function $e(e,n,t,s,o){return e=(e-t)*o|0,n=(n-s)*o|0,e=(e|e<<8)&16711935,e=(e|e<<4)&252645135,e=(e|e<<2)&858993459,e=(e|e<<1)&1431655765,n=(n|n<<8)&16711935,n=(n|n<<4)&252645135,n=(n|n<<2)&858993459,n=(n|n<<1)&1431655765,e|n<<1}function Hn(e){let n=e,t=e;do(n.x<t.x||n.x===t.x&&n.y<t.y)&&(t=n),n=n.next;while(n!==e);return t}function Ne(e,n,t,s,o,r,u,i){return(o-u)*(n-i)>=(e-u)*(r-i)&&(e-u)*(s-i)>=(t-u)*(n-i)&&(t-u)*(r-i)>=(o-u)*(s-i)}function Nn(e,n){let t=Ae(e,n)&&P(e.prev,e,e.next)>0&&P(n.prev,n,n.next)>0;return e.next.i!==n.i&&(t||Ie(e,n)&&Ie(n,e)&&(P(e.prev,e,n.prev)!==0||P(e,n.prev,n)!==0))&&!Vn(e,n)&&(t||Yn(e,n))}function P(e,n,t){return(n.y-e.y)*(t.x-n.x)-(n.x-e.x)*(t.y-n.y)}function Ae(e,n){return e.x===n.x&&e.y===n.y}function yn(e,n,t,s,o=!0){let r=P(e,n,t),u=P(e,n,s),i=P(t,s,e),l=P(t,s,n);return(r>0&&u<0||r<0&&u>0)&&(i>0&&l<0||i<0&&l>0)?!0:o?!!(r===0&&Ee(e,t,n)||u===0&&Ee(e,s,n)||i===0&&Ee(t,e,s)||l===0&&Ee(t,n,s)):!1}function Ee(e,n,t){return n.x<=Math.max(e.x,t.x)&&n.x>=Math.min(e.x,t.x)&&n.y<=Math.max(e.y,t.y)&&n.y>=Math.min(e.y,t.y)}function Vn(e,n){let t=Math.min(e.x,n.x),s=Math.max(e.x,n.x),o=Math.min(e.y,n.y),r=Math.max(e.y,n.y),u=e;do{let i=u.next;if(u.x>s&&i.x>s||u.x<t&&i.x<t||u.y>r&&i.y>r||u.y<o&&i.y<o){u=i;continue}if(u.i!==e.i&&i.i!==e.i&&u.i!==n.i&&i.i!==n.i&&yn(u,i,e,n))return!0;u=i}while(u!==e);return!1}function Ie(e,n){return P(e.prev,e,e.next)<0?P(e,n,e.next)>=0&&P(e,e.prev,n)>=0:P(e,n,e.prev)<0||P(e,e.next,n)<0}function Yn(e,n){let t=e,s=!1,o=(e.x+n.x)/2,r=(e.y+n.y)/2;do{let u=t.next;t.y>r!=u.y>r&&o<(u.x-t.x)*(r-t.y)/(u.y-t.y)+t.x&&(s=!s),t=u}while(t!==e);return s}function pn(e,n){let t=qe(e.i,e.x,e.y),s=qe(n.i,n.x,n.y),o=e.next,r=n.prev;return e.next=n,n.prev=e,t.next=o,o.prev=t,s.next=t,t.prev=s,r.next=s,s.prev=r,s}function mn(e,n,t,s){let o=qe(e,n,t);return s?(o.next=s.next,o.prev=s,s.next.prev=o,s.next=o):(o.prev=o,o.next=o),o}function Te(e){e.next.prev=e.prev,e.prev.next=e.next,e.prevZ&&(e.prevZ.nextZ=e.nextZ),e.nextZ&&(e.nextZ.prevZ=e.prevZ),Ke&&Cn(e.prev,e.next)}function qe(e,n,t){return{i:e,x:n,y:t,prev:null,next:null,z:0,prevZ:null,nextZ:null}}function Xn(e,n,t,s){let o=0;for(let r=n,u=t-s;r<t;r+=s)o+=(e[u]-e[r])*(e[r+1]+e[u+1]),u=r;return o}var Gn=.7,Dn=Math.tan(Math.PI/6),Wn=Math.tan(35*Math.PI/180),Kn=Math.tan(Math.PI/18),ke=.65,Be=.4;function vn(e,n,t,s,o,r,u){let i=String(t.roof_shape??"");if(!i||i==="flat"||i==="sawtooth"||et(e,u))return;let l=$n(e);if(!l||Math.abs(en(e))<Gn*4*l.halfU*l.halfV)return;let c=l.u,a=l.v,f=l.halfU*r,h=l.halfV*r;f<h&&([c,a,f,h]=[a,c,h,f]);let m=wn(t.roof_direction),b=m===void 0?void 0:[Math.sin(m*Math.PI/180),-Math.cos(m*Math.PI/180)];(b!==void 0?Math.abs(b[0]*c[0]+b[1]*c[1])>Math.abs(b[0]*a[0]+b[1]*a[1]):t.roof_orientation==="across")&&i!=="skillion"&&([c,a,f,h]=[a,c,h,f]);let x=Qn(i,f,h,b&&[Mn(b,c),Mn(b,a)]);if(!x)return;let U=Math.max(0,o-s),k=wn(t.roof_height),M,y;if(k!==void 0&&k>0)y=o,M=Math.max(s+Math.min(2,U/2),o-k);else{let p=Math.min(Jn(i,h),U*.6,30);t.height_source==="height"?(y=o,M=o-p):(M=o,y=o+p)}let I=y-M;if(!(I>=.3))return;let[R,V]=l.centre,B=(p,A)=>{let w=(p-R)*r,g=(A-V)*r;return[w*c[0]+g*c[1],w*a[0]+g*a[1]]},j=(p,A)=>[R+(p*c[0]+A*a[0])/r,V+(p*c[1]+A*a[1])/r],Q=(p,A)=>{let w=1/0;for(let[g,N,E]of x)w=Math.min(w,g*p+N*A+E);return Math.max(0,w)},$=(p,A)=>M+I*Q(...B(p,A)),L=[];for(let p=0;p+1<n.length;p+=2)L.push(...B(n[p],n[p+1]));en(L)<0&&nt(L);let z=1.01,q=[-f*z,-h*z,f*z,-h*z,f*z,h*z,-f*z,h*z],Y=[];for(let[p,A]of x.entries()){let w=q;for(let[F,H]of x.entries()){if(F===p)continue;let C=A[0]-H[0],X=A[1]-H[1],ne=A[2]-H[2];if(Math.abs(C)+Math.abs(X)+Math.abs(ne)<1e-9){F<p&&(w=[]);continue}if(w=gn(w,C,X,ne),w.length<6)break}if(w.length<6)continue;let g=L,N=w.length/2;for(let F=0;F<N&&g.length>=6;F+=1){let[H,C]=[w[F*2],w[F*2+1]],[X,ne]=[w[(F+1)%N*2],w[(F+1)%N*2+1]];g=gn(g,ne-C,H-X,-(ne-C)*H-(H-X)*C)}if(g.length<6||Math.abs(en(g))<1e-6)continue;let E=[];for(let F=0;F<g.length;F+=2)E.push(...j(g[F],g[F+1]));let se=Ue(E);if(se.length===0)continue;let ee=I*A[0],oe=I*A[1],T=ee*c[0]+oe*a[0],v=ee*c[1]+oe*a[1],O=Math.hypot(T,v,1);Y.push({ring:E,triangles:se,normal:[-T/O,-v/O]})}if(Y.length!==0)return{eaves:M,ridge:y,faces:Y,heightAt:$,breaks(p,A,w,g){let[N,E]=B(p,A),[se,ee]=B(w,g),oe=[];for(let T=0;T<x.length;T+=1)for(let v=T+1;v<x.length;v+=1){let O=(he,S)=>(x[T][0]-x[v][0])*he+(x[T][1]-x[v][1])*S+(x[T][2]-x[v][2]),F=O(N,E),H=O(se,ee);if(F===H||F*H>0)continue;let C=F/(F-H);if(C<=1e-4||C>=1-1e-4)continue;let X=N+(se-N)*C,ne=E+(ee-E)*C;x[T][0]*X+x[T][1]*ne+x[T][2]<=Q(X,ne)+1e-6&&oe.push(C)}return[...new Set(oe.map(T=>Math.round(T*1e6)/1e6))].sort((T,v)=>T-v)}}}function Jn(e,n){switch(e){case"skillion":return 2*n*Kn;case"pyramidal":return n*Wn;case"gambrel":case"mansard":return n*.8;case"dome":case"onion":case"round":return n;default:return n*Dn}}function Qn(e,n,t,s){let o=n,r=t,u=[[0,-1/r,1],[0,1/r,1]],i=c=>c.flatMap(a=>[1,-1].flatMap(f=>{let h=a==="v"?r:o,m=ke/(Be*r)*f,b=(1-ke)/((1-Be)*r)*f,d=x=>a==="v"?[0,-x]:[-x,0];return[[...d(m),ke/(Be*r)*h],[...d(b),ke+(1-ke)/((1-Be)*r)*(h-Be*r)]]})),l=[.99,.8,.5,.2];switch(e){case"gabled":return u;case"saltbox":{let c=r/3;return[[0,-1/(r-c),1+c/(r-c)],[0,1/(r+c),1-c/(r+c)]]}case"hipped":return[...u,[-1/r,0,o/r],[1/r,0,o/r]];case"half_hipped":return[...u,[-1/r,0,.5+o/r],[1/r,0,.5+o/r]];case"pyramidal":return[...u,[-1/o,0,1],[1/o,0,1]];case"gambrel":return i(["v"]);case"mansard":return i(["v","u"]);case"skillion":{let[c,a]=s??[0,1],f=Math.abs(c)*o+Math.abs(a)*r;return f>0?[[-c/(2*f),-a/(2*f),.5]]:void 0}case"round":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return[1,-1].map(f=>[0,-f*c/(r*a),1/a])})];case"dome":case"onion":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return Array.from({length:12},(f,h)=>{let m=h*Math.PI/6;return[-c*Math.cos(m)/(o*a),-c*Math.sin(m)/(r*a),1/a]})})];default:return}}function gn(e,n,t,s){let o=e.length/2,r=[];for(let u=0;u<o;u+=1){let i=e[u*2],l=e[u*2+1],c=e[(u+1)%o*2],a=e[(u+1)%o*2+1],f=n*i+t*l+s,h=n*c+t*a+s;if(f<=0&&r.push(i,l),f<0&&h>0||f>0&&h<0){let m=f/(f-h);r.push(i+(c-i)*m,l+(a-l)*m)}}return r}function $n(e){let n=qn(e);if(n.length<3)return;let t,s=1/0;for(let o=0;o<n.length;o+=1){let[r,u]=n[o],[i,l]=n[(o+1)%n.length],c=Math.hypot(i-r,l-u);if(c===0)continue;let a=[(i-r)/c,(l-u)/c],f=[-a[1],a[0]],[h,m,b,d]=[1/0,-1/0,1/0,-1/0];for(let[U,k]of n){let M=U*a[0]+k*a[1],y=U*f[0]+k*f[1];h=Math.min(h,M),m=Math.max(m,M),b=Math.min(b,y),d=Math.max(d,y)}let x=(m-h)*(d-b);if(x<s){s=x;let U=(h+m)/2,k=(b+d)/2;t={centre:[U*a[0]+k*f[0],U*a[1]+k*f[1]],u:a,v:f,halfU:(m-h)/2,halfV:(d-b)/2}}}return t&&t.halfU>0&&t.halfV>0?t:void 0}function qn(e){let n=[];for(let r=0;r+1<e.length;r+=2)n.push([e[r],e[r+1]]);n.sort((r,u)=>r[0]-u[0]||r[1]-u[1]);let t=(r,u,i)=>(u[0]-r[0])*(i[1]-r[1])-(u[1]-r[1])*(i[0]-r[0]),s=[];for(let r of n){for(;s.length>=2&&t(s.at(-2),s.at(-1),r)<=0;)s.pop();s.push(r)}let o=[];for(let r of n.reverse()){for(;o.length>=2&&t(o.at(-2),o.at(-1),r)<=0;)o.pop();o.push(r)}return[...s.slice(0,-1),...o.slice(0,-1)]}function et(e,n){let[t,s,o,r]=[1/0,-1/0,1/0,-1/0];for(let i=0;i+1<e.length;i+=2)t=Math.min(t,e[i]),s=Math.max(s,e[i]),o=Math.min(o,e[i+1]),r=Math.max(r,e[i+1]);let u=e.length/2;for(let i=0;i<u;i+=1){let l=e[i*2],c=e[i*2+1],a=e[(i+1)%u*2],f=e[(i+1)%u*2+1];if(t<0&&l===t&&a===t||s>n&&l===s&&a===s||o<0&&c===o&&f===o||r>n&&c===r&&f===r)return!0}return!1}function en(e){let n=0,t=e.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;n+=e[s*2]*e[o*2+1]-e[o*2]*e[s*2+1]}return n/2}function nt(e){let n=[];for(let t=0;t<e.length;t+=2)n.push([e[t],e[t+1]]);n.reverse(),e.splice(0,e.length,...n.flat())}function Mn(e,n){return e[0]*n[0]+e[1]*n[1]}function wn(e){let n=typeof e=="string"?Number(e):e;return typeof n=="number"&&Number.isFinite(n)?n:void 0}var Mt=Object.freeze({surface:0,secondary:1,glass:2,accent:3,roof:4}),wt=Object.freeze({unclassified:0,wall:1,door:2,roof:3,window:4,lamp:5,logo:6}),vt=Object.freeze([1,1,4,1,3]),Rt=Object.freeze({levels:3,vertices:2e5,triangles:2e5,footprintPoints:4096,radius:2e3,height:1e3,models:512,bytes:16*1024*1024});function nn(e,n,t){for(let s of e){let o=!1;for(let r=0,u=s.length-2;r<s.length;u=r,r+=2){let i=s[r],l=s[r+1],c=s[u],a=s[u+1];l>t!=a>t&&n<(c-i)*(t-l)/(a-l)+i&&(o=!o)}if(o)return!0}return!1}function In(e,n,t){for(let s of n){let[o,r,u,i]=An(s);for(let l of e){let[c,a,f,h]=An(l);if(!(c>u-t||f<o+t||a>i-t||h<r+t)&&(Rn(l,s,t)||Rn(s,l,t)))return!0}}return!1}function Rn(e,n,t){let s=(o,r)=>nn([n],o,r)&&ot(n,o,r)>t;for(let o=0;o+1<e.length;o+=2)if(s(e[o],e[o+1]))return!0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[u,i,l,c]=[e[r],e[r+1],e[o],e[o+1]],a=[0,1];for(let f=0,h=n.length-2;f<n.length;h=f,f+=2){let m=tt(u,i,l,c,n[h],n[h+1],n[f],n[f+1]);m!==void 0&&a.push(m)}if(a.length!==2){a.sort((f,h)=>f-h);for(let f=1;f<a.length;f+=1){let h=(a[f-1]+a[f])/2;if(s(u+(l-u)*h,i+(c-i)*h))return!0}}}return!1}function tt(e,n,t,s,o,r,u,i){let l=t-e,c=s-n,a=u-o,f=i-r,h=l*f-c*a;if(h===0)return;let m=((o-e)*f-(r-n)*a)/h,b=((o-e)*c-(r-n)*l)/h;return m>0&&m<1&&b>=0&&b<=1?m:void 0}function ot(e,n,t){let s=1/0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[u,i,l,c]=[e[r],e[r+1],e[o],e[o+1]],a=(l-u)**2+(c-i)**2,f=a>0?Math.max(0,Math.min(1,((n-u)*(l-u)+(t-i)*(c-i))/a)):0;s=Math.min(s,Math.hypot(n-(u+(l-u)*f),t-(i+(c-i)*f)))}return s}function An(e){let[n,t,s,o]=[1/0,1/0,-1/0,-1/0];for(let r=0;r+1<e.length;r+=2)n=Math.min(n,e[r]),s=Math.max(s,e[r]),t=Math.min(t,e[r+1]),o=Math.max(o,e[r+1]);return[n,t,s,o]}var te=8192,tn=28,rt={commercial:1,active:2,destination:3},st={residential:1,...Object.fromEntries(["civic","commercial","education","entertainment","medical","transportation"].map(e=>[e,2])),...Object.fromEntries(["agricultural","industrial","military","outbuilding","religious","service"].map(e=>[e,3]))},it={dining:1,entertainment:2,nightlife:3},on={...Object.fromEntries(["alcoholic_beverage_venue","bar","beer","biergarten","brewery","casino","cocktail_bar","dance_club","karaoke","lounge","music_venue","night_club","nightclub","pub","wine_bar"].map(e=>[e,3])),...Object.fromEntries(["arts_centre","bowling_alley","cinema","comedy_club","concert_hall","movie_theater","opera","performing_arts_venue","theater","theatre","theatre_venue"].map(e=>[e,2])),...Object.fromEntries(["casual_eatery","fast_food","fast_food_restaurant","food_court","restaurant"].map(e=>[e,1]))},ut=6,lt=.5,ct=.5;function Un(e,n){let t=te/e.extent,s=n.tileMetres/te,o=n.defaultHeight??5,r=Number.isFinite(n.edgeRadius)?Math.max(0,Math.min(1,n.edgeRadius))/s:0,u=new sn,i=new Ve,l=new Ve,c=[],a=[],f=0,h=mt(e,n);for(let[y,I]of e.features.entries()){if(I.type!=="Polygon"||c.length>=65535)continue;let R=I.properties,V=R.hide_3d;if(V===!0||V===1||V==="1"||V==="true")continue;let B=Math.min(6e3,Tn(R.render_height)??o),j=Math.min(B,Tn(R.render_min_height)??0),Q=rt[String(R.building_tone??"")]??0,$=st[String(R.class??"")]??0,L=h.get(y)??0,z=c.length,q=!1;for(let Y of bt(I.geometry,t)){let p=Y.map(dt).filter(v=>v.length>=6);if(p.length===0||Ye(p[0])===0)continue;let[A,w]=xt(p[0]);if(n.masks?.length&&(nn(n.masks,A,w)||In(p,n.masks,ct/s)))continue;let g={cell:Math.min(7,Math.max(0,Math.floor(w/1024)))*8+Math.min(7,Math.max(0,Math.floor(A/1024))),bounds:[1/0,1/0,-1/0,-1/0,B+.1],roofsFrom:i.length,roofsCount:0,wallsFrom:l.length,wallsCount:0};for(let v of p)for(let O=0;O<v.length;O+=2)g.bounds[0]=Math.min(g.bounds[0],v[O]-1),g.bounds[1]=Math.min(g.bounds[1],v[O+1]-1),g.bounds[2]=Math.max(g.bounds[2],v[O]+1),g.bounds[3]=Math.max(g.bounds[3],v[O+1]+1);q=!0,f=Math.max(f,B);let N=Number.isSafeInteger(I.id),E=rn(N?I.id*.0731:A*.0731+w*.19137+z*1.618),se=!N&&p[0].some(v=>v<=0||v>=te)?rn(B*7.31+j*3.17+Q):E,ee={base:j,top:B,jitter:se,seed:E,tone:Q,group:$,use:L,cx:A,cy:w,index:z},oe=p.map((v,O)=>ht(v,O,r,n.cornerAngle));p=oe.map(({ring:v})=>v);let T=Y.length===1&&p.length===1?vn(Y[0],p[0],R,j,B,s,te):void 0;T?(f=Math.max(f,T.ridge),g.bounds[4]=Math.max(g.bounds[4],T.ridge+.1),ft(T,ee,u,i)):at(p,ee,u,i);for(let[v,O]of p.entries()){let F=Ye(O)>0,H=v===0?F?-1:1:F?1:-1,C=O.length/2,X=S=>{let Z=(S%C+C)%C;return[O[Z*2],O[Z*2+1]]},ne=S=>{let[Z,W]=X(S-1),[G,D]=X(S),[ie,me]=X(S+1);if(fe(Z,W,G,D)||fe(G,D,ie,me))return!1;let be=Math.hypot(G-Z,D-W),ue=Math.hypot(ie-G,me-D);if(!be||!ue)return!1;let Fe=-(D-W)/be*H,_e=(G-Z)/be*H;return(Fe*(ie-G)+_e*(me-D))/ue>lt},Xe=(S,Z)=>T?T.heightAt(S,Z):B,he=0;for(let S=0;S<C;S+=1){let[Z,W]=X(S),[G,D]=X(S+1),ie=Math.hypot(G-Z,D-W);if(ie===0||fe(Z,W,G,D))continue;let me=-(D-W)/ie*H,be=(G-Z)/ie*H,ue=oe[v].normals[S]??[me,be],Fe=oe[v].normals[(S+1)%C]??[me,be],_e=K=>{let pe=ue[0]+(Fe[0]-ue[0])*K,ge=ue[1]+(Fe[1]-ue[1])*K,le=Math.hypot(pe,ge)||1;return[pe/le,ge/le]},re=[0,...T?T.breaks(Z,W,G,D):[],1];for(let K=0;K+1<re.length;K+=1){let pe=K===0,ge=K+2===re.length,le=Z+(G-Z)*re[K],Pe=W+(D-W)*re[K],Oe=Z+(G-Z)*re[K+1],Le=W+(D-W)*re[K+1],Me=Math.hypot(Oe-le,Le-Pe)/te*n.tileMetres,ze=(pe&&ne(S)?1:0)|(ge&&ne(S+1)?2:0)|(v>0?4:0),Ce=(pe&&un(Z,W)?1:0)|(ge&&un(G,D)?2:0),Ge=he+Me,Se=_e(re[K]),Ze=_e(re[K+1]),cn=Xe(le,Pe),an=Xe(Oe,Le),xe=u.write(ee,le,Pe,0,Se[0],Se[1],1,he,ze,Me,0,Ce,cn);u.write(ee,Oe,Le,0,Ze[0],Ze[1],1,Ge,ze,Me,1,Ce,an),u.write(ee,le,Pe,1,Se[0],Se[1],1,he,ze,Me,0,Ce,cn),u.write(ee,Oe,Le,1,Ze[0],Ze[1],1,Ge,ze,Me,1,Ce,an),l.push(xe,xe+1,xe+2,xe+1,xe+3,xe+2),he=Ge}}}g.roofsCount=i.length-g.roofsFrom,g.wallsCount=l.length-g.wallsFrom,a.push(g)}q&&c.push(R)}if(u.count===0)return;let m=new Uint32Array(i.length+l.length),b=[],d=0,x=i.length,U=i.values(),k=l.values();a.sort((y,I)=>y.cell-I.cell);let M=-1;for(let y of a){y.cell!==M&&(M=y.cell,b.push({bounds:[...y.bounds],roofsFrom:d,roofsCount:0,wallsFrom:x,wallsCount:0}));let I=b.at(-1);for(let R=0;R<5;R+=1)I.bounds[R]=R<2?Math.min(I.bounds[R],y.bounds[R]):Math.max(I.bounds[R],y.bounds[R]);m.set(U.subarray(y.roofsFrom,y.roofsFrom+y.roofsCount),d),m.set(k.subarray(y.wallsFrom,y.wallsFrom+y.wallsCount),x),d+=y.roofsCount,x+=y.wallsCount,I.roofsCount+=y.roofsCount,I.wallsCount+=y.wallsCount}return{groups:b,vertices:u.bytes(),indices:m,wallsFrom:i.length,top:f,features:c}}function at(e,n,t,s){let o=[],r=[];for(let l of e)r.push(o.length/2),o.push(...l);r.push(o.length/2);let u=(l,c)=>{let a=r.findIndex((d,x)=>l>=d&&l<r[x+1]),[f,h]=[r[a],r[a+1]-1];if(c<f||c>h)return!1;let[m,b]=l<c?[l,c]:[c,l];return b-m!==1&&!(m===f&&b===h)?!1:!fe(o[l*2],o[l*2+1],o[c*2],o[c*2+1])},i=Ue(o,r.slice(1,-1),2);for(let l=0;l+2<i.length;l+=3){let c=[i[l],i[l+1],i[l+2]],a=0;for(let f=0;f<3;f+=1)u(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries())s.push(t.write(n,o[h*2],o[h*2+1],1,0,0,0,0,0,0,f,a))}}function ft(e,n,t,s){for(let o of e.faces){let r=o.ring,u=r.length/2,i=(l,c)=>{let[a,f]=l<c?[l,c]:[c,l];return f-a!==1&&!(a===0&&f===u-1)?!1:!fe(r[l*2],r[l*2+1],r[c*2],r[c*2+1])};for(let l=0;l+2<o.triangles.length;l+=3){let c=[o.triangles[l],o.triangles[l+1],o.triangles[l+2]],a=0;for(let f=0;f<3;f+=1)i(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries()){let m=r[h*2],b=r[h*2+1];s.push(t.write(n,m,b,1,o.normal[0],o.normal[1],0,0,0,0,f,a,e.heightAt(m,b)))}}}}function ht(e,n,t,s=Math.PI/6){if(t<1)return{ring:e,normals:[]};let o=e.length/2,r=Ye(e)>0,u=n===0?r?-1:1:r?1:-1,i=[],l=[],c=(a,f,h)=>{a=ye(a),f=ye(f),!(i.at(-2)===a&&i.at(-1)===f)&&(i.push(a,f),l.push(h))};for(let a=0;a<o;a+=1){let f=(a+o-1)%o,h=(a+1)%o,[m,b]=[e[f*2],e[f*2+1]],[d,x]=[e[a*2],e[a*2+1]],[U,k]=[e[h*2],e[h*2+1]],M=Math.hypot(d-m,x-b),y=Math.hypot(U-d,k-x);if(!M||!y||un(d,x)||fe(m,b,d,x)||fe(d,x,U,k)){c(d,x);continue}let[I,R]=[(d-m)/M,(x-b)/M],[V,B]=[(U-d)/y,(k-x)/y],j=Math.atan2(I*B-R*V,I*V+R*B);if(j*u>=-.08||Math.abs(j)>Math.PI*.85){c(d,x);continue}let Q=Math.tan(Math.abs(j)/2),$=Math.min(t*Q,M*.2,y*.2),L=$/Q;if(L<1){c(d,x);continue}let[z,q]=[-R*u,I*u],[Y,p]=[d-I*$-z*L,x-R*$-q*L],A=Math.atan2(q,z),w=Math.max(s<=Math.PI/6?2:1,Math.ceil(Math.abs(j)/Math.max(Math.PI/6,Math.min(Math.PI/3,s))));for(let g=0;g<=w;g+=1){let N=A+j*g/w,E=[Math.cos(N),Math.sin(N)];c(Y+E[0]*L,p+E[1]*L,E)}}return i[0]===i.at(-2)&&i[1]===i.at(-1)&&(i.splice(-2),l.pop()),i.length>=6?{ring:i,normals:l}:{ring:e,normals:[]}}var sn=class{#e=new ArrayBuffer(4096*tn);#n=new DataView(this.#e);count=0;write(n,t,s,o,r,u,i,l,c,a,f=0,h=0,m=n.top){let b=tn;if((this.count+1)*b>this.#e.byteLength){let U=new ArrayBuffer(this.#e.byteLength*2);new Uint8Array(U).set(new Uint8Array(this.#e)),this.#e=U,this.#n=new DataView(U)}let d=this.#n,x=this.count*b;return d.setUint16(x,ye(t),!0),d.setUint16(x+2,ye(s),!0),d.setUint16(x+4,Math.min(65535,Math.round(n.base*10)),!0),d.setUint16(x+6,Math.min(65535,Math.max(0,Math.round(m*10))),!0),d.setInt8(x+8,Math.round(r*127)),d.setInt8(x+9,Math.round(u*127)),d.setUint8(x+10,o),d.setUint8(x+11,i),d.setUint16(x+12,Math.round(l*10)%65536,!0),d.setUint8(x+14,Math.round(n.jitter*255)),d.setUint8(x+15,Math.round(n.seed*255)),d.setUint16(x+16,ye(n.cx),!0),d.setUint16(x+18,ye(n.cy),!0),d.setUint8(x+20,n.tone+4*n.group),d.setUint8(x+21,n.use),d.setUint8(x+22,c),d.setUint8(x+23,Math.min(255,Math.round(a))),d.setUint16(x+24,n.index,!0),d.setUint8(x+26,f),d.setUint8(x+27,h),this.count+=1,this.count-1}bytes(){return new Uint8Array(this.#e.slice(0,this.count*tn))}},Ve=class{#e=new Uint32Array(8192);length=0;push(...n){if(this.length+n.length>this.#e.length){let t=new Uint32Array(Math.max(this.length+n.length,this.#e.length*2));t.set(this.#e),this.#e=t}for(let t of n)this.#e[this.length++]=t}values(){return this.#e.subarray(0,this.length)}};function mt(e,n){let t=new Map;if(e.features.some(i=>i.properties.night_use!==void 0)){for(let[i,l]of e.features.entries()){let c=it[String(l.properties.night_use??"")];c&&t.set(i,c)}return t}let s=n.places;if(!s||s.features.length===0)return t;let o=ut/n.tileMetres*e.extent,r=[];for(let[i,l]of e.features.entries()){if(l.type!=="Polygon")continue;let c=l.properties.hide_3d;if(c===!0||c===1||c==="1"||c==="true")continue;let a=[1/0,1/0,-1/0,-1/0];for(let f of l.geometry)for(let h=0;h+1<f.length;h+=2)a[0]=Math.min(a[0],f[h]),a[1]=Math.min(a[1],f[h+1]),a[2]=Math.max(a[2],f[h]),a[3]=Math.max(a[3],f[h+1]);r.push({index:i,rings:l.geometry,box:a})}let u=e.extent/s.extent;for(let i of s.features){if(i.type!=="Point")continue;let l=i.properties,c=on[String(l.type??"")]??on[String(l.subclass??"")]??on[String(l.class??"")];if(c)for(let a of i.geometry)for(let f=0;f+1<a.length;f+=2){let h=a[f]*u,m=a[f+1]*u,b,d,x=o;for(let k of r){let[M,y,I,R]=k.box;if(h<M-o||h>I+o||m<y-o||m>R+o)continue;let V=!1;for(let B of k.rings)for(let j=0,Q=B.length-2;j+1<B.length;Q=j,j+=2){let $=B[Q],L=B[Q+1],z=B[j],q=B[j+1];q>m!=L>m&&h<($-z)*(m-q)/(L-q)+z&&(V=!V);let Y=z-$,p=q-L,A=Y*Y+p*p,w=A?Math.min(1,Math.max(0,((h-$)*Y+(m-L)*p)/A)):0,g=Math.hypot(h-$-w*Y,m-L-w*p);g<x&&(x=g,d=k.index)}if(V){b=k.index;break}}let U=b??d;U!==void 0&&t.set(U,Math.max(t.get(U)??0,c))}}return t}function ye(e){return Math.min(te,Math.max(0,Math.round(e)))}function Tn(e){let n=Number(e);return Number.isFinite(n)&&n>0?n:void 0}function bt(e,n){let t=[],s;for(let o of e){let r=[];for(let i=0;i+1<o.length;i+=2){let l=o[i]*n,c=o[i+1]*n;r.length&&l===r.at(-2)&&c===r.at(-1)||r.push(l,c)}if(r.length>=6&&r[0]===r.at(-2)&&r[1]===r.at(-1)&&(r.length-=2),r.length<6)continue;let u=Math.sign(Ye(r));u&&(s??=u,u===s||t.length===0?t.push([r]):t.at(-1).push(r))}return t}function Ye(e){let n=0,t=e.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;n+=e[s*2]*e[o*2+1]-e[o*2]*e[s*2+1]}return n}function xt(e){let n=0,t=0,s=e.length/2;for(let o=0;o<s;o+=1)n+=e[o*2],t+=e[o*2+1];return[n/s,t/s]}function un(e,n){return e<=0||n<=0||e>=te||n>=te}function fe(e,n,t,s){let o=te;return e<=0&&t<=0||e>=o&&t>=o||n<=0&&s<=0||n>=o&&s>=o}function dt(e){let n=te,t=[...e],s=[[0,0,o=>o>=0],[0,n,o=>o<=n],[1,0,o=>o>=0],[1,n,o=>o<=n]];for(let[o,r,u]of s){let i=[],l=t.length/2;for(let c=0;c<l;c+=1){let a=[t[c*2],t[c*2+1]],f=[t[(c+1)%l*2],t[(c+1)%l*2+1]],h=u(a[o]),m=u(f[o]);if(h&&i.push(a[0],a[1]),h!==m){let b=(r-a[o])/(f[o]-a[o]);i.push(a[0]+(f[0]-a[0])*b,a[1]+(f[1]-a[1])*b)}}if(t=i,t.length<6)return[]}return t}function rn(e){let n=Math.sin(e*12.9898+78.233)*43758.5453;return n-Math.floor(n)}var ln=globalThis;ln.onmessage=({data:e})=>{try{let n=Un(e.layer,e.options);ln.postMessage({mesh:n},n?[n.vertices.buffer,n.indices.buffer]:[])}catch(n){ln.postMessage({error:String(n)})}};})();\n'], { type: "text/javascript" })
|
|
6582
6687
|
);
|
|
6583
6688
|
try {
|
|
6584
6689
|
return new Worker(url, { name: "tileflow-buildings" });
|
|
@@ -7075,6 +7180,9 @@ uniform float u_corner;
|
|
|
7075
7180
|
uniform float u_night; // 0 by day, 1 at night
|
|
7076
7181
|
uniform float u_windows; // whether to draw night windows
|
|
7077
7182
|
uniform float u_flood; // flood light strength at night
|
|
7183
|
+
uniform float u_architectural_intensity;
|
|
7184
|
+
uniform vec3 u_architectural_colour;
|
|
7185
|
+
uniform vec2 u_architectural_wall; // minimum height, wall radius / building height
|
|
7078
7186
|
uniform float u_edge; // how strongly roof outlines and outer corners catch the light, 0 for none
|
|
7079
7187
|
uniform float u_edge_width; // their width, in the drawing buffer's pixels
|
|
7080
7188
|
uniform vec3 u_edge_colour; // the application's colour for them
|
|
@@ -7227,6 +7335,11 @@ void main() {
|
|
|
7227
7335
|
}
|
|
7228
7336
|
// Flood lights wash the foot of lit buildings at night.
|
|
7229
7337
|
colour += floodOf(v_building.y, top, seed) * u_flood * 0.3 * (1.0 - smoothstep(0.0, 6.0, up));
|
|
7338
|
+
if (top > u_architectural_wall.x && u_architectural_intensity > 0.0) {
|
|
7339
|
+
float radius = top * u_architectural_wall.y;
|
|
7340
|
+
float share = u_architectural_intensity * max(0.0, 1.0 - up / max(radius, 0.001));
|
|
7341
|
+
colour = mix(colour, u_architectural_colour, share);
|
|
7342
|
+
}
|
|
7230
7343
|
// The places busy at night light their ground floor's windows: panes of about 3.6 m, which far
|
|
7231
7344
|
// away give way to their average so nothing shimmers.
|
|
7232
7345
|
if (v_building.w > 0.5 && v_wall.z > 3.0 && v_wall.y < 4.0 && u_flood > 0.0) {
|
|
@@ -7302,6 +7415,40 @@ void main() {
|
|
|
7302
7415
|
float close = near * near * near * near * near * near;
|
|
7303
7416
|
fragColor = vec4((v_flood * 0.35 * near * near + v_shop * 0.12 * close) * u_flood * v_strength, 0.0);
|
|
7304
7417
|
}`;
|
|
7418
|
+
var buildingArchitecturalFloodVertex = (projected) => `#version 300 es
|
|
7419
|
+
${projected}
|
|
7420
|
+
${attributes}
|
|
7421
|
+
${growth}
|
|
7422
|
+
uniform vec2 u_radius;
|
|
7423
|
+
uniform float u_min_height;
|
|
7424
|
+
out float v_out;
|
|
7425
|
+
void main() {
|
|
7426
|
+
if (a_height.y * 0.1 < u_min_height || a_height.x > 5.0 || progressOf() <= 0.0) {
|
|
7427
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
7428
|
+
return;
|
|
7429
|
+
}
|
|
7430
|
+
float radius = mix(u_radius.x, u_radius.y, a_wave.x);
|
|
7431
|
+
gl_Position = projectTileFor3D(a_pos + a_normal * (radius / u_metres) * a_flags.x, 0.0);
|
|
7432
|
+
v_out = a_flags.x;
|
|
7433
|
+
}`;
|
|
7434
|
+
var buildingArchitecturalFloodFragment = `#version 300 es
|
|
7435
|
+
precision highp float;
|
|
7436
|
+
in float v_out;
|
|
7437
|
+
uniform float u_intensity;
|
|
7438
|
+
out vec4 fragColor;
|
|
7439
|
+
void main() {
|
|
7440
|
+
float near = 1.0 - v_out;
|
|
7441
|
+
fragColor = vec4(vec3(1.0 - u_intensity * pow(near, 2.101)), 1.0);
|
|
7442
|
+
}`;
|
|
7443
|
+
var buildingArchitecturalFloodComposite = `#version 300 es
|
|
7444
|
+
precision highp float;
|
|
7445
|
+
uniform sampler2D u_mask;
|
|
7446
|
+
uniform vec3 u_colour;
|
|
7447
|
+
out vec4 fragColor;
|
|
7448
|
+
void main() {
|
|
7449
|
+
float light = 1.0 - texelFetch(u_mask, ivec2(gl_FragCoord.xy), 0).r;
|
|
7450
|
+
fragColor = vec4(u_colour, light);
|
|
7451
|
+
}`;
|
|
7305
7452
|
|
|
7306
7453
|
// src/buildings-3d-view.ts
|
|
7307
7454
|
function buildingBoxVisible(matrix, bounds) {
|
|
@@ -8781,6 +8928,7 @@ function createCameraTiles(options) {
|
|
|
8781
8928
|
const tiles = /* @__PURE__ */ new Map();
|
|
8782
8929
|
const queue = [];
|
|
8783
8930
|
let wanted = [];
|
|
8931
|
+
let covered = [];
|
|
8784
8932
|
let reader;
|
|
8785
8933
|
let zoom = options.zoom;
|
|
8786
8934
|
let origin = [0, 0];
|
|
@@ -8806,7 +8954,6 @@ function createCameraTiles(options) {
|
|
|
8806
8954
|
const currentZoom = map.getZoom();
|
|
8807
8955
|
if (currentZoom < options.minzoom - 1 || !options.active()) return;
|
|
8808
8956
|
const source = ensureReader();
|
|
8809
|
-
if (!source) return;
|
|
8810
8957
|
const centre = map.getCenter();
|
|
8811
8958
|
const pitch = map.getPitch();
|
|
8812
8959
|
const reach = Math.min(5e3, Math.max(800, 1600 * 2 ** (16 - currentZoom) * (1 + pitch / 60)));
|
|
@@ -8831,8 +8978,13 @@ function createCameraTiles(options) {
|
|
|
8831
8978
|
candidates.sort((a, b2) => a.distance - b2.distance);
|
|
8832
8979
|
const chosen = candidates.slice(0, options.maxTiles);
|
|
8833
8980
|
wanted = chosen.map(({ x: x3, y: y2 }) => `${x3}/${y2}`);
|
|
8981
|
+
covered = chosen.map(({ x: x3, y: y2 }) => ({ x: x3, y: y2 }));
|
|
8834
8982
|
origin = cameraTileAt(centre.lng, centre.lat, zoom);
|
|
8835
8983
|
metres = earth * Math.cos(centre.lat * Math.PI / 180) / 2 ** zoom / extent;
|
|
8984
|
+
if (!source) {
|
|
8985
|
+
map.triggerRepaint();
|
|
8986
|
+
return;
|
|
8987
|
+
}
|
|
8836
8988
|
const now = performance.now();
|
|
8837
8989
|
for (const { x: x3, y: y2 } of chosen) {
|
|
8838
8990
|
const key = `${x3}/${y2}`;
|
|
@@ -8955,6 +9107,7 @@ function createCameraTiles(options) {
|
|
|
8955
9107
|
}
|
|
8956
9108
|
return list;
|
|
8957
9109
|
},
|
|
9110
|
+
coverage: () => covered,
|
|
8958
9111
|
all: () => tiles.values(),
|
|
8959
9112
|
get zoom() {
|
|
8960
9113
|
return zoom;
|
|
@@ -9209,11 +9362,157 @@ function inspectModelTriangles(indices, position, limit = 3e3) {
|
|
|
9209
9362
|
return { positions, indices: preview };
|
|
9210
9363
|
}
|
|
9211
9364
|
|
|
9365
|
+
// src/models-3d-materials.ts
|
|
9366
|
+
var tileflowModelSurfaceLimits = Object.freeze({
|
|
9367
|
+
materials: 256,
|
|
9368
|
+
textures: 32,
|
|
9369
|
+
textureDimension: 4096,
|
|
9370
|
+
texturePixels: 16777216,
|
|
9371
|
+
emitters: 128
|
|
9372
|
+
});
|
|
9373
|
+
var tileflowModelTextureChannels = [
|
|
9374
|
+
"baseColorTexture",
|
|
9375
|
+
"metallicRoughnessTexture",
|
|
9376
|
+
"normalTexture",
|
|
9377
|
+
"occlusionTexture",
|
|
9378
|
+
"emissiveTexture"
|
|
9379
|
+
];
|
|
9380
|
+
function checkTileflowModelSurface(surface, vertices, indices) {
|
|
9381
|
+
const fail = (reason) => {
|
|
9382
|
+
throw new RangeError(`Invalid model surface: ${reason}.`);
|
|
9383
|
+
};
|
|
9384
|
+
if (!surface || typeof surface !== "object") fail("missing data");
|
|
9385
|
+
const { attributes: attributes4, materials, textures, ranges, emitters } = surface;
|
|
9386
|
+
if (!(Array.isArray(attributes4) || attributes4 instanceof Float32Array) || attributes4.length !== vertices * 8)
|
|
9387
|
+
fail("each vertex needs UV0, UV1 and RGBA");
|
|
9388
|
+
for (let i2 = 0; i2 < attributes4.length; i2++) {
|
|
9389
|
+
const value = attributes4[i2];
|
|
9390
|
+
if (!Number.isFinite(value) || Math.abs(value) > 65536) fail("unbounded vertex attributes");
|
|
9391
|
+
if (i2 % 8 >= 4 && (value < 0 || value > 1)) fail("vertex RGBA must be zero to one");
|
|
9392
|
+
}
|
|
9393
|
+
if (!Array.isArray(materials) || materials.length < 1 || materials.length > tileflowModelSurfaceLimits.materials)
|
|
9394
|
+
fail("material count exceeds the limit");
|
|
9395
|
+
if (!Array.isArray(textures) || textures.length > tileflowModelSurfaceLimits.textures)
|
|
9396
|
+
fail("texture count exceeds the limit");
|
|
9397
|
+
let pixels = 0;
|
|
9398
|
+
for (const texture of textures) {
|
|
9399
|
+
if (!texture || !(texture.bytes instanceof Uint8Array) || !texture.bytes.length)
|
|
9400
|
+
fail("an embedded image is missing");
|
|
9401
|
+
if (!["image/png", "image/jpeg"].includes(texture.mimeType)) fail("use embedded PNG or JPEG");
|
|
9402
|
+
const dimensions = tileflowModelImageSize(texture.bytes, texture.mimeType);
|
|
9403
|
+
if (!dimensions || dimensions[0] !== texture.width || dimensions[1] !== texture.height)
|
|
9404
|
+
fail("image dimensions disagree with the image");
|
|
9405
|
+
if (Math.max(texture.width, texture.height) > tileflowModelSurfaceLimits.textureDimension)
|
|
9406
|
+
fail("image dimensions exceed the limit");
|
|
9407
|
+
pixels += texture.width * texture.height;
|
|
9408
|
+
if (pixels > tileflowModelSurfaceLimits.texturePixels)
|
|
9409
|
+
fail("decoded image pixels exceed the limit");
|
|
9410
|
+
if (texture.wrapS !== void 0 && ![33071, 33648, 10497].includes(texture.wrapS))
|
|
9411
|
+
fail("unknown horizontal texture wrapping");
|
|
9412
|
+
if (texture.wrapT !== void 0 && ![33071, 33648, 10497].includes(texture.wrapT))
|
|
9413
|
+
fail("unknown vertical texture wrapping");
|
|
9414
|
+
if (texture.minFilter !== void 0 && ![9728, 9729, 9984, 9985, 9986, 9987].includes(texture.minFilter))
|
|
9415
|
+
fail("unknown texture minification");
|
|
9416
|
+
if (texture.magFilter !== void 0 && ![9728, 9729].includes(texture.magFilter))
|
|
9417
|
+
fail("unknown texture magnification");
|
|
9418
|
+
}
|
|
9419
|
+
const tuple = (value, count, min, max2) => Array.isArray(value) && value.length === count && value.every((v2) => typeof v2 === "number" && Number.isFinite(v2) && v2 >= min && v2 <= max2);
|
|
9420
|
+
const number = (value, min, max2) => typeof value === "number" && Number.isFinite(value) && value >= min && value <= max2;
|
|
9421
|
+
for (const material of materials) {
|
|
9422
|
+
if (!material || typeof material !== "object") fail("missing material");
|
|
9423
|
+
if (material.baseColorFactor !== void 0 && !tuple(material.baseColorFactor, 4, 0, 1))
|
|
9424
|
+
fail("base colour must have four zero-to-one components");
|
|
9425
|
+
if (material.emissiveFactor !== void 0 && !tuple(material.emissiveFactor, 3, 0, 1))
|
|
9426
|
+
fail("emission must have three zero-to-one components");
|
|
9427
|
+
for (const key of ["metallicFactor", "roughnessFactor", "alphaCutoff"])
|
|
9428
|
+
if (material[key] !== void 0 && !number(material[key], 0, 1))
|
|
9429
|
+
fail(`${key} must be zero to one`);
|
|
9430
|
+
if (material.emissiveStrength !== void 0 && !number(material.emissiveStrength, 0, 1e3))
|
|
9431
|
+
fail("emissive strength must be zero to 1000");
|
|
9432
|
+
if (material.alphaMode !== void 0 && !["OPAQUE", "MASK", "BLEND"].includes(material.alphaMode))
|
|
9433
|
+
fail("unknown alpha mode");
|
|
9434
|
+
for (const key of ["doubleSided", "unlit"])
|
|
9435
|
+
if (material[key] !== void 0 && typeof material[key] !== "boolean")
|
|
9436
|
+
fail(`${key} must be boolean`);
|
|
9437
|
+
for (const channel of tileflowModelTextureChannels) {
|
|
9438
|
+
const ref = material[channel];
|
|
9439
|
+
if (ref === void 0) continue;
|
|
9440
|
+
if (!ref || typeof ref !== "object") fail("invalid texture reference");
|
|
9441
|
+
if (!Number.isInteger(ref.texture) || ref.texture < 0 || ref.texture >= textures.length)
|
|
9442
|
+
fail("material names a missing texture");
|
|
9443
|
+
if (ref.texCoord !== void 0 && ref.texCoord !== 0 && ref.texCoord !== 1)
|
|
9444
|
+
fail("texture coordinates must name UV0 or UV1");
|
|
9445
|
+
if (ref.offset !== void 0 && !tuple(ref.offset, 2, -65536, 65536))
|
|
9446
|
+
fail("invalid texture offset");
|
|
9447
|
+
if (ref.scale !== void 0 && !tuple(ref.scale, 2, -65536, 65536))
|
|
9448
|
+
fail("invalid texture scale");
|
|
9449
|
+
if (ref.rotation !== void 0 && !number(ref.rotation, -1e3, 1e3))
|
|
9450
|
+
fail("invalid texture rotation");
|
|
9451
|
+
if (ref.strength !== void 0 && !number(ref.strength, 0, channel === "normalTexture" ? 100 : 1))
|
|
9452
|
+
fail("invalid texture channel strength");
|
|
9453
|
+
}
|
|
9454
|
+
}
|
|
9455
|
+
let covered = 0;
|
|
9456
|
+
if (!Array.isArray(ranges) || !ranges.length || ranges.length > indices / 3)
|
|
9457
|
+
fail("missing material ranges");
|
|
9458
|
+
for (const range of ranges) {
|
|
9459
|
+
if (!range || typeof range !== "object") fail("invalid material range");
|
|
9460
|
+
if (range.emissiveBounds !== void 0 && (!tuple(range.emissiveBounds, 2, -2e3, 2e3) || range.emissiveBounds[0] > range.emissiveBounds[1]))
|
|
9461
|
+
fail("invalid material emission bounds");
|
|
9462
|
+
if (!Number.isInteger(range.from) || range.from !== covered || !Number.isInteger(range.count) || range.count <= 0 || range.count % 3 !== 0 || !Number.isInteger(range.material) || range.material < 0 || range.material >= materials.length)
|
|
9463
|
+
fail("material ranges must cover whole triangles in order");
|
|
9464
|
+
covered += range.count;
|
|
9465
|
+
if (covered > indices) fail("material range exceeds the mesh");
|
|
9466
|
+
}
|
|
9467
|
+
if (covered !== indices) fail("material ranges must cover the mesh");
|
|
9468
|
+
if (emitters !== void 0) {
|
|
9469
|
+
if (!Array.isArray(emitters) || emitters.length > tileflowModelSurfaceLimits.emitters)
|
|
9470
|
+
fail("emitter count exceeds the limit");
|
|
9471
|
+
for (const emitter of emitters) {
|
|
9472
|
+
if (!emitter || typeof emitter !== "object") fail("invalid area emitter");
|
|
9473
|
+
if (!tuple(emitter.start, 3, -2e3, 2e3) || !tuple(emitter.end, 3, -2e3, 2e3) || !number(emitter.height, 0.01, 1e3) || emitter.depth !== void 0 && !number(emitter.depth, 0, 100) || !Object.hasOwn(tileflowModelLightingRoles, emitter.role) || !Object.hasOwn(tileflowModelParts, emitter.part))
|
|
9474
|
+
fail("invalid area emitter");
|
|
9475
|
+
if (Math.hypot(emitter.end[0] - emitter.start[0], emitter.end[1] - emitter.start[1]) < 1e-3 || Math.abs(emitter.end[2] - emitter.start[2]) > 0.01)
|
|
9476
|
+
fail("an area emitter needs a horizontal lower edge");
|
|
9477
|
+
}
|
|
9478
|
+
}
|
|
9479
|
+
}
|
|
9480
|
+
function tileflowModelImageSize(bytes, mimeType) {
|
|
9481
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
9482
|
+
if (mimeType === "image/png") {
|
|
9483
|
+
if (bytes.length < 33 || view.getUint32(0) !== 2303741511 || view.getUint32(4) !== 218765834 || view.getUint32(12) !== 1229472850)
|
|
9484
|
+
return void 0;
|
|
9485
|
+
const width = view.getUint32(16), height = view.getUint32(20);
|
|
9486
|
+
return width && height ? [width, height] : void 0;
|
|
9487
|
+
}
|
|
9488
|
+
if (bytes.length < 4 || view.getUint16(0) !== 65496) return void 0;
|
|
9489
|
+
for (let at = 2; at + 4 <= bytes.length; ) {
|
|
9490
|
+
if (bytes[at++] !== 255) return void 0;
|
|
9491
|
+
while (bytes[at] === 255) at++;
|
|
9492
|
+
const marker = bytes[at++];
|
|
9493
|
+
if (marker === 217 || marker === 218) return void 0;
|
|
9494
|
+
if (marker === 1 || marker >= 208 && marker <= 215) continue;
|
|
9495
|
+
if (at + 2 > bytes.length) return void 0;
|
|
9496
|
+
const length = view.getUint16(at);
|
|
9497
|
+
if (length < 2 || at + length > bytes.length) return void 0;
|
|
9498
|
+
if ([192, 193, 194, 195, 197, 198, 199, 201, 202, 203, 205, 206, 207].includes(
|
|
9499
|
+
marker
|
|
9500
|
+
)) {
|
|
9501
|
+
if (length < 8) return void 0;
|
|
9502
|
+
const height = view.getUint16(at + 3), width = view.getUint16(at + 5);
|
|
9503
|
+
return width && height ? [width, height] : void 0;
|
|
9504
|
+
}
|
|
9505
|
+
at += length;
|
|
9506
|
+
}
|
|
9507
|
+
return void 0;
|
|
9508
|
+
}
|
|
9509
|
+
|
|
9212
9510
|
// src/models-3d-file.ts
|
|
9213
9511
|
var levelNames = ["near", "mid", "far"];
|
|
9214
9512
|
var meshopt = "EXT_meshopt_compression";
|
|
9215
9513
|
var quantisation = "KHR_mesh_quantization";
|
|
9216
9514
|
var partCount = Object.keys(tileflowModelParts).length;
|
|
9515
|
+
var roleCount = Object.keys(tileflowModelLightingRoles).length;
|
|
9217
9516
|
var modelId2 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
9218
9517
|
var [glbMagic, jsonChunk, binChunk] = [1179937895, 1313821514, 5130562];
|
|
9219
9518
|
var [short, byte, unsignedByte, unsignedShort, unsignedInt] = [5122, 5120, 5121, 5123, 5125];
|
|
@@ -9249,8 +9548,7 @@ async function decodeTileflowModels(buffer, decompressor) {
|
|
|
9249
9548
|
if (source !== 0 || byteStride !== stride || count * byteStride !== length)
|
|
9250
9549
|
throw new Error();
|
|
9251
9550
|
if (filter !== void 0 && filter !== "NONE") throw new Error();
|
|
9252
|
-
if (mode !== (stride
|
|
9253
|
-
throw new Error();
|
|
9551
|
+
if (mode !== (stride > 4 ? "ATTRIBUTES" : "TRIANGLES")) throw new Error();
|
|
9254
9552
|
out = new Uint8Array(length);
|
|
9255
9553
|
decoder.decodeGltfBuffer(out, count, byteStride, slice(bin, byteOffset, byteLength), mode);
|
|
9256
9554
|
} else {
|
|
@@ -9266,6 +9564,8 @@ async function decodeTileflowModels(buffer, decompressor) {
|
|
|
9266
9564
|
for (const root of roots) {
|
|
9267
9565
|
const node = json.nodes?.[root];
|
|
9268
9566
|
const model = readModelNode(node);
|
|
9567
|
+
const roles = node.extras.tileflow.lightingRoles;
|
|
9568
|
+
if (roles !== void 0 && roles !== 1) return void 0;
|
|
9269
9569
|
const children = Array.isArray(node.children) ? node.children : [];
|
|
9270
9570
|
if (children.length < 1 || children.length > tileflowModelLimits.levels) return void 0;
|
|
9271
9571
|
const levels = [];
|
|
@@ -9275,7 +9575,8 @@ async function decodeTileflowModels(buffer, decompressor) {
|
|
|
9275
9575
|
json,
|
|
9276
9576
|
json.nodes?.[child],
|
|
9277
9577
|
levelNames[at],
|
|
9278
|
-
viewBytes
|
|
9578
|
+
viewBytes,
|
|
9579
|
+
roles === 1
|
|
9279
9580
|
);
|
|
9280
9581
|
const low = level.min.map((value, axis) => level.centre[axis] + value * level.step);
|
|
9281
9582
|
const high = level.max.map((value, axis) => level.centre[axis] + value * level.step);
|
|
@@ -9288,7 +9589,8 @@ async function decodeTileflowModels(buffer, decompressor) {
|
|
|
9288
9589
|
vertices: level.vertices,
|
|
9289
9590
|
indices: level.indices,
|
|
9290
9591
|
centre: level.centre,
|
|
9291
|
-
step: level.step
|
|
9592
|
+
step: level.step,
|
|
9593
|
+
...level.surface ? { surface: level.surface } : {}
|
|
9292
9594
|
});
|
|
9293
9595
|
}
|
|
9294
9596
|
models.push({
|
|
@@ -9338,7 +9640,7 @@ function readModelNode(node) {
|
|
|
9338
9640
|
...own.heading !== void 0 ? { heading: own.heading } : {}
|
|
9339
9641
|
};
|
|
9340
9642
|
}
|
|
9341
|
-
function readLevelNode(json, node, name, viewBytes) {
|
|
9643
|
+
function readLevelNode(json, node, name, viewBytes, hasLightingRoles) {
|
|
9342
9644
|
if (node?.name !== name || node.matrix !== void 0 || node.rotation !== void 0)
|
|
9343
9645
|
throw new Error();
|
|
9344
9646
|
const centre = node.translation;
|
|
@@ -9378,18 +9680,51 @@ function readLevelNode(json, node, name, viewBytes) {
|
|
|
9378
9680
|
tileflowModelVertexBytes,
|
|
9379
9681
|
count * tileflowModelVertexBytes
|
|
9380
9682
|
);
|
|
9381
|
-
for (let vertex = 0; vertex < count; vertex += 1)
|
|
9382
|
-
|
|
9683
|
+
for (let vertex = 0; vertex < count; vertex += 1) {
|
|
9684
|
+
const at = vertex * tileflowModelVertexBytes;
|
|
9685
|
+
const part = vertices[at + 12];
|
|
9686
|
+
if (part >= partCount) throw new Error();
|
|
9687
|
+
if (!hasLightingRoles) vertices[at + 15] = tileflowModelDefaultLightingRoles[part];
|
|
9688
|
+
else if (vertices[at + 15] >= roleCount) throw new Error();
|
|
9689
|
+
}
|
|
9383
9690
|
const indexBytes = viewBytes(index.bufferView, width, indexCount * width);
|
|
9384
9691
|
const indices = width === 4 ? new Uint32Array(indexBytes.buffer, indexBytes.byteOffset, indexCount) : new Uint16Array(indexBytes.buffer, indexBytes.byteOffset, indexCount);
|
|
9385
9692
|
for (const vertex of indices) if (vertex >= count) throw new Error();
|
|
9693
|
+
const physical = node.extras?.tileflow?.surface;
|
|
9694
|
+
let surface;
|
|
9695
|
+
if (physical !== void 0) {
|
|
9696
|
+
if (physical?.version !== 1 || !Array.isArray(physical.attributes) || physical.attributes.length !== 2)
|
|
9697
|
+
throw new Error();
|
|
9698
|
+
const uv = accessor(physical.attributes[0]);
|
|
9699
|
+
const colour = accessor(physical.attributes[1]);
|
|
9700
|
+
if (!uv || !colour || (uv.byteOffset ?? 0) !== 0 || colour.byteOffset !== 16 || uv.bufferView !== colour.bufferView || [uv, colour].some(
|
|
9701
|
+
(value) => value.componentType !== 5126 || value.type !== "VEC4" || value.count !== count || value.normalized === true
|
|
9702
|
+
))
|
|
9703
|
+
throw new Error();
|
|
9704
|
+
const attributeBytes = viewBytes(uv.bufferView, 32, count * 32);
|
|
9705
|
+
if (!Array.isArray(physical.textures) || physical.textures.length > 32) throw new Error();
|
|
9706
|
+
surface = {
|
|
9707
|
+
attributes: new Float32Array(attributeBytes.buffer, attributeBytes.byteOffset, count * 8),
|
|
9708
|
+
materials: physical.materials,
|
|
9709
|
+
ranges: physical.ranges,
|
|
9710
|
+
textures: physical.textures.map((texture) => {
|
|
9711
|
+
const { bufferView, byteLength, ...properties } = texture;
|
|
9712
|
+
if (!Number.isInteger(byteLength) || byteLength <= 0 || byteLength > tileflowModelLimits.bytes)
|
|
9713
|
+
throw new Error();
|
|
9714
|
+
return { ...properties, bytes: viewBytes(bufferView, 1, byteLength) };
|
|
9715
|
+
}),
|
|
9716
|
+
...physical.emitters === void 0 ? {} : { emitters: physical.emitters }
|
|
9717
|
+
};
|
|
9718
|
+
checkTileflowModelSurface(surface, count, indexCount);
|
|
9719
|
+
}
|
|
9386
9720
|
return {
|
|
9387
9721
|
vertices,
|
|
9388
9722
|
indices,
|
|
9389
9723
|
centre: [centre[0], centre[1], centre[2]],
|
|
9390
9724
|
step: scale[0],
|
|
9391
9725
|
min,
|
|
9392
|
-
max: max2
|
|
9726
|
+
max: max2,
|
|
9727
|
+
surface
|
|
9393
9728
|
};
|
|
9394
9729
|
}
|
|
9395
9730
|
function readGlb(bytes) {
|
|
@@ -9429,12 +9764,37 @@ function placed(at) {
|
|
|
9429
9764
|
var tileflowLandmarkTileZoom = 14;
|
|
9430
9765
|
var tileflowLandmarkRegionZoom = 6;
|
|
9431
9766
|
var sha256 = /^[a-f0-9]{64}$/u;
|
|
9767
|
+
var landmarkId = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
9768
|
+
var footprintStep = 10;
|
|
9769
|
+
function tileflowLandmarkTileOf(at) {
|
|
9770
|
+
const [longitude, latitude] = at;
|
|
9771
|
+
if (!(longitude >= -180 && longitude <= 180 && latitude >= -85 && latitude <= 85))
|
|
9772
|
+
throw new RangeError("A landmark stands at a longitude and a latitude on the map.");
|
|
9773
|
+
const n = 2 ** tileflowLandmarkTileZoom;
|
|
9774
|
+
const phi = latitude * Math.PI / 180;
|
|
9775
|
+
return [
|
|
9776
|
+
Math.min(n - 1, Math.floor((longitude + 180) / 360 * n)),
|
|
9777
|
+
Math.floor((1 - Math.log(Math.tan(phi) + 1 / Math.cos(phi)) / Math.PI) / 2 * n)
|
|
9778
|
+
];
|
|
9779
|
+
}
|
|
9432
9780
|
function tileflowLandmarkRegionOf(tile) {
|
|
9433
9781
|
const shift = 2 ** (tileflowLandmarkTileZoom - tileflowLandmarkRegionZoom);
|
|
9434
9782
|
return [Math.floor(tile[0] / shift), Math.floor(tile[1] / shift)];
|
|
9435
9783
|
}
|
|
9784
|
+
function validFootprint(footprint) {
|
|
9785
|
+
if (!Array.isArray(footprint) || footprint.length === 0) return false;
|
|
9786
|
+
let points = 0;
|
|
9787
|
+
for (const ring of footprint) {
|
|
9788
|
+
if (!Array.isArray(ring) || ring.length < 6 || ring.length % 2 !== 0) return false;
|
|
9789
|
+
if (!ring.every((value) => Number.isSafeInteger(value))) return false;
|
|
9790
|
+
points += ring.length / 2;
|
|
9791
|
+
}
|
|
9792
|
+
return points <= tileflowModelLimits.footprintPoints;
|
|
9793
|
+
}
|
|
9436
9794
|
function readTileflowLandmarkRegion(json) {
|
|
9437
|
-
if (!isRecord8(json)
|
|
9795
|
+
if (!isRecord8(json)) return void 0;
|
|
9796
|
+
const named = json.format === "tileflow-landmark-region-v2";
|
|
9797
|
+
if (!named && json.format !== "tileflow-landmark-region-v1") return void 0;
|
|
9438
9798
|
const region = json.region;
|
|
9439
9799
|
if (json.zoom !== tileflowLandmarkTileZoom || !Array.isArray(region) || region.length !== 3 || region[0] !== tileflowLandmarkRegionZoom || !Array.isArray(json.tiles))
|
|
9440
9800
|
return void 0;
|
|
@@ -9442,17 +9802,50 @@ function readTileflowLandmarkRegion(json) {
|
|
|
9442
9802
|
if (!Number.isInteger(x3) || !Number.isInteger(y2)) return void 0;
|
|
9443
9803
|
const tiles = [];
|
|
9444
9804
|
for (const tile of json.tiles) {
|
|
9445
|
-
if (!Array.isArray(tile) || tile.length !== 3) return void 0;
|
|
9446
|
-
const [tx, ty, hash4] = tile;
|
|
9805
|
+
if (!Array.isArray(tile) || tile.length !== (named ? 4 : 3)) return void 0;
|
|
9806
|
+
const [tx, ty, hash4, listed] = tile;
|
|
9447
9807
|
if (!Number.isInteger(tx) || !Number.isInteger(ty) || typeof hash4 !== "string")
|
|
9448
9808
|
return void 0;
|
|
9449
9809
|
if (!sha256.test(hash4)) return void 0;
|
|
9450
9810
|
const [rx, ry] = tileflowLandmarkRegionOf([tx, ty]);
|
|
9451
9811
|
if (rx !== x3 || ry !== y2) return void 0;
|
|
9452
|
-
|
|
9812
|
+
if (!named) {
|
|
9813
|
+
tiles.push([tx, ty, hash4]);
|
|
9814
|
+
continue;
|
|
9815
|
+
}
|
|
9816
|
+
const landmarks = readLandmarks(listed, tx, ty);
|
|
9817
|
+
if (!landmarks) return void 0;
|
|
9818
|
+
tiles.push([tx, ty, hash4, landmarks]);
|
|
9453
9819
|
}
|
|
9454
9820
|
return { region: [x3, y2], tiles };
|
|
9455
9821
|
}
|
|
9822
|
+
function readLandmarks(listed, tx, ty) {
|
|
9823
|
+
if (!Array.isArray(listed) || listed.length === 0) return void 0;
|
|
9824
|
+
const out = [];
|
|
9825
|
+
for (const landmark of listed) {
|
|
9826
|
+
if (!isRecord8(landmark)) return void 0;
|
|
9827
|
+
const { id, at, heading, footprint } = landmark;
|
|
9828
|
+
if (typeof id !== "string" || !landmarkId.test(id)) return void 0;
|
|
9829
|
+
if (!Array.isArray(at) || at.length !== 2 || !at.every((value) => typeof value === "number"))
|
|
9830
|
+
return void 0;
|
|
9831
|
+
if (!(typeof heading === "number" && heading >= 0 && heading < 360)) return void 0;
|
|
9832
|
+
if (!validFootprint(footprint)) return void 0;
|
|
9833
|
+
let own;
|
|
9834
|
+
try {
|
|
9835
|
+
own = tileflowLandmarkTileOf([at[0], at[1]]);
|
|
9836
|
+
} catch {
|
|
9837
|
+
return void 0;
|
|
9838
|
+
}
|
|
9839
|
+
if (own[0] !== tx || own[1] !== ty) return void 0;
|
|
9840
|
+
out.push({
|
|
9841
|
+
id,
|
|
9842
|
+
at: [at[0], at[1]],
|
|
9843
|
+
heading,
|
|
9844
|
+
footprint: footprint.map((ring) => ring.map((value) => value / footprintStep))
|
|
9845
|
+
});
|
|
9846
|
+
}
|
|
9847
|
+
return out;
|
|
9848
|
+
}
|
|
9456
9849
|
function readTileflowLandmarksTileJson(json, url) {
|
|
9457
9850
|
if (!isRecord8(json) || json.format !== "tileflow-landmarks-v2") return void 0;
|
|
9458
9851
|
const tile = Array.isArray(json.tiles) ? json.tiles[0] : void 0;
|
|
@@ -9479,16 +9872,266 @@ function isRecord8(value) {
|
|
|
9479
9872
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9480
9873
|
}
|
|
9481
9874
|
|
|
9875
|
+
// src/models-3d-emitters.ts
|
|
9876
|
+
function modelEmitterGeometry(emitter) {
|
|
9877
|
+
const [a, b2] = [emitter.start, emitter.end];
|
|
9878
|
+
const width = Math.hypot(b2[0] - a[0], b2[1] - a[1]);
|
|
9879
|
+
const tangent = [(b2[0] - a[0]) / width, (b2[1] - a[1]) / width];
|
|
9880
|
+
const normal = [tangent[1], -tangent[0]];
|
|
9881
|
+
const reach = Math.min(10, Math.max(4, emitter.height * 1.3));
|
|
9882
|
+
const depth = emitter.depth ?? 0;
|
|
9883
|
+
const spread = depth > 0 ? Math.min(0.29, width * 0.1 / depth) : 0.29;
|
|
9884
|
+
const half = Math.max(5e-3, width / 2 - depth * (spread + 0.01));
|
|
9885
|
+
const centre = [(a[0] + b2[0]) / 2, (a[1] + b2[1]) / 2, a[2]];
|
|
9886
|
+
const point = (along, out, up) => [
|
|
9887
|
+
centre[0] + tangent[0] * along + normal[0] * out,
|
|
9888
|
+
centre[1] + tangent[1] * along + normal[1] * out,
|
|
9889
|
+
centre[2] + up
|
|
9890
|
+
];
|
|
9891
|
+
const left = point(-half, 0, 0.1), right = point(half, 0, 0.1);
|
|
9892
|
+
const farLeft = point(-half - spread * reach, reach, 0);
|
|
9893
|
+
const farRight = point(half + spread * reach, reach, 0);
|
|
9894
|
+
const positions = new Float32Array([
|
|
9895
|
+
...left,
|
|
9896
|
+
...right,
|
|
9897
|
+
...farLeft,
|
|
9898
|
+
...farRight,
|
|
9899
|
+
...point(-half, 0, emitter.height),
|
|
9900
|
+
...point(half, 0, emitter.height),
|
|
9901
|
+
...left,
|
|
9902
|
+
...right,
|
|
9903
|
+
...farLeft,
|
|
9904
|
+
...farRight
|
|
9905
|
+
]);
|
|
9906
|
+
const h2 = half / reach;
|
|
9907
|
+
const coordinates = new Float32Array([
|
|
9908
|
+
-h2,
|
|
9909
|
+
0,
|
|
9910
|
+
h2,
|
|
9911
|
+
1.3,
|
|
9912
|
+
h2,
|
|
9913
|
+
0,
|
|
9914
|
+
h2,
|
|
9915
|
+
1.3,
|
|
9916
|
+
-h2 - spread,
|
|
9917
|
+
1,
|
|
9918
|
+
h2,
|
|
9919
|
+
0.8,
|
|
9920
|
+
h2 + spread,
|
|
9921
|
+
1,
|
|
9922
|
+
h2,
|
|
9923
|
+
0.8,
|
|
9924
|
+
h2 + spread,
|
|
9925
|
+
0.8,
|
|
9926
|
+
h2,
|
|
9927
|
+
0.7,
|
|
9928
|
+
h2 + spread,
|
|
9929
|
+
0.8,
|
|
9930
|
+
h2,
|
|
9931
|
+
0.7,
|
|
9932
|
+
0,
|
|
9933
|
+
0,
|
|
9934
|
+
h2,
|
|
9935
|
+
1.3,
|
|
9936
|
+
0,
|
|
9937
|
+
0,
|
|
9938
|
+
h2,
|
|
9939
|
+
1.3,
|
|
9940
|
+
h2 + spread,
|
|
9941
|
+
1.2,
|
|
9942
|
+
h2,
|
|
9943
|
+
0.8,
|
|
9944
|
+
h2 + spread,
|
|
9945
|
+
1.2,
|
|
9946
|
+
h2,
|
|
9947
|
+
0.8
|
|
9948
|
+
]);
|
|
9949
|
+
return { positions, coordinates, reach };
|
|
9950
|
+
}
|
|
9951
|
+
var modelEmitterVertex = (projected) => `#version 300 es
|
|
9952
|
+
${projected}
|
|
9953
|
+
uniform vec3 u_emitter_positions[10];
|
|
9954
|
+
uniform vec4 u_emitter_coordinates[10];
|
|
9955
|
+
uniform vec2 u_heading;
|
|
9956
|
+
uniform vec2 u_anchor;
|
|
9957
|
+
uniform float u_model_metres;
|
|
9958
|
+
uniform float u_scale;
|
|
9959
|
+
out vec4 v_light_coordinates;
|
|
9960
|
+
void main() {
|
|
9961
|
+
const int indices[12] = int[12](0, 1, 2, 2, 1, 3, 4, 6, 8, 5, 9, 7);
|
|
9962
|
+
int index = indices[gl_VertexID];
|
|
9963
|
+
vec3 position = u_emitter_positions[index];
|
|
9964
|
+
vec2 turned = vec2(position.x * u_heading.x + position.y * u_heading.y,
|
|
9965
|
+
-position.x * u_heading.y + position.y * u_heading.x);
|
|
9966
|
+
float growth = clamp(u_scale, 0.0, 1.0);
|
|
9967
|
+
gl_Position = projectTileFor3D(u_anchor + turned * vec2(1.0, -1.0) / u_model_metres,
|
|
9968
|
+
position.z * growth * growth * (3.0 - 2.0 * growth));
|
|
9969
|
+
v_light_coordinates = u_emitter_coordinates[index];
|
|
9970
|
+
}`;
|
|
9971
|
+
var modelEmitterFragment = `#version 300 es
|
|
9972
|
+
precision highp float;
|
|
9973
|
+
in vec4 v_light_coordinates;
|
|
9974
|
+
uniform vec3 u_emitter_colour;
|
|
9975
|
+
uniform float u_emitter_strength;
|
|
9976
|
+
uniform float u_opacity;
|
|
9977
|
+
out vec4 fragColor;
|
|
9978
|
+
void main() {
|
|
9979
|
+
if (u_emitter_strength <= 0.0) discard;
|
|
9980
|
+
vec4 light = v_light_coordinates;
|
|
9981
|
+
float across = max(0.0, abs(light.x) - light.z) * 1.3;
|
|
9982
|
+
float distance = length(vec2(across, light.y)) +
|
|
9983
|
+
0.5 * (1.0 - clamp(u_emitter_strength - 1.0, 0.0, 1.0));
|
|
9984
|
+
float alpha = clamp((1.0 - distance * distance) * light.w, 0.0, 1.0);
|
|
9985
|
+
fragColor = vec4(u_emitter_colour, alpha * min(1.0, u_emitter_strength) * u_opacity);
|
|
9986
|
+
}`;
|
|
9987
|
+
|
|
9988
|
+
// src/models-3d-material-shaders.ts
|
|
9989
|
+
var modelMaterialSampling = `
|
|
9990
|
+
in vec4 v_uv;
|
|
9991
|
+
in vec4 v_rgba;
|
|
9992
|
+
uniform bool u_physical;
|
|
9993
|
+
uniform vec4 u_base_colour;
|
|
9994
|
+
uniform int u_alpha_mode; // opaque, mask, blend
|
|
9995
|
+
uniform float u_alpha_cutoff;
|
|
9996
|
+
uniform float u_texture_enabled[5];
|
|
9997
|
+
uniform vec4 u_texture_transform[5]; // offset.xy, scale.xy
|
|
9998
|
+
uniform float u_texture_rotation[5];
|
|
9999
|
+
uniform float u_texture_coord[5];
|
|
10000
|
+
uniform sampler2D u_material_texture0;
|
|
10001
|
+
uniform sampler2D u_material_texture1;
|
|
10002
|
+
uniform sampler2D u_material_texture2;
|
|
10003
|
+
uniform sampler2D u_material_texture3;
|
|
10004
|
+
uniform sampler2D u_material_texture4;
|
|
10005
|
+
|
|
10006
|
+
vec2 materialUV(int channel) {
|
|
10007
|
+
vec2 uv = u_texture_coord[channel] > 0.5 ? v_uv.zw : v_uv.xy;
|
|
10008
|
+
vec4 transform = u_texture_transform[channel];
|
|
10009
|
+
uv *= transform.zw;
|
|
10010
|
+
float angle = u_texture_rotation[channel];
|
|
10011
|
+
return transform.xy + mat2(cos(angle), sin(angle), -sin(angle), cos(angle)) * uv;
|
|
10012
|
+
}
|
|
10013
|
+
|
|
10014
|
+
vec4 materialTexture(int channel) {
|
|
10015
|
+
if (u_texture_enabled[channel] < 0.5) return vec4(1.0);
|
|
10016
|
+
vec2 uv = materialUV(channel);
|
|
10017
|
+
if (channel == 0) return texture(u_material_texture0, uv);
|
|
10018
|
+
if (channel == 1) return texture(u_material_texture1, uv);
|
|
10019
|
+
if (channel == 2) return texture(u_material_texture2, uv);
|
|
10020
|
+
if (channel == 3) return texture(u_material_texture3, uv);
|
|
10021
|
+
return texture(u_material_texture4, uv);
|
|
10022
|
+
}
|
|
10023
|
+
|
|
10024
|
+
float materialAlpha() {
|
|
10025
|
+
if (!u_physical || u_alpha_mode == 0) return 1.0;
|
|
10026
|
+
float alpha = u_base_colour.a * v_rgba.a * materialTexture(0).a;
|
|
10027
|
+
if (u_alpha_mode == 1 && alpha < u_alpha_cutoff) discard;
|
|
10028
|
+
return u_alpha_mode == 1 ? 1.0 : alpha;
|
|
10029
|
+
}
|
|
10030
|
+
`;
|
|
10031
|
+
var modelPhysicalLighting = `
|
|
10032
|
+
in vec3 v_position;
|
|
10033
|
+
flat in int v_part;
|
|
10034
|
+
uniform vec3 u_view_position;
|
|
10035
|
+
uniform float u_metallic;
|
|
10036
|
+
uniform float u_roughness;
|
|
10037
|
+
uniform vec3 u_emissive;
|
|
10038
|
+
uniform float u_normal_strength;
|
|
10039
|
+
uniform float u_occlusion_strength;
|
|
10040
|
+
uniform bool u_unlit;
|
|
10041
|
+
uniform vec3 u_part_colours[5];
|
|
10042
|
+
uniform float u_part_mix[5];
|
|
10043
|
+
uniform vec4 u_role_colour[7];
|
|
10044
|
+
uniform vec4 u_role_material[7]; // metallic, roughness, their respective override weights
|
|
10045
|
+
|
|
10046
|
+
vec3 fromSRGB(vec3 colour) {
|
|
10047
|
+
return pow(max(colour, vec3(0.0)), vec3(2.2));
|
|
10048
|
+
}
|
|
10049
|
+
|
|
10050
|
+
vec3 toSRGB(vec3 colour) {
|
|
10051
|
+
// Use the scene's gamma-2.2 approximation, including at low night radiance.
|
|
10052
|
+
return pow(max(colour, vec3(0.0)), vec3(1.0 / 2.2));
|
|
10053
|
+
}
|
|
10054
|
+
|
|
10055
|
+
vec3 materialNormal(vec3 normal) {
|
|
10056
|
+
if (u_texture_enabled[2] < 0.5) return normal;
|
|
10057
|
+
vec2 uv = materialUV(2);
|
|
10058
|
+
vec2 dx = dFdx(uv), dy = dFdy(uv);
|
|
10059
|
+
float determinant = dx.x * dy.y - dx.y * dy.x;
|
|
10060
|
+
if (abs(determinant) < 1e-8) return normal;
|
|
10061
|
+
vec3 px = dFdx(v_position), py = dFdy(v_position);
|
|
10062
|
+
vec3 tangent = (px * dy.y - py * dx.y) / determinant;
|
|
10063
|
+
vec3 bitangent = (py * dx.x - px * dy.x) / determinant;
|
|
10064
|
+
tangent = normalize(tangent - normal * dot(normal, tangent));
|
|
10065
|
+
bitangent = normalize(bitangent - normal * dot(normal, bitangent));
|
|
10066
|
+
vec3 sampleNormal = materialTexture(2).xyz * 2.0 - 1.0;
|
|
10067
|
+
sampleNormal.xy *= u_normal_strength;
|
|
10068
|
+
return normalize(mat3(tangent, bitangent, normal) * sampleNormal);
|
|
10069
|
+
}
|
|
10070
|
+
|
|
10071
|
+
vec3 materialBase(int role) {
|
|
10072
|
+
vec4 own = u_role_colour[role];
|
|
10073
|
+
vec3 factor = mix(u_base_colour.rgb, fromSRGB(own.rgb), own.a);
|
|
10074
|
+
factor = mix(factor, fromSRGB(u_part_colours[v_part]), u_part_mix[v_part]);
|
|
10075
|
+
return factor * v_rgba.rgb * fromSRGB(materialTexture(0).rgb);
|
|
10076
|
+
}
|
|
10077
|
+
|
|
10078
|
+
vec3 physicalLight(vec3 base, vec3 normal, float lit, int role) {
|
|
10079
|
+
if (u_unlit) return base;
|
|
10080
|
+
vec4 packed = materialTexture(1);
|
|
10081
|
+
vec4 overrideMaterial = u_role_material[role];
|
|
10082
|
+
float metallic = clamp(mix(u_metallic, overrideMaterial.x, overrideMaterial.z) * packed.b, 0.0, 1.0);
|
|
10083
|
+
float roughness = clamp(mix(u_roughness, overrideMaterial.y, overrideMaterial.w) * packed.g, 0.04, 1.0);
|
|
10084
|
+
vec3 view = normalize(u_view_position - v_position);
|
|
10085
|
+
vec3 halfway = normalize(view + u_sun_dir);
|
|
10086
|
+
float nv = max(dot(normal, view), 0.001);
|
|
10087
|
+
float nl = max(dot(normal, u_sun_dir), 0.0);
|
|
10088
|
+
float nh = max(dot(normal, halfway), 0.0);
|
|
10089
|
+
float vh = max(dot(view, halfway), 0.0);
|
|
10090
|
+
float a2 = pow(roughness, 4.0);
|
|
10091
|
+
float denominator = nh * nh * (a2 - 1.0) + 1.0;
|
|
10092
|
+
float distribution = a2 / max(3.14159265 * denominator * denominator, 1e-7);
|
|
10093
|
+
float masking = 0.5 / max(nv * sqrt(a2 + (1.0 - a2) * nl * nl)
|
|
10094
|
+
+ nl * sqrt(a2 + (1.0 - a2) * nv * nv), 1e-5);
|
|
10095
|
+
vec3 f0 = mix(vec3(0.04), base, metallic);
|
|
10096
|
+
vec3 fresnel = f0 + (1.0 - f0) * pow(1.0 - vh, 5.0);
|
|
10097
|
+
vec3 diffuse = base * (1.0 - metallic) * 0.96;
|
|
10098
|
+
// Scene light is cartographic radiance: a Lambert surface uses unit diffuse response.
|
|
10099
|
+
vec3 direct = (diffuse * (1.0 - fresnel) + distribution * masking * fresnel)
|
|
10100
|
+
* u_sun_light * nl * (1.0 - u_shadow * (1.0 - lit));
|
|
10101
|
+
// Analytic fit of the integrated GGX BRDF: reflection scales and biases its normal reflectance.
|
|
10102
|
+
float smoothness = 1.0 - roughness;
|
|
10103
|
+
float fit = smoothness * min(smoothness * smoothness, exp2(-9.28 * nv)) + 0.0425 - 0.0275 * roughness;
|
|
10104
|
+
float reflectionScale = 1.04 - 0.572 * roughness - 1.04 * fit;
|
|
10105
|
+
float reflectionBias = 0.022 * roughness - 0.04 + 1.04 * fit;
|
|
10106
|
+
vec3 reflectedSky = max(vec3(0.0), f0 * reflectionScale + reflectionBias);
|
|
10107
|
+
float directional = dot(u_sun_light, vec3(0.2126, 0.7152, 0.0722));
|
|
10108
|
+
float exposure = (0.96 + 0.04 * clamp(normal.z, -1.0, 1.0)) *
|
|
10109
|
+
(1.0 - 0.3 * min(directional, 1.0) * max(-dot(normal, u_sun_dir), 0.0));
|
|
10110
|
+
float ao = mix(1.0, v_light * mix(1.0, materialTexture(3).r, u_occlusion_strength),
|
|
10111
|
+
u_ambient_occlusion);
|
|
10112
|
+
vec3 ambient = (diffuse + reflectedSky) * u_sky_light * exposure * ao;
|
|
10113
|
+
return ambient + min(direct, vec3(1.0));
|
|
10114
|
+
}
|
|
10115
|
+
`;
|
|
10116
|
+
|
|
9482
10117
|
// src/models-3d-shaders.ts
|
|
9483
10118
|
var tileflowModelAttributes = [
|
|
9484
10119
|
["a_position", 3, "SHORT", false, 0],
|
|
9485
10120
|
["a_normal", 3, "BYTE", true, 8],
|
|
9486
|
-
["a_paint", 3, "UNSIGNED_BYTE", false, 12]
|
|
10121
|
+
["a_paint", 3, "UNSIGNED_BYTE", false, 12],
|
|
10122
|
+
["a_lighting_role", 1, "UNSIGNED_BYTE", false, 15]
|
|
10123
|
+
];
|
|
10124
|
+
var tileflowModelSurfaceAttributes = [
|
|
10125
|
+
["a_uv", 4, "FLOAT", false, 0],
|
|
10126
|
+
["a_rgba", 4, "FLOAT", false, 16]
|
|
9487
10127
|
];
|
|
9488
10128
|
var attributes2 = `
|
|
9489
10129
|
in vec3 a_position; // whole steps from the level's centre, in glTF's axes
|
|
9490
10130
|
in vec3 a_normal; // in glTF's axes
|
|
9491
10131
|
in vec3 a_paint; // part; shade (0 to 255); light (0 to 255)
|
|
10132
|
+
in float a_lighting_role;
|
|
10133
|
+
in vec4 a_uv;
|
|
10134
|
+
in vec4 a_rgba;
|
|
9492
10135
|
`;
|
|
9493
10136
|
var placement = `
|
|
9494
10137
|
uniform vec3 u_level_centre; // in glTF's axes, in metres
|
|
@@ -9530,13 +10173,22 @@ var tileflowModelDepthVertex = `#version 300 es
|
|
|
9530
10173
|
${attributes2}
|
|
9531
10174
|
uniform mat4 u_light_matrix;
|
|
9532
10175
|
${placement}
|
|
10176
|
+
out vec4 v_uv;
|
|
10177
|
+
out vec4 v_rgba;
|
|
9533
10178
|
void main() {
|
|
9534
10179
|
vec3 m = turned(metresOf());
|
|
9535
10180
|
gl_Position = u_light_matrix * vec4(sceneOf(tileOf(m), grown(m.z)), 1.0);
|
|
10181
|
+
v_uv = a_uv;
|
|
10182
|
+
v_rgba = a_rgba;
|
|
9536
10183
|
}`;
|
|
9537
10184
|
var tileflowModelDepthFragment = `#version 300 es
|
|
9538
10185
|
precision highp float;
|
|
9539
|
-
|
|
10186
|
+
${modelMaterialSampling}
|
|
10187
|
+
void main() {
|
|
10188
|
+
float alpha = materialAlpha();
|
|
10189
|
+
// Blended surfaces cast an alpha-tested solar shadow, avoiding solid panes.
|
|
10190
|
+
if (u_alpha_mode == 2 && alpha < 0.5) discard;
|
|
10191
|
+
}`;
|
|
9540
10192
|
var tileflowModelVertex = (projected) => `#version 300 es
|
|
9541
10193
|
${projected}
|
|
9542
10194
|
${attributes2}
|
|
@@ -9544,11 +10196,18 @@ uniform mat4 u_light_matrix;
|
|
|
9544
10196
|
uniform vec3 u_shadow_dir;
|
|
9545
10197
|
uniform float u_normal_offset;
|
|
9546
10198
|
uniform vec3 u_part_colours[5];
|
|
10199
|
+
uniform vec2 u_model_height;
|
|
9547
10200
|
${placement}
|
|
9548
10201
|
out vec3 v_colour;
|
|
9549
10202
|
out vec3 v_normal;
|
|
9550
10203
|
out vec3 v_shadow;
|
|
9551
10204
|
out float v_light;
|
|
10205
|
+
flat out int v_lighting_role;
|
|
10206
|
+
out float v_height;
|
|
10207
|
+
out vec4 v_uv;
|
|
10208
|
+
out vec4 v_rgba;
|
|
10209
|
+
out vec3 v_position;
|
|
10210
|
+
flat out int v_part;
|
|
9552
10211
|
|
|
9553
10212
|
void main() {
|
|
9554
10213
|
vec3 m = turned(metresOf());
|
|
@@ -9561,10 +10220,16 @@ void main() {
|
|
|
9561
10220
|
v_shadow = shadow.xyz * 0.5 + 0.5;
|
|
9562
10221
|
v_normal = normal;
|
|
9563
10222
|
int part = int(a_paint.x + 0.5);
|
|
10223
|
+
v_part = clamp(part, 0, 4);
|
|
10224
|
+
v_uv = a_uv;
|
|
10225
|
+
v_rgba = a_rgba;
|
|
10226
|
+
v_position = vec3(m.xy, z);
|
|
9564
10227
|
vec3 colour = u_part_colours[clamp(part, 0, 4)];
|
|
9565
10228
|
// A material lighter or darker than mid-grey makes its part lighter or darker.
|
|
9566
10229
|
v_colour = colour * (0.55 + 0.45 * (a_paint.y / 255.0) * 2.0);
|
|
9567
10230
|
v_light = a_paint.z / 255.0;
|
|
10231
|
+
v_lighting_role = clamp(int(a_lighting_role + 0.5), 0, 6);
|
|
10232
|
+
v_height = (m.z - u_model_height.x) / max(u_model_height.y, 0.001);
|
|
9568
10233
|
}`;
|
|
9569
10234
|
var tileflowModelFragment = `#version 300 es
|
|
9570
10235
|
precision highp float;
|
|
@@ -9572,18 +10237,29 @@ in vec3 v_colour;
|
|
|
9572
10237
|
in vec3 v_normal;
|
|
9573
10238
|
in vec3 v_shadow;
|
|
9574
10239
|
in float v_light;
|
|
10240
|
+
flat in int v_lighting_role;
|
|
10241
|
+
in float v_height;
|
|
9575
10242
|
uniform vec3 u_sun_dir;
|
|
9576
10243
|
uniform vec3 u_sun_light;
|
|
9577
10244
|
uniform vec3 u_sky_light;
|
|
9578
10245
|
uniform float u_night;
|
|
10246
|
+
uniform float u_ambient_occlusion;
|
|
10247
|
+
uniform float u_role_emission[7];
|
|
10248
|
+
uniform vec4 u_role_emission_colour[7];
|
|
10249
|
+
uniform vec4 u_role_emission_gradient[7];
|
|
10250
|
+
uniform float u_role_emission_power[7];
|
|
10251
|
+
uniform float u_opacity;
|
|
9579
10252
|
out vec4 fragColor;
|
|
9580
10253
|
${tileflowShadowLookup}
|
|
10254
|
+
${modelMaterialSampling}
|
|
10255
|
+
${modelPhysicalLighting}
|
|
9581
10256
|
|
|
9582
10257
|
// The buildings' light: a sunlit roof keeps its colour, a face turned to a low sun glows, and shade
|
|
9583
10258
|
// takes the sky's colour; a face that looks down sees little sky.
|
|
9584
10259
|
vec3 shade(vec3 normal, float lit) {
|
|
9585
10260
|
float facing = dot(normal, u_sun_dir);
|
|
9586
10261
|
vec3 sky = u_sky_light * mix(mix(0.8, 0.5, u_night), 1.0, max(normal.z, -0.4));
|
|
10262
|
+
sky *= mix(1.0, v_light, u_ambient_occlusion);
|
|
9587
10263
|
vec3 light = sky + u_sun_light * max(facing, 0.0) * (1.0 - u_shadow * (1.0 - lit));
|
|
9588
10264
|
vec3 roof = u_sky_light + u_sun_light * max(u_sun_dir.z, 0.0);
|
|
9589
10265
|
return min(pow(light / max(roof, vec3(1e-4)), vec3(1.0 / 2.2)), vec3(1.7));
|
|
@@ -9591,10 +10267,171 @@ vec3 shade(vec3 normal, float lit) {
|
|
|
9591
10267
|
|
|
9592
10268
|
void main() {
|
|
9593
10269
|
vec3 normal = normalize(gl_FrontFacing ? v_normal : -v_normal);
|
|
10270
|
+
if (u_physical) normal = materialNormal(normal);
|
|
9594
10271
|
float lit = dot(normal, u_sun_dir) > 0.0 ? visibility(v_shadow, 0.3) : 0.0;
|
|
9595
|
-
|
|
10272
|
+
int role = clamp(v_lighting_role, 0, 6);
|
|
10273
|
+
vec4 gradient = u_role_emission_gradient[role];
|
|
10274
|
+
float multiplier = 1.0;
|
|
10275
|
+
if (gradient.x != gradient.y) {
|
|
10276
|
+
float height = clamp((v_height - gradient.x) / (gradient.y - gradient.x), 0.0, 1.0);
|
|
10277
|
+
multiplier = mix(gradient.z, gradient.w, pow(height, pow(10.0, u_role_emission_power[role])));
|
|
10278
|
+
}
|
|
10279
|
+
float emission = clamp(u_role_emission[role] * multiplier, 0.0, 1.0);
|
|
10280
|
+
if (u_physical) {
|
|
10281
|
+
float alpha = materialAlpha();
|
|
10282
|
+
vec3 base = materialBase(role);
|
|
10283
|
+
vec3 radiance = physicalLight(base, normal, lit, role);
|
|
10284
|
+
radiance += u_emissive * fromSRGB(materialTexture(4).rgb);
|
|
10285
|
+
vec4 own = u_role_emission_colour[role];
|
|
10286
|
+
radiance = mix(radiance, mix(base, fromSRGB(own.rgb), own.a), emission);
|
|
10287
|
+
fragColor = vec4(toSRGB(radiance), alpha * u_opacity);
|
|
10288
|
+
return;
|
|
10289
|
+
}
|
|
10290
|
+
vec3 colour = v_colour * shade(normal, lit);
|
|
10291
|
+
if (emission > 0.0) {
|
|
10292
|
+
vec4 own = u_role_emission_colour[role];
|
|
10293
|
+
vec3 target = mix(v_colour, own.rgb, own.a);
|
|
10294
|
+
colour = pow(mix(pow(max(colour, vec3(0.0)), vec3(2.2)),
|
|
10295
|
+
pow(max(target, vec3(0.0)), vec3(2.2)), emission), vec3(1.0 / 2.2));
|
|
10296
|
+
}
|
|
10297
|
+
fragColor = vec4(colour, 1.0);
|
|
9596
10298
|
}`;
|
|
9597
10299
|
|
|
10300
|
+
// src/models-3d-materials-browser.ts
|
|
10301
|
+
async function readModelImages(model, signal) {
|
|
10302
|
+
const buffers = /* @__PURE__ */ new Map();
|
|
10303
|
+
const pending = /* @__PURE__ */ new Map();
|
|
10304
|
+
const levels = model.levels.map(
|
|
10305
|
+
(level) => (level.surface?.textures ?? []).map((texture) => {
|
|
10306
|
+
const { bytes } = texture;
|
|
10307
|
+
if (!buffers.has(bytes.buffer)) buffers.set(bytes.buffer, buffers.size);
|
|
10308
|
+
const key = `${buffers.get(bytes.buffer)}:${bytes.byteOffset}:${bytes.byteLength}`;
|
|
10309
|
+
let image = pending.get(key);
|
|
10310
|
+
if (!image) {
|
|
10311
|
+
image = createImageBitmap(new Blob([bytes.slice().buffer], { type: texture.mimeType }), {
|
|
10312
|
+
imageOrientation: "none",
|
|
10313
|
+
premultiplyAlpha: "none",
|
|
10314
|
+
colorSpaceConversion: "none"
|
|
10315
|
+
}).then((bitmap) => {
|
|
10316
|
+
if (bitmap.width !== texture.width || bitmap.height !== texture.height) {
|
|
10317
|
+
bitmap.close();
|
|
10318
|
+
throw new RangeError("Model texture dimensions changed during decoding.");
|
|
10319
|
+
}
|
|
10320
|
+
return bitmap;
|
|
10321
|
+
});
|
|
10322
|
+
pending.set(key, image);
|
|
10323
|
+
}
|
|
10324
|
+
return image;
|
|
10325
|
+
})
|
|
10326
|
+
);
|
|
10327
|
+
const settled = await Promise.allSettled(pending.values());
|
|
10328
|
+
if (signal.aborted || settled.some((result) => result.status === "rejected")) {
|
|
10329
|
+
for (const result of settled) if (result.status === "fulfilled") result.value.close();
|
|
10330
|
+
signal.throwIfAborted();
|
|
10331
|
+
throw new RangeError("A model texture could not be decoded.");
|
|
10332
|
+
}
|
|
10333
|
+
return Promise.all(levels.map((level) => Promise.all(level)));
|
|
10334
|
+
}
|
|
10335
|
+
function closeModelImages(images) {
|
|
10336
|
+
for (const image of new Set(images?.flat())) image.close();
|
|
10337
|
+
}
|
|
10338
|
+
function uploadModelSurface(gl, level, images) {
|
|
10339
|
+
if (!level.surface) return void 0;
|
|
10340
|
+
const vbo = gl.createBuffer();
|
|
10341
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
10342
|
+
gl.bufferData(gl.ARRAY_BUFFER, level.surface.attributes, gl.STATIC_DRAW);
|
|
10343
|
+
for (const [
|
|
10344
|
+
index,
|
|
10345
|
+
[, size, type, normalised, offset]
|
|
10346
|
+
] of tileflowModelSurfaceAttributes.entries()) {
|
|
10347
|
+
const location = index + tileflowModelAttributes.length;
|
|
10348
|
+
gl.enableVertexAttribArray(location);
|
|
10349
|
+
gl.vertexAttribPointer(location, size, gl[type], normalised, 32, offset);
|
|
10350
|
+
}
|
|
10351
|
+
const flip = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL);
|
|
10352
|
+
const premultiply = gl.getParameter(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL);
|
|
10353
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
10354
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
10355
|
+
try {
|
|
10356
|
+
const textures = level.surface.textures.map((source, index) => {
|
|
10357
|
+
const texture = gl.createTexture();
|
|
10358
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
10359
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, source.wrapS ?? gl.REPEAT);
|
|
10360
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, source.wrapT ?? gl.REPEAT);
|
|
10361
|
+
const min = source.minFilter ?? gl.LINEAR_MIPMAP_LINEAR;
|
|
10362
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, min);
|
|
10363
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, source.magFilter ?? gl.LINEAR);
|
|
10364
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, gl.RGBA, gl.UNSIGNED_BYTE, images[index]);
|
|
10365
|
+
if (min >= gl.NEAREST_MIPMAP_NEAREST) gl.generateMipmap(gl.TEXTURE_2D);
|
|
10366
|
+
return texture;
|
|
10367
|
+
});
|
|
10368
|
+
const vertices = new DataView(
|
|
10369
|
+
level.vertices.buffer,
|
|
10370
|
+
level.vertices.byteOffset,
|
|
10371
|
+
level.vertices.byteLength
|
|
10372
|
+
);
|
|
10373
|
+
const centres = new Float32Array(level.surface.ranges.length * 3);
|
|
10374
|
+
for (const [index, range] of level.surface.ranges.entries()) {
|
|
10375
|
+
const centre = [0, 0, 0];
|
|
10376
|
+
for (let at = range.from; at < range.from + range.count; at++)
|
|
10377
|
+
for (let axis = 0; axis < 3; axis++)
|
|
10378
|
+
centre[axis] += level.centre[axis] + vertices.getInt16(level.indices[at] * 16 + axis * 2, true) * level.step;
|
|
10379
|
+
centres.set(
|
|
10380
|
+
[centre[0] / range.count, -centre[2] / range.count, centre[1] / range.count],
|
|
10381
|
+
index * 3
|
|
10382
|
+
);
|
|
10383
|
+
}
|
|
10384
|
+
return {
|
|
10385
|
+
vbo,
|
|
10386
|
+
textures,
|
|
10387
|
+
centres,
|
|
10388
|
+
emitters: (level.surface.emitters ?? []).map(modelEmitterGeometry)
|
|
10389
|
+
};
|
|
10390
|
+
} finally {
|
|
10391
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip);
|
|
10392
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiply);
|
|
10393
|
+
}
|
|
10394
|
+
}
|
|
10395
|
+
function bindModelMaterial(gl, uniforms, material, buffers, blank) {
|
|
10396
|
+
const at = (name) => uniforms[name] ?? null;
|
|
10397
|
+
gl.uniform1i(at("u_physical"), material ? 1 : 0);
|
|
10398
|
+
const enabled = new Float32Array(5), transforms = new Float32Array(20);
|
|
10399
|
+
const rotations = new Float32Array(5), coordinates = new Float32Array(5);
|
|
10400
|
+
for (const [index, channel] of tileflowModelTextureChannels.entries()) {
|
|
10401
|
+
const ref = material?.[channel];
|
|
10402
|
+
gl.activeTexture(gl.TEXTURE0 + 2 + index);
|
|
10403
|
+
gl.bindTexture(gl.TEXTURE_2D, ref ? buffers.textures[ref.texture] : blank);
|
|
10404
|
+
gl.uniform1i(at(`u_material_texture${index}`), 2 + index);
|
|
10405
|
+
enabled[index] = ref ? 1 : 0;
|
|
10406
|
+
transforms.set([...ref?.offset ?? [0, 0], ...ref?.scale ?? [1, 1]], index * 4);
|
|
10407
|
+
rotations[index] = ref?.rotation ?? 0;
|
|
10408
|
+
coordinates[index] = ref?.texCoord ?? 0;
|
|
10409
|
+
}
|
|
10410
|
+
gl.uniform1fv(at("u_texture_enabled"), enabled);
|
|
10411
|
+
gl.uniform4fv(at("u_texture_transform"), transforms);
|
|
10412
|
+
gl.uniform1fv(at("u_texture_rotation"), rotations);
|
|
10413
|
+
gl.uniform1fv(at("u_texture_coord"), coordinates);
|
|
10414
|
+
gl.uniform4fv(at("u_base_colour"), material?.baseColorFactor ?? [1, 1, 1, 1]);
|
|
10415
|
+
gl.uniform1f(at("u_metallic"), material?.metallicFactor ?? 1);
|
|
10416
|
+
gl.uniform1f(at("u_roughness"), material?.roughnessFactor ?? 1);
|
|
10417
|
+
gl.uniform3fv(
|
|
10418
|
+
at("u_emissive"),
|
|
10419
|
+
(material?.emissiveFactor ?? [0, 0, 0]).map((c) => c * (material?.emissiveStrength ?? 1))
|
|
10420
|
+
);
|
|
10421
|
+
gl.uniform1f(at("u_normal_strength"), material?.normalTexture?.strength ?? 1);
|
|
10422
|
+
gl.uniform1f(at("u_occlusion_strength"), material?.occlusionTexture?.strength ?? 1);
|
|
10423
|
+
gl.uniform1i(at("u_unlit"), material?.unlit ? 1 : 0);
|
|
10424
|
+
gl.uniform1i(
|
|
10425
|
+
at("u_alpha_mode"),
|
|
10426
|
+
material?.alphaMode === "MASK" ? 1 : material?.alphaMode === "BLEND" ? 2 : 0
|
|
10427
|
+
);
|
|
10428
|
+
gl.uniform1f(at("u_alpha_cutoff"), material?.alphaCutoff ?? 0.5);
|
|
10429
|
+
if (material && !material.doubleSided) {
|
|
10430
|
+
gl.enable(gl.CULL_FACE);
|
|
10431
|
+
gl.cullFace(gl.BACK);
|
|
10432
|
+
} else gl.disable(gl.CULL_FACE);
|
|
10433
|
+
}
|
|
10434
|
+
|
|
9598
10435
|
// src/models-3d-browser.ts
|
|
9599
10436
|
var earth2 = 40075016686e-3;
|
|
9600
10437
|
var keptModels = 12;
|
|
@@ -9604,7 +10441,11 @@ var reachTiles = 1;
|
|
|
9604
10441
|
var levelDistances = [900, 2600];
|
|
9605
10442
|
var retryAfter = 3e4;
|
|
9606
10443
|
var tilesetRetryAfter = 3e5;
|
|
10444
|
+
var landmarkHold = 3e3;
|
|
10445
|
+
var settleWait = 1500;
|
|
9607
10446
|
function createTileflowPlacedModels(options) {
|
|
10447
|
+
const holdFor = options.timing?.hold ?? landmarkHold;
|
|
10448
|
+
const settleFor = options.timing?.settle ?? settleWait;
|
|
9608
10449
|
const loaded = /* @__PURE__ */ new Map();
|
|
9609
10450
|
const loading = /* @__PURE__ */ new Map();
|
|
9610
10451
|
const failed = /* @__PURE__ */ new Map();
|
|
@@ -9620,6 +10461,11 @@ function createTileflowPlacedModels(options) {
|
|
|
9620
10461
|
const tileFailed = /* @__PURE__ */ new Map();
|
|
9621
10462
|
let wantedTiles = /* @__PURE__ */ new Set();
|
|
9622
10463
|
let active = [];
|
|
10464
|
+
let held = [];
|
|
10465
|
+
const holding = /* @__PURE__ */ new Map();
|
|
10466
|
+
let holdTimer;
|
|
10467
|
+
let lastLandmarks;
|
|
10468
|
+
const waiting = /* @__PURE__ */ new Set();
|
|
9623
10469
|
let dirty = true;
|
|
9624
10470
|
let ownIds = /* @__PURE__ */ new Set();
|
|
9625
10471
|
let lastPlacements;
|
|
@@ -9631,7 +10477,7 @@ function createTileflowPlacedModels(options) {
|
|
|
9631
10477
|
function tilesOf(model, zoom) {
|
|
9632
10478
|
const [x3, y2] = cameraTileAt(model.placement.at[0], model.placement.at[1], zoom);
|
|
9633
10479
|
const metres = earth2 * Math.cos(model.placement.at[1] * Math.PI / 180) / 2 ** zoom;
|
|
9634
|
-
const [minX, minY, , maxX, maxY] = model.
|
|
10480
|
+
const [minX, minY, , maxX, maxY] = model.bounds;
|
|
9635
10481
|
const radius = Math.hypot(Math.max(-minX, maxX), Math.max(-minY, maxY)) / metres;
|
|
9636
10482
|
const out = [];
|
|
9637
10483
|
for (let tx = Math.floor(x3 - radius); tx <= Math.floor(x3 + radius); tx += 1)
|
|
@@ -9654,21 +10500,35 @@ function createTileflowPlacedModels(options) {
|
|
|
9654
10500
|
function arrived() {
|
|
9655
10501
|
dirty = true;
|
|
9656
10502
|
version2 += 1;
|
|
10503
|
+
recheck();
|
|
9657
10504
|
options.changed([], lastZoom);
|
|
9658
10505
|
}
|
|
10506
|
+
function recheck() {
|
|
10507
|
+
for (const waiter of waiting)
|
|
10508
|
+
if (waiter.settled()) {
|
|
10509
|
+
waiting.delete(waiter);
|
|
10510
|
+
waiter.resolve();
|
|
10511
|
+
}
|
|
10512
|
+
}
|
|
9659
10513
|
function load(placement3, key) {
|
|
9660
10514
|
const controller = new AbortController();
|
|
9661
10515
|
loading.set(key, controller);
|
|
9662
|
-
download(placement3.url, controller.signal).then(async (response) => decodeTileflowModels(await response.arrayBuffer())).then((models) => {
|
|
9663
|
-
loading.delete(key);
|
|
10516
|
+
download(placement3.url, controller.signal).then(async (response) => decodeTileflowModels(await response.arrayBuffer())).then(async (models) => {
|
|
9664
10517
|
if (disposed) return;
|
|
9665
10518
|
const model = models?.find((entry) => entry.id === placement3.model) ?? models?.[0];
|
|
9666
10519
|
if (!model) throw new Error("not a Tileflow model");
|
|
9667
|
-
|
|
10520
|
+
const images = await readModelImages(model, controller.signal);
|
|
10521
|
+
loading.delete(key);
|
|
10522
|
+
if (disposed || controller.signal.aborted) {
|
|
10523
|
+
closeModelImages(images);
|
|
10524
|
+
return;
|
|
10525
|
+
}
|
|
10526
|
+
loaded.set(key, { placement: placement3, key, model, images, landmark: false, used: performance.now() });
|
|
9668
10527
|
arrived();
|
|
9669
10528
|
}).catch(() => {
|
|
9670
10529
|
loading.delete(key);
|
|
9671
10530
|
failed.set(key, performance.now() + retryAfter);
|
|
10531
|
+
recheck();
|
|
9672
10532
|
});
|
|
9673
10533
|
}
|
|
9674
10534
|
function loadTileset(url) {
|
|
@@ -9686,6 +10546,7 @@ function createTileflowPlacedModels(options) {
|
|
|
9686
10546
|
if (tilesetLoad?.controller !== controller) return;
|
|
9687
10547
|
tilesetLoad = void 0;
|
|
9688
10548
|
tilesetFailed = { url, until: performance.now() + tilesetRetryAfter };
|
|
10549
|
+
recheck();
|
|
9689
10550
|
});
|
|
9690
10551
|
}
|
|
9691
10552
|
function loadRegion(regionKey, x3, y2) {
|
|
@@ -9702,12 +10563,18 @@ function createTileflowPlacedModels(options) {
|
|
|
9702
10563
|
const region = readTileflowLandmarkRegion(json);
|
|
9703
10564
|
if (!region || region.region[0] !== x3 || region.region[1] !== y2)
|
|
9704
10565
|
throw new Error("not this landmarks region");
|
|
9705
|
-
const tiles = new Map(
|
|
10566
|
+
const tiles = new Map(
|
|
10567
|
+
region.tiles.map((tile) => [
|
|
10568
|
+
`${tile[0]}/${tile[1]}`,
|
|
10569
|
+
{ hash: tile[2], ...tile[3] ? { landmarks: tile[3] } : {} }
|
|
10570
|
+
])
|
|
10571
|
+
);
|
|
9706
10572
|
regions.set(regionKey, { tiles, used: performance.now() });
|
|
9707
10573
|
arrived();
|
|
9708
10574
|
}).catch(() => {
|
|
9709
10575
|
regionLoading.delete(regionKey);
|
|
9710
10576
|
regionFailed.set(regionKey, performance.now() + retryAfter);
|
|
10577
|
+
recheck();
|
|
9711
10578
|
});
|
|
9712
10579
|
}
|
|
9713
10580
|
function loadTile(hash4) {
|
|
@@ -9715,22 +10582,38 @@ function createTileflowPlacedModels(options) {
|
|
|
9715
10582
|
const url = new URL(source.tile.replaceAll("{sha256}", hash4), source.url).href;
|
|
9716
10583
|
const controller = new AbortController();
|
|
9717
10584
|
tileLoading.set(hash4, controller);
|
|
9718
|
-
download(url, controller.signal).then(async (response) => decodeTileflowModels(await response.arrayBuffer())).then((models) => {
|
|
9719
|
-
tileLoading.delete(hash4);
|
|
10585
|
+
download(url, controller.signal).then(async (response) => decodeTileflowModels(await response.arrayBuffer())).then(async (models) => {
|
|
9720
10586
|
if (disposed || tileset !== source) return;
|
|
9721
10587
|
if (!models) throw new Error("not a Tileflow landmarks tile");
|
|
9722
10588
|
const now = performance.now();
|
|
9723
10589
|
const entries = [];
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
10590
|
+
try {
|
|
10591
|
+
for (const model of models) {
|
|
10592
|
+
if (!model.at) continue;
|
|
10593
|
+
const images = await readModelImages(model, controller.signal);
|
|
10594
|
+
const placement3 = { model: model.id, url, at: model.at, heading: model.heading ?? 0 };
|
|
10595
|
+
entries.push({
|
|
10596
|
+
placement: placement3,
|
|
10597
|
+
key: `landmark:${model.id}`,
|
|
10598
|
+
model,
|
|
10599
|
+
images,
|
|
10600
|
+
landmark: true,
|
|
10601
|
+
used: now
|
|
10602
|
+
});
|
|
10603
|
+
}
|
|
10604
|
+
controller.signal.throwIfAborted();
|
|
10605
|
+
if (disposed || tileset !== source) throw new Error("The landmark tile was replaced.");
|
|
10606
|
+
} catch (error) {
|
|
10607
|
+
for (const entry of entries) closeModelImages(entry.images);
|
|
10608
|
+
throw error;
|
|
9728
10609
|
}
|
|
10610
|
+
tileLoading.delete(hash4);
|
|
9729
10611
|
landmarkTiles.set(hash4, { entries, used: now });
|
|
9730
10612
|
arrived();
|
|
9731
10613
|
}).catch(() => {
|
|
9732
10614
|
tileLoading.delete(hash4);
|
|
9733
10615
|
tileFailed.set(hash4, performance.now() + retryAfter);
|
|
10616
|
+
arrived();
|
|
9734
10617
|
});
|
|
9735
10618
|
}
|
|
9736
10619
|
function landmarkTilesNear(tiles, zoom) {
|
|
@@ -9766,10 +10649,81 @@ function createTileflowPlacedModels(options) {
|
|
|
9766
10649
|
}
|
|
9767
10650
|
return out;
|
|
9768
10651
|
}
|
|
10652
|
+
function holdingNow(near, own, now, shown) {
|
|
10653
|
+
const out = [];
|
|
10654
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10655
|
+
for (const landmarks of near.values())
|
|
10656
|
+
for (const landmark of landmarks) {
|
|
10657
|
+
const key = `landmark:${landmark.id}`;
|
|
10658
|
+
if (seen.has(key) || ownIds.has(landmark.id)) continue;
|
|
10659
|
+
seen.add(key);
|
|
10660
|
+
const since = holding.get(key) ?? (shown ? now : void 0);
|
|
10661
|
+
if (since !== void 0) holding.set(key, since);
|
|
10662
|
+
if (since !== void 0 && now - since >= holdFor) continue;
|
|
10663
|
+
const entry = {
|
|
10664
|
+
key,
|
|
10665
|
+
placement: { at: landmark.at, heading: landmark.heading },
|
|
10666
|
+
footprint: landmark.footprint,
|
|
10667
|
+
bounds: boundsOf(landmark.footprint)
|
|
10668
|
+
};
|
|
10669
|
+
if (own.some((model) => footprintsMeet(entry, model))) continue;
|
|
10670
|
+
out.push(entry);
|
|
10671
|
+
}
|
|
10672
|
+
for (const key of [...holding.keys()]) if (!seen.has(key)) holding.delete(key);
|
|
10673
|
+
return out;
|
|
10674
|
+
}
|
|
10675
|
+
function scheduleHoldEnd(now) {
|
|
10676
|
+
if (holdTimer !== void 0) clearTimeout(holdTimer);
|
|
10677
|
+
holdTimer = void 0;
|
|
10678
|
+
let earliest = Infinity;
|
|
10679
|
+
for (const entry of held) {
|
|
10680
|
+
const since = holding.get(entry.key);
|
|
10681
|
+
if (since !== void 0) earliest = Math.min(earliest, since + holdFor);
|
|
10682
|
+
}
|
|
10683
|
+
if (earliest === Infinity || disposed) return;
|
|
10684
|
+
holdTimer = setTimeout(
|
|
10685
|
+
() => {
|
|
10686
|
+
holdTimer = void 0;
|
|
10687
|
+
dirty = true;
|
|
10688
|
+
options.changed([], lastZoom);
|
|
10689
|
+
},
|
|
10690
|
+
Math.max(0, earliest - now) + 1
|
|
10691
|
+
);
|
|
10692
|
+
}
|
|
10693
|
+
function settledAround(tile) {
|
|
10694
|
+
if (disposed) return true;
|
|
10695
|
+
const now = performance.now();
|
|
10696
|
+
for (const placement3 of lastPlacements ?? []) {
|
|
10697
|
+
const [px, py] = cameraTileAt(placement3.at[0], placement3.at[1], tile.z);
|
|
10698
|
+
if (Math.abs(Math.floor(px) - tile.x) > reachTiles) continue;
|
|
10699
|
+
if (Math.abs(Math.floor(py) - tile.y) > reachTiles) continue;
|
|
10700
|
+
const key = keyOf(placement3);
|
|
10701
|
+
if (!loaded.has(key) && (failed.get(key) ?? 0) <= now) return false;
|
|
10702
|
+
}
|
|
10703
|
+
const url = lastLandmarks;
|
|
10704
|
+
if (!url) return true;
|
|
10705
|
+
if (!tileset || tileset.url !== url)
|
|
10706
|
+
return tilesetFailed?.url === url && tilesetFailed.until > now;
|
|
10707
|
+
const scale = 2 ** (tileset.tileZoom - tile.z);
|
|
10708
|
+
const left = Math.floor(tile.x * scale) - 1;
|
|
10709
|
+
const right = Math.ceil((tile.x + 1) * scale);
|
|
10710
|
+
const top = Math.floor(tile.y * scale) - 1;
|
|
10711
|
+
const bottom = Math.ceil((tile.y + 1) * scale);
|
|
10712
|
+
for (const x3 of [left, right])
|
|
10713
|
+
for (const y2 of [top, bottom]) {
|
|
10714
|
+
const [rx, ry] = tileflowLandmarkRegionOf([x3, y2]);
|
|
10715
|
+
const regionKey = `${rx}/${ry}`;
|
|
10716
|
+
if (!tileset.regions.has(regionKey) || regions.has(regionKey)) continue;
|
|
10717
|
+
if ((regionFailed.get(regionKey) ?? 0) > now) continue;
|
|
10718
|
+
return false;
|
|
10719
|
+
}
|
|
10720
|
+
return true;
|
|
10721
|
+
}
|
|
9769
10722
|
return {
|
|
9770
|
-
update(placements, landmarks, tiles, zoom) {
|
|
10723
|
+
update(placements, landmarks, tiles, zoom, shown = true) {
|
|
9771
10724
|
if (disposed) return;
|
|
9772
10725
|
lastZoom = zoom;
|
|
10726
|
+
lastLandmarks = landmarks;
|
|
9773
10727
|
const near = /* @__PURE__ */ new Set();
|
|
9774
10728
|
const now = performance.now();
|
|
9775
10729
|
if (placements !== lastPlacements) {
|
|
@@ -9786,6 +10740,7 @@ function createTileflowPlacedModels(options) {
|
|
|
9786
10740
|
bottom = Math.max(bottom, tile.y);
|
|
9787
10741
|
}
|
|
9788
10742
|
for (const placement3 of placements) {
|
|
10743
|
+
if (readTileflowTeamModelName(placement3.url)) continue;
|
|
9789
10744
|
const [x3, y2] = cameraTileAt(placement3.at[0], placement3.at[1], zoom);
|
|
9790
10745
|
if (x3 < left - reachTiles || x3 >= right + 1 + reachTiles || y2 < top - reachTiles || y2 >= bottom + 1 + reachTiles)
|
|
9791
10746
|
continue;
|
|
@@ -9832,6 +10787,7 @@ function createTileflowPlacedModels(options) {
|
|
|
9832
10787
|
loadTileset(landmarks);
|
|
9833
10788
|
const nearRegions = /* @__PURE__ */ new Set();
|
|
9834
10789
|
const nearHashes = /* @__PURE__ */ new Set();
|
|
10790
|
+
const onTheirWay = /* @__PURE__ */ new Map();
|
|
9835
10791
|
for (const [tileKey, [x3, y2]] of landmarkTilesNear(tiles, zoom)) {
|
|
9836
10792
|
const [rx, ry] = tileflowLandmarkRegionOf([x3, y2]);
|
|
9837
10793
|
const regionKey = `${rx}/${ry}`;
|
|
@@ -9844,12 +10800,17 @@ function createTileflowPlacedModels(options) {
|
|
|
9844
10800
|
continue;
|
|
9845
10801
|
}
|
|
9846
10802
|
region.used = now;
|
|
9847
|
-
const
|
|
9848
|
-
if (!
|
|
10803
|
+
const named = region.tiles.get(tileKey);
|
|
10804
|
+
if (!named) continue;
|
|
10805
|
+
const { hash: hash4 } = named;
|
|
9849
10806
|
nearHashes.add(hash4);
|
|
9850
10807
|
const tile = landmarkTiles.get(hash4);
|
|
9851
10808
|
if (tile) tile.used = now;
|
|
9852
|
-
else
|
|
10809
|
+
else {
|
|
10810
|
+
const failing = (tileFailed.get(hash4) ?? 0) > now;
|
|
10811
|
+
if (!tileLoading.has(hash4) && !failing) loadTile(hash4);
|
|
10812
|
+
if (named.landmarks && !failing) onTheirWay.set(hash4, named.landmarks);
|
|
10813
|
+
}
|
|
9853
10814
|
}
|
|
9854
10815
|
if (nearHashes.size !== wantedTiles.size || [...nearHashes].some((hash4) => !wantedTiles.has(hash4))) {
|
|
9855
10816
|
wantedTiles = nearHashes;
|
|
@@ -9877,25 +10838,71 @@ function createTileflowPlacedModels(options) {
|
|
|
9877
10838
|
landmarkTiles.delete(hash4);
|
|
9878
10839
|
released.push(...tile.entries);
|
|
9879
10840
|
}
|
|
9880
|
-
|
|
10841
|
+
recheck();
|
|
10842
|
+
const next = dirty ? standing() : active;
|
|
10843
|
+
const holdNext = holdingNow(
|
|
10844
|
+
onTheirWay,
|
|
10845
|
+
next.filter((entry) => !entry.landmark),
|
|
10846
|
+
now,
|
|
10847
|
+
shown
|
|
10848
|
+
);
|
|
10849
|
+
const sameHold = holdNext.length === held.length && holdNext.every((entry, at) => entry.key === held[at].key);
|
|
10850
|
+
if (!dirty && sameHold) return;
|
|
9881
10851
|
dirty = false;
|
|
9882
|
-
const next = standing();
|
|
9883
10852
|
const before = new Map(active.map((entry) => [entry, true]));
|
|
9884
10853
|
const changed = [];
|
|
9885
|
-
|
|
9886
|
-
for (const entry of
|
|
10854
|
+
const reach = (entry) => tilesOf({ placement: entry.placement, bounds: entry.model.bounds }, zoom);
|
|
10855
|
+
for (const entry of next)
|
|
10856
|
+
if (!before.delete(entry)) {
|
|
10857
|
+
entry.standingSince = now;
|
|
10858
|
+
changed.push(...reach(entry));
|
|
10859
|
+
}
|
|
10860
|
+
for (const entry of before.keys()) {
|
|
10861
|
+
entry.standingSince = void 0;
|
|
10862
|
+
changed.push(...reach(entry));
|
|
10863
|
+
}
|
|
10864
|
+
const heldBefore = new Map(held.map((entry) => [entry.key, entry]));
|
|
10865
|
+
for (const entry of holdNext)
|
|
10866
|
+
if (!heldBefore.delete(entry.key)) changed.push(...tilesOf(entry, zoom));
|
|
10867
|
+
for (const entry of heldBefore.values()) changed.push(...tilesOf(entry, zoom));
|
|
9887
10868
|
active = next;
|
|
10869
|
+
held = holdNext;
|
|
10870
|
+
scheduleHoldEnd(now);
|
|
9888
10871
|
if (changed.length > 0) {
|
|
9889
10872
|
version2 += 1;
|
|
9890
10873
|
options.changed(changed, zoom);
|
|
9891
10874
|
}
|
|
9892
10875
|
},
|
|
10876
|
+
settle(tile, signal) {
|
|
10877
|
+
if (settledAround(tile)) return Promise.resolve();
|
|
10878
|
+
return new Promise((resolve) => {
|
|
10879
|
+
const waiter = {
|
|
10880
|
+
settled: () => signal.aborted || settledAround(tile),
|
|
10881
|
+
resolve: () => {
|
|
10882
|
+
clearTimeout(timer);
|
|
10883
|
+
signal.removeEventListener("abort", done);
|
|
10884
|
+
resolve();
|
|
10885
|
+
}
|
|
10886
|
+
};
|
|
10887
|
+
const done = () => {
|
|
10888
|
+
waiting.delete(waiter);
|
|
10889
|
+
waiter.resolve();
|
|
10890
|
+
};
|
|
10891
|
+
const timer = setTimeout(done, settleFor);
|
|
10892
|
+
signal.addEventListener("abort", done, { once: true });
|
|
10893
|
+
waiting.add(waiter);
|
|
10894
|
+
});
|
|
10895
|
+
},
|
|
9893
10896
|
masksIn(tile, extent5) {
|
|
9894
10897
|
let masks;
|
|
9895
|
-
|
|
9896
|
-
|
|
10898
|
+
const footprints = [
|
|
10899
|
+
...active.map((entry) => ({ placement: entry.placement, footprint: entry.model.footprint })),
|
|
10900
|
+
...held
|
|
10901
|
+
];
|
|
10902
|
+
for (const entry of footprints) {
|
|
10903
|
+
if (entry.footprint.length === 0) continue;
|
|
9897
10904
|
const rings = tileflowModelFootprintInTile(
|
|
9898
|
-
entry.
|
|
10905
|
+
entry.footprint,
|
|
9899
10906
|
entry.placement,
|
|
9900
10907
|
{ x: tile.x, y: tile.y, zoom: tile.z },
|
|
9901
10908
|
extent5
|
|
@@ -9916,10 +10923,13 @@ function createTileflowPlacedModels(options) {
|
|
|
9916
10923
|
},
|
|
9917
10924
|
shown: () => active,
|
|
9918
10925
|
upload(context) {
|
|
9919
|
-
for (const entry of released.splice(0))
|
|
10926
|
+
for (const entry of released.splice(0)) {
|
|
10927
|
+
releaseBuffers(context, entry);
|
|
10928
|
+
closeModelImages(entry.images);
|
|
10929
|
+
}
|
|
9920
10930
|
for (const entry of active) {
|
|
9921
10931
|
if (entry.gl) continue;
|
|
9922
|
-
entry.gl = entry.model.levels.map((level) => {
|
|
10932
|
+
entry.gl = entry.model.levels.map((level, levelIndex) => {
|
|
9923
10933
|
const vao = context.createVertexArray();
|
|
9924
10934
|
context.bindVertexArray(vao);
|
|
9925
10935
|
const vbo = context.createBuffer();
|
|
@@ -9942,11 +10952,13 @@ function createTileflowPlacedModels(options) {
|
|
|
9942
10952
|
const ibo = context.createBuffer();
|
|
9943
10953
|
context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, ibo);
|
|
9944
10954
|
context.bufferData(context.ELEMENT_ARRAY_BUFFER, level.indices, context.STATIC_DRAW);
|
|
10955
|
+
const surface = uploadModelSurface(context, level, entry.images?.[levelIndex] ?? []);
|
|
9945
10956
|
context.bindVertexArray(null);
|
|
9946
10957
|
return {
|
|
9947
10958
|
vao,
|
|
9948
10959
|
vbo,
|
|
9949
10960
|
ibo,
|
|
10961
|
+
...surface ? { surface } : {},
|
|
9950
10962
|
type: level.indices instanceof Uint32Array ? context.UNSIGNED_INT : context.UNSIGNED_SHORT
|
|
9951
10963
|
};
|
|
9952
10964
|
});
|
|
@@ -9963,16 +10975,25 @@ function createTileflowPlacedModels(options) {
|
|
|
9963
10975
|
},
|
|
9964
10976
|
key: () => String(version2),
|
|
9965
10977
|
release(context) {
|
|
9966
|
-
const
|
|
10978
|
+
const retired = released.splice(0);
|
|
10979
|
+
const all = [...loaded.values(), ...retired];
|
|
9967
10980
|
for (const tile of landmarkTiles.values()) all.push(...tile.entries);
|
|
9968
10981
|
for (const entry of all) releaseBuffers(context, entry);
|
|
10982
|
+
for (const entry of retired) closeModelImages(entry.images);
|
|
9969
10983
|
},
|
|
9970
10984
|
dispose() {
|
|
9971
10985
|
disposed = true;
|
|
10986
|
+
if (holdTimer !== void 0) clearTimeout(holdTimer);
|
|
10987
|
+
for (const waiter of waiting) waiter.resolve();
|
|
10988
|
+
waiting.clear();
|
|
10989
|
+
held = [];
|
|
9972
10990
|
for (const controller of loading.values()) controller.abort();
|
|
9973
10991
|
for (const controller of [...tileLoading.values(), ...regionLoading.values()])
|
|
9974
10992
|
controller.abort();
|
|
9975
10993
|
tilesetLoad?.controller.abort();
|
|
10994
|
+
for (const entry of [...loaded.values(), ...released]) closeModelImages(entry.images);
|
|
10995
|
+
for (const tile of landmarkTiles.values())
|
|
10996
|
+
for (const entry of tile.entries) closeModelImages(entry.images);
|
|
9976
10997
|
loading.clear();
|
|
9977
10998
|
tileLoading.clear();
|
|
9978
10999
|
regionLoading.clear();
|
|
@@ -9984,27 +11005,64 @@ function createTileflowPlacedModels(options) {
|
|
|
9984
11005
|
};
|
|
9985
11006
|
}
|
|
9986
11007
|
function footprintsMeet(landmark, model) {
|
|
9987
|
-
|
|
11008
|
+
const footprint = "model" in landmark ? landmark.model.footprint : landmark.footprint;
|
|
11009
|
+
if (footprint.length === 0 || model.model.footprint.length === 0) return false;
|
|
9988
11010
|
const [x3, y2] = cameraTileAt(landmark.placement.at[0], landmark.placement.at[1], 14);
|
|
9989
11011
|
const tile = { x: Math.floor(x3), y: Math.floor(y2), zoom: 14 };
|
|
9990
|
-
const ours = tileflowModelFootprintInTile(
|
|
9991
|
-
landmark.model.footprint,
|
|
9992
|
-
landmark.placement,
|
|
9993
|
-
tile,
|
|
9994
|
-
8192
|
|
9995
|
-
);
|
|
11012
|
+
const ours = tileflowModelFootprintInTile(footprint, landmark.placement, tile, 8192);
|
|
9996
11013
|
const theirs = tileflowModelFootprintInTile(model.model.footprint, model.placement, tile, 8192);
|
|
9997
11014
|
return tileflowRingsOverlap(ours, theirs, 0);
|
|
9998
11015
|
}
|
|
11016
|
+
function boundsOf(footprint) {
|
|
11017
|
+
let [minX, minY, maxX, maxY] = [0, 0, 0, 0];
|
|
11018
|
+
for (const ring of footprint)
|
|
11019
|
+
for (let at = 0; at + 1 < ring.length; at += 2) {
|
|
11020
|
+
minX = Math.min(minX, ring[at]);
|
|
11021
|
+
maxX = Math.max(maxX, ring[at]);
|
|
11022
|
+
minY = Math.min(minY, ring[at + 1]);
|
|
11023
|
+
maxY = Math.max(maxY, ring[at + 1]);
|
|
11024
|
+
}
|
|
11025
|
+
return [minX, minY, 0, maxX, maxY, 0];
|
|
11026
|
+
}
|
|
9999
11027
|
function releaseBuffers(context, entry) {
|
|
10000
11028
|
for (const level of entry.gl ?? []) {
|
|
10001
11029
|
context.deleteVertexArray(level.vao);
|
|
10002
11030
|
context.deleteBuffer(level.vbo);
|
|
10003
11031
|
context.deleteBuffer(level.ibo);
|
|
11032
|
+
if (level.surface) {
|
|
11033
|
+
context.deleteBuffer(level.surface.vbo);
|
|
11034
|
+
for (const texture of level.surface.textures) context.deleteTexture(texture);
|
|
11035
|
+
}
|
|
10004
11036
|
}
|
|
10005
11037
|
entry.gl = void 0;
|
|
10006
11038
|
}
|
|
10007
11039
|
|
|
11040
|
+
// src/models-3d-role-lighting.ts
|
|
11041
|
+
init_theme_blend();
|
|
11042
|
+
function sampleModelRole(profile, identity) {
|
|
11043
|
+
let seed = 2166136261;
|
|
11044
|
+
for (let i2 = 0; i2 < identity.length; i2++)
|
|
11045
|
+
seed = Math.imul(seed ^ identity.charCodeAt(i2), 16777619);
|
|
11046
|
+
const random = (channel) => {
|
|
11047
|
+
let value = Math.imul(seed ^ channel, 73244475);
|
|
11048
|
+
value = Math.imul(value ^ value >>> 16, 73244475);
|
|
11049
|
+
return ((value ^ value >>> 16) >>> 0) / 4294967296;
|
|
11050
|
+
};
|
|
11051
|
+
const variation = profile?.colorVariation;
|
|
11052
|
+
let color = profile?.color ? tileflowColourChannels(profile.color)?.slice(0, 3) : void 0;
|
|
11053
|
+
if (variation) {
|
|
11054
|
+
const hsl = [variation.hue, variation.saturation, variation.lightness].map(
|
|
11055
|
+
([low, high], channel) => low + (high - low) * random(channel + 1)
|
|
11056
|
+
);
|
|
11057
|
+
color = tileflowColourChannels(`hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`).slice(0, 3);
|
|
11058
|
+
if (variation.space === "linear") color = color.map((c) => c ** (1 / 2.2));
|
|
11059
|
+
}
|
|
11060
|
+
return {
|
|
11061
|
+
strength: (profile?.emissiveStrength ?? 0) + (profile?.emissiveVariation ?? 0) * random(0),
|
|
11062
|
+
color
|
|
11063
|
+
};
|
|
11064
|
+
}
|
|
11065
|
+
|
|
10008
11066
|
// src/scene-3d-browser.ts
|
|
10009
11067
|
var tileflowSceneExtent = 8192;
|
|
10010
11068
|
var tileflowSceneShadowFloor = 15;
|
|
@@ -10390,6 +11448,7 @@ var defaultTransition = 0;
|
|
|
10390
11448
|
var flood = { reach: 10, colour: [1, 0.7, 0.4] };
|
|
10391
11449
|
var edgeLine = { width: 1, day: 0.5 };
|
|
10392
11450
|
var paletteWidth = 256;
|
|
11451
|
+
var modelFade = 500;
|
|
10393
11452
|
var streetLightsLayerId = "tileflow-street-lights";
|
|
10394
11453
|
var derivedParts = {
|
|
10395
11454
|
surface: (rgb) => [...rgb],
|
|
@@ -10419,6 +11478,7 @@ function createTileflowBuildings3D(options) {
|
|
|
10419
11478
|
let linking;
|
|
10420
11479
|
let ground;
|
|
10421
11480
|
const contactMask = createBuildingContactMask();
|
|
11481
|
+
const floodMask = createBuildingContactMask();
|
|
10422
11482
|
let disposed = false;
|
|
10423
11483
|
const scene = tileflowScene3D(map);
|
|
10424
11484
|
let generation = 0;
|
|
@@ -10457,6 +11517,8 @@ function createTileflowBuildings3D(options) {
|
|
|
10457
11517
|
context.UNSIGNED_BYTE,
|
|
10458
11518
|
new Uint8Array([216, 208, 192, 255])
|
|
10459
11519
|
);
|
|
11520
|
+
context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MIN_FILTER, context.NEAREST);
|
|
11521
|
+
context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MAG_FILTER, context.NEAREST);
|
|
10460
11522
|
},
|
|
10461
11523
|
onRemove(_2, context) {
|
|
10462
11524
|
if (!isWebGL2(context)) return;
|
|
@@ -10473,6 +11535,7 @@ function createTileflowBuildings3D(options) {
|
|
|
10473
11535
|
context.deleteBuffer(ground.vbo);
|
|
10474
11536
|
}
|
|
10475
11537
|
contactMask.release(context);
|
|
11538
|
+
floodMask.release(context);
|
|
10476
11539
|
if (blank) context.deleteTexture(blank);
|
|
10477
11540
|
blank = void 0;
|
|
10478
11541
|
programs = {};
|
|
@@ -10485,7 +11548,13 @@ function createTileflowBuildings3D(options) {
|
|
|
10485
11548
|
if (!isWebGL2(context)) return;
|
|
10486
11549
|
ensureCamera();
|
|
10487
11550
|
if (target > 0 || scale > 0)
|
|
10488
|
-
models.update(
|
|
11551
|
+
models.update(
|
|
11552
|
+
design().models ?? [],
|
|
11553
|
+
design().landmarks?.url,
|
|
11554
|
+
camera.coverage(),
|
|
11555
|
+
camera.zoom,
|
|
11556
|
+
visibleScale() > 0
|
|
11557
|
+
);
|
|
10489
11558
|
if (!programs.depth) return;
|
|
10490
11559
|
if (!design().shadows || visibleScale() <= 0) {
|
|
10491
11560
|
if (registered) unregister();
|
|
@@ -10511,6 +11580,7 @@ function createTileflowBuildings3D(options) {
|
|
|
10511
11580
|
if (target > 0 || scale > 0) upload(context);
|
|
10512
11581
|
if (visibleScale() <= 0) {
|
|
10513
11582
|
contactMask.release(context);
|
|
11583
|
+
floodMask.release(context);
|
|
10514
11584
|
return;
|
|
10515
11585
|
}
|
|
10516
11586
|
draw(context, args);
|
|
@@ -10529,6 +11599,8 @@ function createTileflowBuildings3D(options) {
|
|
|
10529
11599
|
const decoded = tile.layers[layers.building];
|
|
10530
11600
|
if (!decoded) return emptyTile;
|
|
10531
11601
|
const places = layers.places ? tile.layers[layers.places] : void 0;
|
|
11602
|
+
await models.settle(tile, signal);
|
|
11603
|
+
signal.throwIfAborted();
|
|
10532
11604
|
const masks = models.masksIn(tile, tileflowBuildingExtent);
|
|
10533
11605
|
const mesh = await mesher.build(
|
|
10534
11606
|
decoded,
|
|
@@ -10868,13 +11940,18 @@ function createTileflowBuildings3D(options) {
|
|
|
10868
11940
|
modelDepth.uniforms.u_offset,
|
|
10869
11941
|
...frame.offsetOf({ x: stand.tileX, y: stand.tileY }, camera.zoom)
|
|
10870
11942
|
);
|
|
10871
|
-
drawModel(context, modelDepth, model, models.levelOf(model, centre));
|
|
11943
|
+
drawModel(context, modelDepth, model, models.levelOf(model, centre), "depth");
|
|
10872
11944
|
}
|
|
10873
11945
|
}
|
|
10874
11946
|
};
|
|
10875
11947
|
function modelsDrawn() {
|
|
10876
11948
|
return models.shown().filter((model) => model.gl);
|
|
10877
11949
|
}
|
|
11950
|
+
function appearanceOf(model, now) {
|
|
11951
|
+
if (model.standingSince === void 0) return 1;
|
|
11952
|
+
const reduced = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
11953
|
+
return reduced ? 1 : Math.min(1, Math.max(0, (now - model.standingSince) / modelFade));
|
|
11954
|
+
}
|
|
10878
11955
|
function standOf(model) {
|
|
10879
11956
|
const [longitude, latitude] = model.placement.at;
|
|
10880
11957
|
const [x3, y2] = cameraTileAt(longitude, latitude, camera.zoom);
|
|
@@ -10899,13 +11976,55 @@ function createTileflowBuildings3D(options) {
|
|
|
10899
11976
|
context.uniform2f(u.u_anchor, stand.anchor[0], stand.anchor[1]);
|
|
10900
11977
|
context.uniform1f(u.u_model_metres, stand.metres);
|
|
10901
11978
|
}
|
|
10902
|
-
function drawModel(context, program, model, level) {
|
|
11979
|
+
function drawModel(context, program, model, level, pass = "solid") {
|
|
10903
11980
|
const buffers = model.gl[level];
|
|
10904
11981
|
const mesh = model.model.levels[level];
|
|
10905
11982
|
context.uniform3f(program.uniforms.u_level_centre, ...mesh.centre);
|
|
10906
11983
|
context.uniform1f(program.uniforms.u_level_step, mesh.step);
|
|
11984
|
+
context.uniform2f(
|
|
11985
|
+
program.uniforms.u_model_height ?? null,
|
|
11986
|
+
model.model.bounds[2],
|
|
11987
|
+
model.model.bounds[5] - model.model.bounds[2]
|
|
11988
|
+
);
|
|
10907
11989
|
context.bindVertexArray(buffers.vao);
|
|
10908
|
-
|
|
11990
|
+
if (!mesh.surface) {
|
|
11991
|
+
if (pass === "blend") return;
|
|
11992
|
+
bindModelMaterial(context, program.uniforms, void 0, void 0, blank);
|
|
11993
|
+
context.drawElements(context.TRIANGLES, mesh.indices.length, buffers.type, 0);
|
|
11994
|
+
return;
|
|
11995
|
+
}
|
|
11996
|
+
const ranges = mesh.surface.ranges.map((range, index) => ({ range, index })).filter(({ range }) => {
|
|
11997
|
+
const transparent2 = mesh.surface.materials[range.material].alphaMode === "BLEND";
|
|
11998
|
+
return pass === "depth" || transparent2 === (pass === "blend");
|
|
11999
|
+
});
|
|
12000
|
+
if (pass === "blend" && ranges.length > 1) {
|
|
12001
|
+
const camera2 = modelCamera(model), turn = model.placement.heading * Math.PI / 180;
|
|
12002
|
+
const distance = (index) => {
|
|
12003
|
+
const c = buffers.surface.centres.subarray(index * 3, index * 3 + 3);
|
|
12004
|
+
const x3 = c[0] * Math.cos(turn) + c[1] * Math.sin(turn);
|
|
12005
|
+
const y2 = -c[0] * Math.sin(turn) + c[1] * Math.cos(turn);
|
|
12006
|
+
return (x3 - camera2[0]) ** 2 + (y2 - camera2[1]) ** 2 + (c[2] - camera2[2]) ** 2;
|
|
12007
|
+
};
|
|
12008
|
+
ranges.sort((a, b2) => distance(b2.index) - distance(a.index));
|
|
12009
|
+
}
|
|
12010
|
+
for (const { range } of ranges) {
|
|
12011
|
+
const [low, high] = range.emissiveBounds ?? [model.model.bounds[2], model.model.bounds[5]];
|
|
12012
|
+
context.uniform2f(program.uniforms.u_model_height ?? null, low, high - low);
|
|
12013
|
+
bindModelMaterial(
|
|
12014
|
+
context,
|
|
12015
|
+
program.uniforms,
|
|
12016
|
+
mesh.surface.materials[range.material],
|
|
12017
|
+
buffers.surface,
|
|
12018
|
+
blank
|
|
12019
|
+
);
|
|
12020
|
+
context.drawElements(
|
|
12021
|
+
context.TRIANGLES,
|
|
12022
|
+
range.count,
|
|
12023
|
+
buffers.type,
|
|
12024
|
+
range.from * mesh.indices.BYTES_PER_ELEMENT
|
|
12025
|
+
);
|
|
12026
|
+
}
|
|
12027
|
+
context.disable(context.CULL_FACE);
|
|
10909
12028
|
}
|
|
10910
12029
|
function partColoursNow() {
|
|
10911
12030
|
const { themes, lower, upper, mix: mix2 } = designsOnScreen();
|
|
@@ -10920,6 +12039,94 @@ function createTileflowBuildings3D(options) {
|
|
|
10920
12039
|
}
|
|
10921
12040
|
return out;
|
|
10922
12041
|
}
|
|
12042
|
+
function partMixNow() {
|
|
12043
|
+
const { themes, lower, upper, mix: mix2 } = designsOnScreen();
|
|
12044
|
+
return Float32Array.from(
|
|
12045
|
+
Object.keys(tileflowModelParts).map(
|
|
12046
|
+
(part) => (themes[lower].modelColors?.[part] ? 1 - mix2 : 0) + (themes[upper].modelColors?.[part] ? mix2 : 0)
|
|
12047
|
+
)
|
|
12048
|
+
);
|
|
12049
|
+
}
|
|
12050
|
+
function modelCamera(model) {
|
|
12051
|
+
const zoom = map.getZoom();
|
|
12052
|
+
const [cx, cy] = cameraTileAt(map.getCenter().lng, map.getCenter().lat, zoom);
|
|
12053
|
+
const [mx, my] = cameraTileAt(...model.placement.at, zoom);
|
|
12054
|
+
const metres = 40075016686e-3 * Math.cos(model.placement.at[1] * Math.PI / 180) / 2 ** zoom;
|
|
12055
|
+
const distance = (map.getCanvas?.().clientHeight ?? 720) * metres / 512 / (2 * Math.tan((map.getVerticalFieldOfView?.() ?? 36.87) * Math.PI / 360));
|
|
12056
|
+
const pitch = map.getPitch() * Math.PI / 180, bearing = map.getBearing() * Math.PI / 180;
|
|
12057
|
+
return [
|
|
12058
|
+
(cx - mx) * metres - distance * Math.sin(pitch) * Math.sin(bearing),
|
|
12059
|
+
(my - cy) * metres - distance * Math.sin(pitch) * Math.cos(bearing),
|
|
12060
|
+
distance * Math.cos(pitch)
|
|
12061
|
+
];
|
|
12062
|
+
}
|
|
12063
|
+
const modelLightingCache = /* @__PURE__ */ new Map();
|
|
12064
|
+
let modelLightingThemes;
|
|
12065
|
+
let modelLightingPosition = "";
|
|
12066
|
+
function modelLightingNow(identity) {
|
|
12067
|
+
const { themes, lower, upper, mix: mix2 } = designsOnScreen();
|
|
12068
|
+
const position = `${lower}:${upper}:${mix2}`;
|
|
12069
|
+
if (themes !== modelLightingThemes || position !== modelLightingPosition) {
|
|
12070
|
+
modelLightingCache.clear();
|
|
12071
|
+
modelLightingThemes = themes;
|
|
12072
|
+
modelLightingPosition = position;
|
|
12073
|
+
}
|
|
12074
|
+
const cached = modelLightingCache.get(identity);
|
|
12075
|
+
if (cached) return cached;
|
|
12076
|
+
const strengths = new Float32Array(7);
|
|
12077
|
+
const colours = new Float32Array(28);
|
|
12078
|
+
const gradients = new Float32Array(28);
|
|
12079
|
+
const powers = new Float32Array(7);
|
|
12080
|
+
const materialColours = new Float32Array(28);
|
|
12081
|
+
const materials = new Float32Array(28);
|
|
12082
|
+
for (const [role, id] of Object.entries(tileflowModelLightingRoles)) {
|
|
12083
|
+
const from = themes[lower].modelLighting?.[role], to = themes[upper].modelLighting?.[role];
|
|
12084
|
+
const sampledFrom = sampleModelRole(from, `${identity}:${role}`);
|
|
12085
|
+
const sampledTo = sampleModelRole(to, `${identity}:${role}`);
|
|
12086
|
+
strengths[id] = sampledFrom.strength * (1 - mix2) + sampledTo.strength * mix2;
|
|
12087
|
+
const ca = sampledFrom.color, cb = sampledTo.color;
|
|
12088
|
+
for (let channel = 0; channel < 3; channel++)
|
|
12089
|
+
materialColours[id * 4 + channel] = (ca?.[channel] ?? cb?.[channel] ?? 0) * (1 - mix2) + (cb?.[channel] ?? ca?.[channel] ?? 0) * mix2;
|
|
12090
|
+
materialColours[id * 4 + 3] = (ca ? (from?.colorMix ?? 1) * (1 - mix2) : 0) + (cb ? (to?.colorMix ?? 1) * mix2 : 0);
|
|
12091
|
+
for (const [channel, key] of ["metallic", "roughness"].entries()) {
|
|
12092
|
+
materials[id * 4 + channel] = (from?.[key] ?? to?.[key] ?? 0) * (1 - mix2) + (to?.[key] ?? from?.[key] ?? 0) * mix2;
|
|
12093
|
+
materials[id * 4 + 2 + channel] = (from?.[key] === void 0 ? 0 : 1 - mix2) + (to?.[key] === void 0 ? 0 : mix2);
|
|
12094
|
+
}
|
|
12095
|
+
const a = rgbOf2(from?.emissiveColor), b2 = rgbOf2(to?.emissiveColor);
|
|
12096
|
+
for (let channel = 0; channel < 3; channel += 1)
|
|
12097
|
+
colours[id * 4 + channel] = (a?.[channel] ?? b2?.[channel] ?? 0) * (1 - mix2) + (b2?.[channel] ?? a?.[channel] ?? 0) * mix2;
|
|
12098
|
+
colours[id * 4 + 3] = (a ? 1 - mix2 : 0) + (b2 ? mix2 : 0);
|
|
12099
|
+
const first = from?.emissiveHeight ?? defaultModelEmissionGradient;
|
|
12100
|
+
const second = to?.emissiveHeight ?? defaultModelEmissionGradient;
|
|
12101
|
+
for (let component = 0; component < 4; component += 1)
|
|
12102
|
+
gradients[id * 4 + component] = first[component] * (1 - mix2) + second[component] * mix2;
|
|
12103
|
+
powers[id] = first[4] * (1 - mix2) + second[4] * mix2;
|
|
12104
|
+
}
|
|
12105
|
+
const result = { strengths, colours, gradients, powers, materialColours, materials };
|
|
12106
|
+
if (modelLightingCache.size >= 1024)
|
|
12107
|
+
modelLightingCache.delete(modelLightingCache.keys().next().value);
|
|
12108
|
+
modelLightingCache.set(identity, result);
|
|
12109
|
+
return result;
|
|
12110
|
+
}
|
|
12111
|
+
function floodLightNow() {
|
|
12112
|
+
const { themes, lower, upper, mix: mix2 } = designsOnScreen();
|
|
12113
|
+
const first = themes[lower].floodLight ?? defaultBuildingFloodLight;
|
|
12114
|
+
const second = themes[upper].floodLight ?? defaultBuildingFloodLight;
|
|
12115
|
+
const number = (a2, b3) => a2 * (1 - mix2) + b3 * mix2;
|
|
12116
|
+
const a = rgbOf2(first.color) ?? rgbOf2(defaultBuildingFloodLight.color);
|
|
12117
|
+
const b2 = rgbOf2(second.color) ?? rgbOf2(defaultBuildingFloodLight.color);
|
|
12118
|
+
return {
|
|
12119
|
+
intensity: number(first.intensity, second.intensity) * buildingFloodLightShare(sunNow(), number(first.brightnessScale, second.brightnessScale)),
|
|
12120
|
+
colour: a.map((value, i2) => number(value, b2[i2])),
|
|
12121
|
+
radius: [
|
|
12122
|
+
number(first.groundRadius[0], second.groundRadius[0]),
|
|
12123
|
+
number(first.groundRadius[1], second.groundRadius[1])
|
|
12124
|
+
],
|
|
12125
|
+
minGroundHeight: number(first.minGroundHeight, second.minGroundHeight),
|
|
12126
|
+
minWallHeight: number(first.minWallHeight, second.minWallHeight),
|
|
12127
|
+
wallHeightRatio: number(first.wallHeightRatio, second.wallHeightRatio)
|
|
12128
|
+
};
|
|
12129
|
+
}
|
|
10923
12130
|
function placed2(x3, y2) {
|
|
10924
12131
|
const frame = scene.frame();
|
|
10925
12132
|
return frame && frame.zoom === camera.zoom ? { offset: frame.offsetOf({ x: x3, y: y2 }, camera.zoom), metres: frame.metres } : { offset: camera.offsetOf({ x: x3, y: y2 }), metres: camera.metres };
|
|
@@ -10986,8 +12193,12 @@ function createTileflowBuildings3D(options) {
|
|
|
10986
12193
|
const shown = visibleScale();
|
|
10987
12194
|
const themes = colouredByData() ? designsOnScreen() : void 0;
|
|
10988
12195
|
const nightLight2 = mixed2((entry) => entry.nightLight);
|
|
12196
|
+
const architectural = floodLightNow();
|
|
10989
12197
|
const edges = mixed2((entry) => entry.edges);
|
|
10990
12198
|
const { occlusion: occlusionOn, windows: windowsOn } = design();
|
|
12199
|
+
const now = performance.now();
|
|
12200
|
+
const appearing = standing.filter((entry) => appearanceOf(entry, now) < 1);
|
|
12201
|
+
const steady = standing.filter((entry) => !appearing.includes(entry));
|
|
10991
12202
|
context.enable(context.DEPTH_TEST);
|
|
10992
12203
|
context.depthFunc(context.LEQUAL);
|
|
10993
12204
|
context.depthMask(true);
|
|
@@ -11010,6 +12221,13 @@ function createTileflowBuildings3D(options) {
|
|
|
11010
12221
|
context.uniform1f(u.u_windows, windowsOn ? 1 : 0);
|
|
11011
12222
|
context.uniform1f(u.u_flood, light.night * nightLight2);
|
|
11012
12223
|
context.uniform3fv(u.u_flood_colour, flood.colour);
|
|
12224
|
+
context.uniform1f(u.u_architectural_intensity, architectural.intensity);
|
|
12225
|
+
context.uniform3fv(u.u_architectural_colour, architectural.colour);
|
|
12226
|
+
context.uniform2f(
|
|
12227
|
+
u.u_architectural_wall,
|
|
12228
|
+
architectural.minWallHeight,
|
|
12229
|
+
architectural.wallHeightRatio
|
|
12230
|
+
);
|
|
11013
12231
|
context.uniform1f(
|
|
11014
12232
|
u.u_variation,
|
|
11015
12233
|
mixed2((entry) => entry.colorVariation)
|
|
@@ -11062,8 +12280,34 @@ function createTileflowBuildings3D(options) {
|
|
|
11062
12280
|
range.from * 4
|
|
11063
12281
|
);
|
|
11064
12282
|
}
|
|
12283
|
+
drawModels(steady);
|
|
12284
|
+
});
|
|
12285
|
+
for (const entry of appearing)
|
|
12286
|
+
drawBuildingOpacity(
|
|
12287
|
+
context,
|
|
12288
|
+
visibleOpacity() * appearanceOf(entry, now),
|
|
12289
|
+
() => drawModels([entry])
|
|
12290
|
+
);
|
|
12291
|
+
if (appearing.length > 0) map.triggerRepaint();
|
|
12292
|
+
context.enable(context.BLEND);
|
|
12293
|
+
context.blendFuncSeparate(
|
|
12294
|
+
context.SRC_ALPHA,
|
|
12295
|
+
context.ONE_MINUS_SRC_ALPHA,
|
|
12296
|
+
context.ZERO,
|
|
12297
|
+
context.ONE
|
|
12298
|
+
);
|
|
12299
|
+
context.depthMask(false);
|
|
12300
|
+
context.depthFunc(context.LEQUAL);
|
|
12301
|
+
drawModels(
|
|
12302
|
+
[...standing].sort((a, b2) => {
|
|
12303
|
+
const distance = (entry) => Math.hypot(...modelCamera(entry));
|
|
12304
|
+
return distance(b2) - distance(a);
|
|
12305
|
+
}),
|
|
12306
|
+
"blend"
|
|
12307
|
+
);
|
|
12308
|
+
function drawModels(list, pass = "solid") {
|
|
11065
12309
|
const model = programs.model;
|
|
11066
|
-
if (model &&
|
|
12310
|
+
if (model && list.length > 0) {
|
|
11067
12311
|
const m = model.uniforms;
|
|
11068
12312
|
context.useProgram(model.program);
|
|
11069
12313
|
context.disable(context.CULL_FACE);
|
|
@@ -11073,20 +12317,37 @@ function createTileflowBuildings3D(options) {
|
|
|
11073
12317
|
context.uniform3fv(m.u_sun_light, light.sunLight);
|
|
11074
12318
|
context.uniform3fv(m.u_sky_light, light.skyLight);
|
|
11075
12319
|
context.uniform1f(m.u_night, light.night);
|
|
12320
|
+
context.uniform1f(
|
|
12321
|
+
m.u_ambient_occlusion,
|
|
12322
|
+
occlusionOn ? mixed2((entry) => entry.modelOcclusion ?? 0.75) : 0
|
|
12323
|
+
);
|
|
11076
12324
|
context.uniform3fv(m.u_part_colours, partColoursNow());
|
|
12325
|
+
context.uniform1fv(m.u_part_mix, partMixNow());
|
|
11077
12326
|
scene.bindShadow(context, m, light.strength);
|
|
11078
12327
|
const centre = map.getCenter();
|
|
11079
|
-
for (const entry of
|
|
12328
|
+
for (const entry of list) {
|
|
12329
|
+
const emission = modelLightingNow(entry.model.id);
|
|
12330
|
+
context.uniform1fv(m.u_role_emission, emission.strengths);
|
|
12331
|
+
context.uniform4fv(m.u_role_emission_colour, emission.colours);
|
|
12332
|
+
context.uniform4fv(m.u_role_emission_gradient, emission.gradients);
|
|
12333
|
+
context.uniform1fv(m.u_role_emission_power, emission.powers);
|
|
12334
|
+
context.uniform4fv(m.u_role_colour, emission.materialColours);
|
|
12335
|
+
context.uniform4fv(m.u_role_material, emission.materials);
|
|
11080
12336
|
const stand = standOf(entry);
|
|
11081
12337
|
project(context, model, args, stand.tileX, stand.tileY);
|
|
11082
12338
|
const at = placed2(stand.tileX, stand.tileY);
|
|
11083
12339
|
context.uniform2f(m.u_offset, ...at.offset);
|
|
11084
12340
|
context.uniform1f(m.u_metres, at.metres);
|
|
11085
12341
|
setPlacement(context, model, entry, stand);
|
|
11086
|
-
|
|
12342
|
+
context.uniform3fv(m.u_view_position, modelCamera(entry));
|
|
12343
|
+
context.uniform1f(
|
|
12344
|
+
m.u_opacity,
|
|
12345
|
+
pass === "blend" ? visibleOpacity() * appearanceOf(entry, now) : 1
|
|
12346
|
+
);
|
|
12347
|
+
drawModel(context, model, entry, models.levelOf(entry, centre), pass);
|
|
11087
12348
|
}
|
|
11088
12349
|
}
|
|
11089
|
-
}
|
|
12350
|
+
}
|
|
11090
12351
|
context.depthMask(false);
|
|
11091
12352
|
context.enable(context.BLEND);
|
|
11092
12353
|
context.blendFuncSeparate(context.DST_COLOR, context.ZERO, context.ZERO, context.ONE);
|
|
@@ -11114,7 +12375,7 @@ function createTileflowBuildings3D(options) {
|
|
|
11114
12375
|
context.drawArrays(context.TRIANGLE_STRIP, 0, 4);
|
|
11115
12376
|
}
|
|
11116
12377
|
}
|
|
11117
|
-
const walls = (program) => {
|
|
12378
|
+
const walls = (program, reach = flood.reach) => {
|
|
11118
12379
|
for (const tile of entries) {
|
|
11119
12380
|
const { x: x3, y: y2, value } = tile;
|
|
11120
12381
|
const mesh = value.mesh;
|
|
@@ -11122,7 +12383,7 @@ function createTileflowBuildings3D(options) {
|
|
|
11122
12383
|
project(context, program, args, x3, y2);
|
|
11123
12384
|
context.uniform2f(program.uniforms.u_offset, ...camera.offsetOf({ x: x3, y: y2 }));
|
|
11124
12385
|
context.bindVertexArray(value.gl.vao);
|
|
11125
|
-
for (const range of viewRanges(args, tile,
|
|
12386
|
+
for (const range of viewRanges(args, tile, reach, true))
|
|
11126
12387
|
context.drawElements(
|
|
11127
12388
|
context.TRIANGLES,
|
|
11128
12389
|
range.count,
|
|
@@ -11185,6 +12446,95 @@ function createTileflowBuildings3D(options) {
|
|
|
11185
12446
|
context.uniform3fv(program.uniforms.u_flood_colour, flood.colour);
|
|
11186
12447
|
walls(program);
|
|
11187
12448
|
}
|
|
12449
|
+
if (architectural.intensity > 0 && entries.length > 0 && ground) {
|
|
12450
|
+
const program = programs.architecturalFlood;
|
|
12451
|
+
const texture = floodMask.render(context, () => {
|
|
12452
|
+
context.useProgram(program.program);
|
|
12453
|
+
context.uniform1f(program.uniforms.u_scale, shown);
|
|
12454
|
+
context.uniform1f(program.uniforms.u_metres, camera.metres);
|
|
12455
|
+
context.uniform2f(
|
|
12456
|
+
program.uniforms.u_radius,
|
|
12457
|
+
architectural.radius[0],
|
|
12458
|
+
architectural.radius[1]
|
|
12459
|
+
);
|
|
12460
|
+
context.uniform1f(program.uniforms.u_min_height, architectural.minGroundHeight);
|
|
12461
|
+
context.uniform1f(
|
|
12462
|
+
program.uniforms.u_intensity,
|
|
12463
|
+
architectural.intensity * visibleOpacity()
|
|
12464
|
+
);
|
|
12465
|
+
walls(program, architectural.radius[1]);
|
|
12466
|
+
});
|
|
12467
|
+
const composite = programs.architecturalFloodComposite;
|
|
12468
|
+
context.useProgram(composite.program);
|
|
12469
|
+
context.activeTexture(context.TEXTURE2);
|
|
12470
|
+
context.bindTexture(context.TEXTURE_2D, texture);
|
|
12471
|
+
context.uniform1i(composite.uniforms.u_mask, 2);
|
|
12472
|
+
context.uniform3fv(composite.uniforms.u_colour, architectural.colour);
|
|
12473
|
+
context.blendFuncSeparate(
|
|
12474
|
+
context.SRC_ALPHA,
|
|
12475
|
+
context.ONE_MINUS_SRC_ALPHA,
|
|
12476
|
+
context.ZERO,
|
|
12477
|
+
context.ONE
|
|
12478
|
+
);
|
|
12479
|
+
context.bindVertexArray(ground.vao);
|
|
12480
|
+
const xs = entries.map(({ x: x3 }) => x3), ys = entries.map(({ y: y2 }) => y2);
|
|
12481
|
+
for (let x3 = Math.min(...xs) - 1; x3 <= Math.max(...xs) + 1; x3 += 1)
|
|
12482
|
+
for (let y2 = Math.min(...ys) - 1; y2 <= Math.max(...ys) + 1; y2 += 1) {
|
|
12483
|
+
if (!groundVisible(args, x3, y2)) continue;
|
|
12484
|
+
project(context, composite, args, x3, y2);
|
|
12485
|
+
context.drawArrays(context.TRIANGLE_STRIP, 0, 4);
|
|
12486
|
+
}
|
|
12487
|
+
context.bindTexture(context.TEXTURE_2D, null);
|
|
12488
|
+
} else {
|
|
12489
|
+
floodMask.release(context);
|
|
12490
|
+
}
|
|
12491
|
+
const emitterProgram = programs.modelEmitter;
|
|
12492
|
+
if (emitterProgram) {
|
|
12493
|
+
const u = emitterProgram.uniforms;
|
|
12494
|
+
context.useProgram(emitterProgram.program);
|
|
12495
|
+
context.disable(context.CULL_FACE);
|
|
12496
|
+
context.blendFuncSeparate(
|
|
12497
|
+
context.SRC_ALPHA,
|
|
12498
|
+
context.ONE_MINUS_SRC_ALPHA,
|
|
12499
|
+
context.ZERO,
|
|
12500
|
+
context.ONE
|
|
12501
|
+
);
|
|
12502
|
+
context.bindVertexArray(null);
|
|
12503
|
+
context.uniform1f(u.u_scale, shown);
|
|
12504
|
+
const palette = partColoursNow();
|
|
12505
|
+
const paletteMix = partMixNow();
|
|
12506
|
+
for (const entry of standing) {
|
|
12507
|
+
const level = models.levelOf(entry, map.getCenter());
|
|
12508
|
+
const surface = entry.model.levels[level].surface;
|
|
12509
|
+
if (!surface?.emitters?.length) continue;
|
|
12510
|
+
const stand = standOf(entry), lighting2 = modelLightingNow(entry.model.id);
|
|
12511
|
+
project(context, emitterProgram, args, stand.tileX, stand.tileY);
|
|
12512
|
+
setPlacement(context, emitterProgram, entry, stand);
|
|
12513
|
+
context.uniform1f(u.u_opacity, visibleOpacity() * appearanceOf(entry, now));
|
|
12514
|
+
for (const [index, emitter] of surface.emitters.entries()) {
|
|
12515
|
+
const role = tileflowModelLightingRoles[emitter.role], part = tileflowModelParts[emitter.part];
|
|
12516
|
+
const geometry = entry.gl[level].surface.emitters[index];
|
|
12517
|
+
const gradient = lighting2.gradients.subarray(role * 4, role * 4 + 4);
|
|
12518
|
+
const bounds = entry.model.bounds;
|
|
12519
|
+
const height = (emitter.start[2] - bounds[2]) / Math.max(1e-3, bounds[5] - bounds[2]);
|
|
12520
|
+
const at = gradient[0] === gradient[1] ? 1 : Math.min(1, Math.max(0, (height - gradient[0]) / (gradient[1] - gradient[0])));
|
|
12521
|
+
const multiplier = gradient[0] === gradient[1] ? 1 : gradient[2] + (gradient[3] - gradient[2]) * at ** 10 ** lighting2.powers[role];
|
|
12522
|
+
const strength = lighting2.strengths[role] * multiplier;
|
|
12523
|
+
if (strength <= 0) continue;
|
|
12524
|
+
const own = lighting2.colours.subarray(role * 4, role * 4 + 4);
|
|
12525
|
+
const tint = lighting2.materialColours.subarray(role * 4, role * 4 + 4);
|
|
12526
|
+
const tintWeight = tint[3] * (1 - paletteMix[part]);
|
|
12527
|
+
const colour = [0, 1, 2].map(
|
|
12528
|
+
(channel) => own[3] > 0 ? own[channel] : palette[part * 3 + channel] * (1 - tintWeight) + tint[channel] * tintWeight
|
|
12529
|
+
);
|
|
12530
|
+
context.uniform3fv(u.u_emitter_positions, geometry.positions);
|
|
12531
|
+
context.uniform4fv(u.u_emitter_coordinates, geometry.coordinates);
|
|
12532
|
+
context.uniform3fv(u.u_emitter_colour, colour);
|
|
12533
|
+
context.uniform1f(u.u_emitter_strength, strength);
|
|
12534
|
+
context.drawArrays(context.TRIANGLES, 0, 12);
|
|
12535
|
+
}
|
|
12536
|
+
}
|
|
12537
|
+
}
|
|
11188
12538
|
context.bindVertexArray(null);
|
|
11189
12539
|
context.depthMask(true);
|
|
11190
12540
|
}
|
|
@@ -11197,9 +12547,18 @@ ${shaderData.define}`;
|
|
|
11197
12547
|
ground: [tileflowBuildingGroundVertex(projected), tileflowBuildingGroundFragment],
|
|
11198
12548
|
contact: [buildingContactVertex(projected), buildingContactFragment],
|
|
11199
12549
|
contactComposite: [tileflowBuildingGroundVertex(projected), buildingContactCompositeFragment],
|
|
11200
|
-
flood: [tileflowBuildingBandVertex(projected), tileflowBuildingFloodFragment]
|
|
12550
|
+
flood: [tileflowBuildingBandVertex(projected), tileflowBuildingFloodFragment],
|
|
12551
|
+
architecturalFlood: [
|
|
12552
|
+
buildingArchitecturalFloodVertex(projected),
|
|
12553
|
+
buildingArchitecturalFloodFragment
|
|
12554
|
+
],
|
|
12555
|
+
architecturalFloodComposite: [
|
|
12556
|
+
tileflowBuildingGroundVertex(projected),
|
|
12557
|
+
buildingArchitecturalFloodComposite
|
|
12558
|
+
]
|
|
11201
12559
|
};
|
|
11202
12560
|
sources.model = [tileflowModelVertex(projected), tileflowModelFragment];
|
|
12561
|
+
sources.modelEmitter = [modelEmitterVertex(projected), modelEmitterFragment];
|
|
11203
12562
|
if (!programs.depth)
|
|
11204
12563
|
sources.depth = [tileflowBuildingDepthVertex, tileflowBuildingDepthFragment];
|
|
11205
12564
|
if (!programs.modelDepth)
|
|
@@ -11216,7 +12575,7 @@ ${shaderData.define}`;
|
|
|
11216
12575
|
const fragment = shader(context.FRAGMENT_SHADER, fragmentText);
|
|
11217
12576
|
context.attachShader(program, vertex);
|
|
11218
12577
|
context.attachShader(program, fragment);
|
|
11219
|
-
const bound = name === "model" || name === "modelDepth" ? tileflowModelAttributes : tileflowBuildingAttributes;
|
|
12578
|
+
const bound = name === "model" || name === "modelDepth" ? [...tileflowModelAttributes, ...tileflowModelSurfaceAttributes] : tileflowBuildingAttributes;
|
|
11220
12579
|
for (const [index, [attribute]] of bound.entries())
|
|
11221
12580
|
context.bindAttribLocation(program, index, attribute);
|
|
11222
12581
|
context.linkProgram(program);
|
|
@@ -15380,6 +16739,120 @@ function validated5(shot) {
|
|
|
15380
16739
|
throw new TypeError("Shot easing is smooth or linear.");
|
|
15381
16740
|
}
|
|
15382
16741
|
|
|
16742
|
+
// src/notices.ts
|
|
16743
|
+
var tileflowNoticesMetadataKey = "tileflow:notices";
|
|
16744
|
+
var tileflowNoticeLimits = Object.freeze({
|
|
16745
|
+
maxNotices: 8,
|
|
16746
|
+
maxSources: 16,
|
|
16747
|
+
maxTextLength: 120
|
|
16748
|
+
});
|
|
16749
|
+
var identifierPattern = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/u;
|
|
16750
|
+
function readTileflowNotices(metadata) {
|
|
16751
|
+
if (!isRecord11(metadata)) return [];
|
|
16752
|
+
const value = metadata[tileflowNoticesMetadataKey];
|
|
16753
|
+
if (!Array.isArray(value)) return [];
|
|
16754
|
+
const notices = [];
|
|
16755
|
+
const ids = /* @__PURE__ */ new Set();
|
|
16756
|
+
for (const entry of value.slice(0, tileflowNoticeLimits.maxNotices)) {
|
|
16757
|
+
const notice = readNotice(entry);
|
|
16758
|
+
if (!notice || ids.has(notice.id)) continue;
|
|
16759
|
+
ids.add(notice.id);
|
|
16760
|
+
notices.push(notice);
|
|
16761
|
+
}
|
|
16762
|
+
return Object.freeze(notices);
|
|
16763
|
+
}
|
|
16764
|
+
function readNotice(value) {
|
|
16765
|
+
if (!isRecord11(value)) return void 0;
|
|
16766
|
+
const { id, sources, text: text2 } = value;
|
|
16767
|
+
if (typeof id !== "string" || !identifierPattern.test(id)) return void 0;
|
|
16768
|
+
if (typeof text2 !== "string" || text2.trim() !== text2 || !text2 || text2.length > tileflowNoticeLimits.maxTextLength || /\p{Cc}/u.test(text2)) {
|
|
16769
|
+
return void 0;
|
|
16770
|
+
}
|
|
16771
|
+
if (!Array.isArray(sources) || sources.length > tileflowNoticeLimits.maxSources || !sources.every((source) => typeof source === "string" && identifierPattern.test(source))) {
|
|
16772
|
+
return void 0;
|
|
16773
|
+
}
|
|
16774
|
+
return Object.freeze({ id, sources: Object.freeze([...sources]), text: text2 });
|
|
16775
|
+
}
|
|
16776
|
+
function isRecord11(value) {
|
|
16777
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
16778
|
+
}
|
|
16779
|
+
|
|
16780
|
+
// src/notices-browser.ts
|
|
16781
|
+
var styleChangeDelayMs = 200;
|
|
16782
|
+
function attachTileflowNotices(map, options = {}) {
|
|
16783
|
+
const container = map.getContainer();
|
|
16784
|
+
const document2 = container.ownerDocument;
|
|
16785
|
+
const browser = document2.defaultView;
|
|
16786
|
+
const position = options.position ?? "bottom-right";
|
|
16787
|
+
const element = document2.createElement("div");
|
|
16788
|
+
element.className = "maplibregl-ctrl tileflow-notice";
|
|
16789
|
+
element.setAttribute("role", "note");
|
|
16790
|
+
Object.assign(element.style, {
|
|
16791
|
+
background: "rgba(255, 255, 255, 0.82)",
|
|
16792
|
+
borderRadius: "3px",
|
|
16793
|
+
color: "rgba(0, 0, 0, 0.78)",
|
|
16794
|
+
font: '600 11px/18px "Helvetica Neue", Arial, Helvetica, sans-serif',
|
|
16795
|
+
letterSpacing: "0.01em",
|
|
16796
|
+
padding: "0 6px",
|
|
16797
|
+
whiteSpace: "nowrap"
|
|
16798
|
+
});
|
|
16799
|
+
const control = {
|
|
16800
|
+
onAdd: () => element,
|
|
16801
|
+
onRemove: () => element.remove()
|
|
16802
|
+
};
|
|
16803
|
+
let added = false;
|
|
16804
|
+
let disposed = false;
|
|
16805
|
+
let pending;
|
|
16806
|
+
function sync() {
|
|
16807
|
+
pending = void 0;
|
|
16808
|
+
if (disposed) return;
|
|
16809
|
+
let metadata;
|
|
16810
|
+
try {
|
|
16811
|
+
metadata = map.getStyle()?.metadata;
|
|
16812
|
+
} catch {
|
|
16813
|
+
return;
|
|
16814
|
+
}
|
|
16815
|
+
const notices = readTileflowNotices(metadata);
|
|
16816
|
+
if (notices.length === 0) {
|
|
16817
|
+
if (added) map.removeControl(control);
|
|
16818
|
+
added = false;
|
|
16819
|
+
return;
|
|
16820
|
+
}
|
|
16821
|
+
element.textContent = notices.map(({ text: text2 }) => text2).join(" \xB7 ");
|
|
16822
|
+
element.dataset.tileflowNotice = notices.map(({ id }) => id).join(" ");
|
|
16823
|
+
if (!added) map.addControl(control, position);
|
|
16824
|
+
added = true;
|
|
16825
|
+
}
|
|
16826
|
+
function cancelPending() {
|
|
16827
|
+
if (pending !== void 0) browser?.clearTimeout(pending);
|
|
16828
|
+
pending = void 0;
|
|
16829
|
+
}
|
|
16830
|
+
function scheduleSync() {
|
|
16831
|
+
if (disposed || !browser) return;
|
|
16832
|
+
cancelPending();
|
|
16833
|
+
pending = browser.setTimeout(sync, styleChangeDelayMs);
|
|
16834
|
+
}
|
|
16835
|
+
function onStyleLoad() {
|
|
16836
|
+
cancelPending();
|
|
16837
|
+
sync();
|
|
16838
|
+
}
|
|
16839
|
+
function dispose() {
|
|
16840
|
+
if (disposed) return;
|
|
16841
|
+
disposed = true;
|
|
16842
|
+
cancelPending();
|
|
16843
|
+
map.off("style.load", onStyleLoad);
|
|
16844
|
+
map.off("styledata", scheduleSync);
|
|
16845
|
+
map.off("remove", dispose);
|
|
16846
|
+
if (added) map.removeControl(control);
|
|
16847
|
+
added = false;
|
|
16848
|
+
}
|
|
16849
|
+
map.on("style.load", onStyleLoad);
|
|
16850
|
+
map.on("styledata", scheduleSync);
|
|
16851
|
+
map.on("remove", dispose);
|
|
16852
|
+
sync();
|
|
16853
|
+
return dispose;
|
|
16854
|
+
}
|
|
16855
|
+
|
|
15383
16856
|
// src/data/world-release-id-values.ts
|
|
15384
16857
|
var tileflowWorldReleaseIdMinimumLength = 12;
|
|
15385
16858
|
var tileflowWorldReleaseIdMaximumLength = 128;
|
|
@@ -17484,6 +18957,7 @@ export {
|
|
|
17484
18957
|
attachTileflowAtmosphere,
|
|
17485
18958
|
attachTileflowFairUseNotice,
|
|
17486
18959
|
attachTileflowMapLifecycle,
|
|
18960
|
+
attachTileflowNotices,
|
|
17487
18961
|
createBoundaryFeatureState,
|
|
17488
18962
|
createTileflowBuildings3D,
|
|
17489
18963
|
createTileflowDepthMarkers,
|
|
@@ -17507,9 +18981,11 @@ export {
|
|
|
17507
18981
|
playTileflowShot,
|
|
17508
18982
|
readTileflowBuildings3D,
|
|
17509
18983
|
readTileflowMapTileSource,
|
|
18984
|
+
readTileflowNotices,
|
|
17510
18985
|
readTileflowRouteInks,
|
|
17511
18986
|
readTileflowStreetLights,
|
|
17512
18987
|
readTileflowSunPreset,
|
|
18988
|
+
readTileflowTeamModelName,
|
|
17513
18989
|
readTileflowVegetation3D,
|
|
17514
18990
|
readTileflowVegetationDisplay,
|
|
17515
18991
|
registerTileflowContourProtocol,
|
|
@@ -17531,6 +19007,8 @@ export {
|
|
|
17531
19007
|
tileflowLayerDomainMetadataKey,
|
|
17532
19008
|
tileflowLineProgressGradient,
|
|
17533
19009
|
tileflowMaplibreContourVersion,
|
|
19010
|
+
tileflowNoticeLimits,
|
|
19011
|
+
tileflowNoticesMetadataKey,
|
|
17534
19012
|
tileflowPmtilesMaximumDirectoryDepth,
|
|
17535
19013
|
tileflowPmtilesProtocol,
|
|
17536
19014
|
tileflowRouteMetadataKey,
|
|
@@ -17544,6 +19022,7 @@ export {
|
|
|
17544
19022
|
tileflowSunLightMatrix,
|
|
17545
19023
|
tileflowSunMetadataKey,
|
|
17546
19024
|
tileflowSunPosition,
|
|
19025
|
+
tileflowTeamModelUrl,
|
|
17547
19026
|
tileflowTilePointToLngLat,
|
|
17548
19027
|
tileflowTilesForBounds,
|
|
17549
19028
|
tileflowTreeBytes,
|