isard 2.1.3 → 2.2.1
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/CHANGELOG.md +23 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/isard_webgl2_bg.wasm +0 -0
- package/dist/isard_webgpu_bg.wasm +0 -0
- package/dist/map/clip-box.d.ts +52 -0
- package/dist/map/clip-box.js +226 -0
- package/dist/map/clip-box.js.map +1 -0
- package/dist/map/events.js +1 -0
- package/dist/map/events.js.map +1 -1
- package/dist/map.d.ts +11 -1
- package/dist/map.js +40 -0
- package/dist/map.js.map +1 -1
- package/dist/react/ClipBox.d.ts +9 -0
- package/dist/react/ClipBox.js +21 -0
- package/dist/react/ClipBox.js.map +1 -0
- package/dist/react/IsardMap.d.ts +1 -0
- package/dist/react/IsardMap.js +1 -0
- package/dist/react/IsardMap.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/types.d.ts +28 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/style.js +6 -0
- package/dist/ui/style.js.map +1 -1
- package/dist/wasm/isard_webgl2.d.ts +15 -2
- package/dist/wasm/isard_webgl2.js +32 -11
- package/dist/wasm/isard_webgpu.d.ts +15 -2
- package/dist/wasm/isard_webgpu.js +32 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ versions [Semantic Versioning](https://semver.org) (before 1.0.0, a minor versio
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [2.2.1] - 2026-11-01
|
|
9
|
+
|
|
10
|
+
With `isard-react-native` and `isard-map` (Python) 2.2.1.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The section box cuts what lies inside it by default, all around it staying (`cut: 'inside'`); `cut: 'outside'` for
|
|
15
|
+
a BIM viewer's section box. The example A section box takes the roof and the top of the walls away
|
|
16
|
+
- The section box's handle that moves it at its middle: under its foot it hid the bottom face's
|
|
17
|
+
|
|
18
|
+
## [2.2.0] - 2026-11-01
|
|
19
|
+
|
|
20
|
+
With `isard-react-native` and `isard-map` (Python) 2.2.0.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- A section box: `map.setClipBox({ position, altitude, size, heading, cut, sectionColor, handles })` cuts the 3D
|
|
25
|
+
layers (models, 3D Tiles, extrusions, point clouds) by a box, what lies outside it (a BIM viewer's section box) or
|
|
26
|
+
inside it (a cutaway), in the frame, the sun's shadows and the picking alike; the faces the cut opens filled, as a
|
|
27
|
+
section on a plan. Its handles (`handles: true`) size it by its faces and move it by its foot, each change told by
|
|
28
|
+
the `'clipbox'` event; `getClipBox()`. In React, `<ClipBox>` (`onChange`) and `onClipBox`. The example
|
|
29
|
+
[A section box](https://ignf.github.io/isard/examples/section-box) on buildingSMART's sample building
|
|
30
|
+
|
|
8
31
|
## [2.1.3] - 2026-11-01
|
|
9
32
|
|
|
10
33
|
With `isard-react-native` and `isard-map` (Python) 2.1.3.
|
package/dist/index.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ export { type VectorFeature, VectorLayer, type VectorSublayer } from './layers/v
|
|
|
67
67
|
export type { HoverPicking } from './map/hover-pick.ts';
|
|
68
68
|
export { GeoMap, type LayerSpec } from './map.ts';
|
|
69
69
|
export { PANORAMAX_API, type PanoramaxPicture, type PanoramaxSequenceOptions, panoramaxPanoramas, panoramaxPhoto, panoramaxPicture, panoramaxSequence, panoramaxStyle, } from './panoramax.ts';
|
|
70
|
-
export type { AnimationOptions, AtmosphereOptions, Basemap, BasemapName, BodyOptions, CelestialBody, Drape, FeatureColor, FeatureHit, FirstPersonView, FlyToOptions, GeoMapOptions, LoadingMode, LonLat, MapEvents, MapGpuTimings, MapHoverFeatureEvent, MapMemory, MapPointerEvent, NetworkOptions, NetworkStyle, PickOptions, Placement, Point, Projection, Quality, RasterGrid, RasterOptions, RasterStyle, SunOptions, SwipeOptions, TerrainEncoding, TerrainModel, TerrainOptions, Tiles3dOptions, Tiles3dStyle, ViewshedOptions, } from './types.ts';
|
|
70
|
+
export type { AnimationOptions, AtmosphereOptions, Basemap, BasemapName, BodyOptions, CelestialBody, ClipBox, Drape, FeatureColor, FeatureHit, FirstPersonView, FlyToOptions, GeoMapOptions, LoadingMode, LonLat, MapEvents, MapGpuTimings, MapHoverFeatureEvent, MapMemory, MapPointerEvent, NetworkOptions, NetworkStyle, PickOptions, Placement, Point, Projection, Quality, RasterGrid, RasterOptions, RasterStyle, SunOptions, SwipeOptions, TerrainEncoding, TerrainModel, TerrainOptions, Tiles3dOptions, Tiles3dStyle, ViewshedOptions, } from './types.ts';
|
|
71
71
|
export { AttributionControl, type AttributionOptions, type Control, type ControlPosition, NavigationControl, type NavigationOptions, } from './ui/controls.ts';
|
|
72
72
|
export { MinimapControl, type MinimapOptions } from './ui/minimap.ts';
|
|
73
73
|
export { type Anchor, Marker, type MarkerEvent, type MarkerEvents, type MarkerOptions, Panel, type PanelOptions, Popup, type PopupEvents, type PopupOptions, } from './ui/overlay.ts';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAE3F,OAAO,EAAE,YAAY,EAAmB,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EACL,OAAO,EAIP,WAAW,EACX,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAuB,MAAM,2BAA2B,CAAC;AAiB5E,OAAO,EAAE,QAAQ,EAAuB,MAAM,kCAAkC,CAAC;AAEjF,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAsB,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAGL,MAAM,EACN,qBAAqB,EACrB,WAAW,EACX,SAAS,EACT,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAOL,OAAO,EACP,OAAO,GACR,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAoC,MAAM,oBAAoB,CAAC;AAErF,OAAO,EACL,gBAAgB,EAEhB,WAAW,EACX,eAAe,EACf,WAAW,EAEX,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,WAAW,EAEX,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAa,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAoBxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEhH,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEtF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAKL,SAAS,GAEV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAqB,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC3F,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAmC,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAsB,WAAW,EAAuB,MAAM,qBAAqB,CAAC;AAE3F,OAAO,EAAE,MAAM,EAAkB,MAAM,UAAU,CAAC;AAClD,OAAO,EACL,aAAa,EAGb,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,GACf,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * isard: a WebGL2 map engine for large, interactive data layers, labels and 3D Tiles.\n *\n * ```ts\n * import { GeoMap } from 'isard';\n * const map = await GeoMap.create(container, { center: [2.4246, 48.8457], zoom: 13 });\n * const network = map.addNetwork({ nodes, edges, max: 1800, contour: 300 });\n * map.on('hover', ({ lonLat }) => lonLat && network.setValues(travelTimes(lonLat)));\n * map.addTiles3d({ url: 'https://…/tileset.json' });\n * map.addControl(new NavigationControl());\n * new Marker().setLonLat([2.4246, 48.8457]).setPopup(new Popup({ offset: [0, -34] }).setText('Here')).addTo(map);\n * ```\n */\nexport { OPENFREEMAP_ATTRIBUTION, TERRAIN_TILES_ATTRIBUTION } from './basemap/basemaps.ts';\nexport type { TileGrid, TileMatrix } from './basemap/grid.ts';\nexport { coniferModel, type TreeColors, treeModel } from './basemap/models/trees.ts';\nexport {\n PMTiles,\n type PmtilesEntry,\n type PmtilesHeader,\n type PmtilesTileType,\n tileIdToZxy,\n zxyToTileId,\n} from './basemap/pmtiles.ts';\nexport { basicStyle, type PmtilesOptions } from './basemap/pmtileslayer.ts';\nexport type {\n CandidateIcon,\n CandidateOverlap,\n Json,\n LabelCandidate,\n RasterSource,\n Style,\n StyleLayer,\n} from './basemap/vectorstyle.ts';\nexport type { VectorExtrusionOptions } from './basemap/vectortiles/extrusions.ts';\nexport type { FurnitureOptions } from './basemap/vectortiles/furniture.ts';\nexport type { LabelsOptions } from './basemap/vectortiles/labels.ts';\nexport type { LampsOptions } from './basemap/vectortiles/lamps.ts';\nexport type { StreetWindLayer, StreetWindOptions } from './basemap/vectortiles/street-wind.ts';\nexport type { LoadedStyle } from './basemap/vectortiles/style.ts';\nexport type { TreesOptions } from './basemap/vectortiles/trees.ts';\nexport { isStreet, type VectorsOptions } from './basemap/vectortiles/vectors.ts';\nexport type { VectorRasterOptions, VectorTilesOptions } from './basemap/vectortiles.ts';\nexport { OPENFREEMAP_STYLES, PLAN_STYLES, VectorTiles } from './basemap/vectortiles.ts';\nexport { parseWmtsGrid, wmtsGrid } from './basemap/wmts.ts';\nexport { type ColladaImages, colladaImages, loadCollada, parseCollada } from './data/collada.ts';\nexport {\n type Crs,\n type CrsLike,\n getCrs,\n lambertConformalConic,\n registerCrs,\n reproject,\n transverseMercator,\n} from './data/crs.ts';\nexport { parseGpx } from './data/gpx.ts';\nexport {\n type IfcElement,\n type IfcModel,\n type IfcOptions,\n type IfcPlace,\n type IfcProperties,\n type IfcStorey,\n loadIfc,\n WEB_IFC,\n} from './data/ifc.ts';\nexport { parseKml } from './data/kml.ts';\nexport { parseTopoJson, type TopoGeometry, type Topology } from './data/topojson.ts';\nexport type { DecoderUrls } from './decoding/decoders.ts';\nexport {\n BATIMENTS_3D_URL,\n type BdTopoBuilding,\n batiments3d,\n bdtopoBuildings,\n bdtopoRoofs,\n type GeocodedPlace,\n geoplateformeGeocode,\n geoplateformeWfs,\n geoplateformeWms,\n geoplateformeWmts,\n IGN_ATTRIBUTION,\n IGN_PRESETS,\n type IgnPreset,\n lambert93Grid,\n} from './geoplateforme.ts';\nexport { type Grid, windAt, windFields } from './grids.ts';\nexport type { CustomFrame, CustomLayerInterface } from './layers/custom.ts';\nexport { CustomLayer } from './layers/custom.ts';\nexport type { ExtrusionOptions, ExtrusionStyle, PolygonArrays, TileKey } from './layers/extrusions.ts';\nexport { ExtrusionLayer } from './layers/extrusions.ts';\nexport type {\n CircleOptions,\n CircleStyle,\n Feature,\n FeatureCollection,\n FeatureData,\n FeatureLayerStyle,\n FillOptions,\n FillStyle,\n Geometry,\n LineOptions,\n LineStyle,\n Position,\n Properties,\n Ramp,\n Size,\n Stops,\n Styled,\n} from './layers/features.ts';\nexport { CircleLayer, FeatureLayer, FillLayer, interpolate, LineLayer, parseColor } from './layers/features.ts';\nexport type { FlowField, FlowOptions, FlowStyle, TimedField } from './layers/flow.ts';\nexport { FlowLayer, flowFieldFromImage, flowFieldsFromImage } from './layers/flow.ts';\nexport type { TextPart } from './layers/glyphs.ts';\nexport { DEFAULT_FONT, GLYPHS_URL } from './layers/glyphs.ts';\nexport type { GroundOptions, GroundTexture, GroundUniform } from './layers/ground.ts';\nexport { GroundLayer } from './layers/ground.ts';\nexport type { ImageOptions, Pixels, SpriteIcon } from './layers/icons.ts';\nexport { Layer } from './layers/layers.ts';\nexport type { ModelInstance, ModelOptions } from './layers/models.ts';\nexport { ModelLayer } from './layers/models.ts';\nexport { NetworkLayer } from './layers/network.ts';\nexport {\n type CalibratedPanoramaOptions,\n type Panorama,\n type PanoramaOptions,\n type PanoramaPhoto,\n Panoramas,\n type PhotoPanoramaOptions,\n} from './layers/panoramas.ts';\nexport { type PhotoOptions, photoModel } from './layers/photos.ts';\nexport type { ClassStyle, PointCloudOptions, PointCloudStyle, PointColor } from './layers/pointclouds.ts';\nexport { CLASSIFICATIONS, ELEVATION_RAMP, PointCloudLayer } from './layers/pointclouds.ts';\nexport { RasterLayer } from './layers/raster.ts';\nexport type { Overlap, SymbolOptions, SymbolStyle } from './layers/symbols.ts';\nexport { SymbolLayer } from './layers/symbols.ts';\nexport { Tiles3dLayer } from './layers/tiles3d.ts';\nexport { type TreeKind, type TreeOptions, treeKind } from './layers/trees.ts';\nexport { type VectorFeature, VectorLayer, type VectorSublayer } from './layers/vectors.ts';\nexport type { HoverPicking } from './map/hover-pick.ts';\nexport { GeoMap, type LayerSpec } from './map.ts';\nexport {\n PANORAMAX_API,\n type PanoramaxPicture,\n type PanoramaxSequenceOptions,\n panoramaxPanoramas,\n panoramaxPhoto,\n panoramaxPicture,\n panoramaxSequence,\n panoramaxStyle,\n} from './panoramax.ts';\nexport type {\n AnimationOptions,\n AtmosphereOptions,\n Basemap,\n BasemapName,\n BodyOptions,\n CelestialBody,\n ClipBox,\n Drape,\n FeatureColor,\n FeatureHit,\n FirstPersonView,\n FlyToOptions,\n GeoMapOptions,\n LoadingMode,\n LonLat,\n MapEvents,\n MapGpuTimings,\n MapHoverFeatureEvent,\n MapMemory,\n MapPointerEvent,\n NetworkOptions,\n NetworkStyle,\n PickOptions,\n Placement,\n Point,\n Projection,\n Quality,\n RasterGrid,\n RasterOptions,\n RasterStyle,\n SunOptions,\n SwipeOptions,\n TerrainEncoding,\n TerrainModel,\n TerrainOptions,\n Tiles3dOptions,\n Tiles3dStyle,\n ViewshedOptions,\n} from './types.ts';\nexport {\n AttributionControl,\n type AttributionOptions,\n type Control,\n type ControlPosition,\n NavigationControl,\n type NavigationOptions,\n} from './ui/controls.ts';\nexport { MinimapControl, type MinimapOptions } from './ui/minimap.ts';\nexport {\n type Anchor,\n Marker,\n type MarkerEvent,\n type MarkerEvents,\n type MarkerOptions,\n Panel,\n type PanelOptions,\n Popup,\n type PopupEvents,\n type PopupOptions,\n} from './ui/overlay.ts';\nexport { lengthLabel, roundLength, ScaleControl, type ScaleOptions } from './ui/scale.ts';\nexport { SearchControl, type SearchOptions } from './ui/search.ts';\nexport { setRequestHeaders } from './util/requests.ts';\nexport type { RoofMode } from './util/roofs.ts';\nexport { sunPosition } from './util/sun.ts';\nexport type {\n Camera,\n NetworkStats,\n PickHit,\n PickResult,\n PointCloudInfo,\n PointCloudPoint,\n PointCloudStats,\n RasterLayerStats,\n Tiles3dStats,\n} from './wasm/isard_webgpu.js';\n"]}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The clipping box (`GeoMap.setClipBox`): given to the engine, and its handles over the map, an outline of its edges
|
|
3
|
+
* and a disc on each face to size it, one at its middle to move it, placed after every frame.
|
|
4
|
+
*/
|
|
5
|
+
import type { ClipBox, LonLat, Point } from '../types.ts';
|
|
6
|
+
import type { Positioned } from '../ui/overlay.ts';
|
|
7
|
+
import type { Engine } from '../wasm/isard_webgpu.js';
|
|
8
|
+
/** Sets the box in the engine, or clears it without one */
|
|
9
|
+
export declare const applyClipBox: (engine: Engine, box: ClipBox | undefined) => void;
|
|
10
|
+
/** What the handles need from the map */
|
|
11
|
+
export interface ClipBoxHost {
|
|
12
|
+
project(lonLat: LonLat, height?: number): Point | undefined;
|
|
13
|
+
unproject(point: Point): LonLat | undefined;
|
|
14
|
+
elevationAt(lonLat: LonLat): number | undefined;
|
|
15
|
+
getCanvas(): HTMLCanvasElement;
|
|
16
|
+
/** The box changed by a handle: set, and told */
|
|
17
|
+
moved(box: ClipBox): void;
|
|
18
|
+
}
|
|
19
|
+
/** The handles of a box, over the map */
|
|
20
|
+
export declare class ClipBoxHandles implements Positioned {
|
|
21
|
+
private readonly host;
|
|
22
|
+
private box;
|
|
23
|
+
/** Metres of the body's radius (metres to degrees) */
|
|
24
|
+
private readonly radius;
|
|
25
|
+
/** Over the canvas, its drawing within */
|
|
26
|
+
readonly element: HTMLDivElement;
|
|
27
|
+
private readonly lines;
|
|
28
|
+
private readonly discs;
|
|
29
|
+
/** The handle held (its disc), the pointer and the box where it was pressed */
|
|
30
|
+
private held;
|
|
31
|
+
constructor(host: ClipBoxHost, box: ClipBox,
|
|
32
|
+
/** Metres of the body's radius (metres to degrees) */
|
|
33
|
+
radius: number);
|
|
34
|
+
/** The box the handles follow from the next placing (`position`, after each frame) */
|
|
35
|
+
set(box: ClipBox): void;
|
|
36
|
+
position(): void;
|
|
37
|
+
/** A point of the box, metres across and along its heading from its foot's center and up from its foot */
|
|
38
|
+
private at;
|
|
39
|
+
private corners;
|
|
40
|
+
/** Where a handle stands on the screen: a face's center, or the box's */
|
|
41
|
+
private handleAt;
|
|
42
|
+
private local;
|
|
43
|
+
private down;
|
|
44
|
+
private move;
|
|
45
|
+
/** Metres the pointer dragged a face outward: the drag along the face's axis on the screen */
|
|
46
|
+
private dragged;
|
|
47
|
+
/** The box with a face moved `metres` outward, the opposite face where it was */
|
|
48
|
+
private resized;
|
|
49
|
+
/** A position `east` and `north` metres from another */
|
|
50
|
+
private offset;
|
|
51
|
+
private up;
|
|
52
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The clipping box (`GeoMap.setClipBox`): given to the engine, and its handles over the map, an outline of its edges
|
|
3
|
+
* and a disc on each face to size it, one at its middle to move it, placed after every frame.
|
|
4
|
+
*/
|
|
5
|
+
import { rgb } from "../settings.js";
|
|
6
|
+
/** The faces' color by default: plaster, as a section on a plan */
|
|
7
|
+
const SECTION = '#d9cfc1';
|
|
8
|
+
/** Metres a side of the box keeps at least */
|
|
9
|
+
const SMALLEST = 0.2;
|
|
10
|
+
const SVG = 'http://www.w3.org/2000/svg';
|
|
11
|
+
/** Sets the box in the engine, or clears it without one */
|
|
12
|
+
export const applyClipBox = (engine, box) => {
|
|
13
|
+
if (!box) {
|
|
14
|
+
engine.clearClipBox();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const [width, length, height] = box.size;
|
|
18
|
+
const section = box.sectionColor === null ? [] : rgb(box.sectionColor ?? SECTION);
|
|
19
|
+
engine.setClipBox(new Float64Array([
|
|
20
|
+
box.position[0],
|
|
21
|
+
box.position[1],
|
|
22
|
+
box.altitude ?? 0,
|
|
23
|
+
width,
|
|
24
|
+
length,
|
|
25
|
+
height,
|
|
26
|
+
box.heading ?? 0,
|
|
27
|
+
box.cut === 'outside' ? 0 : 1,
|
|
28
|
+
box.altitudeReference === 'sea' ? 0 : 1,
|
|
29
|
+
]), new Float32Array(section));
|
|
30
|
+
};
|
|
31
|
+
/** Metres across, along and up from the foot's center of a face's center, `out` metres out of it */
|
|
32
|
+
const faceAt = (box, { axis, side }, out = 0) => {
|
|
33
|
+
const [, , h] = box.size;
|
|
34
|
+
const offset = [0, 0, h / 2];
|
|
35
|
+
offset[axis] = axis === 2 ? (side > 0 ? h : 0) + side * out : side * (box.size[axis] / 2 + out);
|
|
36
|
+
return offset;
|
|
37
|
+
};
|
|
38
|
+
/** The 12 edges of the box, as pairs of corners (bit 0 across, 1 along, 2 up) */
|
|
39
|
+
const EDGES = [
|
|
40
|
+
[0, 1],
|
|
41
|
+
[2, 3],
|
|
42
|
+
[4, 5],
|
|
43
|
+
[6, 7],
|
|
44
|
+
[0, 2],
|
|
45
|
+
[1, 3],
|
|
46
|
+
[4, 6],
|
|
47
|
+
[5, 7],
|
|
48
|
+
[0, 4],
|
|
49
|
+
[1, 5],
|
|
50
|
+
[2, 6],
|
|
51
|
+
[3, 7],
|
|
52
|
+
];
|
|
53
|
+
const HANDLES = [
|
|
54
|
+
{ axis: 0, side: -1 },
|
|
55
|
+
{ axis: 0, side: 1 },
|
|
56
|
+
{ axis: 1, side: -1 },
|
|
57
|
+
{ axis: 1, side: 1 },
|
|
58
|
+
{ axis: 2, side: -1 },
|
|
59
|
+
{ axis: 2, side: 1 },
|
|
60
|
+
'move',
|
|
61
|
+
];
|
|
62
|
+
/** The handles of a box, over the map */
|
|
63
|
+
export class ClipBoxHandles {
|
|
64
|
+
host;
|
|
65
|
+
box;
|
|
66
|
+
radius;
|
|
67
|
+
/** Over the canvas, its drawing within */
|
|
68
|
+
element;
|
|
69
|
+
lines;
|
|
70
|
+
discs;
|
|
71
|
+
/** The handle held (its disc), the pointer and the box where it was pressed */
|
|
72
|
+
held;
|
|
73
|
+
constructor(host, box,
|
|
74
|
+
/** Metres of the body's radius (metres to degrees) */
|
|
75
|
+
radius) {
|
|
76
|
+
this.host = host;
|
|
77
|
+
this.box = box;
|
|
78
|
+
this.radius = radius;
|
|
79
|
+
const svg = document.createElementNS(SVG, 'svg');
|
|
80
|
+
this.element = document.createElement('div');
|
|
81
|
+
this.element.className = 'isard-clipbox';
|
|
82
|
+
this.element.append(svg);
|
|
83
|
+
this.lines = EDGES.map(() => svg.appendChild(document.createElementNS(SVG, 'line')));
|
|
84
|
+
this.discs = HANDLES.map((handle) => {
|
|
85
|
+
const disc = document.createElementNS(SVG, 'circle');
|
|
86
|
+
disc.setAttribute('r', handle === 'move' ? '8' : '6');
|
|
87
|
+
disc.setAttribute('class', handle === 'move' ? 'isard-clipbox-move' : 'isard-clipbox-face');
|
|
88
|
+
disc.addEventListener('pointerdown', (e) => this.down(e, handle, disc));
|
|
89
|
+
disc.addEventListener('pointermove', (e) => this.move(e));
|
|
90
|
+
disc.addEventListener('pointerup', (e) => this.up(e));
|
|
91
|
+
disc.addEventListener('pointercancel', (e) => this.up(e));
|
|
92
|
+
return svg.appendChild(disc);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** The box the handles follow from the next placing (`position`, after each frame) */
|
|
96
|
+
set(box) {
|
|
97
|
+
this.box = box;
|
|
98
|
+
}
|
|
99
|
+
position() {
|
|
100
|
+
const corners = this.corners(this.box);
|
|
101
|
+
const centers = HANDLES.map((handle) => this.handleAt(this.box, handle));
|
|
102
|
+
EDGES.forEach(([a, b], i) => {
|
|
103
|
+
const [p, q] = [corners[a], corners[b]];
|
|
104
|
+
const line = this.lines[i];
|
|
105
|
+
if (!line)
|
|
106
|
+
return;
|
|
107
|
+
line.style.display = p && q ? '' : 'none';
|
|
108
|
+
if (!p || !q)
|
|
109
|
+
return;
|
|
110
|
+
line.setAttribute('x1', String(p[0]));
|
|
111
|
+
line.setAttribute('y1', String(p[1]));
|
|
112
|
+
line.setAttribute('x2', String(q[0]));
|
|
113
|
+
line.setAttribute('y2', String(q[1]));
|
|
114
|
+
});
|
|
115
|
+
centers.forEach((p, i) => {
|
|
116
|
+
const disc = this.discs[i];
|
|
117
|
+
if (!disc)
|
|
118
|
+
return;
|
|
119
|
+
disc.style.display = p ? '' : 'none';
|
|
120
|
+
if (!p)
|
|
121
|
+
return;
|
|
122
|
+
disc.setAttribute('cx', String(p[0]));
|
|
123
|
+
disc.setAttribute('cy', String(p[1]));
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/** A point of the box, metres across and along its heading from its foot's center and up from its foot */
|
|
127
|
+
at(box, across, along, up) {
|
|
128
|
+
const heading = ((box.heading ?? 0) * Math.PI) / 180;
|
|
129
|
+
const [sin, cos] = [Math.sin(heading), Math.cos(heading)];
|
|
130
|
+
const lonLat = this.offset(box.position, across * cos + along * sin, along * cos - across * sin);
|
|
131
|
+
// `project` measures from the relief: an altitude above the ellipsoid less the relief under the box
|
|
132
|
+
const ground = box.altitudeReference === 'sea' ? (this.host.elevationAt(box.position) ?? 0) : 0;
|
|
133
|
+
return this.host.project(lonLat, (box.altitude ?? 0) - ground + up);
|
|
134
|
+
}
|
|
135
|
+
corners(box) {
|
|
136
|
+
const [w, l, h] = box.size;
|
|
137
|
+
return Array.from({ length: 8 }, (_, k) => this.at(box, (k & 1 ? 0.5 : -0.5) * w, (k & 2 ? 0.5 : -0.5) * l, k & 4 ? h : 0));
|
|
138
|
+
}
|
|
139
|
+
/** Where a handle stands on the screen: a face's center, or the box's */
|
|
140
|
+
handleAt(box, handle) {
|
|
141
|
+
return handle === 'move' ? this.at(box, 0, 0, box.size[2] / 2) : this.at(box, ...faceAt(box, handle));
|
|
142
|
+
}
|
|
143
|
+
local(e) {
|
|
144
|
+
const rect = this.host.getCanvas().getBoundingClientRect();
|
|
145
|
+
return [e.clientX - rect.left, e.clientY - rect.top];
|
|
146
|
+
}
|
|
147
|
+
down(e, handle, disc) {
|
|
148
|
+
if (e.button !== 0)
|
|
149
|
+
return;
|
|
150
|
+
const start = this.local(e);
|
|
151
|
+
// The spot of the ground grabbed, kept under the pointer as the box moves
|
|
152
|
+
const grab = handle === 'move' ? this.host.unproject(start) : undefined;
|
|
153
|
+
// The map must not pan under the handle
|
|
154
|
+
e.stopPropagation();
|
|
155
|
+
e.preventDefault();
|
|
156
|
+
disc.setPointerCapture(e.pointerId);
|
|
157
|
+
this.held = {
|
|
158
|
+
handle,
|
|
159
|
+
disc,
|
|
160
|
+
start,
|
|
161
|
+
box: { ...this.box, size: [...this.box.size] },
|
|
162
|
+
grab: grab ? [grab[0] - this.box.position[0], grab[1] - this.box.position[1]] : [0, 0],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
move(e) {
|
|
166
|
+
if (!this.held)
|
|
167
|
+
return;
|
|
168
|
+
e.stopPropagation();
|
|
169
|
+
const { handle, start, box, grab } = this.held;
|
|
170
|
+
const at = this.local(e);
|
|
171
|
+
if (handle === 'move') {
|
|
172
|
+
// The ground under the pointer, the grabbed spot kept under it
|
|
173
|
+
const lonLat = this.host.unproject(at);
|
|
174
|
+
if (lonLat)
|
|
175
|
+
this.host.moved({ ...box, position: [lonLat[0] - grab[0], lonLat[1] - grab[1]] });
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const metres = this.dragged(box, handle, [at[0] - start[0], at[1] - start[1]]);
|
|
179
|
+
if (metres !== undefined)
|
|
180
|
+
this.host.moved(this.resized(box, handle, metres));
|
|
181
|
+
}
|
|
182
|
+
/** Metres the pointer dragged a face outward: the drag along the face's axis on the screen */
|
|
183
|
+
dragged(box, face, [dx, dy]) {
|
|
184
|
+
const from = this.at(box, ...faceAt(box, face));
|
|
185
|
+
// Where the face's center goes a metre out, on the screen: pixels per metre along its axis
|
|
186
|
+
const to = this.at(box, ...faceAt(box, face, 1));
|
|
187
|
+
if (!from || !to)
|
|
188
|
+
return undefined;
|
|
189
|
+
const [ax, ay] = [to[0] - from[0], to[1] - from[1]];
|
|
190
|
+
const squared = ax * ax + ay * ay;
|
|
191
|
+
return squared > 1e-6 ? (dx * ax + dy * ay) / squared : undefined;
|
|
192
|
+
}
|
|
193
|
+
/** The box with a face moved `metres` outward, the opposite face where it was */
|
|
194
|
+
resized(box, { axis, side }, metres) {
|
|
195
|
+
const size = [...box.size];
|
|
196
|
+
const grown = Math.max(SMALLEST, size[axis] + metres);
|
|
197
|
+
const moved = grown - size[axis];
|
|
198
|
+
size[axis] = grown;
|
|
199
|
+
if (axis === 2) {
|
|
200
|
+
// The foot lowered when the bottom face goes down
|
|
201
|
+
return side > 0 ? { ...box, size } : { ...box, size, altitude: (box.altitude ?? 0) - moved };
|
|
202
|
+
}
|
|
203
|
+
// The center half-way along the move, on the side of the face moved
|
|
204
|
+
const heading = ((box.heading ?? 0) * Math.PI) / 180;
|
|
205
|
+
const [sin, cos] = [Math.sin(heading), Math.cos(heading)];
|
|
206
|
+
const shift = (side * moved) / 2;
|
|
207
|
+
const [east, north] = axis === 0 ? [shift * cos, -shift * sin] : [shift * sin, shift * cos];
|
|
208
|
+
return { ...box, size, position: this.offset(box.position, east, north) };
|
|
209
|
+
}
|
|
210
|
+
/** A position `east` and `north` metres from another */
|
|
211
|
+
offset([lon, lat], east, north) {
|
|
212
|
+
return [
|
|
213
|
+
lon + (east / (this.radius * Math.cos((lat * Math.PI) / 180))) * (180 / Math.PI),
|
|
214
|
+
lat + (north / this.radius) * (180 / Math.PI),
|
|
215
|
+
];
|
|
216
|
+
}
|
|
217
|
+
up(e) {
|
|
218
|
+
if (!this.held)
|
|
219
|
+
return;
|
|
220
|
+
e.stopPropagation();
|
|
221
|
+
if (this.held.disc.hasPointerCapture(e.pointerId))
|
|
222
|
+
this.held.disc.releasePointerCapture(e.pointerId);
|
|
223
|
+
this.held = undefined;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=clip-box.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clip-box.js","sourceRoot":"","sources":["../../src/map/clip-box.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The clipping box (`GeoMap.setClipBox`): given to the engine, and its handles over the map, an outline of its edges\n * and a disc on each face to size it, one at its middle to move it, placed after every frame.\n */\n\nimport { rgb } from '../settings.ts';\nimport type { ClipBox, LonLat, Point } from '../types.ts';\nimport type { Positioned } from '../ui/overlay.ts';\nimport type { Engine } from '../wasm/isard_webgpu.js';\n\n/** The faces' color by default: plaster, as a section on a plan */\nconst SECTION = '#d9cfc1';\n/** Metres a side of the box keeps at least */\nconst SMALLEST = 0.2;\nconst SVG = 'http://www.w3.org/2000/svg';\n\n/** Sets the box in the engine, or clears it without one */\nexport const applyClipBox = (engine: Engine, box: ClipBox | undefined): void => {\n if (!box) {\n engine.clearClipBox();\n\n return;\n }\n\n const [width, length, height] = box.size;\n const section = box.sectionColor === null ? [] : rgb(box.sectionColor ?? SECTION);\n\n engine.setClipBox(\n new Float64Array([\n box.position[0],\n box.position[1],\n box.altitude ?? 0,\n width,\n length,\n height,\n box.heading ?? 0,\n box.cut === 'outside' ? 0 : 1,\n box.altitudeReference === 'sea' ? 0 : 1,\n ]),\n new Float32Array(section),\n );\n};\n\n/** What the handles need from the map */\nexport interface ClipBoxHost {\n project(lonLat: LonLat, height?: number): Point | undefined;\n unproject(point: Point): LonLat | undefined;\n elevationAt(lonLat: LonLat): number | undefined;\n getCanvas(): HTMLCanvasElement;\n /** The box changed by a handle: set, and told */\n moved(box: ClipBox): void;\n}\n\n/** A face's handle: its axis (0 across, 1 along, 2 up) and side */\ninterface Face {\n axis: 0 | 1 | 2;\n side: -1 | 1;\n}\n\n/** A handle: on a face, or at the middle (moves the box, clear of the bottom face's) */\ntype Handle = Face | 'move';\n\n/** Metres across, along and up from the foot's center of a face's center, `out` metres out of it */\nconst faceAt = (box: ClipBox, { axis, side }: Face, out = 0): [number, number, number] => {\n const [, , h] = box.size;\n const offset: [number, number, number] = [0, 0, h / 2];\n\n offset[axis] = axis === 2 ? (side > 0 ? h : 0) + side * out : side * (box.size[axis] / 2 + out);\n\n return offset;\n};\n\n/** The 12 edges of the box, as pairs of corners (bit 0 across, 1 along, 2 up) */\nconst EDGES: [number, number][] = [\n [0, 1],\n [2, 3],\n [4, 5],\n [6, 7],\n [0, 2],\n [1, 3],\n [4, 6],\n [5, 7],\n [0, 4],\n [1, 5],\n [2, 6],\n [3, 7],\n];\n\nconst HANDLES: Handle[] = [\n { axis: 0, side: -1 },\n { axis: 0, side: 1 },\n { axis: 1, side: -1 },\n { axis: 1, side: 1 },\n { axis: 2, side: -1 },\n { axis: 2, side: 1 },\n 'move',\n];\n\n/** The handles of a box, over the map */\nexport class ClipBoxHandles implements Positioned {\n /** Over the canvas, its drawing within */\n readonly element: HTMLDivElement;\n private readonly lines: SVGLineElement[];\n private readonly discs: SVGCircleElement[];\n /** The handle held (its disc), the pointer and the box where it was pressed */\n private held: { handle: Handle; disc: SVGCircleElement; start: Point; box: ClipBox; grab: Point } | undefined;\n\n constructor(\n private readonly host: ClipBoxHost,\n private box: ClipBox,\n /** Metres of the body's radius (metres to degrees) */\n private readonly radius: number,\n ) {\n const svg = document.createElementNS(SVG, 'svg');\n\n this.element = document.createElement('div');\n this.element.className = 'isard-clipbox';\n this.element.append(svg);\n this.lines = EDGES.map(() => svg.appendChild(document.createElementNS(SVG, 'line')));\n this.discs = HANDLES.map((handle) => {\n const disc = document.createElementNS(SVG, 'circle');\n\n disc.setAttribute('r', handle === 'move' ? '8' : '6');\n disc.setAttribute('class', handle === 'move' ? 'isard-clipbox-move' : 'isard-clipbox-face');\n disc.addEventListener('pointerdown', (e) => this.down(e, handle, disc));\n disc.addEventListener('pointermove', (e) => this.move(e));\n disc.addEventListener('pointerup', (e) => this.up(e));\n disc.addEventListener('pointercancel', (e) => this.up(e));\n\n return svg.appendChild(disc);\n });\n }\n\n /** The box the handles follow from the next placing (`position`, after each frame) */\n set(box: ClipBox): void {\n this.box = box;\n }\n\n position(): void {\n const corners = this.corners(this.box);\n const centers = HANDLES.map((handle) => this.handleAt(this.box, handle));\n\n EDGES.forEach(([a, b], i) => {\n const [p, q] = [corners[a], corners[b]];\n const line = this.lines[i];\n\n if (!line) return;\n line.style.display = p && q ? '' : 'none';\n if (!p || !q) return;\n line.setAttribute('x1', String(p[0]));\n line.setAttribute('y1', String(p[1]));\n line.setAttribute('x2', String(q[0]));\n line.setAttribute('y2', String(q[1]));\n });\n centers.forEach((p, i) => {\n const disc = this.discs[i];\n\n if (!disc) return;\n disc.style.display = p ? '' : 'none';\n if (!p) return;\n disc.setAttribute('cx', String(p[0]));\n disc.setAttribute('cy', String(p[1]));\n });\n }\n\n /** A point of the box, metres across and along its heading from its foot's center and up from its foot */\n private at(box: ClipBox, across: number, along: number, up: number): Point | undefined {\n const heading = ((box.heading ?? 0) * Math.PI) / 180;\n const [sin, cos] = [Math.sin(heading), Math.cos(heading)];\n const lonLat = this.offset(box.position, across * cos + along * sin, along * cos - across * sin);\n // `project` measures from the relief: an altitude above the ellipsoid less the relief under the box\n const ground = box.altitudeReference === 'sea' ? (this.host.elevationAt(box.position) ?? 0) : 0;\n\n return this.host.project(lonLat, (box.altitude ?? 0) - ground + up);\n }\n\n private corners(box: ClipBox): (Point | undefined)[] {\n const [w, l, h] = box.size;\n\n return Array.from({ length: 8 }, (_, k) =>\n this.at(box, (k & 1 ? 0.5 : -0.5) * w, (k & 2 ? 0.5 : -0.5) * l, k & 4 ? h : 0),\n );\n }\n\n /** Where a handle stands on the screen: a face's center, or the box's */\n private handleAt(box: ClipBox, handle: Handle): Point | undefined {\n return handle === 'move' ? this.at(box, 0, 0, box.size[2] / 2) : this.at(box, ...faceAt(box, handle));\n }\n\n private local(e: PointerEvent): Point {\n const rect = this.host.getCanvas().getBoundingClientRect();\n\n return [e.clientX - rect.left, e.clientY - rect.top];\n }\n\n private down(e: PointerEvent, handle: Handle, disc: SVGCircleElement): void {\n if (e.button !== 0) return;\n\n const start = this.local(e);\n // The spot of the ground grabbed, kept under the pointer as the box moves\n const grab = handle === 'move' ? this.host.unproject(start) : undefined;\n\n // The map must not pan under the handle\n e.stopPropagation();\n e.preventDefault();\n disc.setPointerCapture(e.pointerId);\n this.held = {\n handle,\n disc,\n start,\n box: { ...this.box, size: [...this.box.size] },\n grab: grab ? [grab[0] - this.box.position[0], grab[1] - this.box.position[1]] : [0, 0],\n };\n }\n\n private move(e: PointerEvent): void {\n if (!this.held) return;\n e.stopPropagation();\n\n const { handle, start, box, grab } = this.held;\n const at = this.local(e);\n\n if (handle === 'move') {\n // The ground under the pointer, the grabbed spot kept under it\n const lonLat = this.host.unproject(at);\n\n if (lonLat) this.host.moved({ ...box, position: [lonLat[0] - grab[0], lonLat[1] - grab[1]] });\n\n return;\n }\n\n const metres = this.dragged(box, handle, [at[0] - start[0], at[1] - start[1]]);\n\n if (metres !== undefined) this.host.moved(this.resized(box, handle, metres));\n }\n\n /** Metres the pointer dragged a face outward: the drag along the face's axis on the screen */\n private dragged(box: ClipBox, face: Face, [dx, dy]: Point): number | undefined {\n const from = this.at(box, ...faceAt(box, face));\n // Where the face's center goes a metre out, on the screen: pixels per metre along its axis\n const to = this.at(box, ...faceAt(box, face, 1));\n\n if (!from || !to) return undefined;\n\n const [ax, ay] = [to[0] - from[0], to[1] - from[1]];\n const squared = ax * ax + ay * ay;\n\n return squared > 1e-6 ? (dx * ax + dy * ay) / squared : undefined;\n }\n\n /** The box with a face moved `metres` outward, the opposite face where it was */\n private resized(box: ClipBox, { axis, side }: Face, metres: number): ClipBox {\n const size: [number, number, number] = [...box.size];\n const grown = Math.max(SMALLEST, size[axis] + metres);\n const moved = grown - size[axis];\n\n size[axis] = grown;\n if (axis === 2) {\n // The foot lowered when the bottom face goes down\n return side > 0 ? { ...box, size } : { ...box, size, altitude: (box.altitude ?? 0) - moved };\n }\n\n // The center half-way along the move, on the side of the face moved\n const heading = ((box.heading ?? 0) * Math.PI) / 180;\n const [sin, cos] = [Math.sin(heading), Math.cos(heading)];\n const shift = (side * moved) / 2;\n const [east, north] = axis === 0 ? [shift * cos, -shift * sin] : [shift * sin, shift * cos];\n return { ...box, size, position: this.offset(box.position, east, north) };\n }\n\n /** A position `east` and `north` metres from another */\n private offset([lon, lat]: LonLat, east: number, north: number): LonLat {\n return [\n lon + (east / (this.radius * Math.cos((lat * Math.PI) / 180))) * (180 / Math.PI),\n lat + (north / this.radius) * (180 / Math.PI),\n ];\n }\n\n private up(e: PointerEvent): void {\n if (!this.held) return;\n e.stopPropagation();\n if (this.held.disc.hasPointerCapture(e.pointerId)) this.held.disc.releasePointerCapture(e.pointerId);\n this.held = undefined;\n }\n}\n"]}
|
package/dist/map/events.js
CHANGED
package/dist/map/events.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/map/events.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAG9E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,iDAAiD;AACjD,MAAM,OAAO,YAAY;IACN,SAAS,GAAc;QACtC,SAAS,EAAE,IAAI,GAAG,EAAE;QACpB,IAAI,EAAE,IAAI,GAAG,EAAE;QACf,OAAO,EAAE,IAAI,GAAG,EAAE;QAClB,MAAM,EAAE,IAAI,GAAG,EAAE;QACjB,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,MAAM,EAAE,IAAI,GAAG,EAAE;QACjB,OAAO,EAAE,IAAI,GAAG,EAAE;QAClB,IAAI,EAAE,IAAI,GAAG,EAAE;QACf,KAAK,EAAE,IAAI,GAAG,EAAE;
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/map/events.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAG9E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,iDAAiD;AACjD,MAAM,OAAO,YAAY;IACN,SAAS,GAAc;QACtC,SAAS,EAAE,IAAI,GAAG,EAAE;QACpB,IAAI,EAAE,IAAI,GAAG,EAAE;QACf,OAAO,EAAE,IAAI,GAAG,EAAE;QAClB,MAAM,EAAE,IAAI,GAAG,EAAE;QACjB,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,MAAM,EAAE,IAAI,GAAG,EAAE;QACjB,OAAO,EAAE,IAAI,GAAG,EAAE;QAClB,IAAI,EAAE,IAAI,GAAG,EAAE;QACf,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,OAAO,EAAE,IAAI,GAAG,EAAE;KACnB,CAAC;IAEF,qEAAqE;IACrE,EAAE,CAA4B,IAAO,EAAE,QAAuC;QAC5E,MAAM,SAAS,GAAuC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAE3E,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAExB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED,kGAAkG;IAClG,IAAI,CAA4B,IAAO,EAAE,KAAmB;QAC1D,MAAM,SAAS,GAAuC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAE3E,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YAC7B,IAAI,CAAC;gBACH,QAAQ,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,kCAAkC;IAClC,GAAG,CAAC,IAAqB;QACvB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;IACvC,CAAC;IAED,6BAA6B;IAC7B,KAAK;QACH,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;YAClD,SAAS,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;CACF","sourcesContent":["/** The listeners of the map's events: added, called, cleared with the map. */\n\nimport type { MapEvents } from '../types.ts';\nimport { report } from './checks.ts';\n\ntype Listeners = { [K in keyof MapEvents]: Set<(event: MapEvents[K]) => void> };\n\n/** The listeners of the map's events, by type */\nexport class MapListeners {\n private readonly listeners: Listeners = {\n movestart: new Set(),\n move: new Set(),\n moveend: new Set(),\n resize: new Set(),\n hover: new Set(),\n hoverfeature: new Set(),\n click: new Set(),\n render: new Set(),\n quality: new Set(),\n idle: new Set(),\n leave: new Set(),\n clipbox: new Set(),\n };\n\n /** Listens to an event; returns the function that stops listening */\n on<K extends keyof MapEvents>(type: K, listener: (event: MapEvents[K]) => void): () => void {\n const listeners: Set<(event: MapEvents[K]) => void> = this.listeners[type];\n\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n }\n\n /** Calls the listeners; one that throws is reported and does not stop the others nor the frame */\n emit<K extends keyof MapEvents>(type: K, event: MapEvents[K]): void {\n const listeners: Set<(event: MapEvents[K]) => void> = this.listeners[type];\n\n listeners.forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n report(error);\n }\n });\n }\n\n /** Something listens to `type` */\n has(type: keyof MapEvents): boolean {\n return this.listeners[type].size > 0;\n }\n\n /** Forgets every listener */\n clear(): void {\n Object.values(this.listeners).forEach((listeners) => {\n listeners.clear();\n });\n }\n}\n"]}
|
package/dist/map.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ import { Tiles3dLayer } from './layers/tiles3d.ts';
|
|
|
26
26
|
import { type TreeOptions } from './layers/trees.ts';
|
|
27
27
|
import { type HoverPicking } from './map/hover-pick.ts';
|
|
28
28
|
import { type LayerSpec } from './map/layer-spec.ts';
|
|
29
|
-
import type { AnimationOptions, AtmosphereOptions, Basemap, FeatureHit, FirstPersonView, FlyToOptions, GeoMapOptions, LoadingMode, LonLat, MapEvents, MapGpuTimings, MapMemory, NetworkOptions, PickOptions, Point, Projection, Quality, RasterOptions, SunOptions, SwipeOptions, TerrainModel, TerrainOptions, Tiles3dOptions, ViewshedOptions } from './types.ts';
|
|
29
|
+
import type { AnimationOptions, AtmosphereOptions, Basemap, ClipBox, FeatureHit, FirstPersonView, FlyToOptions, GeoMapOptions, LoadingMode, LonLat, MapEvents, MapGpuTimings, MapMemory, NetworkOptions, PickOptions, Point, Projection, Quality, RasterOptions, SunOptions, SwipeOptions, TerrainModel, TerrainOptions, Tiles3dOptions, ViewshedOptions } from './types.ts';
|
|
30
30
|
import { type Control, type ControlPosition } from './ui/controls.ts';
|
|
31
31
|
import type { Camera, PickHit, RasterLayerStats } from './wasm/isard_webgpu.js';
|
|
32
32
|
export type { LayerSpec } from './map/layer-spec.ts';
|
|
@@ -181,6 +181,16 @@ export declare class GeoMap {
|
|
|
181
181
|
* loaded for its view). Several observers (16 at most): what any of them sees is seen, what is hidden from all those
|
|
182
182
|
* that reach it is hidden, in the colors and opacity of the first. Undefined or none removes it */
|
|
183
183
|
setViewshed(options: ViewshedOptions | readonly ViewshedOptions[] | undefined): void;
|
|
184
|
+
private clipBox;
|
|
185
|
+
/** The clipping box's handles over the map, while it has them */
|
|
186
|
+
private clipHandles;
|
|
187
|
+
/** Cuts the 3D layers (models, 3D Tiles, extrusions, point clouds) by a box: what lies inside it, or outside it
|
|
188
|
+
* (`cut`), the faces the cut opens filled; with `handles`, sized and moved with the pointer (`'clipbox'` tells each
|
|
189
|
+
* change). Undefined removes it */
|
|
190
|
+
setClipBox(box: ClipBox | undefined): void;
|
|
191
|
+
/** The clipping box set (`setClipBox`), as its handles left it */
|
|
192
|
+
getClipBox(): ClipBox | undefined;
|
|
193
|
+
private addClipHandles;
|
|
184
194
|
/** Relief under the basemap: true for the body's terrain model (on the Earth the worldwide Terrain Tiles, NASA's
|
|
185
195
|
* on the Moon and Mars), one of the Earth's models by name (`'ign'`), its options, or false; on another body, its
|
|
186
196
|
* options with a `url` */
|
package/dist/map.js
CHANGED
|
@@ -29,6 +29,7 @@ import { VectorLayer } from "./layers/vectors.js";
|
|
|
29
29
|
import { BasemapLayers } from "./map/basemap-layers.js";
|
|
30
30
|
import { createCanvas, labelCanvas, watchContext, watchPixelRatio } from "./map/canvas.js";
|
|
31
31
|
import { checkBasemap, report, validateNetwork } from "./map/checks.js";
|
|
32
|
+
import { applyClipBox, ClipBoxHandles } from "./map/clip-box.js";
|
|
32
33
|
import { Credits } from "./map/credits.js";
|
|
33
34
|
import { mapSettings, sendHeaders } from "./map/engine-settings.js";
|
|
34
35
|
import { MapEngines } from "./map/engines.js";
|
|
@@ -308,6 +309,7 @@ export class GeoMap {
|
|
|
308
309
|
// What the old engine drew over the scene: the viewshed, the sun's shadows, the panoramas shown
|
|
309
310
|
if (this.viewshed)
|
|
310
311
|
this.setViewshed(this.viewshed);
|
|
312
|
+
applyClipBox(this.engine, this.clipBox);
|
|
311
313
|
if (this.sun)
|
|
312
314
|
this.setSun(this.sun);
|
|
313
315
|
this.panoramaSets.forEach((set) => {
|
|
@@ -563,6 +565,44 @@ export class GeoMap {
|
|
|
563
565
|
if (applyViewshed(this.engine, options))
|
|
564
566
|
this.requestRender();
|
|
565
567
|
}
|
|
568
|
+
clipBox;
|
|
569
|
+
/** The clipping box's handles over the map, while it has them */
|
|
570
|
+
clipHandles;
|
|
571
|
+
/** Cuts the 3D layers (models, 3D Tiles, extrusions, point clouds) by a box: what lies inside it, or outside it
|
|
572
|
+
* (`cut`), the faces the cut opens filled; with `handles`, sized and moved with the pointer (`'clipbox'` tells each
|
|
573
|
+
* change). Undefined removes it */
|
|
574
|
+
setClipBox(box) {
|
|
575
|
+
this.clipBox = box;
|
|
576
|
+
applyClipBox(this.engine, box);
|
|
577
|
+
if (box?.handles) {
|
|
578
|
+
this.clipHandles ??= this.addClipHandles(box);
|
|
579
|
+
this.clipHandles.set(box);
|
|
580
|
+
this.overlays.position(this.clipHandles);
|
|
581
|
+
}
|
|
582
|
+
else if (this.clipHandles) {
|
|
583
|
+
this.overlays.detach(this.clipHandles, this.clipHandles.element);
|
|
584
|
+
this.clipHandles = undefined;
|
|
585
|
+
}
|
|
586
|
+
this.requestRender();
|
|
587
|
+
}
|
|
588
|
+
/** The clipping box set (`setClipBox`), as its handles left it */
|
|
589
|
+
getClipBox() {
|
|
590
|
+
return this.clipBox;
|
|
591
|
+
}
|
|
592
|
+
addClipHandles(box) {
|
|
593
|
+
const handles = new ClipBoxHandles({
|
|
594
|
+
project: (lonLat, height) => this.project(lonLat, height),
|
|
595
|
+
unproject: (point) => this.unproject(point),
|
|
596
|
+
elevationAt: (lonLat) => this.elevationAt(lonLat),
|
|
597
|
+
getCanvas: () => this.canvas,
|
|
598
|
+
moved: (moved) => {
|
|
599
|
+
this.setClipBox(moved);
|
|
600
|
+
this.emit('clipbox', moved);
|
|
601
|
+
},
|
|
602
|
+
}, box, this.settings.body.radius);
|
|
603
|
+
this.overlays.attach(handles, handles.element);
|
|
604
|
+
return handles;
|
|
605
|
+
}
|
|
566
606
|
/** Relief under the basemap: true for the body's terrain model (on the Earth the worldwide Terrain Tiles, NASA's
|
|
567
607
|
* on the Moon and Mars), one of the Earth's models by name (`'ign'`), its options, or false; on another body, its
|
|
568
608
|
* options with a `url` */
|