isard 2.2.2 → 2.3.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/CHANGELOG.md +17 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/isard_grid_bg.wasm +0 -0
- package/dist/isard_webgl2_bg.wasm +0 -0
- package/dist/isard_webgpu_bg.wasm +0 -0
- package/dist/map/clip-volume.d.ts +60 -0
- package/dist/map/clip-volume.js +352 -0
- package/dist/map/clip-volume.js.map +1 -0
- package/dist/map/events.js +1 -1
- package/dist/map/events.js.map +1 -1
- package/dist/map.d.ts +9 -9
- package/dist/map.js +21 -21
- package/dist/map.js.map +1 -1
- package/dist/react/ClipVolume.d.ts +10 -0
- package/dist/react/ClipVolume.js +22 -0
- package/dist/react/ClipVolume.js.map +1 -0
- package/dist/react/IsardMap.d.ts +1 -1
- package/dist/react/IsardMap.js +1 -1
- package/dist/react/IsardMap.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/types.d.ts +44 -18
- package/dist/types.js.map +1 -1
- package/dist/ui/style.js +6 -6
- package/dist/ui/style.js.map +1 -1
- package/dist/wasm/isard_webgl2.d.ts +11 -9
- package/dist/wasm/isard_webgl2.js +15 -10
- package/dist/wasm/isard_webgpu.d.ts +11 -9
- package/dist/wasm/isard_webgpu.js +15 -10
- package/package.json +1 -1
- package/dist/map/clip-box.d.ts +0 -52
- package/dist/map/clip-box.js +0 -233
- package/dist/map/clip-box.js.map +0 -1
- package/dist/react/ClipBox.d.ts +0 -9
- package/dist/react/ClipBox.js +0 -21
- package/dist/react/ClipBox.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ versions [Semantic Versioning](https://semver.org) (before 1.0.0, a minor versio
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [2.3.0] - 2026-11-01
|
|
9
|
+
|
|
10
|
+
With `isard-react-native` and `isard-map` (Python) 2.3.0.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The section box is a clipping volume, a box, a cylinder or a prism standing on a polygon:
|
|
15
|
+
`map.setClipVolume({ shape, … })` in place of `setClipBox`, `getClipVolume()`, the `'clipvolume'` event, in React
|
|
16
|
+
`<ClipVolume>` and `onClipVolume`. A box's size is its `width`, `length` and `height` (`size` gone)
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- A cylinder (`shape: 'cylinder'`, its `position` and `radius`) and a prism on a polygon (`shape: 'polygon'`, 3 to 16
|
|
21
|
+
vertices) cut the 3D layers as the box does; their handles: a cylinder's rim, a polygon's vertices dragged on the
|
|
22
|
+
ground, the top, the bottom and the middle of each. The example A section box chooses its shape, on the KIT's
|
|
23
|
+
FZK-Haus (a house of two storeys, set on its plot in Karlsruhe) in place of buildingSMART's test scene
|
|
24
|
+
|
|
8
25
|
## [2.2.2] - 2026-11-01
|
|
9
26
|
|
|
10
27
|
With `isard-react-native` and `isard-map` (Python) 2.2.2.
|
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, 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';
|
|
70
|
+
export type { AnimationOptions, AtmosphereOptions, Basemap, BasemapName, BodyOptions, CelestialBody, ClipBox, ClipCylinder, ClipPolygon, ClipVolume, 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 ClipCylinder,\n ClipPolygon,\n ClipVolume,\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"]}
|
package/dist/isard_grid_bg.wasm
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The clipping volume (`GeoMap.setClipVolume`): given to the engine, and its handles over the map, an outline of its
|
|
3
|
+
* edges and discs to size it (on a box's faces, a cylinder's rim, a polygon's vertices, its top and bottom) and one at
|
|
4
|
+
* its middle to move it, placed after every frame.
|
|
5
|
+
*/
|
|
6
|
+
import type { ClipVolume, LonLat, Point } from '../types.ts';
|
|
7
|
+
import type { Positioned } from '../ui/overlay.ts';
|
|
8
|
+
import type { Engine } from '../wasm/isard_webgpu.js';
|
|
9
|
+
/** Sets the volume in the engine, or clears it without one; `radius` the body's (metres to degrees) */
|
|
10
|
+
export declare const applyClipVolume: (engine: Engine, volume: ClipVolume | undefined, radius: number) => void;
|
|
11
|
+
/** What the handles need from the map */
|
|
12
|
+
export interface ClipVolumeHost {
|
|
13
|
+
project(lonLat: LonLat, height?: number): Point | undefined;
|
|
14
|
+
unproject(point: Point): LonLat | undefined;
|
|
15
|
+
elevationAt(lonLat: LonLat): number | undefined;
|
|
16
|
+
getCanvas(): HTMLCanvasElement;
|
|
17
|
+
/** The volume changed by a handle: set, and told */
|
|
18
|
+
moved(volume: ClipVolume): void;
|
|
19
|
+
}
|
|
20
|
+
/** The handles of a volume, over the map */
|
|
21
|
+
export declare class ClipVolumeHandles implements Positioned {
|
|
22
|
+
private readonly host;
|
|
23
|
+
private volume;
|
|
24
|
+
/** Metres of the body's radius (metres to degrees) */
|
|
25
|
+
private readonly radius;
|
|
26
|
+
/** Over the canvas, its drawing within */
|
|
27
|
+
readonly element: HTMLDivElement;
|
|
28
|
+
private readonly svg;
|
|
29
|
+
private lines;
|
|
30
|
+
private discs;
|
|
31
|
+
/** The handles drawn, as the discs are */
|
|
32
|
+
private handles;
|
|
33
|
+
/** The handle held (its disc), the pointer and the volume where it was pressed, the ground grabbed */
|
|
34
|
+
private held;
|
|
35
|
+
constructor(host: ClipVolumeHost, volume: ClipVolume,
|
|
36
|
+
/** Metres of the body's radius (metres to degrees) */
|
|
37
|
+
radius: number);
|
|
38
|
+
/** The volume the handles follow from the next placing (`position`, after each frame) */
|
|
39
|
+
set(volume: ClipVolume): void;
|
|
40
|
+
position(): void;
|
|
41
|
+
/** `elements` grown or cut to `count`, those made put `under` the others (the lines under the discs) or over them */
|
|
42
|
+
private resized;
|
|
43
|
+
private disc;
|
|
44
|
+
/** A point of the volume on the screen */
|
|
45
|
+
private screen;
|
|
46
|
+
/** The volume's foot outline (metres east and north of its center), closed by its first point */
|
|
47
|
+
private foot;
|
|
48
|
+
/** The edges: the foot's and the top's outlines, and the uprights at their corners (four of a cylinder's) */
|
|
49
|
+
private outline;
|
|
50
|
+
/** The handles of a volume: its sides', its top's and bottom's, the one that moves it */
|
|
51
|
+
private handlesOf;
|
|
52
|
+
/** The handles on a volume's sides: a box's four faces, a cylinder's rim, a polygon's vertices */
|
|
53
|
+
private sides;
|
|
54
|
+
private local;
|
|
55
|
+
private down;
|
|
56
|
+
private move;
|
|
57
|
+
/** Metres the pointer dragged a handle along its direction: the drag along that direction on the screen */
|
|
58
|
+
private dragged;
|
|
59
|
+
private up;
|
|
60
|
+
}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The clipping volume (`GeoMap.setClipVolume`): given to the engine, and its handles over the map, an outline of its
|
|
3
|
+
* edges and discs to size it (on a box's faces, a cylinder's rim, a polygon's vertices, its top and bottom) and one at
|
|
4
|
+
* its middle to move it, placed after every frame.
|
|
5
|
+
*/
|
|
6
|
+
import { rgb } from "../settings.js";
|
|
7
|
+
/** The faces' color by default: plaster, as a section on a plan */
|
|
8
|
+
const SECTION = '#d9cfc1';
|
|
9
|
+
/** Metres a side, a radius or a height keeps at least */
|
|
10
|
+
const SMALLEST = 0.2;
|
|
11
|
+
/** The vertices a polygon keeps at most (the engine's) */
|
|
12
|
+
const POLYGON = 16;
|
|
13
|
+
/** Segments of a cylinder's outline */
|
|
14
|
+
const ROUND = 48;
|
|
15
|
+
const SVG = 'http://www.w3.org/2000/svg';
|
|
16
|
+
const SHAPES = { box: 0, cylinder: 1, polygon: 2 };
|
|
17
|
+
/** A volume's foot's center: a box's or a cylinder's position, a polygon's vertices' mean */
|
|
18
|
+
const originOf = (volume) => {
|
|
19
|
+
if (volume.shape !== 'polygon')
|
|
20
|
+
return volume.position;
|
|
21
|
+
const n = Math.max(1, volume.polygon.length);
|
|
22
|
+
const [lon, lat] = volume.polygon.reduce(([a, b], [x, y]) => [a + x, b + y], [0, 0]);
|
|
23
|
+
return [lon / n, lat / n];
|
|
24
|
+
};
|
|
25
|
+
/** Metres east and north of `origin` to degrees, and back, on a body of `radius` metres */
|
|
26
|
+
const metric = (origin, radius) => {
|
|
27
|
+
const degrees = 180 / Math.PI;
|
|
28
|
+
const across = radius * Math.cos(origin[1] / degrees);
|
|
29
|
+
return {
|
|
30
|
+
lonLat: (east, north) => [
|
|
31
|
+
origin[0] + (east / across) * degrees,
|
|
32
|
+
origin[1] + (north / radius) * degrees,
|
|
33
|
+
],
|
|
34
|
+
local: ([lon, lat]) => [
|
|
35
|
+
((lon - origin[0]) / degrees) * across,
|
|
36
|
+
((lat - origin[1]) / degrees) * radius,
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
/** A volume's metres across and along (a cylinder's diameter) and its heading, for the engine */
|
|
41
|
+
const footprintOf = (volume) => {
|
|
42
|
+
if (volume.shape === 'box')
|
|
43
|
+
return [volume.width, volume.length, volume.heading ?? 0];
|
|
44
|
+
if (volume.shape === 'cylinder')
|
|
45
|
+
return [volume.radius * 2, volume.radius * 2, 0];
|
|
46
|
+
return [0, 0, 0];
|
|
47
|
+
};
|
|
48
|
+
/** Sets the volume in the engine, or clears it without one; `radius` the body's (metres to degrees) */
|
|
49
|
+
export const applyClipVolume = (engine, volume, radius) => {
|
|
50
|
+
if (!volume) {
|
|
51
|
+
engine.clearClipVolume();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const origin = originOf(volume);
|
|
55
|
+
const { local } = metric(origin, radius);
|
|
56
|
+
const section = volume.sectionColor === null ? [] : rgb(volume.sectionColor ?? SECTION);
|
|
57
|
+
const [width, length, heading] = footprintOf(volume);
|
|
58
|
+
const polygon = volume.shape === 'polygon' ? volume.polygon.slice(0, POLYGON).flatMap((v) => local(v)) : [];
|
|
59
|
+
engine.setClipVolume(new Float64Array([
|
|
60
|
+
origin[0],
|
|
61
|
+
origin[1],
|
|
62
|
+
volume.altitude ?? 0,
|
|
63
|
+
width,
|
|
64
|
+
length,
|
|
65
|
+
volume.height,
|
|
66
|
+
heading,
|
|
67
|
+
volume.cut === 'outside' ? 0 : 1,
|
|
68
|
+
volume.altitudeReference === 'sea' ? 0 : 1,
|
|
69
|
+
SHAPES[volume.shape],
|
|
70
|
+
]), new Float64Array(polygon), new Float32Array(section));
|
|
71
|
+
};
|
|
72
|
+
/** The volume's foot and top moved: `top` the top face, else the bottom one, `metres` outward */
|
|
73
|
+
const heightened = (volume, top, metres) => {
|
|
74
|
+
const height = Math.max(SMALLEST, volume.height + metres);
|
|
75
|
+
return top
|
|
76
|
+
? { ...volume, height }
|
|
77
|
+
: { ...volume, height, altitude: (volume.altitude ?? 0) - (height - volume.height) };
|
|
78
|
+
};
|
|
79
|
+
/** The volume moved `east` and `north` metres */
|
|
80
|
+
const moved = (volume, east, north, radius) => {
|
|
81
|
+
if (volume.shape === 'polygon') {
|
|
82
|
+
return {
|
|
83
|
+
...volume,
|
|
84
|
+
polygon: volume.polygon.map((v) => {
|
|
85
|
+
const { local, lonLat } = metric(v, radius);
|
|
86
|
+
const [e, n] = local(v);
|
|
87
|
+
return lonLat(e + east, n + north);
|
|
88
|
+
}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { ...volume, position: metric(volume.position, radius).lonLat(east, north) };
|
|
92
|
+
};
|
|
93
|
+
/** The handles of a volume, over the map */
|
|
94
|
+
export class ClipVolumeHandles {
|
|
95
|
+
host;
|
|
96
|
+
volume;
|
|
97
|
+
radius;
|
|
98
|
+
/** Over the canvas, its drawing within */
|
|
99
|
+
element;
|
|
100
|
+
svg;
|
|
101
|
+
lines = [];
|
|
102
|
+
discs = [];
|
|
103
|
+
/** The handles drawn, as the discs are */
|
|
104
|
+
handles = [];
|
|
105
|
+
/** The handle held (its disc), the pointer and the volume where it was pressed, the ground grabbed */
|
|
106
|
+
held;
|
|
107
|
+
constructor(host, volume,
|
|
108
|
+
/** Metres of the body's radius (metres to degrees) */
|
|
109
|
+
radius) {
|
|
110
|
+
this.host = host;
|
|
111
|
+
this.volume = volume;
|
|
112
|
+
this.radius = radius;
|
|
113
|
+
this.svg = document.createElementNS(SVG, 'svg');
|
|
114
|
+
this.element = document.createElement('div');
|
|
115
|
+
this.element.className = 'isard-clipvolume';
|
|
116
|
+
this.element.append(this.svg);
|
|
117
|
+
}
|
|
118
|
+
/** The volume the handles follow from the next placing (`position`, after each frame) */
|
|
119
|
+
set(volume) {
|
|
120
|
+
this.volume = volume;
|
|
121
|
+
}
|
|
122
|
+
position() {
|
|
123
|
+
const segments = this.outline(this.volume);
|
|
124
|
+
this.handles = this.handlesOf(this.volume);
|
|
125
|
+
this.lines = this.resized(this.lines, segments.length, () => document.createElementNS(SVG, 'line'), true);
|
|
126
|
+
this.discs = this.resized(this.discs, this.handles.length, () => this.disc(), false);
|
|
127
|
+
segments.forEach(([a, b], i) => {
|
|
128
|
+
const [p, q] = [this.screen(a), this.screen(b)];
|
|
129
|
+
const line = this.lines[i];
|
|
130
|
+
if (!line)
|
|
131
|
+
return;
|
|
132
|
+
line.style.display = p && q ? '' : 'none';
|
|
133
|
+
if (!p || !q)
|
|
134
|
+
return;
|
|
135
|
+
line.setAttribute('x1', String(p[0]));
|
|
136
|
+
line.setAttribute('y1', String(p[1]));
|
|
137
|
+
line.setAttribute('x2', String(q[0]));
|
|
138
|
+
line.setAttribute('y2', String(q[1]));
|
|
139
|
+
});
|
|
140
|
+
this.handles.forEach((handle, i) => {
|
|
141
|
+
const p = this.screen(handle.at);
|
|
142
|
+
const disc = this.discs[i];
|
|
143
|
+
if (!disc)
|
|
144
|
+
return;
|
|
145
|
+
disc.setAttribute('r', handle.move ? '8' : '6');
|
|
146
|
+
disc.setAttribute('class', handle.move ? 'isard-clipvolume-move' : 'isard-clipvolume-size');
|
|
147
|
+
disc.style.display = p ? '' : 'none';
|
|
148
|
+
if (!p)
|
|
149
|
+
return;
|
|
150
|
+
disc.setAttribute('cx', String(p[0]));
|
|
151
|
+
disc.setAttribute('cy', String(p[1]));
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/** `elements` grown or cut to `count`, those made put `under` the others (the lines under the discs) or over them */
|
|
155
|
+
resized(elements, count, make, under) {
|
|
156
|
+
elements.slice(count).forEach((e) => {
|
|
157
|
+
e.remove();
|
|
158
|
+
});
|
|
159
|
+
const kept = elements.slice(0, count);
|
|
160
|
+
const made = Array.from({ length: count - kept.length }, make);
|
|
161
|
+
made.forEach((e) => {
|
|
162
|
+
if (under)
|
|
163
|
+
this.svg.prepend(e);
|
|
164
|
+
else
|
|
165
|
+
this.svg.append(e);
|
|
166
|
+
});
|
|
167
|
+
return [...kept, ...made];
|
|
168
|
+
}
|
|
169
|
+
disc() {
|
|
170
|
+
const disc = document.createElementNS(SVG, 'circle');
|
|
171
|
+
disc.addEventListener('pointerdown', (e) => this.down(e, disc));
|
|
172
|
+
disc.addEventListener('pointermove', (e) => this.move(e));
|
|
173
|
+
disc.addEventListener('pointerup', (e) => this.up(e));
|
|
174
|
+
disc.addEventListener('pointercancel', (e) => this.up(e));
|
|
175
|
+
// Safari may let the capture go: the release would then reach the map, not the disc
|
|
176
|
+
disc.addEventListener('lostpointercapture', (e) => this.up(e));
|
|
177
|
+
return disc;
|
|
178
|
+
}
|
|
179
|
+
/** A point of the volume on the screen */
|
|
180
|
+
screen([east, north, up], volume = this.volume) {
|
|
181
|
+
const origin = originOf(volume);
|
|
182
|
+
const lonLat = metric(origin, this.radius).lonLat(east, north);
|
|
183
|
+
// `project` measures from the relief: an altitude above the ellipsoid less the relief under the volume
|
|
184
|
+
const ground = volume.altitudeReference === 'sea' ? (this.host.elevationAt(origin) ?? 0) : 0;
|
|
185
|
+
return this.host.project(lonLat, (volume.altitude ?? 0) - ground + up);
|
|
186
|
+
}
|
|
187
|
+
/** The volume's foot outline (metres east and north of its center), closed by its first point */
|
|
188
|
+
foot(volume) {
|
|
189
|
+
if (volume.shape === 'cylinder') {
|
|
190
|
+
return Array.from({ length: ROUND }, (_, k) => {
|
|
191
|
+
const a = (2 * Math.PI * k) / ROUND;
|
|
192
|
+
return [volume.radius * Math.cos(a), volume.radius * Math.sin(a)];
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (volume.shape === 'polygon') {
|
|
196
|
+
const { local } = metric(originOf(volume), this.radius);
|
|
197
|
+
return volume.polygon.map((v) => local(v));
|
|
198
|
+
}
|
|
199
|
+
const heading = ((volume.heading ?? 0) * Math.PI) / 180;
|
|
200
|
+
const [sin, cos] = [Math.sin(heading), Math.cos(heading)];
|
|
201
|
+
const [w, l] = [volume.width / 2, volume.length / 2];
|
|
202
|
+
return [
|
|
203
|
+
[-w, -l],
|
|
204
|
+
[w, -l],
|
|
205
|
+
[w, l],
|
|
206
|
+
[-w, l],
|
|
207
|
+
].map(([a, b]) => [(a ?? 0) * cos + (b ?? 0) * sin, (b ?? 0) * cos - (a ?? 0) * sin]);
|
|
208
|
+
}
|
|
209
|
+
/** The edges: the foot's and the top's outlines, and the uprights at their corners (four of a cylinder's) */
|
|
210
|
+
outline(volume) {
|
|
211
|
+
const foot = this.foot(volume);
|
|
212
|
+
const h = volume.height;
|
|
213
|
+
const ring = (up) => foot.map(([e, n], k) => {
|
|
214
|
+
const [e2, n2] = foot[(k + 1) % foot.length] ?? [e, n];
|
|
215
|
+
return [
|
|
216
|
+
[e, n, up],
|
|
217
|
+
[e2, n2, up],
|
|
218
|
+
];
|
|
219
|
+
});
|
|
220
|
+
const uprights = (volume.shape === 'cylinder' ? foot.filter((_, k) => k % (ROUND / 4) === 0) : foot).map(([e, n]) => [
|
|
221
|
+
[e, n, 0],
|
|
222
|
+
[e, n, h],
|
|
223
|
+
]);
|
|
224
|
+
return [...ring(0), ...ring(h), ...uprights];
|
|
225
|
+
}
|
|
226
|
+
/** The handles of a volume: its sides', its top's and bottom's, the one that moves it */
|
|
227
|
+
handlesOf(volume) {
|
|
228
|
+
const h = volume.height;
|
|
229
|
+
const vertical = [
|
|
230
|
+
{ at: [0, 0, h], along: [0, 0, 1], resize: (v, m) => heightened(v, true, m) },
|
|
231
|
+
{ at: [0, 0, 0], along: [0, 0, -1], resize: (v, m) => heightened(v, false, m) },
|
|
232
|
+
];
|
|
233
|
+
const move = {
|
|
234
|
+
at: [0, 0, h / 2],
|
|
235
|
+
move: true,
|
|
236
|
+
ground: (v, east, north) => moved(v, east, north, this.radius),
|
|
237
|
+
};
|
|
238
|
+
return [...this.sides(volume), ...vertical, move];
|
|
239
|
+
}
|
|
240
|
+
/** The handles on a volume's sides: a box's four faces, a cylinder's rim, a polygon's vertices */
|
|
241
|
+
sides(volume) {
|
|
242
|
+
const h = volume.height / 2;
|
|
243
|
+
if (volume.shape === 'cylinder') {
|
|
244
|
+
return [
|
|
245
|
+
{
|
|
246
|
+
at: [volume.radius, 0, h],
|
|
247
|
+
along: [1, 0, 0],
|
|
248
|
+
resize: (v, m) => (v.shape === 'cylinder' ? { ...v, radius: Math.max(SMALLEST, v.radius + m) } : v),
|
|
249
|
+
},
|
|
250
|
+
];
|
|
251
|
+
}
|
|
252
|
+
if (volume.shape === 'polygon') {
|
|
253
|
+
const { local } = metric(originOf(volume), this.radius);
|
|
254
|
+
const shifted = (vertex, east, north) => metric(vertex, this.radius).lonLat(east, north);
|
|
255
|
+
return volume.polygon.map((vertex, k) => {
|
|
256
|
+
const [e, n] = local(vertex);
|
|
257
|
+
return {
|
|
258
|
+
at: [e, n, 0],
|
|
259
|
+
ground: (v, east, north) => v.shape === 'polygon'
|
|
260
|
+
? { ...v, polygon: v.polygon.map((p, i) => (i === k ? shifted(p, east, north) : p)) }
|
|
261
|
+
: v,
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
const heading = ((volume.heading ?? 0) * Math.PI) / 180;
|
|
266
|
+
const [sin, cos] = [Math.sin(heading), Math.cos(heading)];
|
|
267
|
+
// Across and along its heading, east and north
|
|
268
|
+
const across = [cos, -sin];
|
|
269
|
+
const along = [sin, cos];
|
|
270
|
+
const face = (axis, half, side, width) => ({
|
|
271
|
+
at: [axis[0] * half * side, axis[1] * half * side, h],
|
|
272
|
+
along: [axis[0] * side, axis[1] * side, 0],
|
|
273
|
+
resize: (v, m) => {
|
|
274
|
+
if (v.shape !== 'box')
|
|
275
|
+
return v;
|
|
276
|
+
const size = Math.max(SMALLEST, (width ? v.width : v.length) + m);
|
|
277
|
+
const shift = (side * (size - (width ? v.width : v.length))) / 2;
|
|
278
|
+
const box = width ? { ...v, width: size } : { ...v, length: size };
|
|
279
|
+
// The center half-way along the move, on the side of the face moved: the opposite face where it was
|
|
280
|
+
return moved(box, axis[0] * shift, axis[1] * shift, this.radius);
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
return [
|
|
284
|
+
face(across, volume.width / 2, -1, true),
|
|
285
|
+
face(across, volume.width / 2, 1, true),
|
|
286
|
+
face(along, volume.length / 2, -1, false),
|
|
287
|
+
face(along, volume.length / 2, 1, false),
|
|
288
|
+
];
|
|
289
|
+
}
|
|
290
|
+
local(e) {
|
|
291
|
+
const rect = this.host.getCanvas().getBoundingClientRect();
|
|
292
|
+
return [e.clientX - rect.left, e.clientY - rect.top];
|
|
293
|
+
}
|
|
294
|
+
down(e, disc) {
|
|
295
|
+
const handle = this.handles[this.discs.indexOf(disc)];
|
|
296
|
+
if (e.button !== 0 || !handle)
|
|
297
|
+
return;
|
|
298
|
+
const start = this.local(e);
|
|
299
|
+
// The map must not pan under the handle
|
|
300
|
+
e.stopPropagation();
|
|
301
|
+
e.preventDefault();
|
|
302
|
+
disc.setPointerCapture(e.pointerId);
|
|
303
|
+
// The spot of the ground grabbed, kept under the pointer as the volume moves
|
|
304
|
+
this.held = { handle, disc, start, volume: this.volume, grab: this.host.unproject(start) };
|
|
305
|
+
}
|
|
306
|
+
move(e) {
|
|
307
|
+
if (!this.held)
|
|
308
|
+
return;
|
|
309
|
+
// No button held any more: its release was missed (the capture lost), the drag is over
|
|
310
|
+
if (e.buttons === 0) {
|
|
311
|
+
this.up(e);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
e.stopPropagation();
|
|
315
|
+
const { handle, start, volume, grab } = this.held;
|
|
316
|
+
const at = this.local(e);
|
|
317
|
+
if ('ground' in handle) {
|
|
318
|
+
const lonLat = this.host.unproject(at);
|
|
319
|
+
if (!lonLat || !grab)
|
|
320
|
+
return;
|
|
321
|
+
// As far as the ground under the pointer moved from the spot grabbed
|
|
322
|
+
const [east, north] = metric(grab, this.radius).local(lonLat);
|
|
323
|
+
this.host.moved(handle.ground(volume, east, north));
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const metres = this.dragged(volume, handle, [at[0] - start[0], at[1] - start[1]]);
|
|
327
|
+
if (metres !== undefined)
|
|
328
|
+
this.host.moved(handle.resize(volume, metres));
|
|
329
|
+
}
|
|
330
|
+
/** Metres the pointer dragged a handle along its direction: the drag along that direction on the screen */
|
|
331
|
+
dragged(volume, handle, [dx, dy]) {
|
|
332
|
+
const [e, n, u] = handle.at;
|
|
333
|
+
const [de, dn, du] = handle.along;
|
|
334
|
+
const from = this.screen(handle.at, volume);
|
|
335
|
+
// Where the handle goes a metre that way, on the screen: pixels per metre along it
|
|
336
|
+
const to = this.screen([e + de, n + dn, u + du], volume);
|
|
337
|
+
if (!from || !to)
|
|
338
|
+
return undefined;
|
|
339
|
+
const [ax, ay] = [to[0] - from[0], to[1] - from[1]];
|
|
340
|
+
const squared = ax * ax + ay * ay;
|
|
341
|
+
return squared > 1e-6 ? (dx * ax + dy * ay) / squared : undefined;
|
|
342
|
+
}
|
|
343
|
+
up(e) {
|
|
344
|
+
if (!this.held)
|
|
345
|
+
return;
|
|
346
|
+
e.stopPropagation();
|
|
347
|
+
if (this.held.disc.hasPointerCapture(e.pointerId))
|
|
348
|
+
this.held.disc.releasePointerCapture(e.pointerId);
|
|
349
|
+
this.held = undefined;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=clip-volume.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clip-volume.js","sourceRoot":"","sources":["../../src/map/clip-volume.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The clipping volume (`GeoMap.setClipVolume`): given to the engine, and its handles over the map, an outline of its\n * edges and discs to size it (on a box's faces, a cylinder's rim, a polygon's vertices, its top and bottom) and one at\n * its middle to move it, placed after every frame.\n */\n\nimport { rgb } from '../settings.ts';\nimport type { ClipVolume, 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, a radius or a height keeps at least */\nconst SMALLEST = 0.2;\n/** The vertices a polygon keeps at most (the engine's) */\nconst POLYGON = 16;\n/** Segments of a cylinder's outline */\nconst ROUND = 48;\nconst SVG = 'http://www.w3.org/2000/svg';\nconst SHAPES = { box: 0, cylinder: 1, polygon: 2 } as const;\n\n/** Metres east, north and up of a volume's foot's center */\ntype Local = [east: number, north: number, up: number];\n\n/** A volume's foot's center: a box's or a cylinder's position, a polygon's vertices' mean */\nconst originOf = (volume: ClipVolume): LonLat => {\n if (volume.shape !== 'polygon') return volume.position;\n\n const n = Math.max(1, volume.polygon.length);\n const [lon, lat] = volume.polygon.reduce(([a, b], [x, y]) => [a + x, b + y], [0, 0]);\n\n return [lon / n, lat / n];\n};\n\n/** Metres east and north of `origin` to degrees, and back, on a body of `radius` metres */\nconst metric = (origin: LonLat, radius: number) => {\n const degrees = 180 / Math.PI;\n const across = radius * Math.cos(origin[1] / degrees);\n\n return {\n lonLat: (east: number, north: number): LonLat => [\n origin[0] + (east / across) * degrees,\n origin[1] + (north / radius) * degrees,\n ],\n local: ([lon, lat]: LonLat): [number, number] => [\n ((lon - origin[0]) / degrees) * across,\n ((lat - origin[1]) / degrees) * radius,\n ],\n };\n};\n\n/** A volume's metres across and along (a cylinder's diameter) and its heading, for the engine */\nconst footprintOf = (volume: ClipVolume): [number, number, number] => {\n if (volume.shape === 'box') return [volume.width, volume.length, volume.heading ?? 0];\n if (volume.shape === 'cylinder') return [volume.radius * 2, volume.radius * 2, 0];\n\n return [0, 0, 0];\n};\n\n/** Sets the volume in the engine, or clears it without one; `radius` the body's (metres to degrees) */\nexport const applyClipVolume = (engine: Engine, volume: ClipVolume | undefined, radius: number): void => {\n if (!volume) {\n engine.clearClipVolume();\n\n return;\n }\n\n const origin = originOf(volume);\n const { local } = metric(origin, radius);\n const section = volume.sectionColor === null ? [] : rgb(volume.sectionColor ?? SECTION);\n const [width, length, heading] = footprintOf(volume);\n const polygon = volume.shape === 'polygon' ? volume.polygon.slice(0, POLYGON).flatMap((v) => local(v)) : [];\n\n engine.setClipVolume(\n new Float64Array([\n origin[0],\n origin[1],\n volume.altitude ?? 0,\n width,\n length,\n volume.height,\n heading,\n volume.cut === 'outside' ? 0 : 1,\n volume.altitudeReference === 'sea' ? 0 : 1,\n SHAPES[volume.shape],\n ]),\n new Float64Array(polygon),\n new Float32Array(section),\n );\n};\n\n/** What the handles need from the map */\nexport interface ClipVolumeHost {\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 volume changed by a handle: set, and told */\n moved(volume: ClipVolume): void;\n}\n\n/**\n * A handle: where it stands, and how a drag changes the volume. `along`: dragged along a direction (metres east, north\n * and up a metre), the volume changed by the metres dragged that way; `ground`: dragged on the ground, the volume\n * changed by the metres east and north the ground under the pointer moved\n */\ntype Handle = { at: Local; move?: true } & (\n | { along: Local; resize: (volume: ClipVolume, metres: number) => ClipVolume }\n | { ground: (volume: ClipVolume, east: number, north: number) => ClipVolume }\n);\n\n/** The volume's foot and top moved: `top` the top face, else the bottom one, `metres` outward */\nconst heightened = (volume: ClipVolume, top: boolean, metres: number): ClipVolume => {\n const height = Math.max(SMALLEST, volume.height + metres);\n\n return top\n ? { ...volume, height }\n : { ...volume, height, altitude: (volume.altitude ?? 0) - (height - volume.height) };\n};\n\n/** The volume moved `east` and `north` metres */\nconst moved = (volume: ClipVolume, east: number, north: number, radius: number): ClipVolume => {\n if (volume.shape === 'polygon') {\n return {\n ...volume,\n polygon: volume.polygon.map((v) => {\n const { local, lonLat } = metric(v, radius);\n const [e, n] = local(v);\n\n return lonLat(e + east, n + north);\n }),\n };\n }\n\n return { ...volume, position: metric(volume.position, radius).lonLat(east, north) };\n};\n\n/** The handles of a volume, over the map */\nexport class ClipVolumeHandles implements Positioned {\n /** Over the canvas, its drawing within */\n readonly element: HTMLDivElement;\n private readonly svg: SVGSVGElement;\n private lines: SVGLineElement[] = [];\n private discs: SVGCircleElement[] = [];\n /** The handles drawn, as the discs are */\n private handles: Handle[] = [];\n /** The handle held (its disc), the pointer and the volume where it was pressed, the ground grabbed */\n private held:\n | { handle: Handle; disc: SVGCircleElement; start: Point; volume: ClipVolume; grab: LonLat | undefined }\n | undefined;\n\n constructor(\n private readonly host: ClipVolumeHost,\n private volume: ClipVolume,\n /** Metres of the body's radius (metres to degrees) */\n private readonly radius: number,\n ) {\n this.svg = document.createElementNS(SVG, 'svg');\n this.element = document.createElement('div');\n this.element.className = 'isard-clipvolume';\n this.element.append(this.svg);\n }\n\n /** The volume the handles follow from the next placing (`position`, after each frame) */\n set(volume: ClipVolume): void {\n this.volume = volume;\n }\n\n position(): void {\n const segments = this.outline(this.volume);\n\n this.handles = this.handlesOf(this.volume);\n this.lines = this.resized(this.lines, segments.length, () => document.createElementNS(SVG, 'line'), true);\n this.discs = this.resized(this.discs, this.handles.length, () => this.disc(), false);\n segments.forEach(([a, b], i) => {\n const [p, q] = [this.screen(a), this.screen(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 this.handles.forEach((handle, i) => {\n const p = this.screen(handle.at);\n const disc = this.discs[i];\n\n if (!disc) return;\n disc.setAttribute('r', handle.move ? '8' : '6');\n disc.setAttribute('class', handle.move ? 'isard-clipvolume-move' : 'isard-clipvolume-size');\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 /** `elements` grown or cut to `count`, those made put `under` the others (the lines under the discs) or over them */\n private resized<T extends SVGElement>(elements: T[], count: number, make: () => T, under: boolean): T[] {\n elements.slice(count).forEach((e) => {\n e.remove();\n });\n\n const kept = elements.slice(0, count);\n const made = Array.from({ length: count - kept.length }, make);\n\n made.forEach((e) => {\n if (under) this.svg.prepend(e);\n else this.svg.append(e);\n });\n\n return [...kept, ...made];\n }\n\n private disc(): SVGCircleElement {\n const disc = document.createElementNS(SVG, 'circle');\n\n disc.addEventListener('pointerdown', (e) => this.down(e, 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 // Safari may let the capture go: the release would then reach the map, not the disc\n disc.addEventListener('lostpointercapture', (e) => this.up(e));\n\n return disc;\n }\n\n /** A point of the volume on the screen */\n private screen([east, north, up]: Local, volume = this.volume): Point | undefined {\n const origin = originOf(volume);\n const lonLat = metric(origin, this.radius).lonLat(east, north);\n // `project` measures from the relief: an altitude above the ellipsoid less the relief under the volume\n const ground = volume.altitudeReference === 'sea' ? (this.host.elevationAt(origin) ?? 0) : 0;\n\n return this.host.project(lonLat, (volume.altitude ?? 0) - ground + up);\n }\n\n /** The volume's foot outline (metres east and north of its center), closed by its first point */\n private foot(volume: ClipVolume): [number, number][] {\n if (volume.shape === 'cylinder') {\n return Array.from({ length: ROUND }, (_, k) => {\n const a = (2 * Math.PI * k) / ROUND;\n\n return [volume.radius * Math.cos(a), volume.radius * Math.sin(a)];\n });\n }\n if (volume.shape === 'polygon') {\n const { local } = metric(originOf(volume), this.radius);\n\n return volume.polygon.map((v) => local(v));\n }\n\n const heading = ((volume.heading ?? 0) * Math.PI) / 180;\n const [sin, cos] = [Math.sin(heading), Math.cos(heading)];\n const [w, l] = [volume.width / 2, volume.length / 2];\n\n return [\n [-w, -l],\n [w, -l],\n [w, l],\n [-w, l],\n ].map(([a, b]) => [(a ?? 0) * cos + (b ?? 0) * sin, (b ?? 0) * cos - (a ?? 0) * sin]);\n }\n\n /** The edges: the foot's and the top's outlines, and the uprights at their corners (four of a cylinder's) */\n private outline(volume: ClipVolume): [Local, Local][] {\n const foot = this.foot(volume);\n const h = volume.height;\n const ring = (up: number) =>\n foot.map(([e, n], k): [Local, Local] => {\n const [e2, n2] = foot[(k + 1) % foot.length] ?? [e, n];\n\n return [\n [e, n, up],\n [e2, n2, up],\n ];\n });\n const uprights = (volume.shape === 'cylinder' ? foot.filter((_, k) => k % (ROUND / 4) === 0) : foot).map(\n ([e, n]): [Local, Local] => [\n [e, n, 0],\n [e, n, h],\n ],\n );\n\n return [...ring(0), ...ring(h), ...uprights];\n }\n\n /** The handles of a volume: its sides', its top's and bottom's, the one that moves it */\n private handlesOf(volume: ClipVolume): Handle[] {\n const h = volume.height;\n const vertical: Handle[] = [\n { at: [0, 0, h], along: [0, 0, 1], resize: (v, m) => heightened(v, true, m) },\n { at: [0, 0, 0], along: [0, 0, -1], resize: (v, m) => heightened(v, false, m) },\n ];\n const move: Handle = {\n at: [0, 0, h / 2],\n move: true,\n ground: (v, east, north) => moved(v, east, north, this.radius),\n };\n\n return [...this.sides(volume), ...vertical, move];\n }\n\n /** The handles on a volume's sides: a box's four faces, a cylinder's rim, a polygon's vertices */\n private sides(volume: ClipVolume): Handle[] {\n const h = volume.height / 2;\n\n if (volume.shape === 'cylinder') {\n return [\n {\n at: [volume.radius, 0, h],\n along: [1, 0, 0],\n resize: (v, m) => (v.shape === 'cylinder' ? { ...v, radius: Math.max(SMALLEST, v.radius + m) } : v),\n },\n ];\n }\n if (volume.shape === 'polygon') {\n const { local } = metric(originOf(volume), this.radius);\n const shifted = (vertex: LonLat, east: number, north: number) => metric(vertex, this.radius).lonLat(east, north);\n\n return volume.polygon.map((vertex, k) => {\n const [e, n] = local(vertex);\n\n return {\n at: [e, n, 0],\n ground: (v, east, north) =>\n v.shape === 'polygon'\n ? { ...v, polygon: v.polygon.map((p, i) => (i === k ? shifted(p, east, north) : p)) }\n : v,\n };\n });\n }\n\n const heading = ((volume.heading ?? 0) * Math.PI) / 180;\n const [sin, cos] = [Math.sin(heading), Math.cos(heading)];\n // Across and along its heading, east and north\n const across: [number, number] = [cos, -sin];\n const along: [number, number] = [sin, cos];\n const face = (axis: [number, number], half: number, side: -1 | 1, width: boolean): Handle => ({\n at: [axis[0] * half * side, axis[1] * half * side, h],\n along: [axis[0] * side, axis[1] * side, 0],\n resize: (v, m) => {\n if (v.shape !== 'box') return v;\n\n const size = Math.max(SMALLEST, (width ? v.width : v.length) + m);\n const shift = (side * (size - (width ? v.width : v.length))) / 2;\n const box = width ? { ...v, width: size } : { ...v, length: size };\n\n // The center half-way along the move, on the side of the face moved: the opposite face where it was\n return moved(box, axis[0] * shift, axis[1] * shift, this.radius);\n },\n });\n\n return [\n face(across, volume.width / 2, -1, true),\n face(across, volume.width / 2, 1, true),\n face(along, volume.length / 2, -1, false),\n face(along, volume.length / 2, 1, false),\n ];\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, disc: SVGCircleElement): void {\n const handle = this.handles[this.discs.indexOf(disc)];\n\n if (e.button !== 0 || !handle) return;\n\n const start = this.local(e);\n\n // The map must not pan under the handle\n e.stopPropagation();\n e.preventDefault();\n disc.setPointerCapture(e.pointerId);\n // The spot of the ground grabbed, kept under the pointer as the volume moves\n this.held = { handle, disc, start, volume: this.volume, grab: this.host.unproject(start) };\n }\n\n private move(e: PointerEvent): void {\n if (!this.held) return;\n // No button held any more: its release was missed (the capture lost), the drag is over\n if (e.buttons === 0) {\n this.up(e);\n\n return;\n }\n e.stopPropagation();\n\n const { handle, start, volume, grab } = this.held;\n const at = this.local(e);\n\n if ('ground' in handle) {\n const lonLat = this.host.unproject(at);\n\n if (!lonLat || !grab) return;\n\n // As far as the ground under the pointer moved from the spot grabbed\n const [east, north] = metric(grab, this.radius).local(lonLat);\n\n this.host.moved(handle.ground(volume, east, north));\n\n return;\n }\n\n const metres = this.dragged(volume, handle, [at[0] - start[0], at[1] - start[1]]);\n\n if (metres !== undefined) this.host.moved(handle.resize(volume, metres));\n }\n\n /** Metres the pointer dragged a handle along its direction: the drag along that direction on the screen */\n private dragged(volume: ClipVolume, handle: Handle & { along: Local }, [dx, dy]: Point): number | undefined {\n const [e, n, u] = handle.at;\n const [de, dn, du] = handle.along;\n const from = this.screen(handle.at, volume);\n // Where the handle goes a metre that way, on the screen: pixels per metre along it\n const to = this.screen([e + de, n + dn, u + du], volume);\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 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;QAChB,
|
|
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,UAAU,EAAE,IAAI,GAAG,EAAE;KACtB,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 clipvolume: 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"]}
|