osmgl 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/osmgl.d.cts CHANGED
@@ -618,6 +618,13 @@ declare class Context {
618
618
  constructor(gl: WebGL2RenderingContext);
619
619
  useProgram(program: WebGLProgram | null): boolean;
620
620
  bindVAO(vao: WebGLVertexArrayObject | null): void;
621
+ /**
622
+ * Куда уходит «экранный» кадр. По умолчанию `null` — канва. Диагностика
623
+ * подменяет его на свой буфер: проходы внутри рендера (тени, пикинг)
624
+ * возвращаются с bindFramebuffer(null), и без этой подмены весь кадр после
625
+ * первого же такого прохода уходил бы мимо буфера.
626
+ */
627
+ screen: WebGLFramebuffer | null;
621
628
  bindFramebuffer(fb: WebGLFramebuffer | null): void;
622
629
  bindTexture(unit: number, texture: WebGLTexture | null, target?: number): void;
623
630
  viewport(x: number, y: number, w: number, h: number): void;
@@ -688,6 +695,24 @@ declare class Shader {
688
695
  * Стиль хранится в `styles/*.json` и целиком вшивается в бандл — карта должна
689
696
  * рисоваться без единого сетевого запроса за конфигом.
690
697
  */
698
+ /**
699
+ * Цвет, ВЫВЕДЕННЫЙ ИЗ ЦВЕТА ДРУГОГО СЛОЯ: «как у него, только темнее».
700
+ *
701
+ * Нужен обводке моста. Собственный цвет ей задать нельзя: тем два десятка, каждая красит дороги
702
+ * по-своему, и любой фиксированный хекс на половине из них смотрелся бы чужим. Полупрозрачная
703
+ * затемняющая краска эту задачу решала, но полупрозрачной линии нельзя: капсулы соседних отрезков
704
+ * перекрываются на изломах, и краска ложится дважды — по вершинам идёт цепочка тёмных бусин.
705
+ * (Трафарет «красить пиксель один раз» бусины убирает, но взамен оставляет светлый шов по
706
+ * сглаженному краю первой капсулы — проверено.)
707
+ *
708
+ * Ссылка снимает вопрос: тема красит обводку дороги, а обводка моста едет следом сама.
709
+ */
710
+ interface ColorFrom {
711
+ /** Идентификатор слоя, у которого берём цвет. */
712
+ from: string;
713
+ /** Насколько затемнить, 0..1 (0.25 — четверть). */
714
+ darken?: number;
715
+ }
691
716
  /** Значение, зависящее от зума: число/цвет либо остановки для интерполяции. */
692
717
  type Interpolated<T> = T | {
693
718
  stops: [number, T][];
@@ -827,13 +852,41 @@ interface FillLayerSpec extends LayerBase {
827
852
  }
828
853
  interface LineLayerSpec extends LayerBase {
829
854
  type: 'line';
855
+ /**
856
+ * СЕТЬ, внутри которой порядок отрисовки решают не слои, а уровни пересечения.
857
+ *
858
+ * Все дорожные слои стиля — обводки, полотна, тень моста — помечаются одним именем (`roads`), и
859
+ * рисуются они не подряд, а по уровням: сначала вся сеть уровня −1 (тоннели), потом вся сеть
860
+ * земли, потом вся сеть эстакад. Поэтому мост со своей тенью и обводкой ложится ЦЕЛИКОМ поверх
861
+ * улицы под ним, какого бы класса та ни была, — а не «полотно моста поверх, обводка под».
862
+ *
863
+ * Так же устроено у Яндекса: у каждого дорожного слоя `line-network: "roads"` и
864
+ * `line-z-level: ["get","z_level"]`. Уровень фичи считается по `generators/level`.
865
+ *
866
+ * Сеть рисуется целиком НА МЕСТЕ ПЕРВОГО своего слоя в стиле; порядок внутри уровня — обычный,
867
+ * по порядку слоёв.
868
+ */
869
+ network?: string;
830
870
  paint: MaterialPaint & {
831
- color: Interpolated<string>;
871
+ /** Цвет линии; `{ from, darken }` — «как у такого-то слоя, только темнее» (см. `ColorFrom`). */
872
+ color: Interpolated<string> | ColorFrom;
832
873
  /** Ширина в ЭКРАННЫХ пикселях: линия не толстеет с приближением к камере. */
833
874
  width: Interpolated<number>;
834
875
  opacity?: Interpolated<number>;
835
876
  /** Сдвиг поперёк линии в пикселях — для обочин и параллельных путей. */
836
877
  offset?: Interpolated<number>;
878
+ /**
879
+ * РАЗМЫТИЕ КРАЯ в пикселях: край растягивается на столько вместо обычного полупикселя.
880
+ *
881
+ * Нужно тени моста. У Яндекса тени нет вовсе как отдельной сущности — там на каждый мост
882
+ * рисуется ВОСЕМЬ вложенных контуров (`road-sd-shadow-1…8__bridge`), каждый с прозрачностью
883
+ * 0.03–0.08, и их сумма изображает размытие. Это обход отсутствующего в их движке размытия:
884
+ * восемь слоёв в стиле и восемь вызовов отрисовки на тайл ради градиента.
885
+ *
886
+ * У нас край линии и так вырезает фрагментный шейдер по расстоянию до оси — растянуть переход
887
+ * стоит одного юниформа. Поэтому тень моста здесь ОДИН слой с честным мягким краем.
888
+ */
889
+ blur?: Interpolated<number>;
837
890
  /**
838
891
  * Уровень полотна в МЕТРАХ — для рельефных вариантов карты: дорожка внутри
839
892
  * приподнятого парка должна лежать на нём, а не тонуть под ним. Меняется
@@ -1349,6 +1402,13 @@ interface LayerPlan {
1349
1402
  maxzoom: number;
1350
1403
  /** Разбить слой на меши по значению свойства (этажи — см. `LayerBase.splitBy`). */
1351
1404
  splitBy?: string;
1405
+ /**
1406
+ * Слой входит в СЕТЬ (`LineLayerSpec.network`): меш бьётся по уровням пересечения.
1407
+ *
1408
+ * Воркеру важно не имя сети, а сам факт: по уровню он раскладывает геометрию, а порядок
1409
+ * расставляет уже рендер.
1410
+ */
1411
+ network?: string;
1352
1412
  /** Скругление углов в метрах — геометрия, поэтому едет в воркер. */
1353
1413
  cornerRadius?: number;
1354
1414
  cornerSegments?: number;
@@ -1616,6 +1676,14 @@ interface LoadedResponse {
1616
1676
  * этажи вообще есть в этом здании.
1617
1677
  */
1618
1678
  splits?: Record<string, (string | number)[]>;
1679
+ /**
1680
+ * Уровни пересечения, встретившиеся в этом тайле (см. `generators/level`), по возрастанию.
1681
+ *
1682
+ * Дорожная сеть рисуется НЕ по слоям, а по уровням: сначала всё, что под землёй, потом земля,
1683
+ * потом эстакады. Какие уровни в тайле есть, знает только тот, кто его разобрал, — рендеру
1684
+ * перебирать все возможные значения было бы дороже, чем передать список.
1685
+ */
1686
+ levels?: number[];
1619
1687
  timing: {
1620
1688
  fetchMs: number;
1621
1689
  parseMs: number;
@@ -3715,6 +3783,18 @@ declare class LayerRenderer {
3715
3783
  private applyMaterial;
3716
3784
  private drawFill;
3717
3785
  private drawLine;
3786
+ /**
3787
+ * СЕТЬ ЦЕЛИКОМ, по уровням пересечения (см. `LineLayerSpec.network`).
3788
+ *
3789
+ * Обычный слой рисуется на своём месте в стиле. Сеть так рисовать нельзя: обводка эстакады
3790
+ * должна лежать поверх ПОЛОТНА улицы под ней, а полотно улицы в стиле идёт позже обводок.
3791
+ * Поэтому вся сеть рисуется одним куском на месте своего первого слоя, и снаружи внутрь порядок
3792
+ * такой: уровень (тоннели → земля → эстакады), а внутри уровня — обычный порядок слоёв.
3793
+ *
3794
+ * Какие уровни в кадре есть, считаем по видимым тайлам: в обычном городе это один нулевой, и
3795
+ * тогда всё сводится к прежней отрисовке слой за слоем.
3796
+ */
3797
+ private drawNetwork;
3718
3798
  /**
3719
3799
  * Подсветка выбранного здания.
3720
3800
  *
@@ -4297,7 +4377,22 @@ declare function clipLineToRect(line: ScreenPoint[], width: number, height: numb
4297
4377
  */
4298
4378
  declare const BUILDING_VERTEX_STRIDE = 18;
4299
4379
  /** Множитель упаковки высоты в Uint16. */
4300
- declare const BUILDING_HEIGHT_SCALE = 4;
4380
+ declare const BUILDING_HEIGHT_SCALE = 32;
4381
+ /**
4382
+ * Множитель упаковки ПОЛОЖЕНИЯ вершины: сколько единиц Int16 приходится на единицу тайла.
4383
+ *
4384
+ * Было единица — то есть 0.47 м на z14, потому что координаты в MVT целые и вершину писали как
4385
+ * есть. Пока углы дома были прямыми, этого хватало: вершина и так стояла в узле сетки. Со
4386
+ * СКРУГЛЕНИЕМ хватать перестало — дуга радиусом 1.6 м это семь точек в пределах трёх единиц тайла,
4387
+ * и округление сажает их на ту же сетку: вместо дуги выходит лесенка со ступенью в полметра, она же
4388
+ * «угловатость» на ближнем зуме.
4389
+ *
4390
+ * Восемь единиц на тайловую — это 5.9 см, и дуга снова читается дугой. Потолок: тайл 0..4096, при
4391
+ * ×8 это 0..32768, и последнее значение на единицу больше, чем помещается в Int16, — оно
4392
+ * прижимается (`clampI16`). Ошибка при этом 6 см и только у вершины, стоящей ровно на дальней
4393
+ * границе тайла.
4394
+ */
4395
+ declare const BUILDING_POS_SCALE = 8;
4301
4396
  interface BuildingsMesh {
4302
4397
  vertices: ArrayBuffer;
4303
4398
  indices: ArrayBuffer;
@@ -4722,11 +4817,47 @@ interface LineMesh {
4722
4817
  vertexCount: number;
4723
4818
  indexCount: number;
4724
4819
  }
4820
+ interface LineOptions {
4821
+ /**
4822
+ * Уровень пересечения (см. `generators/level`): берём только фичи этого уровня.
4823
+ *
4824
+ * `undefined` — уровни не разделяем, слой строится целиком (так живут все слои, кроме дорожной
4825
+ * сети: у реки или границы пересечений без связи не бывает).
4826
+ */
4827
+ level?: number;
4828
+ }
4725
4829
  declare function generateLine(layer: VectorTileLayer, filter: Filter | undefined, maxSegment?: number,
4726
4830
  /** Уровни поверхностей тайла: по ним дорога ложится на то, по чему идёт. Нет — лежит на нуле. */
4727
4831
  ground?: GroundField,
4728
4832
  /** Метров в единице тайла. Ноль — плавный переход ширины на стыках не строится. */
4729
- metersPerUnit?: number): LineMesh | null;
4833
+ metersPerUnit?: number, opts?: LineOptions): LineMesh | null;
4834
+
4835
+ /**
4836
+ * УРОВЕНЬ ПЕРЕСЕЧЕНИЯ — какой из двух дорог быть сверху там, где они пересекаются без связи.
4837
+ *
4838
+ * ЗАЧЕМ. Плоская карта рисуется краской: слои идут в порядке стиля, и внутри слоя порядок фич
4839
+ * произволен. Поэтому эстакада над улицей ничем не отличалась от перекрёстка — обводки сливались,
4840
+ * полотна пересекались, и место читалось как съезд, которого в жизни нет. Ровно этим у Яндекса
4841
+ * занят `line-z-level` (`["get","z_level"]` у КАЖДОГО дорожного слоя, все они в одной сети
4842
+ * `line-network: "roads"`): сеть рисуется не по слоям, а по уровням, и мост со своей тенью и
4843
+ * обводкой целиком ложится поверх того, что под ним.
4844
+ *
4845
+ * ОТКУДА ЧИСЛО. Готового `z_level` в наших тайлах нет, зато есть то, из чего его считают:
4846
+ * `layer` — тег OSM, честный уровень (−1 подземный, 1 над землёй, изредка 2…5);
4847
+ * `brunnel` — «мост / тоннель / брод», он приходит и там, где `layer` не проставлен.
4848
+ * Поэтому берём `layer`, а если его нет — выводим уровень из `brunnel`. Мост без `layer`
4849
+ * (в Душанбе таких большинство) получает 1, тоннель — −1, обычная улица — 0.
4850
+ *
4851
+ * Считается в ВОРКЕРЕ, при тесселяции: по уровню бьётся меш, и рендеру остаётся только порядок.
4852
+ */
4853
+ /**
4854
+ * Дальше этого уровни не разводим. В OSM попадается и `layer=5`, и опечатки в сотни: каждое
4855
+ * значение — это отдельный бакет и отдельный вызов отрисовки, поэтому хвост подрезаем. Пять этажей
4856
+ * развязки — больше, чем бывает на настоящих.
4857
+ */
4858
+ declare const LEVEL_LIMIT = 5;
4859
+ /** Уровень фичи: `layer` из тегов, иначе вывод из `brunnel`. */
4860
+ declare function featureLevel(props: Record<string, unknown>): number;
4730
4861
 
4731
4862
  /**
4732
4863
  * Положение камеры в адресе страницы.
@@ -5676,10 +5807,19 @@ declare class Map$1 extends Evented<MapEvents> {
5676
5807
  };
5677
5808
  /** Просит нарисовать кадр. Движок рисует по требованию, а не постоянно. */
5678
5809
  triggerRepaint(): void;
5810
+ private debugFB;
5811
+ /**
5812
+ * Рисует один кадр в float-буфер и возвращает прямоугольник пикселей.
5813
+ * `x`, `y` — в координатах GL (снизу вверх), размеры в пикселях канвы.
5814
+ */
5815
+ debugFloatPixels(x: number, y: number, w: number, h: number): {
5816
+ data: Float32Array;
5817
+ format: string;
5818
+ } | null;
5679
5819
  private renderFrame;
5680
5820
  private stopFrameLoop;
5681
5821
  get isLoaded(): boolean;
5682
5822
  destroy(): void;
5683
5823
  }
5684
5824
 
5685
- export { type AnimationOptions, AssetLoader, type AssetLoaderOptions, BASE_SOURCE, BUILDING_HEIGHT_SCALE, BUILDING_VERTEX_STRIDE, type BuildingHit, type BuildingsMesh, CITY_STYLE, type CameraOptions, Circle, type CircleOptions, type CoverOptions, DEFAULT_ICONS, DEFAULT_LIGHT, DEFAULT_SHADOWS, DEFAULT_SKY, DEFAULT_STYLE, DepthTarget, type DeviceHints, EARTH_CIRCUMFERENCE, FILL_VERTEX_STRIDE, type Filter, type FitBoundsOptions, type FitViewport, type FogPlanes, GLOBE_MAX_PITCH, GLOBE_ZOOM_MAX, GLOBE_ZOOM_MIN, GLYPH_BORDER, GLYPH_SIZE, GROUND_SCALE, GeomType, type GestureOptions, type Glyph, type GlyphMetric, GlyphSource, GroundArrows, IconSource, IndoorMask, type Interpolated, LINE_VERTEX_STRIDE, type LayerPlan, LayerRenderer, type LightSpec, type LinePlacement, type LngLat, LngLatBounds, type LngLatLike, MAX_LATITUDE, MODEL_VERTEX_STRIDE, Map$1 as Map, type MapEvents, MapObject, type MapObjectOptions, type MapOptions, Marker, type MarkerOptions, Model, ModelLayer, type ModelMesh, type ModelOptions, type ModelPrimitive, ObjectManager, type ObjectPaint, type ObjectPart, type Padding, type PlacedGlyph, type PlacedSymbol, Polygon, type PolygonOptions, Polyline, type PolylineOptions, type PolylineSegment, Popup, type PopupOptions, type Projection, type QualityName, type QualityProfile, RELIEF_STYLE, RUNTIME_FILL_STRIDE, RUNTIME_LINE_STRIDE, RasterSource, type RasterSourceOptions, RasterTile, type RoundOptions, type RuntimeMesh, SDF_EDGE, SDF_PX, SOFT_STYLE, SPLIT_SEP, SURFACE_CUT_BLEED, SURFACE_LEVEL_SCALE, SURFACE_VERTEX_STRIDE, type ScreenPoint$1 as ScreenPoint, SdfAtlas, type ShadowSettings, type SkySpec, Style, type StyleLayerSpec, type StyleSpec, type SymbolHit, SymbolRenderer, THEMES, TILE_SIZE, type ThemeSpec, TileID, Transform, VectorTile, VectorTileFeature, VectorTileLayer, abbreviateStreet, addGround, altitudeFromMercatorZ, applyTheme, arrowLabelOffset, arrowVertices, buildRuntimeFill, buildRuntimeLine, cameraForBounds, circumferenceAtLatitude, clampLat, clampPixelRatio, clipLineToRect, coordinateDigits, coveringTiles, createGround, Map$1 as default, deviceHints, distToSegment, evaluateColor, evaluateNumber, extractFeature, fillFootprint, fogPlanes, footprintOf, formatHash, generateBuildings, generateFill, generateLine, generateSurface, generateSymbols, globeBasis, globeFlatMix, globeRadius, globeToLocal, groundAt, iconRotation, insideFootprint, latFromMercatorY, lngFromMercatorX, lngLatToUnit, matchesFilter, mercatorMetersPerTile, mercatorX, mercatorY, mercatorZFromAltitude, metersPerPixel, metersPerTile, nextQualityDown, parseGlb, parseGlbAsync, parseGlyphs, parseHash, patchUV, pickBuilding, pickFill, pitchIntent, pixelsPerMeter, placeAlongLine, pointInRing, qualityFor, qualityProfile, raySphere, resolveTemplate, roundRing, sdfFromAlpha, shapeText, splitBucketKey, splitByGround, splitPolygons, styleWithTheme, tileOriginMeters, tileUrl, toLngLat, unitToLngLat, worldSize };
5825
+ export { type AnimationOptions, AssetLoader, type AssetLoaderOptions, BASE_SOURCE, BUILDING_HEIGHT_SCALE, BUILDING_POS_SCALE, BUILDING_VERTEX_STRIDE, type BuildingHit, type BuildingsMesh, CITY_STYLE, type CameraOptions, Circle, type CircleOptions, type CoverOptions, DEFAULT_ICONS, DEFAULT_LIGHT, DEFAULT_SHADOWS, DEFAULT_SKY, DEFAULT_STYLE, DepthTarget, type DeviceHints, EARTH_CIRCUMFERENCE, FILL_VERTEX_STRIDE, type Filter, type FitBoundsOptions, type FitViewport, type FogPlanes, GLOBE_MAX_PITCH, GLOBE_ZOOM_MAX, GLOBE_ZOOM_MIN, GLYPH_BORDER, GLYPH_SIZE, GROUND_SCALE, GeomType, type GestureOptions, type Glyph, type GlyphMetric, GlyphSource, GroundArrows, IconSource, IndoorMask, type Interpolated, LEVEL_LIMIT, LINE_VERTEX_STRIDE, type LayerPlan, LayerRenderer, type LightSpec, type LinePlacement, type LngLat, LngLatBounds, type LngLatLike, MAX_LATITUDE, MODEL_VERTEX_STRIDE, Map$1 as Map, type MapEvents, MapObject, type MapObjectOptions, type MapOptions, Marker, type MarkerOptions, Model, ModelLayer, type ModelMesh, type ModelOptions, type ModelPrimitive, ObjectManager, type ObjectPaint, type ObjectPart, type Padding, type PlacedGlyph, type PlacedSymbol, Polygon, type PolygonOptions, Polyline, type PolylineOptions, type PolylineSegment, Popup, type PopupOptions, type Projection, type QualityName, type QualityProfile, RELIEF_STYLE, RUNTIME_FILL_STRIDE, RUNTIME_LINE_STRIDE, RasterSource, type RasterSourceOptions, RasterTile, type RoundOptions, type RuntimeMesh, SDF_EDGE, SDF_PX, SOFT_STYLE, SPLIT_SEP, SURFACE_CUT_BLEED, SURFACE_LEVEL_SCALE, SURFACE_VERTEX_STRIDE, type ScreenPoint$1 as ScreenPoint, SdfAtlas, type ShadowSettings, type SkySpec, Style, type StyleLayerSpec, type StyleSpec, type SymbolHit, SymbolRenderer, THEMES, TILE_SIZE, type ThemeSpec, TileID, Transform, VectorTile, VectorTileFeature, VectorTileLayer, abbreviateStreet, addGround, altitudeFromMercatorZ, applyTheme, arrowLabelOffset, arrowVertices, buildRuntimeFill, buildRuntimeLine, cameraForBounds, circumferenceAtLatitude, clampLat, clampPixelRatio, clipLineToRect, coordinateDigits, coveringTiles, createGround, Map$1 as default, deviceHints, distToSegment, evaluateColor, evaluateNumber, extractFeature, featureLevel, fillFootprint, fogPlanes, footprintOf, formatHash, generateBuildings, generateFill, generateLine, generateSurface, generateSymbols, globeBasis, globeFlatMix, globeRadius, globeToLocal, groundAt, iconRotation, insideFootprint, latFromMercatorY, lngFromMercatorX, lngLatToUnit, matchesFilter, mercatorMetersPerTile, mercatorX, mercatorY, mercatorZFromAltitude, metersPerPixel, metersPerTile, nextQualityDown, parseGlb, parseGlbAsync, parseGlyphs, parseHash, patchUV, pickBuilding, pickFill, pitchIntent, pixelsPerMeter, placeAlongLine, pointInRing, qualityFor, qualityProfile, raySphere, resolveTemplate, roundRing, sdfFromAlpha, shapeText, splitBucketKey, splitByGround, splitPolygons, styleWithTheme, tileOriginMeters, tileUrl, toLngLat, unitToLngLat, worldSize };
package/dist/osmgl.d.ts CHANGED
@@ -618,6 +618,13 @@ declare class Context {
618
618
  constructor(gl: WebGL2RenderingContext);
619
619
  useProgram(program: WebGLProgram | null): boolean;
620
620
  bindVAO(vao: WebGLVertexArrayObject | null): void;
621
+ /**
622
+ * Куда уходит «экранный» кадр. По умолчанию `null` — канва. Диагностика
623
+ * подменяет его на свой буфер: проходы внутри рендера (тени, пикинг)
624
+ * возвращаются с bindFramebuffer(null), и без этой подмены весь кадр после
625
+ * первого же такого прохода уходил бы мимо буфера.
626
+ */
627
+ screen: WebGLFramebuffer | null;
621
628
  bindFramebuffer(fb: WebGLFramebuffer | null): void;
622
629
  bindTexture(unit: number, texture: WebGLTexture | null, target?: number): void;
623
630
  viewport(x: number, y: number, w: number, h: number): void;
@@ -688,6 +695,24 @@ declare class Shader {
688
695
  * Стиль хранится в `styles/*.json` и целиком вшивается в бандл — карта должна
689
696
  * рисоваться без единого сетевого запроса за конфигом.
690
697
  */
698
+ /**
699
+ * Цвет, ВЫВЕДЕННЫЙ ИЗ ЦВЕТА ДРУГОГО СЛОЯ: «как у него, только темнее».
700
+ *
701
+ * Нужен обводке моста. Собственный цвет ей задать нельзя: тем два десятка, каждая красит дороги
702
+ * по-своему, и любой фиксированный хекс на половине из них смотрелся бы чужим. Полупрозрачная
703
+ * затемняющая краска эту задачу решала, но полупрозрачной линии нельзя: капсулы соседних отрезков
704
+ * перекрываются на изломах, и краска ложится дважды — по вершинам идёт цепочка тёмных бусин.
705
+ * (Трафарет «красить пиксель один раз» бусины убирает, но взамен оставляет светлый шов по
706
+ * сглаженному краю первой капсулы — проверено.)
707
+ *
708
+ * Ссылка снимает вопрос: тема красит обводку дороги, а обводка моста едет следом сама.
709
+ */
710
+ interface ColorFrom {
711
+ /** Идентификатор слоя, у которого берём цвет. */
712
+ from: string;
713
+ /** Насколько затемнить, 0..1 (0.25 — четверть). */
714
+ darken?: number;
715
+ }
691
716
  /** Значение, зависящее от зума: число/цвет либо остановки для интерполяции. */
692
717
  type Interpolated<T> = T | {
693
718
  stops: [number, T][];
@@ -827,13 +852,41 @@ interface FillLayerSpec extends LayerBase {
827
852
  }
828
853
  interface LineLayerSpec extends LayerBase {
829
854
  type: 'line';
855
+ /**
856
+ * СЕТЬ, внутри которой порядок отрисовки решают не слои, а уровни пересечения.
857
+ *
858
+ * Все дорожные слои стиля — обводки, полотна, тень моста — помечаются одним именем (`roads`), и
859
+ * рисуются они не подряд, а по уровням: сначала вся сеть уровня −1 (тоннели), потом вся сеть
860
+ * земли, потом вся сеть эстакад. Поэтому мост со своей тенью и обводкой ложится ЦЕЛИКОМ поверх
861
+ * улицы под ним, какого бы класса та ни была, — а не «полотно моста поверх, обводка под».
862
+ *
863
+ * Так же устроено у Яндекса: у каждого дорожного слоя `line-network: "roads"` и
864
+ * `line-z-level: ["get","z_level"]`. Уровень фичи считается по `generators/level`.
865
+ *
866
+ * Сеть рисуется целиком НА МЕСТЕ ПЕРВОГО своего слоя в стиле; порядок внутри уровня — обычный,
867
+ * по порядку слоёв.
868
+ */
869
+ network?: string;
830
870
  paint: MaterialPaint & {
831
- color: Interpolated<string>;
871
+ /** Цвет линии; `{ from, darken }` — «как у такого-то слоя, только темнее» (см. `ColorFrom`). */
872
+ color: Interpolated<string> | ColorFrom;
832
873
  /** Ширина в ЭКРАННЫХ пикселях: линия не толстеет с приближением к камере. */
833
874
  width: Interpolated<number>;
834
875
  opacity?: Interpolated<number>;
835
876
  /** Сдвиг поперёк линии в пикселях — для обочин и параллельных путей. */
836
877
  offset?: Interpolated<number>;
878
+ /**
879
+ * РАЗМЫТИЕ КРАЯ в пикселях: край растягивается на столько вместо обычного полупикселя.
880
+ *
881
+ * Нужно тени моста. У Яндекса тени нет вовсе как отдельной сущности — там на каждый мост
882
+ * рисуется ВОСЕМЬ вложенных контуров (`road-sd-shadow-1…8__bridge`), каждый с прозрачностью
883
+ * 0.03–0.08, и их сумма изображает размытие. Это обход отсутствующего в их движке размытия:
884
+ * восемь слоёв в стиле и восемь вызовов отрисовки на тайл ради градиента.
885
+ *
886
+ * У нас край линии и так вырезает фрагментный шейдер по расстоянию до оси — растянуть переход
887
+ * стоит одного юниформа. Поэтому тень моста здесь ОДИН слой с честным мягким краем.
888
+ */
889
+ blur?: Interpolated<number>;
837
890
  /**
838
891
  * Уровень полотна в МЕТРАХ — для рельефных вариантов карты: дорожка внутри
839
892
  * приподнятого парка должна лежать на нём, а не тонуть под ним. Меняется
@@ -1349,6 +1402,13 @@ interface LayerPlan {
1349
1402
  maxzoom: number;
1350
1403
  /** Разбить слой на меши по значению свойства (этажи — см. `LayerBase.splitBy`). */
1351
1404
  splitBy?: string;
1405
+ /**
1406
+ * Слой входит в СЕТЬ (`LineLayerSpec.network`): меш бьётся по уровням пересечения.
1407
+ *
1408
+ * Воркеру важно не имя сети, а сам факт: по уровню он раскладывает геометрию, а порядок
1409
+ * расставляет уже рендер.
1410
+ */
1411
+ network?: string;
1352
1412
  /** Скругление углов в метрах — геометрия, поэтому едет в воркер. */
1353
1413
  cornerRadius?: number;
1354
1414
  cornerSegments?: number;
@@ -1616,6 +1676,14 @@ interface LoadedResponse {
1616
1676
  * этажи вообще есть в этом здании.
1617
1677
  */
1618
1678
  splits?: Record<string, (string | number)[]>;
1679
+ /**
1680
+ * Уровни пересечения, встретившиеся в этом тайле (см. `generators/level`), по возрастанию.
1681
+ *
1682
+ * Дорожная сеть рисуется НЕ по слоям, а по уровням: сначала всё, что под землёй, потом земля,
1683
+ * потом эстакады. Какие уровни в тайле есть, знает только тот, кто его разобрал, — рендеру
1684
+ * перебирать все возможные значения было бы дороже, чем передать список.
1685
+ */
1686
+ levels?: number[];
1619
1687
  timing: {
1620
1688
  fetchMs: number;
1621
1689
  parseMs: number;
@@ -3715,6 +3783,18 @@ declare class LayerRenderer {
3715
3783
  private applyMaterial;
3716
3784
  private drawFill;
3717
3785
  private drawLine;
3786
+ /**
3787
+ * СЕТЬ ЦЕЛИКОМ, по уровням пересечения (см. `LineLayerSpec.network`).
3788
+ *
3789
+ * Обычный слой рисуется на своём месте в стиле. Сеть так рисовать нельзя: обводка эстакады
3790
+ * должна лежать поверх ПОЛОТНА улицы под ней, а полотно улицы в стиле идёт позже обводок.
3791
+ * Поэтому вся сеть рисуется одним куском на месте своего первого слоя, и снаружи внутрь порядок
3792
+ * такой: уровень (тоннели → земля → эстакады), а внутри уровня — обычный порядок слоёв.
3793
+ *
3794
+ * Какие уровни в кадре есть, считаем по видимым тайлам: в обычном городе это один нулевой, и
3795
+ * тогда всё сводится к прежней отрисовке слой за слоем.
3796
+ */
3797
+ private drawNetwork;
3718
3798
  /**
3719
3799
  * Подсветка выбранного здания.
3720
3800
  *
@@ -4297,7 +4377,22 @@ declare function clipLineToRect(line: ScreenPoint[], width: number, height: numb
4297
4377
  */
4298
4378
  declare const BUILDING_VERTEX_STRIDE = 18;
4299
4379
  /** Множитель упаковки высоты в Uint16. */
4300
- declare const BUILDING_HEIGHT_SCALE = 4;
4380
+ declare const BUILDING_HEIGHT_SCALE = 32;
4381
+ /**
4382
+ * Множитель упаковки ПОЛОЖЕНИЯ вершины: сколько единиц Int16 приходится на единицу тайла.
4383
+ *
4384
+ * Было единица — то есть 0.47 м на z14, потому что координаты в MVT целые и вершину писали как
4385
+ * есть. Пока углы дома были прямыми, этого хватало: вершина и так стояла в узле сетки. Со
4386
+ * СКРУГЛЕНИЕМ хватать перестало — дуга радиусом 1.6 м это семь точек в пределах трёх единиц тайла,
4387
+ * и округление сажает их на ту же сетку: вместо дуги выходит лесенка со ступенью в полметра, она же
4388
+ * «угловатость» на ближнем зуме.
4389
+ *
4390
+ * Восемь единиц на тайловую — это 5.9 см, и дуга снова читается дугой. Потолок: тайл 0..4096, при
4391
+ * ×8 это 0..32768, и последнее значение на единицу больше, чем помещается в Int16, — оно
4392
+ * прижимается (`clampI16`). Ошибка при этом 6 см и только у вершины, стоящей ровно на дальней
4393
+ * границе тайла.
4394
+ */
4395
+ declare const BUILDING_POS_SCALE = 8;
4301
4396
  interface BuildingsMesh {
4302
4397
  vertices: ArrayBuffer;
4303
4398
  indices: ArrayBuffer;
@@ -4722,11 +4817,47 @@ interface LineMesh {
4722
4817
  vertexCount: number;
4723
4818
  indexCount: number;
4724
4819
  }
4820
+ interface LineOptions {
4821
+ /**
4822
+ * Уровень пересечения (см. `generators/level`): берём только фичи этого уровня.
4823
+ *
4824
+ * `undefined` — уровни не разделяем, слой строится целиком (так живут все слои, кроме дорожной
4825
+ * сети: у реки или границы пересечений без связи не бывает).
4826
+ */
4827
+ level?: number;
4828
+ }
4725
4829
  declare function generateLine(layer: VectorTileLayer, filter: Filter | undefined, maxSegment?: number,
4726
4830
  /** Уровни поверхностей тайла: по ним дорога ложится на то, по чему идёт. Нет — лежит на нуле. */
4727
4831
  ground?: GroundField,
4728
4832
  /** Метров в единице тайла. Ноль — плавный переход ширины на стыках не строится. */
4729
- metersPerUnit?: number): LineMesh | null;
4833
+ metersPerUnit?: number, opts?: LineOptions): LineMesh | null;
4834
+
4835
+ /**
4836
+ * УРОВЕНЬ ПЕРЕСЕЧЕНИЯ — какой из двух дорог быть сверху там, где они пересекаются без связи.
4837
+ *
4838
+ * ЗАЧЕМ. Плоская карта рисуется краской: слои идут в порядке стиля, и внутри слоя порядок фич
4839
+ * произволен. Поэтому эстакада над улицей ничем не отличалась от перекрёстка — обводки сливались,
4840
+ * полотна пересекались, и место читалось как съезд, которого в жизни нет. Ровно этим у Яндекса
4841
+ * занят `line-z-level` (`["get","z_level"]` у КАЖДОГО дорожного слоя, все они в одной сети
4842
+ * `line-network: "roads"`): сеть рисуется не по слоям, а по уровням, и мост со своей тенью и
4843
+ * обводкой целиком ложится поверх того, что под ним.
4844
+ *
4845
+ * ОТКУДА ЧИСЛО. Готового `z_level` в наших тайлах нет, зато есть то, из чего его считают:
4846
+ * `layer` — тег OSM, честный уровень (−1 подземный, 1 над землёй, изредка 2…5);
4847
+ * `brunnel` — «мост / тоннель / брод», он приходит и там, где `layer` не проставлен.
4848
+ * Поэтому берём `layer`, а если его нет — выводим уровень из `brunnel`. Мост без `layer`
4849
+ * (в Душанбе таких большинство) получает 1, тоннель — −1, обычная улица — 0.
4850
+ *
4851
+ * Считается в ВОРКЕРЕ, при тесселяции: по уровню бьётся меш, и рендеру остаётся только порядок.
4852
+ */
4853
+ /**
4854
+ * Дальше этого уровни не разводим. В OSM попадается и `layer=5`, и опечатки в сотни: каждое
4855
+ * значение — это отдельный бакет и отдельный вызов отрисовки, поэтому хвост подрезаем. Пять этажей
4856
+ * развязки — больше, чем бывает на настоящих.
4857
+ */
4858
+ declare const LEVEL_LIMIT = 5;
4859
+ /** Уровень фичи: `layer` из тегов, иначе вывод из `brunnel`. */
4860
+ declare function featureLevel(props: Record<string, unknown>): number;
4730
4861
 
4731
4862
  /**
4732
4863
  * Положение камеры в адресе страницы.
@@ -5676,10 +5807,19 @@ declare class Map$1 extends Evented<MapEvents> {
5676
5807
  };
5677
5808
  /** Просит нарисовать кадр. Движок рисует по требованию, а не постоянно. */
5678
5809
  triggerRepaint(): void;
5810
+ private debugFB;
5811
+ /**
5812
+ * Рисует один кадр в float-буфер и возвращает прямоугольник пикселей.
5813
+ * `x`, `y` — в координатах GL (снизу вверх), размеры в пикселях канвы.
5814
+ */
5815
+ debugFloatPixels(x: number, y: number, w: number, h: number): {
5816
+ data: Float32Array;
5817
+ format: string;
5818
+ } | null;
5679
5819
  private renderFrame;
5680
5820
  private stopFrameLoop;
5681
5821
  get isLoaded(): boolean;
5682
5822
  destroy(): void;
5683
5823
  }
5684
5824
 
5685
- export { type AnimationOptions, AssetLoader, type AssetLoaderOptions, BASE_SOURCE, BUILDING_HEIGHT_SCALE, BUILDING_VERTEX_STRIDE, type BuildingHit, type BuildingsMesh, CITY_STYLE, type CameraOptions, Circle, type CircleOptions, type CoverOptions, DEFAULT_ICONS, DEFAULT_LIGHT, DEFAULT_SHADOWS, DEFAULT_SKY, DEFAULT_STYLE, DepthTarget, type DeviceHints, EARTH_CIRCUMFERENCE, FILL_VERTEX_STRIDE, type Filter, type FitBoundsOptions, type FitViewport, type FogPlanes, GLOBE_MAX_PITCH, GLOBE_ZOOM_MAX, GLOBE_ZOOM_MIN, GLYPH_BORDER, GLYPH_SIZE, GROUND_SCALE, GeomType, type GestureOptions, type Glyph, type GlyphMetric, GlyphSource, GroundArrows, IconSource, IndoorMask, type Interpolated, LINE_VERTEX_STRIDE, type LayerPlan, LayerRenderer, type LightSpec, type LinePlacement, type LngLat, LngLatBounds, type LngLatLike, MAX_LATITUDE, MODEL_VERTEX_STRIDE, Map$1 as Map, type MapEvents, MapObject, type MapObjectOptions, type MapOptions, Marker, type MarkerOptions, Model, ModelLayer, type ModelMesh, type ModelOptions, type ModelPrimitive, ObjectManager, type ObjectPaint, type ObjectPart, type Padding, type PlacedGlyph, type PlacedSymbol, Polygon, type PolygonOptions, Polyline, type PolylineOptions, type PolylineSegment, Popup, type PopupOptions, type Projection, type QualityName, type QualityProfile, RELIEF_STYLE, RUNTIME_FILL_STRIDE, RUNTIME_LINE_STRIDE, RasterSource, type RasterSourceOptions, RasterTile, type RoundOptions, type RuntimeMesh, SDF_EDGE, SDF_PX, SOFT_STYLE, SPLIT_SEP, SURFACE_CUT_BLEED, SURFACE_LEVEL_SCALE, SURFACE_VERTEX_STRIDE, type ScreenPoint$1 as ScreenPoint, SdfAtlas, type ShadowSettings, type SkySpec, Style, type StyleLayerSpec, type StyleSpec, type SymbolHit, SymbolRenderer, THEMES, TILE_SIZE, type ThemeSpec, TileID, Transform, VectorTile, VectorTileFeature, VectorTileLayer, abbreviateStreet, addGround, altitudeFromMercatorZ, applyTheme, arrowLabelOffset, arrowVertices, buildRuntimeFill, buildRuntimeLine, cameraForBounds, circumferenceAtLatitude, clampLat, clampPixelRatio, clipLineToRect, coordinateDigits, coveringTiles, createGround, Map$1 as default, deviceHints, distToSegment, evaluateColor, evaluateNumber, extractFeature, fillFootprint, fogPlanes, footprintOf, formatHash, generateBuildings, generateFill, generateLine, generateSurface, generateSymbols, globeBasis, globeFlatMix, globeRadius, globeToLocal, groundAt, iconRotation, insideFootprint, latFromMercatorY, lngFromMercatorX, lngLatToUnit, matchesFilter, mercatorMetersPerTile, mercatorX, mercatorY, mercatorZFromAltitude, metersPerPixel, metersPerTile, nextQualityDown, parseGlb, parseGlbAsync, parseGlyphs, parseHash, patchUV, pickBuilding, pickFill, pitchIntent, pixelsPerMeter, placeAlongLine, pointInRing, qualityFor, qualityProfile, raySphere, resolveTemplate, roundRing, sdfFromAlpha, shapeText, splitBucketKey, splitByGround, splitPolygons, styleWithTheme, tileOriginMeters, tileUrl, toLngLat, unitToLngLat, worldSize };
5825
+ export { type AnimationOptions, AssetLoader, type AssetLoaderOptions, BASE_SOURCE, BUILDING_HEIGHT_SCALE, BUILDING_POS_SCALE, BUILDING_VERTEX_STRIDE, type BuildingHit, type BuildingsMesh, CITY_STYLE, type CameraOptions, Circle, type CircleOptions, type CoverOptions, DEFAULT_ICONS, DEFAULT_LIGHT, DEFAULT_SHADOWS, DEFAULT_SKY, DEFAULT_STYLE, DepthTarget, type DeviceHints, EARTH_CIRCUMFERENCE, FILL_VERTEX_STRIDE, type Filter, type FitBoundsOptions, type FitViewport, type FogPlanes, GLOBE_MAX_PITCH, GLOBE_ZOOM_MAX, GLOBE_ZOOM_MIN, GLYPH_BORDER, GLYPH_SIZE, GROUND_SCALE, GeomType, type GestureOptions, type Glyph, type GlyphMetric, GlyphSource, GroundArrows, IconSource, IndoorMask, type Interpolated, LEVEL_LIMIT, LINE_VERTEX_STRIDE, type LayerPlan, LayerRenderer, type LightSpec, type LinePlacement, type LngLat, LngLatBounds, type LngLatLike, MAX_LATITUDE, MODEL_VERTEX_STRIDE, Map$1 as Map, type MapEvents, MapObject, type MapObjectOptions, type MapOptions, Marker, type MarkerOptions, Model, ModelLayer, type ModelMesh, type ModelOptions, type ModelPrimitive, ObjectManager, type ObjectPaint, type ObjectPart, type Padding, type PlacedGlyph, type PlacedSymbol, Polygon, type PolygonOptions, Polyline, type PolylineOptions, type PolylineSegment, Popup, type PopupOptions, type Projection, type QualityName, type QualityProfile, RELIEF_STYLE, RUNTIME_FILL_STRIDE, RUNTIME_LINE_STRIDE, RasterSource, type RasterSourceOptions, RasterTile, type RoundOptions, type RuntimeMesh, SDF_EDGE, SDF_PX, SOFT_STYLE, SPLIT_SEP, SURFACE_CUT_BLEED, SURFACE_LEVEL_SCALE, SURFACE_VERTEX_STRIDE, type ScreenPoint$1 as ScreenPoint, SdfAtlas, type ShadowSettings, type SkySpec, Style, type StyleLayerSpec, type StyleSpec, type SymbolHit, SymbolRenderer, THEMES, TILE_SIZE, type ThemeSpec, TileID, Transform, VectorTile, VectorTileFeature, VectorTileLayer, abbreviateStreet, addGround, altitudeFromMercatorZ, applyTheme, arrowLabelOffset, arrowVertices, buildRuntimeFill, buildRuntimeLine, cameraForBounds, circumferenceAtLatitude, clampLat, clampPixelRatio, clipLineToRect, coordinateDigits, coveringTiles, createGround, Map$1 as default, deviceHints, distToSegment, evaluateColor, evaluateNumber, extractFeature, featureLevel, fillFootprint, fogPlanes, footprintOf, formatHash, generateBuildings, generateFill, generateLine, generateSurface, generateSymbols, globeBasis, globeFlatMix, globeRadius, globeToLocal, groundAt, iconRotation, insideFootprint, latFromMercatorY, lngFromMercatorX, lngLatToUnit, matchesFilter, mercatorMetersPerTile, mercatorX, mercatorY, mercatorZFromAltitude, metersPerPixel, metersPerTile, nextQualityDown, parseGlb, parseGlbAsync, parseGlyphs, parseHash, patchUV, pickBuilding, pickFill, pitchIntent, pixelsPerMeter, placeAlongLine, pointInRing, qualityFor, qualityProfile, raySphere, resolveTemplate, roundRing, sdfFromAlpha, shapeText, splitBucketKey, splitByGround, splitPolygons, styleWithTheme, tileOriginMeters, tileUrl, toLngLat, unitToLngLat, worldSize };