@bendyline/squisq 2.11.11 → 2.11.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,7 +20,7 @@ npm install @bendyline/squisq
20
20
  | **schemas** | Type definitions — `Doc`, `BlockTemplate`, `Viewport`, `Theme`, `LayoutStrategy` |
21
21
  | **doc** | Template registry, 26 block templates, animation/theme utilities |
22
22
  | **markdown** | Markdown parsing, stringifying, AST types (`MarkdownDocument`), tree utilities |
23
- | **spatial** | Haversine distance, Geohash encode/decode |
23
+ | **spatial** | Haversine distance and bearing, great-circle paths, Geohash encode/decode |
24
24
  | **storage** | `StorageAdapter` interface, Memory + LocalStorage + LocalForage adapters |
25
25
 
26
26
  ## New in this release
@@ -92,10 +92,19 @@ the original fence byte-for-byte for markdown round-trips.
92
92
  ### Spatial Utilities
93
93
 
94
94
  ```ts
95
- import { encodeGeohash, haversineDistance } from '@bendyline/squisq/spatial';
95
+ import {
96
+ destinationPoint,
97
+ encodeGeohash,
98
+ haversineDistance,
99
+ sampleGreatCircle,
100
+ } from '@bendyline/squisq/spatial';
96
101
 
97
102
  const distanceKm = haversineDistance({ lat: 47.6, lng: -122.3 }, { lat: 37.7, lng: -122.4 });
98
103
  const hash = encodeGeohash(47.6, -122.3, 7);
104
+
105
+ // Points along the shortest route, and a position projected 5 km due east.
106
+ const route = sampleGreatCircle({ lat: 47.6, lng: -122.3 }, { lat: 51.5, lng: -0.1 }, 20);
107
+ const ahead = destinationPoint({ lat: 47.6, lng: -122.3 }, 90, 5_000);
99
108
  ```
100
109
 
101
110
  ### Materialize a Block
@@ -329,20 +329,43 @@ function scopeContainer(parent, prefix) {
329
329
  // src/storage/MediaProviderFromContainer.ts
330
330
  function createMediaProviderFromContainer(container) {
331
331
  const blobUrlCache = /* @__PURE__ */ new Map();
332
- return {
333
- async resolveUrl(relativePath) {
334
- const cached = blobUrlCache.get(relativePath);
335
- if (cached) return cached;
332
+ const pending = /* @__PURE__ */ new Map();
333
+ const generations = /* @__PURE__ */ new Map();
334
+ let disposed = false;
335
+ function invalidate(path) {
336
+ generations.set(path, (generations.get(path) ?? 0) + 1);
337
+ pending.delete(path);
338
+ const cached = blobUrlCache.get(path);
339
+ if (cached) URL.revokeObjectURL(cached);
340
+ blobUrlCache.delete(path);
341
+ }
342
+ function resolveUrl(relativePath) {
343
+ if (disposed) return Promise.resolve(relativePath);
344
+ const cached = blobUrlCache.get(relativePath);
345
+ if (cached) return Promise.resolve(cached);
346
+ const existing = pending.get(relativePath);
347
+ if (existing) return existing;
348
+ const generation = generations.get(relativePath) ?? 0;
349
+ const work = (async () => {
336
350
  const data = await container.readFile(relativePath);
351
+ const entries = data ? await container.listFiles() : [];
352
+ if (disposed) return relativePath;
353
+ if ((generations.get(relativePath) ?? 0) !== generation) return resolveUrl(relativePath);
337
354
  if (!data) return relativePath;
338
- const entries = await container.listFiles();
339
- const entry = entries.find((e) => e.path === relativePath);
340
- const mimeType = entry?.mimeType ?? "application/octet-stream";
341
- const blob = new Blob([data], { type: mimeType });
342
- const url = URL.createObjectURL(blob);
355
+ const mimeType = entries.find((e) => e.path === relativePath)?.mimeType;
356
+ const url = URL.createObjectURL(
357
+ new Blob([data], { type: mimeType ?? "application/octet-stream" })
358
+ );
343
359
  blobUrlCache.set(relativePath, url);
344
360
  return url;
345
- },
361
+ })().finally(() => {
362
+ if (pending.get(relativePath) === work) pending.delete(relativePath);
363
+ });
364
+ pending.set(relativePath, work);
365
+ return work;
366
+ }
367
+ return {
368
+ resolveUrl,
346
369
  async listMedia() {
347
370
  const entries = await container.listFiles();
348
371
  return entries.filter((e) => !e.path.toLowerCase().endsWith(".md")).map((e) => ({
@@ -352,11 +375,7 @@ function createMediaProviderFromContainer(container) {
352
375
  }));
353
376
  },
354
377
  async addMedia(name, data, mimeType) {
355
- const cached = blobUrlCache.get(name);
356
- if (cached) {
357
- URL.revokeObjectURL(cached);
358
- blobUrlCache.delete(name);
359
- }
378
+ invalidate(name);
360
379
  let buffer;
361
380
  if (data instanceof Blob) {
362
381
  buffer = await data.arrayBuffer();
@@ -364,17 +383,17 @@ function createMediaProviderFromContainer(container) {
364
383
  buffer = data;
365
384
  }
366
385
  await container.writeFile(name, buffer, mimeType);
386
+ invalidate(name);
367
387
  return name;
368
388
  },
369
389
  async removeMedia(relativePath) {
370
- const cached = blobUrlCache.get(relativePath);
371
- if (cached) {
372
- URL.revokeObjectURL(cached);
373
- blobUrlCache.delete(relativePath);
374
- }
390
+ invalidate(relativePath);
375
391
  await container.removeFile(relativePath);
392
+ invalidate(relativePath);
376
393
  },
377
394
  dispose() {
395
+ disposed = true;
396
+ pending.clear();
378
397
  for (const url of blobUrlCache.values()) {
379
398
  URL.revokeObjectURL(url);
380
399
  }
@@ -109,6 +109,53 @@ function geohashOverlapsBounds(hash, bounds) {
109
109
  return !(hashNorth < bounds.south || hashSouth > bounds.north || hashEast < bounds.west || hashWest > bounds.east);
110
110
  }
111
111
 
112
+ // src/spatial/GreatCircle.ts
113
+ var DEG2RAD = Math.PI / 180;
114
+ var RAD2DEG = 180 / Math.PI;
115
+ var EARTH_RADIUS_METERS = 6371e3;
116
+ function interpolateGreatCircle(from, to, fraction) {
117
+ const lat1 = from.lat * DEG2RAD;
118
+ const lon1 = from.lng * DEG2RAD;
119
+ const lat2 = to.lat * DEG2RAD;
120
+ const lon2 = to.lng * DEG2RAD;
121
+ const dLat = lat2 - lat1;
122
+ const dLon = lon2 - lon1;
123
+ const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2;
124
+ const delta = 2 * Math.asin(Math.min(1, Math.sqrt(a)));
125
+ if (delta === 0) return { lat: from.lat, lng: from.lng };
126
+ const A = Math.sin((1 - fraction) * delta) / Math.sin(delta);
127
+ const B = Math.sin(fraction * delta) / Math.sin(delta);
128
+ const x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2);
129
+ const y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2);
130
+ const z = A * Math.sin(lat1) + B * Math.sin(lat2);
131
+ const lat = Math.atan2(z, Math.sqrt(x * x + y * y));
132
+ const lon = Math.atan2(y, x);
133
+ return { lat: lat * RAD2DEG, lng: lon * RAD2DEG };
134
+ }
135
+ function sampleGreatCircle(from, to, samples) {
136
+ const n = Math.max(2, Math.floor(samples));
137
+ const points = [];
138
+ for (let i = 0; i < n; i++) {
139
+ points.push(interpolateGreatCircle(from, to, i / (n - 1)));
140
+ }
141
+ return points;
142
+ }
143
+ function destinationPoint(from, bearingDeg, distanceMeters) {
144
+ const delta = distanceMeters / EARTH_RADIUS_METERS;
145
+ const theta = bearingDeg * DEG2RAD;
146
+ const lat1 = from.lat * DEG2RAD;
147
+ const lon1 = from.lng * DEG2RAD;
148
+ const lat2 = Math.asin(
149
+ Math.sin(lat1) * Math.cos(delta) + Math.cos(lat1) * Math.sin(delta) * Math.cos(theta)
150
+ );
151
+ const lon2 = lon1 + Math.atan2(
152
+ Math.sin(theta) * Math.sin(delta) * Math.cos(lat1),
153
+ Math.cos(delta) - Math.sin(lat1) * Math.sin(lat2)
154
+ );
155
+ const lng = ((lon2 * RAD2DEG + 180) % 360 + 360) % 360 - 180;
156
+ return { lat: lat2 * RAD2DEG, lng };
157
+ }
158
+
112
159
  export {
113
160
  haversineDistance,
114
161
  calculateBearing,
@@ -119,5 +166,8 @@ export {
119
166
  getGeohashPrefix,
120
167
  geohashToHierarchicalPath,
121
168
  getGeohashPath,
122
- geohashOverlapsBounds
169
+ geohashOverlapsBounds,
170
+ interpolateGreatCircle,
171
+ sampleGreatCircle,
172
+ destinationPoint
123
173
  };
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export { M as MediaEntry, a as MediaProvider } from './MediaProvider-wpSe21B3.js
7
7
  export { E as EditorLayerMeta, I as ImageEditCanvas, a as ImageEditDoc, b as ImageEditLayer, c as ImageEditLayerKind, d as ImageEditMeta } from './ImageEditDoc-CYRQK8so.js';
8
8
  export { ASCII_CHAR_H, ASCII_CHAR_W, ASCII_DIAGRAM_FENCE_LANGS, ASCII_TIMELINE_FENCE_LANGS, AccentLayout, AsciiDiagram, AsciiDiagramDetection, AsciiDiagramEdge, AsciiDiagramNode, AsciiTimeline, AsciiTimelineDetection, AsciiTimelineEvent, AsciiTimelineLink, AsciiTimelineMarker, AsciiTimelineSide, AsciiTimelineStats, AsciiTimelineStyle, AsciiTimelineTrack, AudioSegmentTiming, BASE_INPUT_DESCRIPTORS, BLOCK_MEDIA_LAYOUT_POLICIES, BUILTIN_DASHBOARD_LAYOUTS, BlockLayerMaterialization, BlockMediaLayoutPolicy, BuildPreviewDocOptions, BuiltInTemplateName, CONTAINER_TEMPLATES, COVER_SLIDE_FRONTMATTER_KEYS, COVER_SLIDE_TEMPLATE_OPTIONS, ClipBox, ConnectorAnchor, ConnectorPort, ConnectorRouting, ConnectorSnapPoint, CoverBlockInput, CoverSlidePlayback, CoverSlideSettings, CoverSlideTemplate, CoverSlideTemplateOption, DASHBOARD_AUTO_LAYOUT_ID, DASHBOARD_FRONTMATTER_KEYS, DASHBOARD_STYLES, DASHBOARD_STYLE_IDS, DASHBOARD_ZOOM_LEVELS, DATA_FILE_EXTENSIONS, DEFAULT_COVER_SLIDE_SETTINGS, DEFAULT_DASHBOARD_SETTINGS, DEFAULT_DASHBOARD_STYLE, DEFAULT_LAYOUT, DEFAULT_WORD_STAGGER, DIAGRAM_LABEL_HORIZONTAL_PADDING, DIAGRAM_LABEL_LINE_HEIGHT, DIAGRAM_LABEL_MIN_FONT_SIZE, DIAGRAM_LABEL_VERTICAL_PADDING, DashboardCell, DashboardCellChrome, DashboardCellDefinition, DashboardCellFrame, DashboardDiagnostic, DashboardLayoutDefinition, DashboardLayoutSummary, DashboardLayoutValidationError, DashboardLayoutValidationResult, DashboardMaterialization, DashboardRectPct, DashboardSettings, DashboardSettingsOverrides, DashboardStyleId, DashboardStyleSummary, DashboardTitle, DashboardTitleSlotDefinition, DashboardZoomCandidate, DashboardZoomLevel, DashboardZoomMode, DataFenceParseResult, DataSourceReadOptions, DataSourceReader, DataSourceStats, DataSourceTable, DeriveTemplateInputsOptions, DetectAsciiTimelineOptions, DiagramEdge, DiagramLabelFit, DiagramLayout, DiagramLayoutOptions, DiagramNodePosition, DrawingConnector, DrawingLayout, DrawingLayoutOptions, DrawingShape, DrawingShapeKind, EmbeddedVideo, ExpandDocBlocksOptions, ExtractedTableData, FRONTMATTER_DASHBOARD_LAYOUTS_KEY, FirstImage, Flashcard, FlashcardChoice, FlashcardDeck, FlashcardDiagnostic, FlashcardDiagnosticCode, FlashcardFace, FlashcardKind, FlashcardSourceMode, GEOMETRY_BOUND_TEMPLATES, InputCoercion, LayerMaterializationDiagnostic, LayerMaterializationFailureMode, LayerMaterializationSource, LayoutLayerDefaults, LayoutLayersResult, MAX_COVER_SLIDE_DURATION_SECONDS, MarkdownToDocOptions, MarkdownValidationResult, MaterializeBlockLayersOptions, MaterializeDashboardOptions, MaterializeFlashcardsOptions, NarrationResolution, NativeMediaLayout, NoMediaLayout, PAGE_BASE_CSS, PATH_SHAPE_KINDS, PageSectionContext, PageSectionDraft, ParsedStat, RenderAsciiDiagramOptions, RenderAsciiTimelineOptions, RenderTreeOptions, RepairResult, ResolveDataReferencesOptions, ResolvedDashboardCell, ResolvedDataReferences, ResolvedPageBlock, RichListItem, RuntimeTemplateRegistry, SHAPE_NAMES, SectionExtractor, SupplementalMediaLayoutVariant, SupplementalMediaShape, SupplementalMediaVariantMatrix, TABLE_FED_TEMPLATES, TEMPLATE_AUTHORING_METADATA, TEMPLATE_INPUT_DESCRIPTORS, TEMPLATE_METADATA, TREE_FENCE_LANGS, TemplateAuthoringMetadata, TemplateAuthoringRole, TemplateBodyPolicy, TemplateInputDescriptor, TemplateMediaOwnership, TemplateMetadata, TemplateParamFinding, Tree, TreeDetection, TreeItem, TreeNode, UnconsumedMediaBehavior, ValidateOptions, adjustY, anchorPoint, applyNarrationTiming, applyRenderStyleToLayers, asciiCellToCanvas, asciiDiagramFromBlocks, asciiDiagramFromTemplateData, asciiDiagramToTemplateData, asciiTimelineToTemplateData, autoTemplatePreservesContent, bigText, buildDashboardCellChrome, buildPageCss, buildPageCssVars, buildPreviewDoc, buildRegistry, canvasToAsciiCell, chooseDashboardLayout, clipEndpoints, clipPoint, coerceTemplateParams, comparisonBar, composeDashboardLayers, computeDiagramLayout, computeDrawingLayout, computeLayoutLayers, connectorPath, contentBlock, countBlocks, countUpSpecFor, coverBlock, createAccentLayers, cssFilterForTreatment, dashboardCanvasFill, dashboardCellAccent, dataSidecarPrefix, dataTable, dateEvent, definitionCard, deriveTemplateInputs, desiredCellZoom, detectAsciiDiagram, detectAsciiTimeline, detectTree, diagramBlock, docToMarkdown, documentTitleFromFileName, drawingBlock, expandCoverBlock, expandDocBlocks, expandPersistentLayers, extractBlockquoteText, extractBodyPlainText, extractEmbeddedVideos, extractFirstEmbeddedVideo, extractFirstImage, extractImages, extractListItems, extractRichListItems, extractTableData, extractTableFromContents, factCard, fallbackBlockLayers, findFirstList, findFirstTable, fitBigTextSize, fitDiagramLabel, flattenBlocks, flattenRenderableBlocks, formatCountUp, formatCountValue, fullBleedQuote, getAccentLayout, getAnimationProgress, getAnimationStyle, getAvailableTemplates, getBlockBodyText, getBlockDepth, getBlockMediaLayoutPolicy, getDashboardLayoutSummaries, getDefaultAnimation, getDefaultAnimationDuration, getMotionProfile, getOverlayOpacity, getPersistentLayersFromTheme, getPinnedBlockMeta, getTemplateHint, getThemeFont, getTransitionClass, hasTemplate, imageWithCaption, isAsciiDiagramFence, isAsciiTimelineFence, isContainerTemplate, isDataFence, isDataFilePath, isEligibleAsciiFenceLang, isEligibleAsciiTimelineFenceLang, isEligibleTreeFenceLang, isExplicitDiagramLang, isExplicitTimelineLang, isExplicitTreeLang, isGeometryBoundTemplate, isRepairableDiagram, isShapeName, isTemplatedPageBlock, isTreeFence, isWrappedFlowTimelineSource, layoutBlock, layoutCapacity, leftFeature, lineStyleDasharray, lintTemplateParams, listBlock, listDashboardLayouts, mapBlock, markdownBlockSeparatorLines, markdownToDoc, markerPath, materializeBlockLayers, materializeDashboard, materializeFlashcards, motionDelay, motionEntrance, motionStateTransform, nearestSnapPoint, normalizeDashboardZoom, normalizeShapeKind, pageStyleDataAttributes, parseAsciiDiagram, parseAsciiTimeline, parseAsciiTimelineWithStats, parseDataFence, parseStatNumber, parseTree, parseWrappedFlowTimeline, parseYamlSubset, photoGrid, pullQuote, quoteBlock, readCustomTemplatesFromFrontmatter, readCustomThemesFromFrontmatter, readDashboardLayoutsFromFrontmatter, renderAsciiDiagram, renderAsciiTimeline, renderMarkdownBlocksHtml, renderTree, repairAsciiDiagram, replaceDataFence, resolveAudioMapping, resolveColorScheme, resolveCoverSlideSettings, resolveDashboardLayoutDefinition, resolveDashboardSettings, resolveDashboardStyleId, resolveDashboardZooms, resolveDataReferences, resolveFlashcardMarker, resolveLayoutCells, resolvePageBlock, resolvePersistentLayers, resolveTemplateName, resolveThemeForDoc, rightFeature, scaleAnimationDuration, scoreTextSimilarity, sectionExtractors, sectionHeader, shapePath, shouldUseShadow, snapEndpoints, snapPoints, statHighlight, stripBlockBackdropLayer, stripsBlockBackdrop, templateRegistry, themeWantsAmbientMotion, themedEntrance, themedFontSize, themedImageTreatment, themedScrim, themedSurfaceGradient, timelineBlock, titleBlock, transformOriginFor, transposeCells, treeBlock, treeFromMarkdownList, treeFromTemplateData, treeToTemplateData, twoColumn, validateDashboardLayoutDefinition, validateMarkdownDoc, validateMarkdownSource, videoPullQuote, videoWithCaption, wordRevealDelay, wrapWithPersistentLayers, writeCustomTemplatesToFrontmatter, writeCustomThemesToFrontmatter, writeDashboardLayoutsToFrontmatter } from './doc/index.js';
9
9
  export { M as MaterializePageSectionOptions, a as MaterializePageSectionsOptions, P as PageMedia, b as PageRichText, c as PageSection, d as PageSectionDiagnostic, e as PageSectionItem, f as PageSectionMaterialization, g as PageSectionSource, h as PageSpatialKind, i as PageTransformHints, m as materializePageSection, j as materializePageSections, r as resolvePageStyle } from './materializePageSection-Db7pABI0.js';
10
- export { calculateBearing, decodeGeohash, encodeGeohash, geohashOverlapsBounds, geohashToHierarchicalPath, getGeohash4Neighbors, getGeohashPath, getGeohashPrefix, getNeighbors, haversineDistance } from './spatial/index.js';
10
+ export { calculateBearing, decodeGeohash, destinationPoint, encodeGeohash, geohashOverlapsBounds, geohashToHierarchicalPath, getGeohash4Neighbors, getGeohashPath, getGeohashPrefix, getNeighbors, haversineDistance, interpolateGreatCircle, sampleGreatCircle } from './spatial/index.js';
11
11
  export { LocalForageAdapter, LocalForageAdapterOptions, LocalStorageAdapter, MemoryStorageAdapter, ScopedContentContainer, StorageAdapter, createMediaProviderFromContainer, scopeContainer } from './storage/index.js';
12
12
  export { C as ContentContainer, a as ContentEntry, M as MemoryContentContainer, f as findDocumentPath } from './ContentContainer-B2w9sUoL.js';
13
13
  export { DEFAULT_INTERACTIVE_RESOURCE_POLICY, DEFAULT_RESOURCE_MAX_BYTES, DEFAULT_RESOURCE_TIMEOUT_MS, DEFAULT_WRAP_WIDTH, FetchResourceBytesOptions, FetchedResource, HtmlPolicy, LOCAL_ONLY_RESOURCE_POLICY, MARKDOWN_SOURCE_TRANSFORMS, MarkdownSourceEdit, MarkdownSourceTransform, MarkdownSourceTransformId, MarkdownSourceTransformOptions, MarkdownSourceTransformResult, MarkdownWrapState, ResourcePolicy, ResourcePolicyError, SanitizeUrlOptions, TrailingAnnotationMatch, UrlKind, applyMarkdownSourceTransform, cleanupMarkdownSource, countNodes, createDocument, detectMarkdownWrapState, extractPlainText, fetchResourceBytes, findNodesByType, formatBlockScalar, formatFrontmatterValue, formatFrontmatterYaml, fromMdast, getChildren, inferDocumentTitle, isResourceUrlAllowed, matchTrailingPandocAttr, matchTrailingTemplateAnnotation, needsQuoting, parseFrontmatter, parseHtmlToNodes, parseMarkdown, parsePandocAttrTokens, plainTextFromInlineHtml, quoteAttrValue, readFrontmatterThemeId, sanitizeHtmlNodes, sanitizeUrl, serializePandocAttributes, setFrontmatterValues, splitFrontmatterBlock, splitKeyValueToken, stringifyHtmlNodes, stringifyMarkdown, toMdast, tokenizeAttrTokens, unquoteAttrValue, unwrapMarkdownSource, walkMarkdownTree, wrapMarkdownSource } from './markdown/index.js';
package/dist/index.js CHANGED
@@ -158,6 +158,7 @@ import {
158
158
  import {
159
159
  calculateBearing,
160
160
  decodeGeohash,
161
+ destinationPoint,
161
162
  encodeGeohash,
162
163
  geohashOverlapsBounds,
163
164
  geohashToHierarchicalPath,
@@ -165,8 +166,10 @@ import {
165
166
  getGeohashPath,
166
167
  getGeohashPrefix,
167
168
  getNeighbors,
168
- haversineDistance
169
- } from "./chunk-R3KTTOMS.js";
169
+ haversineDistance,
170
+ interpolateGreatCircle,
171
+ sampleGreatCircle
172
+ } from "./chunk-JQLUGVII.js";
170
173
  import {
171
174
  BUILTIN_DASHBOARD_LAYOUTS,
172
175
  COVER_SLIDE_FRONTMATTER_KEYS,
@@ -525,7 +528,7 @@ import {
525
528
  createMediaProviderFromContainer,
526
529
  findDocumentPath,
527
530
  scopeContainer
528
- } from "./chunk-UNVHAK3A.js";
531
+ } from "./chunk-GIYWXYCK.js";
529
532
  import {
530
533
  DEFAULT_INTERACTIVE_RESOURCE_POLICY,
531
534
  DEFAULT_RESOURCE_MAX_BYTES,
@@ -844,6 +847,7 @@ export {
844
847
  deriveScale,
845
848
  deriveTemplateInputs,
846
849
  desiredCellZoom,
850
+ destinationPoint,
847
851
  detectAsciiDiagram,
848
852
  detectAsciiTimeline,
849
853
  detectMarkdownWrapState,
@@ -961,6 +965,7 @@ export {
961
965
  inferDocumentTitle,
962
966
  inferSchema,
963
967
  inspectAnimatedImage,
968
+ interpolateGreatCircle,
964
969
  isAsciiDiagramFence,
965
970
  isAsciiTimelineFence,
966
971
  isContainerTemplate,
@@ -1115,6 +1120,7 @@ export {
1115
1120
  revertToImageEditVersion,
1116
1121
  revertToVersion,
1117
1122
  rightFeature,
1123
+ sampleGreatCircle,
1118
1124
  sanitizeHtmlNodes,
1119
1125
  sanitizeUrl,
1120
1126
  saveImageEditVersion,
@@ -113,4 +113,32 @@ declare function geohashOverlapsBounds(hash: string, bounds: {
113
113
  west: number;
114
114
  }): boolean;
115
115
 
116
- export { calculateBearing, decodeGeohash, encodeGeohash, geohashOverlapsBounds, geohashToHierarchicalPath, getGeohash4Neighbors, getGeohashPath, getGeohashPrefix, getNeighbors, haversineDistance };
116
+ /**
117
+ * Great-circle paths on a spherical Earth.
118
+ *
119
+ * `interpolateGreatCircle` and `sampleGreatCircle` walk the shortest path between two
120
+ * coordinates (spherical linear interpolation), which stays true over long and high-latitude
121
+ * routes where a straight lat/lng line distorts badly: use them to sample points along a route,
122
+ * for example to find places near it. `destinationPoint` goes the other way: from a coordinate
123
+ * along a compass bearing for a distance, for example to project a moving position ahead of
124
+ * itself. The sphere is the same one `haversineDistance` uses (mean radius 6,371 km), so the
125
+ * two agree.
126
+ */
127
+
128
+ /**
129
+ * Interpolate a point a `fraction` (0..1) of the way along the great-circle path from `from`
130
+ * to `to`. At fraction 0 returns `from`; at 1 returns `to`.
131
+ */
132
+ declare function interpolateGreatCircle(from: Coordinates, to: Coordinates, fraction: number): Coordinates;
133
+ /**
134
+ * Sample `samples` evenly spaced points (both endpoints included) along the great-circle path.
135
+ * `samples` is clamped to at least 2.
136
+ */
137
+ declare function sampleGreatCircle(from: Coordinates, to: Coordinates, samples: number): Coordinates[];
138
+ /**
139
+ * The point `distanceMeters` from `from` along the great circle that starts at compass bearing
140
+ * `bearingDeg` (0 north, 90 east). Longitude is normalized to [-180, 180).
141
+ */
142
+ declare function destinationPoint(from: Coordinates, bearingDeg: number, distanceMeters: number): Coordinates;
143
+
144
+ export { calculateBearing, decodeGeohash, destinationPoint, encodeGeohash, geohashOverlapsBounds, geohashToHierarchicalPath, getGeohash4Neighbors, getGeohashPath, getGeohashPrefix, getNeighbors, haversineDistance, interpolateGreatCircle, sampleGreatCircle };
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  calculateBearing,
3
3
  decodeGeohash,
4
+ destinationPoint,
4
5
  encodeGeohash,
5
6
  geohashOverlapsBounds,
6
7
  geohashToHierarchicalPath,
@@ -8,11 +9,14 @@ import {
8
9
  getGeohashPath,
9
10
  getGeohashPrefix,
10
11
  getNeighbors,
11
- haversineDistance
12
- } from "../chunk-R3KTTOMS.js";
12
+ haversineDistance,
13
+ interpolateGreatCircle,
14
+ sampleGreatCircle
15
+ } from "../chunk-JQLUGVII.js";
13
16
  export {
14
17
  calculateBearing,
15
18
  decodeGeohash,
19
+ destinationPoint,
16
20
  encodeGeohash,
17
21
  geohashOverlapsBounds,
18
22
  geohashToHierarchicalPath,
@@ -20,5 +24,7 @@ export {
20
24
  getGeohashPath,
21
25
  getGeohashPrefix,
22
26
  getNeighbors,
23
- haversineDistance
27
+ haversineDistance,
28
+ interpolateGreatCircle,
29
+ sampleGreatCircle
24
30
  };
@@ -7,7 +7,7 @@ import {
7
7
  createMediaProviderFromContainer,
8
8
  findDocumentPath,
9
9
  scopeContainer
10
- } from "../chunk-UNVHAK3A.js";
10
+ } from "../chunk-GIYWXYCK.js";
11
11
  export {
12
12
  LocalForageAdapter,
13
13
  LocalStorageAdapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq",
3
- "version": "2.11.11",
3
+ "version": "2.11.12",
4
4
  "description": "Headless utilities for doc/block rendering, spatial math, Markdown, and storage",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",